Repository navigation
fix(chat): recover unfinished Bot requests automatically - #6896
Conversation
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
Signed-off-by: LoopX Agent <337587101+loopx-agent@users.noreply.github.com>
loopx-agent
left a comment
There was a problem hiding this comment.
Reviewer: model_agent; gpt-6.1-sol; OpenAI; runtime_reported; reasoning_effort=max
Approval conclusion (author-owned PR; GitHub blocks formal self-approval)
动机
受影响的是向已绑定的个人 Bot 发送消息、却只收到失败提示的用户。旧实现把“错误提示已经送达”当作扫描结束条件;即使任务没有开始执行,恢复后也不会自动接续。另一个问题是,未来提示词移除重复历史后,已有会话仍保留旧格式,下一条消息继续撞上上下文限制。现在原 Bot 能在确认未执行、原授权和来源仍有效后,通过自己的队列接续原请求。
本片修复默认恢复入口、旧上下文迁移和原消息回传关联,不承诺每个上游故障都能自动重试。真实 Bot 对业务的理解、生产网络可用性与全部积压的最终答复,仍须在安装后的原会话逐项验收。
改动思路
最强的反对理由是:把历史失败直接重跑,可能重复外部操作;只刷新上下文,则仍会让已送达错误提示的请求漏出队列。这里复用已有扫描器、Core 请求、Session 队列和 Inbox 回传,增加有边界的继续执行,而不重开原失败 Turn。
Core 决定继续执行的资格;原生协议只提供已持久化的失败与 item 清单;Lark 适配层读取原消息并发送回复。继续执行需要实际读回:原失败只有一条用户输入,没有工具、回答、纠正或未知 item,相关原生 Turn 都已终止,且原消息正文未被替换。来源、权限或效果不明时保留原记录,不排队。60 秒发现退避只是本地提示,不授予执行权。
各责任沿用现有边界,冻结的继续执行意图和原失败证据不可由送达标记推导。本 PR 的边界是可证明未开始工作的旧格式私有 Codex Steward 请求,以及它使用的默认迁移、排队和回传路径。没有第二消息消费者、全局历史重放或新授权。
具体改动
本次评审针对 49f2fb90a28a5d7c0eb4fa158cbc26698514c408 的完整差异。依据是已有 App conversation / async Inbox RFC 中原来源身份(original-source-identity)、独立送达意图(durable-separate-delivery)和 canonical scanner(canonical-scanner)的约束;新增的中英文 checkpoint 明确本次有界恢复及验收边界。
pending_delivery_paths/reconcile_request让已经送达失败通知、业务仍未完成的请求重新进入现有后台扫描。实际来源读回通过后,Core 冻结一个确定的 continuation id;原失败文件与原通知均保留,最终答复使用独立的terminal.recovery.21送达意图。planContextRecovery/recover_failed_request把资格判断留在既有 Core action。Host 只执行原生读回、来源 IO 与现有 enqueue。预留后、入队后或 transport 指针更新前中断,都按同一确定身份恢复,不能增加第二次执行。failed_request_readback分页读取原生 Turn 和 item;它不恢复、注入或启动模型 Turn。未知效果、非暂时性错误和活跃原生工作均不构成继续执行资格。_ensure_adapter_locked按 context representation 21 通过现有 native history 路径迁移完整可见历史。新 thread、版本及 append-only provenance 一起写入 Session;旧 native Goal 不走此迁移。prepare_turn_context传入已读回原消息和已有答复,要求 Agent 检查剩余工作、保留已完成效果。- 原生 Inbox 的 Python 读 IO 归并回现有 adapter,删除重复 transport 模块并更新全部当前调用和故障注入点;TS 决策 owner 没有移动。消除 base 上已复现的双模块计数与 canonical digest consumer 清单缺口,没有提高 twin budget 或放宽 digest 规则。Session store 的 12 行增长用于检查 append-only provenance,在同一差异明确审阅 ceiling。
执行证据:671 项组合回归通过;后续 170 项 Inbox/原生会话用例通过,搬迁后一个旧 fixture import 失败已修正,该文件 3 项重跑通过。完整 TS 运行原有 1 项 consumer 清单缺口已在 base 独立复现;修正后相关 TS 36 项通过。最终 wheel 的 17 项扫描器、迁移和故障恢复用例通过,13 个变更生产模块/TS 文件与当前提交字节一致,未从源码路径代入。configured mypy、Ruff、TS typecheck、RFC 双语、public boundary 和标准 premerge 均有当前验证。旧版本两个相同恢复用例按预期失败。原生生产协议的只读历史失败查询已实际验证;这些证据没有被写成真实业务补办成功。
对主干的风险
主要风险是把“没有完整回复”误判成“没有任何效果”,或在迁移后丢失旧请求的关联。对应防线是完整原生 item 清单、严格身份匹配、原来源复核、append-only provenance、确定 queue identity 和冻结的独立回传意图。原生 Goal、受 steering 的失败、工具/回答/未知 item、撤销绑定、换正文和当前格式的失败均有拒绝用例;来源不可读时也不会猜测继续执行。
历史迁移覆盖可见用户与 Agent 消息,不声称重建不可见推理。模型能否理解已有业务答复是生产验收边界,提示词和合成 provider 不能证明它。原生控制客户端有界关闭;单条继续执行失败不会无限重跑,也不会删除旧失败和送达记录。未查询或等待远端 CI,依本次本地验证合同执行;安装和原消息最终回传仍需运行维护读回。
我的整体评价
结论是 APPROVE,作为有实际价值的有界增量:把“通知已经发出”与“任务已完成”分开,并让正常服务恢复后有默认接续入口。长程工作和用户体验因此改善:符合资格的原请求经过重启仍由原 Bot 继续,用户无需再重发;效果或权限不明的请求保留,不被隐式重放。
各责任沿用现有边界,冻结的继续执行意图和原失败证据不可由送达标记推导。本 PR 的边界是可证明未开始工作的旧格式私有 Codex Steward 请求,以及它使用的默认迁移、排队和回传路径。最小替代方案仅改提示词或仅扫描失败通知,都不能覆盖两个已复现缺陷;独立常驻补办器则会重复队列与权限职责,因此未采用。生产采用和每项原请求的真正最终答复仍须独立核验,不从测试通过、已读或失败通知推断完成。
English verdict: APPROVE — exact head 49f2fb90a28a5d7c0eb4fa158cbc26698514c408. The existing Bot scanner can continue a strictly verified, pre-execution legacy failure through its original queue and delivery path, preserving the old failure and native provenance. Source, packaged-scanner, migration, refusal and crash tests establish the bounded implementation; live business catch-up remains separate operational acceptance. No remote CI was consulted.
Private Lark requests could receive a verified failure notice and then disappear from the reply scanner, even though their business work had never started. Removing transcript duplication from future prompts also left existing native conversations stuck on their old, overfull context.
The existing scanner now admits one correlated continuation for a legacy private Codex Steward request only after it rechecks the original source, current binding and native persisted failure inventory. The old failed Turn and its notice remain intact. The continuation uses the existing Session queue and a distinct, source-bound final delivery phase. Unknown effects, steering, changed sources, revoked bindings and native Goal conversations remain held.
Context representation 21 migrates the complete visible history through the existing native-history restore path and retains prior native-thread provenance. Recovery guidance requires checking actual replies and completed work before acting. There is no second message consumer or new execution grant.
Validation covers background-scanner recovery, crash windows, duplicate delivery, full-history migration and refusal cases; the same positive recovery cases reproduce the failures on the base revision. Production native metadata/item reads were also checked without starting a model Turn. The source tests use synthetic providers; installation and real Bot catch-up are separate operational acceptance steps.
Final integration qualification also consolidates the unchanged Python Inbox read transport into its existing adapter, pins the actual canonical digest consumer, and explicitly settles the 12-line append-only Session provenance validation growth. The two Inbox qualification gaps reproduce on the base revision; no semantic twin budget or identity rule is widened.