fix(app): preserve readable conversation history and recover without replay - #5328
huangruiteng wants to merge 4 commits into
Conversation
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com>
huangruiteng
left a comment
There was a problem hiding this comment.
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
Reviewed full PR at 2a0f224259be1a160991d01adb4eccd4f0488a01, against base 649826221289cd4cb3dd8880d016e0afbbaca0fc. 没有未解决的阻塞项。结论覆盖历史恢复这一可独立验收的增量;不把读取恢复视为原生委派、接收方采用或完整 GQ09 已通过。
动机
普通任务是“接着昨天的做”。旧版对所有 Session 使用 Promise.all:一个旧记录返回 503,当前可读消息也不显示,界面还可能呈现空会话。不可用的读取应当明确显示,不能成为重放任务或另建驱动的理由。此次改动使已有对话、结果位置和草稿在这种失败中可继续使用,直接改善 R1 恢复路径。
改动思路
最小的 Promise.allSettled 替换只能保住初次读取,仍缺少缺失记录恢复、实时消息去重和当前执行器的发送判定。采用现有 Chat HTTP/file store、reconcileConversationReturns 和会话恢复入口;新 useConversationHistory 只是管家与 Goal 共用的 TS 读取 owner。普通历史重读和有回传义务的轮询保持不同触发条件,但共享同一消息身份和合并规则。没有新增 capability、权限、收件箱、lease、模型调用或并行 Python 决策源。
具体改动
关键代码讲解
chat.ts:789的fetchChatHistory保留可读快照,单次读取有超时,退避重读仅覆盖缺失项;消息身份从单独 message ID 改为 Session+message ID。现有真实 HTTP 列表按 channel/Goal/Agent 过滤,无展示截断被误当完整来源的问题。use-conversation-history.ts:14的useConversationHistory在当前选择的执行器上判定记录是否可读;更“新”的其他执行器 Session 不能绕过等待。旧记录恢复只补历史,不重新连接当前流。作用域切换取消旧写回,成功完整读取不持续轮询。conversation-returns.ts:64的reconcileConversationHistory/reconcileConversationReturns保留实时文本、缺失时的最后回传状态和原始位置;同一 Turn 的用户消息与答复分别绑定。Dashboard 给已接受消息和状态投影写入准确身份,导航回来不产生重复消息。personal-workspace-page.tsx:1574的PersonalWorkspacePage.sendMessage与按钮共用发送判定,Enter 也不能绕过;草稿保持,恢复后在原 Session 提交一次。中英提示、单个重试按钮和现有主题样式都放在输入区旁,桌面与窄屏已实际渲染。- Python
chat_server.py:614的_record_projection_exchange返回现有写入产生的两个 message ID,仍是原 v1 的附加字段,未增加副作用;真实服务器已逐项对照落盘消息。registry-I/O census 只修正该文件引起的两个行号变化,分类未改。 - 单元、真实 HTTP/file-store 和已有 packaged 浏览器框架补上负例;现有 fixture 的省略执行器行为按实际 host 默认值纠正,全套 25 场景通过。RFC 将共享读取恢复及其验收边界写清。
对主干的风险
默认变化是:当前已列出的 Session 无法读取时,普通发送等待原记录恢复;旧记录不可读而当前可读时仍能发送。这是明确的正确性条件,已在 RFC、PR 和负例披露。重试会产生 GET,按 3–30 秒退避,没有任务重放;持续存储故障仍会保持等待,需要原读取来源恢复。
反向风险也验证了:只坏旧记录不应过度阻塞;只坏当前记录不能被别的执行器或 Enter 绕过;恢复不丢草稿、不重复请求;实时答复和状态投影均能跨导航保留正确身份。真正的存储故障通过隔离 Chat HTTP/file backend 验证;浏览器的执行答复为脚本化 fixture,因此不证明 live provider 或接收方实际采用。
验证包括 strict TS/打包、回传单元测试、真实 Chat server、105 项现有 Chat 回归、25 个 packaged 浏览器场景,以及相同 harness 的 base/head 反证:base 在真实 GET 和可读当前消息的浏览器断言上均失败,head 通过。最终 head 的 16 项 selected+5 项 direct premerge 检查全部通过,无失败、超时或跳过;首次 premerge 发现的 census 行号过期已用现有生成器修正。功能验证在 16d055fa6 完成;之后唯一源码差异是这两个 census 行号,最终 head 再跑原生检查和 exact-scope quality verify。未查询或等待远端 CI。
语义与 CI 对齐
沿用现有 Chat Session、Turn 和回传位置词汇,读取状态是 TS 派生投影。新增 message ID 来自已有落盘记录,不构成新的授权或协作协议。原生检查采用当前仓库本地要求,UI 另外用 strict TS、真实 HTTP 和 packaged 交互补足;没有把远端 CI 当作评审依据。
我的整体评价
APPROVE,完整交付历史恢复这一范围。 相比只吞掉异常,这个增量让用户能看见已有工作、保留输入、恢复后继续一次,且通用 TS owner 可用于管家与 Goal。相关 #5293 的原始回传定位契约继续复用;本 PR 没有借机重写委派或协调。未来扩展仍围绕该读取边界,不需要复制一个 manager 专用实现。
最强的剩余验证是安装后的原生负责人接收与采用闭环,本 PR 未声称完成它;持续性规模与损坏的 Session 索引也不在当前 HTTP 读取故障范围内。界面证据使用 PR 中的合成 before/after、窄屏和当前记录等待截图。依照仓库规则,运行时/产品变更留给维护者合并。
English verdict: APPROVE - HEAD 2a0f224 preserves readable history and exact message lineage, retries reads without replay, and resumes the original Session once. Real HTTP/store, packaged negative paths, baseline counterfactuals and final-head premerge passed; native adoption and installed rollout remain unclaimed.
Goal And Delivered Outcome
A temporarily unreadable older Session currently hides the entire App conversation. For “接着昨天的做。” / “Pick up where we left off,” readable messages should remain visible and retain their original result locations.
This change isolates failed history reads in a shared TypeScript boundary used by both steward and Goal conversations. Only missing records retry, with backoff and a visible retry control. If the selected executor’s current Session is unreadable, Send and Enter wait for that record while preserving the draft; recovery continues once in the original Session. Another executor’s readable Session cannot bypass that gate.
main; owning contract: App conversation and async inbox RFC, roadmap R1 recovery.Validation
2a0f224259be1a160991d01adb4eccd4f0488a01. Functional build/browser/runtime checks ran at16d055fa67116f293ecb7746c9aa8eb9a950b521; the final commit only refreshes two source-line locators. Native premerge and census validation ran on final head.npm run build: strict TS, desktop and bundled Chat asset integrity; configured Python mypy and changed-file Ruff; public/private scan and DCO.npm run test:conversation-returns; focused chat CORS, input-validation and active-turn suites (105 passed). Exact session/message IDs and user/answer roles retain distinct records.npm run smoke:conversation-historyagainst disposable real Chat HTTP/file storage: partial read, channel isolation, missing-only retry, unchanged storage and zero Turns.uv run --extra test python examples/loopx-chat-server-smoke.py: projection exchange IDs equal persisted user/answer records.npm run smoke:personal-workspace-packaged: all 25 browser scenarios. Current-session failure blocks Send and Enter, preserves the draft, resumes the exact Session once; streamed and projection-only navigation do not duplicate messages.649826221289cd4cb3dd8880d016e0afbbaca0fc, then pass on head. The injected fault changes reads only. Healthy recovery and existing packaged journeys remain covered.loopx canary premerge --from-git-diff --goal-id GOAL: 16 selected and 5 direct checks; exact-scope quality receipt verified. The first run exposed stale census line locators, corrected with the existing generator and revalidated.Coverage: source packaging and isolated real Chat storage are verified. Live providers, full native delegation/adoption and an installed desktop rollout were not exercised. No model API calls, historical request replays or active Goal state mutations were used for validation.
Frontend / Visual Evidence
Type of Change
LoopX Area
Shared-authority RFC fixture impact
N/A: no authority-store, provider-routing, promotion or compatibility-projection change. This is shared App read reconciliation using the existing Chat store and HTTP contract; the projection response adds stored message IDs without new effects.
Boundary Checklist