Skip to content

Deliver native child results on the next Turn - #8670

Open
huangruiteng wants to merge 1 commit into
mainfrom
codex/peer-result-next-turn-inbox-20261113
Open

huangruiteng wants to merge 1 commit into
mainfrom
codex/peer-result-next-turn-inbox-20261113

Conversation

@huangruiteng

Copy link
Copy Markdown
Collaborator

A host-native child could finish successfully while its final text disappeared after the parent Turn: LoopX retained only the typed terminal receipt and discarded the host's result message. This change stores observed final text in an owner-private, Goal-lifetime-scoped inbox before exposing the terminal receipt, then delivers it through the existing manager-inbox hook on the next Turn.

Reading records delivery without consuming the result. The parent must explicitly acknowledge accepted, deferred, or rejected; unacknowledged results replay with a stable ID after restart. Exact ingest and acknowledgement retries are idempotent, conflicting text or dispositions fail closed, large results use digest-verified private file references, and corrupt or truncated scans remain visible instead of starving later results. Disabling future child spawning does not prevent acknowledgement of an already delivered result.

The built-in managed Codex CLI and app-server observers call this contract directly. Other hosts must provide the same live terminal event; persisted Codex history is not treated as a reliable reconstruction source.

Validation:

  • npm run typecheck:control-plane
  • 78 related TypeScript control-plane tests
  • 72 related Python host, CLI, manager-inbox, and receipt tests
  • diff-scoped semantic vocabulary advisory
  • loopx check public-boundary scan over all 13 changed files

Signed-off-by: huangruiteng <huangrt01@163.com>

@loopx-agent loopx-agent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewer: model_agent · gpt-6.1-sol · OpenAI · runtime_reported · reasoning_effort=max

评审提交 8d76419078daaeae1d10070f0bb12cd2dc7c0572,对照主干 167b0a1f3a3307c284b021ea9a09c7c0b5e5a11f。

[P1] 拒绝的终态重放仍会把矛盾正文送进 Inbox。 同一 native wait 先报告 completed,宿主这次未提供 message。恢复原 invocation 后,重放同一 wait,改为 errored 并携带文字。 主干和候选的终态记录均保留 completed,且都拒绝了矛盾重放。候选的下一次 manager-inbox read 却返回了 failed 和这次已拒绝的文字,主干未出现该候选结果。 让正文在原终态约束及身份核验下保存,并只在匹配的终态提交后对 Inbox 可见。处理提交中断时可重用同一待提交正文,冲突记录始终不可见。 先写正文再校验终态

[P2] 2,000 条已确认历史会让新结果长期不可达。 同一 Goal 生命周期内先保留 2,000 条已确认结果,再交付一条哈希文件名排在其后的未确认结果。 两次普通 Inbox 读取都返回零条结果、scan_complete=false 和 has_more=true,外层没有续读游标。仅在对照夹具里移走一条已确认元数据,下一次读回才出现同一个新结果 ID。 提供能越过已确认历史的有界读取路径,保留确认回执和幂等性。可使用可续读游标或由持久索引先选未确认项,保留普通读取的恢复入口。 按文件名截断扫描 Inbox 返回字段

动机 / Motivation

运行托管 Codex 子任务、下一 Turn 要依据其结论继续工作的主 Agent 会遇到这个问题。 子任务已结束,主干留下完成回执,却无法在下一次 Inbox 读取中交回宿主实际观察到的最终文字。 候选版能在正常完成后返回原文和稳定 ID,读取后重启仍可查看,显式确认才停止重放。 同一终态事件的矛盾重放可以进入 Inbox,已有大量已确认历史的父任务也可能一直读不到新结果。 评审运行了隔离的源码 Host adapter、真实进程流解析、落盘和 Inbox CLI。真实模型、安装版和 Lark 未验证。

改动思路 / Approach

托管 exec 和 app-server 从已有连接取得终态事件。原事件流维持操作、准入和终态,新增私有存储保存原文及交付状态。既有 manager-context hook 提醒父 Agent 先读 Inbox,父级用明确 disposition 确认处理。正文可单独保存在私有 runtime,是否能交付仍应由已有终态回执决定。当前先写可见记录,再校验终态,两个读回会互相矛盾。 本 PR 应让同一原始操作的已核对终态与私有正文一起恢复,保留未确认内容的可达性和既有父级验收。

具体改动 / Changes

关键代码讲解

  • CodexNativeChildObserver.observe 用原 wait 绑定关联终态。新分支先保存 message,之后才调用原 recorder 校验冲突。两者之间缺少可见性约束。 宿主分支
  • TypeScript Inbox 把稳定结果 ID、摘要与内容落盘。短文字内联,长文字走摘要验证后的私有文件引用。分离 delivery 与 acknowledgement 让一次读取后的重启仍可复用原 ID。扫描先按哈希文件名截断,再过滤已确认项,历史可以占满整个可扫窗口。 私有存储
  • 现有 manager-inbox read 和 before-work hook 接入同一列表。Python 生成原 Turn 的 ACK 命令,受控交付和普通读取都写本地 delivery。ACK 可在关闭未来 spawn 后使用,原父级验收仍按终态、证据和独立验证执行。 读取与交付 启动读取 确认入口

按 167b0a1f3a3307c284b021ea9a09c7c0b5e5a11f 的 docs/integrations/host-native-child-receipts.md 检查现有生命周期。 terminal-replay-integrity 未满足,矛盾正文可见。 parent-review-boundary 在有效路径保持父级验收独立。 private-readback-scope 的本地 Goal/Agent 隔离和公开回执无正文已覆盖。 主干约束

对主干的风险 / Risks

配对用例经过真实 CLI Host 进程、流解析、持久化和独立的 Inbox 读取,宿主事件由合成进程发出。普通路径中,主干只写完成回执,候选还能读回原文和相同 ID。冲突路径两版都保留唯一 completed 终态、一个 guard 回执和零扣费,候选额外暴露了 failed 内容。规模对照通过同一 CLI 读取,当前说明提示扫描不完整,却没有给下一页的操作入口。

语义与 CI 对齐

110 项聚焦 Python、6 项 TypeScript、控制面 typecheck 和 quick premerge 的 6 项直接检查及 3 项选中检查通过。两项原生宿主 opt-in 测试跳过。托管 CI 未查询。用实际 Host 事件入口重放先无正文、后带正文的冲突终态。保留 completed 回执,下一 Turn 不得读到被拒文字。另测合法补齐和两步提交之间中断后的原身份恢复。 在普通 manager-inbox read 上保留 2,000 条已确认元数据,新增一个确定排序在边界外的结果,按公开返回的下一步读到原 ID 并验证重复确认。

我的整体评价 / Verdict

将结果正文的可见条件收敛到现有权威终态。让长期保留的确认历史和待交付结果在有界读取中都可对账,父任务才能可靠接续。改动集中在 Host adapter、私有存储和 CLI 投递,diff 没有页面组件或可视交互。 评审运行了隔离的源码 Host adapter、真实进程流解析、落盘和 Inbox CLI。真实模型、安装版和 Lark 未验证。

English verdict: REQUEST_CHANGES. The reviewed head exposes text from a rejected terminal replay and can strand new results behind 2,000 acknowledged records. Gate visibility on the matching terminal receipt and provide bounded continuation past acknowledged history.

if operation_id is not None:
message = state.get("message")
if isinstance(message, str):
ingest_native_child_result(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] 拒绝的终态重放仍会把矛盾正文送进 Inbox。 同一 native wait 先报告 completed,宿主这次未提供 message。恢复原 invocation 后,重放同一 wait,改为 errored 并携带文字。 主干和候选的终态记录均保留 completed,且都拒绝了矛盾重放。候选的下一次 manager-inbox read 却返回了 failed 和这次已拒绝的文字,主干未出现该候选结果。 让正文在原终态约束及身份核验下保存,并只在匹配的终态提交后对 Inbox 可见。处理提交中断时可重用同一待提交正文,冲突记录始终不可见。

try { names = (await readdir(source.root)).filter(name => /^ncr_[0-9a-f]{32}\.json$/u.test(name)).sort(); }
catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error; }
const scanComplete = names.length <= MAX_SCAN;
names = names.slice(0, MAX_SCAN);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 2,000 条已确认历史会让新结果长期不可达。 同一 Goal 生命周期内先保留 2,000 条已确认结果,再交付一条哈希文件名排在其后的未确认结果。 两次普通 Inbox 读取都返回零条结果、scan_complete=false 和 has_more=true,外层没有续读游标。仅在对照夹具里移走一条已确认元数据,下一次读回才出现同一个新结果 ID。 提供能越过已确认历史的有界读取路径,保留确认回执和幂等性。可使用可续读游标或由持久索引先选未确认项,保留普通读取的恢复入口。

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants