DSH / Atlas
2026-08-10implementedarchitecture

Forked children preserve the parent request prefix

Fork child 保留 parent 请求前缀

Fork differs from spawn by seeding the child Session with the parent's completed-turn prefix. That seed costs tokens, and its intended payoff is provider-side prefix reuse: under the same provider and model, a child request whose leading bytes match the parent's does not prefill the shared span again. A child-only system-prompt section or tool schema ahead of the inherited history defeats that payoff. The earlier shi

English

Problem

Fork differs from spawn by seeding the child Session with the parent's completed-turn prefix. That seed costs tokens, and its intended payoff is provider-side prefix reuse: under the same provider and model, a child request whose leading bytes match the parent's does not prefill the shared span again. A child-only system-prompt section or tool schema ahead of the inherited history defeats that payoff.

The earlier shipped composition avoided this mismatch by keeping forked children one-shot. That restriction was a consequence of the former child-only return tool, not an intrinsic property of continuable fork.

Decision

The shared activation entry point, external execution ownership, and caller-versus-parent result delivery are governed by Unified subagent activations. This record retains the independent rationale described below.

The model-facing send_message tool is registered globally for every Agent in a composition. A continuable forked child therefore receives the same tool name, description, schema, and ordering as its parent. Its initial task is appended after the inherited Session seed, and the task includes the direct parent id plus guidance to return results with send_message({ agent_id, message }) when that tool is visible to the child.

The base bundle and the cordis, standard, and ptc CLI presets bind fork to the continuable lifecycle. Base-backed profiles, including headless, inherit that default. ForkInProcessProvider.prepareContinuable() and ctx.subagents.startActivation() preserve the same request-prefix behavior across these compositions.

Byte-identical prefix reuse depends on the assembled inputs. Structured output or generated SDK differences can change the request head. Historical child descriptors can also restore persona or tool restrictions during cold resume; filtering out send_message removes both its schema and return guidance. New fork starts inherit the parent preset without these override fields.

Alternatives considered

Keep every fork one-shot. This preserves the prefix but unnecessarily gives up durable, multi-turn forked children after the child-only schema difference is gone.

Install a child-only return alias. A recipient-free alias would make child calls shorter, but it would recreate a tool-schema and prompt delta before inherited history and duplicate the adjacent-Agent operation.

Add the return instruction to the system prompt. This would place child-only bytes ahead of inherited messages. Appending it to the initial user task preserves the inherited prefix and keeps the parent id next to the task that needs it.

Ignore a historical child toolFilter on resume. A stored restriction must continue to determine both schema visibility and guidance; silently discarding it changes the restored agent’s behavior.

Consequences

  • Parent and continuable-fork child expose byte-identical ordered tool schemas when they use the same preset and have no child-only structured output or restored historical overrides.
  • The inherited Session seed precedes the child's initial task and return guidance.
  • The base bundle and full CLI presets use local continuable fork for both parent and caller delivery, without a child-only request-head addition.
  • The child can send messages to its direct parent through send_message. Parent delivery adds a separate runtime settlement notice; caller delivery returns the result through the activation receipt.
  • Keyless snapshots and package tests pin schema equality, inherited-history ordering, parent-id guidance, and child-to-parent delivery through the same send_message operation used in the other direction.

Accepted risks

Provider-side prefix reuse still depends on the selected provider and model and matching assembled inputs. The harness tests equality of its assembled request-head inputs, not a provider’s cache behavior.

中文

问题

fork 与 spawn 的差异在于:fork 会用 parent 已完成轮次的前缀作为 child Session 的种子。该种子会消耗 token,其预期收益是提供方侧的前缀复用:使用相同提供方和模型时,如果 child 请求的开头字节与 parent 相同,共享区段就无需再次预填充。任何位于继承历史之前、仅属于 child 的系统提示词 section 或工具 schema 都会破坏这项收益。

先前的随附组合通过把 fork child 保持为 one-shot 来避开这种不匹配。该限制源于原来的 child-only 返回工具,并非可继续 fork 的固有属性。

决策

共享 activation 入口、外部执行所有权,以及调用方与父级之间的结果投递选择由统一 subagent activation 决策拥有。本记录保留下述独立理由。

面向模型的 send_message 工具在组合中的每个 Agent 上全局注册。因此,可继续 fork child 获得与 parent 相同的工具名称、描述、schema 和顺序。其初始任务追加在继承的 Session 种子之后;当 child 可以看到该工具时,任务还会包含直接 parent id,以及使用 send_message({ agent_id, message }) 返回结果的指引。

base 组合包与 cordis、standard 和 ptc CLI preset 将 fork 绑定为可继续生命周期。基于 base 的 profile(包括 headless)继承这一默认值。ForkInProcessProvider.prepareContinuable() 与 ctx.subagents.startActivation() 在这些组合中保持相同的请求前缀行为。

逐字节相同的前缀复用取决于组装输入。结构化输出或生成 SDK 差异可以改变请求头。历史子级 descriptor 也可在冷恢复时恢复 persona 或工具限制;过滤掉 send_message 会同时移除其 schema 和返回指引。新 fork 启动继承父级 preset,不含这些覆盖字段。

考虑过的替代方案

让所有 fork 保持 one-shot。 这能保留前缀,但 child-only schema 差异消失后,继续放弃持久且多轮的 fork child 已无必要。

安装 child-only 返回别名。 无需填写接收方的别名可以缩短 child 调用,但会在继承历史之前重新产生工具 schema 与提示词差异,并重复相邻 Agent 操作。

把返回指令放进系统提示词。 这会在继承消息之前加入 child-only 字节。将其追加到初始用户任务,可以保留继承前缀,并让 parent id 紧邻需要它的任务。

恢复时忽略历史子级 toolFilter。 存储的限制必须继续决定 schema 可见性和指引;静默丢弃它会改变恢复 agent 的行为。

后果

  • 使用相同 preset 且没有子级专属结构化输出或恢复的历史覆盖时,父级与可继续的 fork 子级公开逐字节相同、顺序一致的工具 schema。
  • 继承的 Session 种子位于 child 初始任务及其返回指引之前。
  • base 组合包与完整 CLI preset 对 parent 和 caller 投递均使用本地可继续 fork,不增加 child-only 请求头字段。
  • child 可以通过 send_message 向直接 parent 发消息。parent 投递会追加独立的运行时结算通知;caller 投递通过 activation 回执返回结果。
  • keyless snapshot 与包测试固定 schema 相等性、继承历史顺序、parent-id 指引,以及通过同一个 send_message 操作完成的 child-to-parent 投递。

已接受风险

提供方侧前缀复用仍取决于所选提供方和模型,以及组装输入是否相同。harness 测试的是自身组装请求头输入相等,而非提供方的缓存行为。