fix: address codex review round 3

- Strict steer additionally requires an OPEN STEP: between steps the
  loop may be awaiting its continuation/turn-stop checkpoints, where
  pending steering was already folded and a terminal stop discards a
  later arrival. A message accepted during an open step is drained and
  recorded at that step's settlement before any terminal decision, so
  the acknowledged-then-discarded window is closed. New keyless test
  holds agent/turn-stop open and pins the rejection.
- tool-subagent-control README: distinguish synchronous not-delivered
  errors from started-Task failures (unknown/foreign/descriptor-less
  ids settle the started Task as failed), and drop the claim that the
  completion notice carries the child's response.
This commit is contained in:
Dudu-0223
2026-07-23 18:47:19 +08:00
committed by imccyu
parent 4eda48d002
commit 9e5ae0d12e
7 changed files with 49 additions and 7 deletions
@@ -49,7 +49,7 @@ durable child Session
每个可继续 child 轮次都通过这条由 Task 支撑的路径准入。非终态 Task 是唯一受支持的存活激活;不存在激活时,其 run 已被 dispose,持久化 child 可以恢复。在路由任何按 id 的操作之前,控制服务会同步将自身关联与 `ctx.agents.get(childId)` 比较。如果注册表中的 Agent 没有关联,或者它与所关联的 `run.localAgent` 不同,就属于所有权冲突:控制服务会失败,而不会接管 idle Agent 或附加未受跟踪的轮次。二者均不存在时,可以从持久化存储恢复;如果检查后又有竞争方发布,仍会在 Agent 注册表的冲突边界上失败。
系统依据 Task 关联进行路由。运行中的 Task 通过 run 可选且严格的 `SubagentRun.steer` 功能接收在线消息。Task 不存在时,系统创建新 Task,并从持久化存储恢复 child。进程内 spawn 和 fork 用与 `Agent.steer()` 调用共享同一同步帧的检查来实现该功能:child 必须处于 `running` 状态,其轮次在日志中必须仍然打开(已关闭轮次的持久化 flush 期间状态仍是 `running`,此时循环会丢弃排空的 steering 消息),且不得已有结构化捕获提交(其终止性 stop 会让循环丢弃迟到的 steering)。提供方不得将 Agent 层的 idle fallback 暴露为严格 steering(中途引导),因为观察到的 run 结束后,该 fallback 可能启动一个未受 Task 跟踪的轮次。如果 Task 在查找关联与执行这项严格检查之间进入结算,`steer()` 会失败,`send_message` 会报告消息未送达,而且该次调用不会改用从持久化存储恢复路径;在 Task 终态发布后重试,才可能启动下一次激活。
系统依据 Task 关联进行路由。运行中的 Task 通过 run 可选且严格的 `SubagentRun.steer` 功能接收在线消息。Task 不存在时,系统创建新 Task,并从持久化存储恢复 child。进程内 spawn 和 fork 用与 `Agent.steer()` 调用共享同一同步帧的检查来实现该功能:child 必须处于 `running` 状态,其轮次在日志中必须仍然打开(已关闭轮次的持久化 flush 期间状态仍是 `running`,此时循环会丢弃排空的 steering 消息),必须有打开的 step(step 之间循环可能停在其 continuation/turn-stop 检查点上,此时 steering 已被折叠,终止性 stop 会丢弃之后到达的消息),且不得已有结构化捕获提交(其终止性 stop 会让循环丢弃迟到的 steering)。提供方不得将 Agent 层的 idle fallback 暴露为严格 steering(中途引导),因为观察到的 run 结束后,该 fallback 可能启动一个未受 Task 跟踪的轮次。如果 Task 在查找关联与执行这项严格检查之间进入结算,`steer()` 会失败,`send_message` 会报告消息未送达,而且该次调用不会改用从持久化存储恢复路径;在 Task 终态发布后重试,才可能启动下一次激活。
控制服务不会串行化两个通过其外部路径同时争抢已停止 child 的调用方,也不会为结果产生与 dispose 之间的阶段单独建立 settling 状态。在 producer 首次 await 之前同步安装的关联,使本进程内每个 child 只准入一次激活——resume 加载期间竞争的 `sendMessage` 会观察到待处理的激活并显式失败——而绕开该关联的发布仍会在 Agent 注册表相同会话的冲突边界上失败。发送也可能因与启动、取消、完成或清理发生竞态而失败。这些限制是明确的,而非隐藏在更大的生命周期抽象之后。