@deepseek-ai/dsh-subagent 0.1.5-rc.2 → 0.1.6-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/subagent/subagent/README.md
5
- README.md: 5f9668fd7a14cdfca33ab72a217c07372a24502f
6
- README.zh.md: 563ed06237088c4bc250ca3c4f5a863b6ffe8239
5
+ README.md: 4634f0a33f71709668732b7070a2bccded3db5c0
6
+ README.zh.md: d3d8a597f23153b0c82e663d98142a9966f48c22
package/README.md CHANGED
@@ -42,6 +42,18 @@ Mount the service with a provider and the delegation tool. The provider register
42
42
 
43
43
  An agent that calls the tool gets the child's final answer as the tool result. Mounting the service alone changes nothing: nothing can delegate until a provider and a tool are composed.
44
44
 
45
+ ### Delegation settings
46
+
47
+ The limits section on the **Plugins → Subagent** page edits the Host’s `subagent` settings section. User values override this plugin's composition; reset removes the user override. `maxDepth` defaults to `1` and supplies the delegation tools' depth when their own configuration omits it. An explicit tool depth, including `provider-managed`, takes precedence. Depth `0` disables delegation through tools inheriting this setting; depth `1` permits direct children only. Changes apply on the next delegation attempt. Direct service callers continue to supply their own optional request depth.
48
+
49
+ ### Continuable capacity
50
+
51
+ Set `maxActiveSubagents` on the host `dsh-subagent` plugin to limit live children sharing uninterrupted continuable parent links. It defaults to `8` and accepts positive safe integers. A non-continuable parent starts a separate pool and does not consume a slot; continuable descendants inherit that pool. Fresh creation and cold resume reserve before reconstructing the Agent, and cleanup returns the slot after handle disposal. A waiting parent, pending inbox work, and an Activation being stopped still occupy slots. Messages to a resident child reuse its slot. One-shot and external-provider runs are outside this limit. Pool inheritance does not cross a one-shot parent; its continuable children share a separate pool. Depth remains the delegation tool's separate policy.
52
+
53
+ The current `maxActiveSubagents` value is sampled before every new or cold-resumed Activation. Raising it admits more children in existing trees; lowering it leaves resident children running and refuses further admissions until usage is below the limit.
54
+
55
+ At capacity, creation or cold resume rejects with `ACTIVATION_LIMIT_REACHED` (browser prompts receive `subagent/delivery-unavailable`): wait for a child to finish or continue using the existing agents. Admission does not queue, because a parent waiting for descendants must not wait for its own occupied slot. Slots are process-local and do not constrain cumulative Session history or token usage.
56
+
45
57
  ### One-shot and continuable children
46
58
 
47
59
  One-shot children run once and settle with a single result, plus an optional structured output and a safe diagnostic on failure. A start request may override the child Agent's provider, model, reasoning effort, and output-token limit through `agentOptions`; every requested option requires the provider's matching capability. Continuable children keep a durable session and accept later messages in order: the caller receives a stable child id, sends adjacent-Agent messages, and can interrupt the current turn without destroying the child. The tool row's `backgroundMode` picks the shape (`one-shot` by default, or `continuable` on providers that support it).
@@ -90,7 +102,7 @@ This section explains how the service is built and where the observable behavior
90
102
 
91
103
  ### One-shot flow
92
104
 
93
- A request is validated against the provider's advertised capabilities, a durable descriptor is snapshotted, and the provider builds the child. Both in-process providers advertise `agentOptions`: child creation merges requested fields over the provider, model, and reasoning effort in the parent's latest logged request, falls back to creation options before the first request, and retains the configured token limit. A route change without an explicit effort clears the inherited route-owned effort so the selected model resolves its default. DSH SDK also advertises this capability and publishes immutable `agentRouteDefaults`, which supply its instance provider/model defaults before exact-route preflight; `start()` still owns direct callers and the output cap. ACP, Codex, and Claude Code reject agent-route overrides rather than silently ignoring them. On success the run is published and ownership transfers to the caller; on failure the provider rolls back every unpublished resource. The result carries the child's final output, an optional structured value, a stop reason, and an optional safe diagnostic.
105
+ A request is validated against the provider's advertised capabilities, a durable descriptor is snapshotted, and the provider builds the child. Both in-process providers advertise `agentOptions`: child creation merges requested fields over the provider, model, and reasoning effort in the parent's latest logged request, falls back to creation options before the first request, and retains the configured token limit. They also snapshot delegated permission state before the first await: an Auto or Full access parent gives the child the same `permission/preset` identity, while the existing sandbox override and approval-policy pin continue to apply. Recording both identities prevents an older same-bundle fork value from winning. Auto then reviews every supported child call independently: ordinary project-local work is low risk and allowed, medium-risk work requires explicit action, exact-target and scope authorization from the existing creation prompt or an authenticated human/direct-parent message, without conflicting human limits, while high-risk work is always denied. The reviewer derives that context from `parentSession` and existing messages; delegation adds no parent call metadata, delegation records, review receipt, or Session format. A route change without an explicit effort clears the inherited route-owned effort so the selected model resolves its default. DSH SDK also advertises `agentOptions` but runs a separate child runtime, so it does not inherit Auto; ACP, Codex, and Claude Code likewise retain their own permission systems after the parent delegation call passes review. On success the run is published and ownership transfers to the caller; on failure the provider rolls back every unpublished resource. The result carries the child's final output, an optional structured value, a stop reason, and an optional safe diagnostic.
94
106
 
95
107
  ### Continuable flow
96
108
 
@@ -118,6 +130,7 @@ Read these pages when the package-level contract is not enough. They move from t
118
130
  - [Subagent capability seam](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.md) — the design record for the delegation capability family.
119
131
  - [Continuable subagents](../../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.md) — durable children that accept follow-up turns.
120
132
  - [In-process spawn backend](../subagent-spawn-in-process/README.md) — the simplest provider to compose.
133
+ - [Auto review](../../experimental/auto-review/README.md) — the current-session authorization mode inherited only by in-process DSH children.
121
134
  - [Out-of-process ACP backend](../subagent-acp/README.md) — children with their own runtime over the Agent Client Protocol.
122
135
  - [tool-subagent-control README](../tool-subagent-control/README.md) — the follow-up, interrupt, and listing surface.
123
136
 
@@ -130,11 +143,11 @@ Read these pages when the package-level contract is not enough. They move from t
130
143
 
131
144
  #### What the model sees
132
145
 
133
- One user-role parent message opening with the outcome — `Background subagent <child-id> finished and will do no further work unless you send it more.`, or the matching line for a child that was stopped, ran out of room, declined, or failed — followed by `Its closing message:` and the child's final assistant content, or `It left no closing message.` when it produced none. This runtime-owned notice is distinct from model-authored parent/child messages, which use `sendMessage()` and `AgentMessageSource`; delegation schemas and model controls belong to the Consumer packages.
146
+ One user-role parent message opening with the outcome — `Background subagent <child-id> finished and will do no further work unless you send it more.`, or the matching line for a child that was stopped, ran out of room, declined, or failed — followed by `Its closing message:` and the nonempty text blocks from the child's final assistant output, preserving their content and order. Reasoning and other nontext blocks are excluded; when no nonempty text remains, the notice says `It left no closing message.` This runtime-owned notice is distinct from model-authored parent/child messages, which use `sendMessage()` and `AgentMessageSource`; delegation schemas and model controls belong to the Consumer packages.
134
147
 
135
148
  #### Token effect
136
149
 
137
- One notice per settled Activation in the parent's request, sized by the child's final message. A child that sends its own message and then settles costs the parent both.
150
+ One notice per settled Activation in the parent's request, sized by the child's final text. A child that sends its own message and then settles costs the parent both.
138
151
 
139
152
  #### KV Cache effect
140
153
 
@@ -173,6 +186,7 @@ These limits define when the seam is a poor fit or needs special operational car
173
186
  - **Wake gap during cancellation convergence** — a follow-up accepted after an interrupt signal but before the driver becomes idle stays queued until another waking send.
174
187
  - **Pending injected context retains an Activation** — settlement conservatively treats every Inbox occurrence as unfinished. Context parked after the Agent becomes idle keeps the child and its live ancestors resident until a waking delivery claims it, a queue mutation removes it, or manager teardown discards it.
175
188
  - **Process-local residency** — the Activation inbox and ownership graph do not coordinate two harness processes; concurrent access to one persistence store needs a durable mailbox and cross-process lease protocol.
189
+ - **Saved settlement notices are not rewritten** — a saved user-role notice containing reasoning still fails DeepSeek Messages serialization while it remains in the parent's request history.
176
190
  - **No replay of accepted-but-unlogged messages** — a crash can lose an accepted prompt that never reached the child's session log; the lost message is not replayed automatically.
177
191
  - **No durable parent mailbox** — child-to-parent messages require a resident continuable child and live direct parent, and provide acceptance identity rather than exactly-once delivery.
178
192
  - **Lifecycle events are observe-only** — a run-affecting `subagent/end` continuation or decision API waits for a concrete consumer.
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "面向用户与维护者的 subagent 委派 seam,用于选择提供方后端、组装委派工具或排查子 agent 运行问题。"
2
+ description: "面向用户与维护者的 subagent 委派 seam,用于选择提供方后端、组装委派工具或排查子 agent(智能体)运行问题。"
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 使用 `dsh-subagent` 把工作委派给具名子 agent、收集结果,并跨轮次继续受支持的子级对话。一个组合可以并排提供进程内、ACP、SDK、Codex 或 Claude Code 子级。需要单个结果时选择一次性子级;需要后续消息与中断能力时选择可继续子级。你还可以检查可用子级及其模式、活动状态与血缘,而无需加载或恢复它们。启用时需要至少一个受支持的子级后端和一个委派工具。
12
+ 使用 `dsh-subagent` 把工作委派给具名子 agent、收集结果,并跨轮次继续受支持的子级对话。一个组合可以并排提供进程内、ACP(Agent Client Protocol)、SDK、Codex 或 Claude Code 子级。需要单个结果时选择一次性子级;需要后续消息与中断能力时选择可继续子级。你还可以检查可用子级及其模式、活动状态与谱系,而无需加载或恢复它们。启用时需要至少一个受支持的子级后端和一个委派工具。
13
13
 
14
14
  ## 目录
15
15
 
@@ -42,17 +42,29 @@ kind: "package-reference"
42
42
 
43
43
  调用该工具的 agent 会把子 agent 的最终答案作为工具结果收到。只挂载服务本身不会改变任何行为:在组合出提供方和工具之前,什么都不能委派。
44
44
 
45
+ ### 委派设置
46
+
47
+ **插件 → Subagent** 页面的限制部分编辑 Host 的 `subagent` 设置分节。用户值覆盖本插件的组合配置;恢复默认会删除用户覆盖。`maxDepth` 默认为 `1`,在委派工具自身未配置深度时提供默认值。工具显式指定的深度(包括 `provider-managed`)优先。深度 `0` 禁止继承此设置的工具委派;深度 `1` 只允许直接子代理。修改在下一次委派时生效。直接调用服务的调用方仍自行提供可选的请求深度。
48
+
49
+ ### 可续接子代理容量
50
+
51
+ 在 Host 的 `dsh-subagent` 插件上设置 `maxActiveSubagents`,限制通过连续可续接父子关系共享名额的存活子代理数。默认值为 `8`,接受正安全整数。非可续接父代理建立独立的池,自身不占名额;可续接后代继承该池。新建和冷恢复在重建 Agent 前预占名额,清理在 handle 释放后归还名额。等待后代的父代理、有待处理收件箱内容的代理以及正在停止的 Activation 仍占名额。向驻留子代理发送消息复用其名额。一次性和外部提供方运行不受此限制。池的继承不会跨越一次性父代理;其可续接子代理共享独立的池。深度仍由委派工具的独立策略决定。
52
+
53
+ 每次新建或冷恢复 Activation 前都会读取当前 `maxActiveSubagents`。调高后已有树可接纳更多子代理;调低后驻留子代理继续运行,使用量降至上限以下前拒绝新接纳。
54
+
55
+ 容量耗尽时,新建或冷恢复以 `ACTIVATION_LIMIT_REACHED` 拒绝(浏览器消息返回 `subagent/delivery-unavailable`):等待子代理完成,或继续使用现有代理。接纳不会排队,避免等待后代的父代理又等待自己占用的名额。名额仅存在于当前进程,不限制累计 Session 历史或 token 用量。
56
+
45
57
  ### 一次性与可继续子级
46
58
 
47
- 一次性子 agent 只运行一次,并以单个结果结算,可附带可选的结构化输出与失败时的安全诊断。启动请求可以通过 `agentOptions` 覆盖子 Agent 的提供方、模型、推理等级与输出 token 上限;每个请求的选项都要求提供方声明对应能力。可继续子 agent 保留持久会话并按顺序接受后续消息:调用方收到稳定的子 agent id、发送相邻 Agent 消息,并可中断当前轮次而不销毁子 agent。工具行的 `backgroundMode` 选择形态(默认 `one-shot`,或在支持的提供方上使用 `continuable`)。
59
+ 一次性子 agent 只运行一次,并以单个结果结算,可附带可选的结构化输出与失败时的安全诊断。启动请求可以通过 `agentOptions` 覆盖子 Agent 的提供方、模型、推理强度与输出 token 上限;每个请求的选项都要求提供方声明对应能力。可继续子 agent 保留持久会话并按顺序接受后续消息:调用方收到稳定的子 agent id、发送相邻 Agent 消息,并可中断当前轮次而不销毁子 agent。工具行的 `backgroundMode` 选择形态(默认 `one-shot`,或在支持的提供方上使用 `continuable`)。
48
60
 
49
61
  ### 消息、中断与发现
50
62
 
51
- 每个确切在线 Agent 都可以对直接可继续 child 使用 `sendMessage()`;驻留的可继续 child 还可以对自己的直接 parent 使用它。正在工作的目标通过 Steer 在最近 step 接收 Agent 消息;空闲目标启动轮次,且只有直接 child 可以冷恢复。parent 也可以随时中断正在运行的后代或列举自己的子级。浏览器发出的继续执行 prompt 会独立选择 Queue 或 Steer,并且可以携带图片部分:Host 先通过附件存储完成整批图片的准入与持久化,子级 inbox 才接受这条消息;当子级声明的模型不接受图片输入时拒绝投递。发现覆盖两种形态:服务列举直接子级与完整后代树——模式、活动状态与血缘——直接读取在线会话状态与可选持久化,不加载任何子 agent。
63
+ 每个确切在线 Agent 都可以对直接可继续 child 使用 `sendMessage()`;驻留的可继续 child 还可以对自己的直接 parent 使用它。正在工作的目标通过 Steer 在最近 step 接收 Agent 消息;空闲目标启动轮次,且只有直接 child 可以冷恢复。parent 也可以随时中断正在运行的后代或列举自己的子级。浏览器发出的继续执行提示词会独立选择 Queue 或 Steer,并且可以携带图片部分:Host 先通过附件存储完成整批图片的准入与持久化,子级 inbox 才接受这条消息;当子级声明的模型不接受图片输入时拒绝投递。发现覆盖两种形态:服务列举直接子级与完整后代树——模式、活动状态与谱系——直接读取在线会话状态与可选持久化,不加载任何子 agent。
52
64
 
53
65
  ### 失败与恢复
54
66
 
55
- 需要所选提供方不具备的能力的请求会在启动时响亮失败,而不会被静默忽略。失败的子 agent 运行会返回停止原因,提供方后端还会附加安全诊断;被取消的请求以 `aborted` 结算。子 agent 相互隔离:崩溃或行为异常的子 agent 无法破坏父级会话。
67
+ 需要所选提供方不具备的能力的请求会在启动时明确报错,而不会被静默忽略。失败的子 agent 运行会返回停止原因,提供方后端还会附加安全诊断;被取消的请求以 `aborted` 结算。子 agent 相互隔离:崩溃或行为异常的子 agent 无法破坏父级会话。
56
68
 
57
69
  -----
58
70
 
@@ -90,20 +102,20 @@ kind: "package-reference"
90
102
 
91
103
  ### 一次性流程
92
104
 
93
- 请求先对照提供方声明的能力进行校验,随后对持久化描述符做快照,再由提供方构建子 agent。两个进程内提供方都声明 `agentOptions`:创建子级时把请求字段叠加到父级最新已记录请求的提供方、模型与推理等级之上;父级还没有请求时回退到创建选项,并保留配置的 token 上限。更改路由而不显式指定推理等级时,会清除继承的路由自有等级,使所选模型解析自己的默认值。DSH SDK 也声明该能力并公开不可变的 `agentRouteDefaults`,使其实例持有的提供方/模型默认值在确切路由预检前成为基线;`start()` 仍负责直接调用方与输出上限。ACP、Codex 与 Claude Code 会拒绝 agent 路由覆盖,而不是静默忽略。成功时运行被发布、所有权转移给调用方;失败时提供方回滚每个尚未发布的资源。结果携带子 agent 的最终输出、可选的结构化值、停止原因与可选的安全诊断。
105
+ 请求先对照提供方声明的能力进行校验,随后对持久化描述符做快照,再由提供方构建子 agent。两个进程内提供方都声明 `agentOptions`:创建子级时把请求字段叠加到父级最新已记录请求的提供方、模型与推理强度之上;父级还没有请求时回退到创建选项,并保留配置的 token 上限。它们还会在第一次 await 前快照委派权限状态:Auto 或 Full access 父级让子级获得相同的 `permission/preset` 身份,而既有沙箱覆盖与审批策略固定仍然生效;同时记录这两个身份可防止 fork 中更早的同旋钮组合身份胜出。Auto 随后会独立审查 child 的每个受支持调用:普通项目内工作为低风险并直接允许;中风险工作必须在既有创建 prompt 或已核验的 human/直接父级消息中获得动作、准确目标和范围的明确授权,且不与 human 限制冲突;高风险工作始终拒绝。reviewer 从 `parentSession` 与既有消息派生这份上下文;委派不会新增父 call metadata、委派记录、review receipt 或 Session format。更改路由而不显式指定推理强度时,会清除继承的路由自有强度,使所选模型解析自己的默认值。DSH SDK 也声明 `agentOptions`,但会运行独立子运行时,因此不继承 Auto;ACP、Codex 与 Claude Code 同样在父级委派调用通过审查后保留各自的权限系统。成功时运行被发布、所有权转移给调用方;失败时提供方回滚每个尚未发布的资源。结果携带子 agent 的最终输出、可选的结构化值、停止原因与可选的安全诊断。
94
106
 
95
107
  ### 可继续流程
96
108
 
97
- 管理器预留 child 身份、解析持久化描述符、创建(或冷恢复)child、把它安装进 Activation 并提交提示词。模型编写的消息通过固定 Steer 调度跨一条 parent/child 边;浏览器人类 prompt 通过内部适配器选择 Queue 或 best-effort Steer,其他 host 协议仍可保留 Queue 以创建独立轮次。Session queue command 仅根据 child 自身的 continuable descriptor 准入在线 subagent-owned Agent。Settlement 会等待 Agent 活动结束、Inbox 为空且没有所拥有子级,再在准入开放时 flush 最终 Session 状态。管理器随后在 child lock 内重新验证 wake generation、Session 序号、Inbox 与所拥有子级;`Agent.runMaintenance()` 的同步 task 入口会占用 idle 阶段,并在同一个 JavaScript turn 内关闭私有 subagent Inbox,然后才 dispose handle。直接 child 不存在 Activation 时会从持久化会话冷恢复。当驻留 Activation 结算时,管理器会在 parent 自身的轮次流中告知该 child 的直接 parent。
109
+ 管理器预留 child 身份、解析持久化描述符、创建(或冷恢复)child、把它安装进 Activation 并提交提示词。模型编写的消息通过固定 Steer 调度跨一条 parent/child 边;浏览器人类 prompt 通过内部适配器选择 Queue 或 best-effort Steer,其他 host 协议仍可保留 Queue 以创建独立轮次。Session queue command 仅根据 child 自身的 continuable descriptor 准入在线 subagent-owned Agent。Settlement 会等待 Agent 活动结束、Inbox 为空且没有所拥有子级,再在准入开放时 flush 最终 Session 状态。管理器随后在 child lock 内重新验证 wake generation、Session 序号、Inbox 与所拥有子级;`Agent.runMaintenance()` 的同步 task 入口会占用 idle 阶段,并在同一个 JavaScript turn 内关闭私有 subagent Inbox,然后才释放句柄。直接 child 不存在 Activation 时会从持久化会话冷恢复。当驻留 Activation 结算时,管理器会在 parent 自身的轮次流中告知该 child 的直接 parent。
98
110
 
99
- 本地子级创建成功时,父 Session 追加一条 `subagent/catalog` 事实。一次性创建在 provider 返回后记录;可继续创建在初始 inbox 准入后、返回子级 id 前记录。失败会释放子级,不发布补偿性目录事件。一次性目录追加失败时会处理 run 的结果拒绝,并保留目录错误;资源释放失败会单独记录。`subagentCatalog` projection 排除 fork 继承的事实,通过 Session 观察和客户端快照中的 `projections.values.subagentCatalog` 暴露直接子级列表。无效的自身 catalog payload(包括不支持的版本)会使 projection 恢复失败。其不可变存储和检查点校验使用 [`dsh-chunked-list`](../../util/chunked-list/README.zh.md)。其视图对 D 条事实以 O(D) 时间保留父目录事件顺序。[父目录决策](../../../.agents/notes/implemented/architecture/2026-09-01-parent-owned-subagent-catalog.zh.md) 说明排序、持久化成本和替代方案。
111
+ 本地子级创建成功时,父 Session 追加一条 `subagent/catalog` 事实。一次性创建在提供方返回后记录;可继续创建在初始 inbox 准入后、返回子级 id 前记录。失败会释放子级,不发布补偿性目录事件。一次性目录追加失败时会处理 run 的结果拒绝,并保留目录错误;资源释放失败会单独记录。`subagentCatalog` projection 排除 fork 继承的事实,通过 Session 观察和客户端快照中的 `projections.values.subagentCatalog` 暴露直接子级列表。无效的自身 catalog payload(包括不支持的版本)会使 projection 恢复失败。其不可变存储和检查点校验使用 [`dsh-chunked-list`](../../util/chunked-list/README.zh.md)。其视图对 D 条事实以 O(D) 时间保留父目录事件顺序。[父目录决策](../../../.agents/notes/implemented/architecture/2026-09-01-parent-owned-subagent-catalog.zh.md) 说明排序、持久化成本和替代方案。
100
112
 
101
113
  ### 所有权与不变式
102
114
 
103
115
  - **发布即边界**——发布前提供方拥有设置并须在失败时回滚;发布后调用方拥有运行并须 dispose(资源释放)它。
104
116
  - **注册受 effect 作用域约束**——移除提供方会阻止新启动,但绝不撤销已接受的运行。
105
117
  - **Agent 消息权限基于确切相邻关系**——`sendMessage()` 要求确切在线 sender;每个 sender 都可以指定直接可继续 child,只有具备驻留可继续 Activation 的 sender 可以指定自己的直接 parent。
106
- - **描述符仅进日志**——它是会话事件,不进入模型历史,并跨压缩(compaction)保留;可继续描述符会显式记录解析后的子级提供方、模型与推理等级,用于冷恢复。
118
+ - **描述符仅进日志**——它是会话事件,不进入模型历史,并跨压缩(compaction)保留;可继续描述符会显式记录解析后的子级提供方、模型与推理强度,用于冷恢复。
107
119
 
108
120
  </details>
109
121
 
@@ -118,6 +130,7 @@ kind: "package-reference"
118
130
  - [Subagent 能力 seam](../../../.agents/notes/implemented/feature/2026-06-21-subagent-capability-seam.zh.md)——委派能力家族的设计记录。
119
131
  - [可继续的 subagent](../../../.agents/notes/implemented/feature/2026-07-28-continuable-subagent-conversations.zh.md)——接受后续轮次的持久子级。
120
132
  - [进程内 spawn 后端](../subagent-spawn-in-process/README.zh.md)——最容易组合的提供方。
133
+ - [Auto review](../../experimental/auto-review/README.zh.md)——只有进程内 DSH 子级继承的当前会话授权模式。
121
134
  - [进程外 ACP 后端](../subagent-acp/README.zh.md)——经 Agent Client Protocol 拥有自有运行时的子级。
122
135
  - [tool-subagent-control README](../tool-subagent-control/README.zh.md)——后续消息、中断与列举面。
123
136
 
@@ -130,11 +143,11 @@ kind: "package-reference"
130
143
 
131
144
  #### 模型看到什么
132
145
 
133
- 一条用户角色的父级消息,开头是结果本身——`Background subagent <child-id> finished and will do no further work unless you send it more.`,或子级被停止、耗尽额度、拒绝任务或失败时的对应句子——随后是 `Its closing message:` 与子级的最终 assistant 内容;若子级没有产出内容,则是 `It left no closing message.`。这条由 runtime 生成的通知与模型编写的父子消息相互独立;后者使用 `sendMessage()` 与 `AgentMessageSource`。委派 schema 与模型控制工具归 Consumer 包所有。
146
+ 一条用户角色的父级消息,开头是结果本身——`Background subagent <child-id> finished and will do no further work unless you send it more.`,或子级被停止、耗尽额度、拒绝任务或失败时的对应句子——随后是 `Its closing message:` 与子级最终 assistant 输出中的非空文本块,保留原始内容与顺序。推理与其他非文本块不会进入通知;若没有剩余的非空文本,通知会写明 `It left no closing message.`。这条由运行时生成的通知与模型编写的父子消息相互独立;后者使用 `sendMessage()` 与 `AgentMessageSource`。委派 schema 与模型控制工具归消费方包所有。
134
147
 
135
148
  #### Token 影响
136
149
 
137
- 父级请求中,每个已结算的 Activation 一条通知,长度取决于子级的最终消息。如果子级先发送自己的消息再结算,父级请求会同时承担两者。
150
+ 父级请求中,每个已结算的 Activation 一条通知,长度取决于子级的最终文本。如果子级先发送自己的消息再结算,父级请求会同时承担两者。
138
151
 
139
152
  #### KV Cache 影响
140
153
 
@@ -171,8 +184,9 @@ You are a delegated subagent: your permission scope was fixed when you were star
171
184
  - **仅允许相邻模型消息**——`sendMessage()` 要求确切在线 sender;每个 sender 都可以指定直接可继续 child,只有具备驻留可继续 Activation 的 sender 可以指定自己的直接 parent。浏览器提示使用独立的人类 Queue 或 Steer 控制路径。
172
185
  - **child 到 parent 的投递要求直接 parent 保持在线**——服务没有持久 parent mailbox;parent 缺失时会拒绝消息,而非接受无法唤醒的工作。
173
186
  - **取消收敛期间存在唤醒缺口**——中断信号发出后、driver 进入 idle 前被接受的后续消息会保持排队,直到另一条唤醒发送到达。
174
- - **待处理的注入 context 会保留 Activation**——settlement 会保守地把每个 Inbox occurrence 都视为未完成。Agent 进入 idle 后停放的 context 会让 child 及其在线祖先继续驻留,直到唤醒投递将其 claim、queue 变更将其移除,或 manager teardown 将其丢弃。
187
+ - **待处理的注入上下文会保留 Activation**——settlement 会保守地把每个 Inbox occurrence 都视为未完成。Agent 进入 idle 后停放的上下文会让 child 及其在线祖先继续驻留,直到唤醒投递将其 claim、queue 变更将其移除,或 manager teardown 将其丢弃。
175
188
  - **驻留仅限进程内**——Activation inbox 与所有权图不会在两个 harness 进程之间协调;对单个持久化存储的并发访问需要持久化邮箱与跨进程租约协议。
189
+ - **已保存的结算通知不会被改写**——若用户角色的已保存通知含有推理块,只要它仍在父级请求历史中,DeepSeek Messages 序列化就会失败。
176
190
  - **不回放已接受但未记录的消息**——崩溃可能丢失从未写入子会话日志、已被接受的提示词;丢失的消息不会自动回放。
177
191
  - **没有持久化 parent mailbox**——child 到 parent 的消息要求驻留的可继续 child 与在线直接 parent,提供的是接受标识,不保证恰好一次投递。
178
192
  - **生命周期事件只供观察**——影响运行的 `subagent/end` 延续或决策接口仍需等待具体消费方。
package/lib/index.js CHANGED
@@ -1,9 +1,10 @@
1
+ import z from "@deepseek-ai/schemastery";
1
2
  import { scopeTarget } from "@deepseek-ai/dsh-scope";
2
3
  import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
3
4
  import { canonicalClientTimeZone } from "@deepseek-ai/dsh-util-time";
4
5
  import { Remote, RemoteError, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
5
6
  import { AttachmentError } from "@deepseek-ai/dsh-attachment";
6
- import { z } from "zod";
7
+ import { z as z$1 } from "zod";
7
8
  import { HarnessError, ReasoningEffortId, boundContextSummary, contentHasImage, createUserMessage, errorChain, joinAssistantStreamText } from "@deepseek-ai/dsh-llm";
8
9
  import { randomUUID } from "node:crypto";
9
10
  import { foldConsumedWork } from "@deepseek-ai/dsh-agent";
@@ -35,19 +36,19 @@ var SubagentError = class extends HarnessError {
35
36
  *
36
37
  * @module @deepseek-ai/dsh-subagent
37
38
  */
38
- const SESSION_ID_SCHEMA = z.string().min(1);
39
+ const SESSION_ID_SCHEMA = z$1.string().min(1);
39
40
  const CONTROL_ID_SCHEMAS = {
40
- "subagent.list": z.object({ parentSessionId: SESSION_ID_SCHEMA }),
41
- "subagent.prompt": z.object({
41
+ "subagent.list": z$1.object({ parentSessionId: SESSION_ID_SCHEMA }),
42
+ "subagent.prompt": z$1.object({
42
43
  parentSessionId: SESSION_ID_SCHEMA,
43
44
  childSessionId: SESSION_ID_SCHEMA,
44
- mode: z.literal("continuable"),
45
- delivery: z.enum(["queue", "steer"])
45
+ mode: z$1.literal("continuable"),
46
+ delivery: z$1.enum(["queue", "steer"])
46
47
  }),
47
- "subagent.interrupt": z.object({
48
+ "subagent.interrupt": z$1.object({
48
49
  parentSessionId: SESSION_ID_SCHEMA,
49
50
  childSessionId: SESSION_ID_SCHEMA,
50
- mode: z.literal("continuable")
51
+ mode: z$1.literal("continuable")
51
52
  })
52
53
  };
53
54
  /**
@@ -113,6 +114,7 @@ function rejectPrompt(error, childSessionId, signal) {
113
114
  case "UNAUTHORIZED": throw new RemoteError("subagent/unauthorized", "subagent does not belong to this parent", { childSessionId }, { cause: error });
114
115
  case "DRAINING":
115
116
  case "ACTIVATION_CLOSING":
117
+ case "ACTIVATION_LIMIT_REACHED":
116
118
  case "CONTINUATION_UNAVAILABLE":
117
119
  case "PERSISTENCE_UNAVAILABLE": throw new RemoteError("subagent/delivery-unavailable", "subagent follow-up is temporarily unavailable", { childSessionId }, { cause: error });
118
120
  default: break;
@@ -554,17 +556,20 @@ function applyChildComposition(childCtx, parent, composition) {
554
556
  if (composition.toolFilter !== void 0) childCtx.tools.restrict(composition.toolFilter);
555
557
  }
556
558
  /**
557
- * Capture the policy to seed into one delegation. Call synchronously before
559
+ * Capture the permission state to seed into one delegation. Call synchronously before
558
560
  * the child start's first await: a later parent switch belongs to the
559
- * parent's future, not to this child. Only the parent session's explicit
560
- * sandbox override is captured — never deployment defaults or one-shot
561
- * grants — and the approval policy is pinned to `'never'` regardless of the
562
- * parent's own policy.
561
+ * parent's future, not to this child. Auto and Full access identities are
562
+ * inherited only through the in-process DSH path so either can replace a stale
563
+ * same-bundle fork value. Only the parent session's explicit sandbox override
564
+ * is captured — never deployment defaults or one-shot grants — and the approval
565
+ * policy is pinned to `'never'` regardless of the parent's own policy.
563
566
  * @param parent - the delegating parent agent.
564
567
  * @returns the sandbox override (or `undefined` without one) and the approval pin.
565
568
  */
566
569
  function captureDelegatedPolicyOverrides(parent) {
570
+ const preset = parent.ctx.get("permissionPresets")?.current(parent.session);
567
571
  return {
572
+ permissionPreset: preset === "auto" || preset === "danger-full-access" ? preset : void 0,
568
573
  sandboxMode: parent.ctx.get("sandboxPolicy")?.overrideOf(parent.session),
569
574
  approvalPolicy: parent.ctx.get("approval") === void 0 ? void 0 : "never"
570
575
  };
@@ -587,6 +592,7 @@ function appendDelegatedPolicyOverrides(childSession, overrides) {
587
592
  policy: overrides.approvalPolicy,
588
593
  source: "delegation"
589
594
  });
595
+ if (overrides.permissionPreset !== void 0) childSession.append("permission/preset", { preset: overrides.permissionPreset });
590
596
  }
591
597
  //#endregion
592
598
  //#region lib/types/continuation-messages.js
@@ -653,24 +659,25 @@ function settlementSummary(childId, stopReason) {
653
659
  }
654
660
  }
655
661
  /**
656
- * Build the runtime-owned settlement notice delivered to a child's parent.
662
+ * Build the runtime-owned settlement notice from the child's nonempty closing text.
657
663
  * @param childId - durable child session id named in the notice.
658
664
  * @param terminal - recorded terminal state for the settled Activation.
659
665
  * @returns the durable user-message representation delivered to the parent.
660
666
  */
661
667
  function createSettlementMessage(childId, terminal) {
662
668
  const summary = settlementSummary(childId, terminal.stopReason);
669
+ const closingText = (terminal.output ?? []).flatMap((block) => block.type === "text" && block.text.length > 0 ? [block] : []);
663
670
  return createUserMessage({
664
671
  content: [{
665
672
  type: "text",
666
673
  text: summary
667
- }, ...terminal.output === void 0 ? [{
674
+ }, ...closingText.length === 0 ? [{
668
675
  type: "text",
669
676
  text: "It left no closing message."
670
677
  }] : [{
671
678
  type: "text",
672
679
  text: "Its closing message:"
673
- }, ...terminal.output]],
680
+ }, ...closingText]],
674
681
  source: {
675
682
  kind: "subagent-settled",
676
683
  form: "notice",
@@ -747,6 +754,19 @@ var SubagentInbox = class {
747
754
  *
748
755
  * @module @deepseek-ai/dsh-subagent/continuation-activation
749
756
  */
757
+ /** Process-local slots shared through uninterrupted continuable parent links. */
758
+ var ActivationPool = class {
759
+ slots = /* @__PURE__ */ new Set();
760
+ /** Reserve before reconstruction; the returned release also tolerates unpublished rollback. */
761
+ reserve(capacity) {
762
+ if (this.slots.size >= capacity) throw new SubagentError(`subagent limit reached (active child limit: ${capacity}); wait for an existing child to finish or complete this work with the current agents`, "ACTIVATION_LIMIT_REACHED");
763
+ const slot = Symbol();
764
+ this.slots.add(slot);
765
+ return () => {
766
+ this.slots.delete(slot);
767
+ };
768
+ }
769
+ };
750
770
  /** Serialize each durable child's delivery, release, and disposal. */
751
771
  var ChildLock = class {
752
772
  tails = /* @__PURE__ */ new Map();
@@ -770,8 +790,11 @@ var ChildLock = class {
770
790
  var ContinuableActivationRegistry = class {
771
791
  ctx;
772
792
  observeActivation;
793
+ maxActiveSubagents;
773
794
  /** Child session id → its live Activation. Process-local, never durable. */
774
795
  resident = /* @__PURE__ */ new Map();
796
+ /** Root identities retain their pool across child settlement without retaining dead roots. */
797
+ rootPools = /* @__PURE__ */ new WeakMap();
775
798
  /** Materializations admitted before drain, tracked through publication or rollback. */
776
799
  materializations = /* @__PURE__ */ new Set();
777
800
  /** Per-child serializer shared by delivery, release, and disposal. */
@@ -791,9 +814,10 @@ var ContinuableActivationRegistry = class {
791
814
  * @param ctx - context providing Agents, Sessions, and teardown ownership.
792
815
  * @param observeActivation - build the lifecycle observer for one residency epoch.
793
816
  */
794
- constructor(ctx, observeActivation) {
817
+ constructor(ctx, observeActivation, maxActiveSubagents) {
795
818
  this.ctx = ctx;
796
819
  this.observeActivation = observeActivation;
820
+ this.maxActiveSubagents = maxActiveSubagents;
797
821
  const scope = ctx.plugin(function activationOwner() {});
798
822
  this.ownerCtx = scope.ctx;
799
823
  ctx.on("agent/disposed", ({ agent }) => {
@@ -974,14 +998,20 @@ var ContinuableActivationRegistry = class {
974
998
  */
975
999
  materialize(inputs) {
976
1000
  this.assertAdmitting(inputs.parent);
977
- const settled = Promise.withResolvers();
1001
+ inputs.signal.throwIfAborted();
978
1002
  const lineage = this.liveLineage(inputs.parent);
1003
+ const pool = this.resident.get(inputs.parent.id)?.pool ?? this.rootPool(inputs.parent);
1004
+ const releaseSlot = pool.reserve(this.maxActiveSubagents());
1005
+ const settled = Promise.withResolvers();
979
1006
  const materialization = {
980
1007
  lineage,
981
1008
  settled: settled.promise
982
1009
  };
983
1010
  this.materializations.add(materialization);
984
- return this.materializeTracked(inputs, lineage).finally(() => {
1011
+ return this.materializeTracked(inputs, lineage, pool, releaseSlot).catch((error) => {
1012
+ releaseSlot();
1013
+ throw error;
1014
+ }).finally(() => {
985
1015
  this.materializations.delete(materialization);
986
1016
  settled.resolve();
987
1017
  });
@@ -1056,8 +1086,17 @@ var ContinuableActivationRegistry = class {
1056
1086
  const lineage = this.liveLineage(agent);
1057
1087
  for (const [root, members] of this.closingScopes) if (members.has(agent) || lineage.includes(root)) return root;
1058
1088
  }
1089
+ /** Resolve a root's pool once; descendants inherit their resident parent's pool directly. */
1090
+ rootPool(parent) {
1091
+ let pool = this.rootPools.get(parent);
1092
+ if (pool === void 0) {
1093
+ pool = new ActivationPool();
1094
+ this.rootPools.set(parent, pool);
1095
+ }
1096
+ return pool;
1097
+ }
1059
1098
  /** Perform one tracked materialization through publication or rollback. */
1060
- async materializeTracked(inputs, parentLineage) {
1099
+ async materializeTracked(inputs, parentLineage, pool, releaseSlot) {
1061
1100
  const { childId, provider, parent, create } = inputs;
1062
1101
  inputs.signal.throwIfAborted();
1063
1102
  const setup = (childCtx, child) => {
@@ -1085,6 +1124,8 @@ var ContinuableActivationRegistry = class {
1085
1124
  setup
1086
1125
  });
1087
1126
  const activation = {
1127
+ pool,
1128
+ releaseSlot,
1088
1129
  childId,
1089
1130
  parentSession: parent.id,
1090
1131
  provider,
@@ -1123,6 +1164,7 @@ var ContinuableActivationRegistry = class {
1123
1164
  await activation.handle.dispose();
1124
1165
  } finally {
1125
1166
  this.resident.delete(activation.childId);
1167
+ activation.releaseSlot();
1126
1168
  this.releaseOwnership(activation.childId);
1127
1169
  }
1128
1170
  });
@@ -1236,6 +1278,7 @@ var ContinuableActivationRegistry = class {
1236
1278
  if (failures.length === 1) failure = failures[0];
1237
1279
  else if (failures.length > 1) failure = new SubagentError(`subagent "${childId}" activation teardown failed at ${failures.length} boundaries: ` + failures.map((item) => errorChain(item)).join("; "), "ACTIVATION_TEARDOWN_FAILED", { cause: new AggregateError(failures) });
1238
1280
  this.resident.delete(childId);
1281
+ activation.releaseSlot();
1239
1282
  this.notifySettlement(activation, activation.observer.terminal(failure));
1240
1283
  this.releaseOwnership(childId);
1241
1284
  activation.observer.settle(failure);
@@ -1427,23 +1470,23 @@ function foldSubagentDescriptor(events) {
1427
1470
  if (event === void 0) return void 0;
1428
1471
  return parseSubagentDescriptor(event.data);
1429
1472
  }
1430
- const sessionIdSchema = z.string();
1431
- const oneShotCatalogSchema = z.object({
1432
- version: z.literal(0),
1473
+ const sessionIdSchema = z$1.string();
1474
+ const oneShotCatalogSchema = z$1.object({
1475
+ version: z$1.literal(0),
1433
1476
  childId: sessionIdSchema,
1434
- childCreatedAt: z.number().int().nonnegative(),
1435
- mode: z.literal("one-shot"),
1436
- label: z.string().optional()
1477
+ childCreatedAt: z$1.number().int().nonnegative(),
1478
+ mode: z$1.literal("one-shot"),
1479
+ label: z$1.string().optional()
1437
1480
  }).strict();
1438
- const continuableCatalogSchema = z.object({
1439
- version: z.literal(0),
1481
+ const continuableCatalogSchema = z$1.object({
1482
+ version: z$1.literal(0),
1440
1483
  childId: sessionIdSchema,
1441
- childCreatedAt: z.number().int().nonnegative(),
1442
- mode: z.literal("continuable"),
1443
- label: z.string()
1484
+ childCreatedAt: z$1.number().int().nonnegative(),
1485
+ mode: z$1.literal("continuable"),
1486
+ label: z$1.string()
1444
1487
  }).strict();
1445
- const eventDataSchema = z.union([oneShotCatalogSchema, continuableCatalogSchema]);
1446
- const viewSchema = z.array(z.union([oneShotCatalogSchema.omit({
1488
+ const eventDataSchema = z$1.union([oneShotCatalogSchema, continuableCatalogSchema]);
1489
+ const viewSchema = z$1.array(z$1.union([oneShotCatalogSchema.omit({
1447
1490
  version: true,
1448
1491
  childId: true,
1449
1492
  childCreatedAt: true
@@ -1458,8 +1501,8 @@ const viewSchema = z.array(z.union([oneShotCatalogSchema.omit({
1458
1501
  id: sessionIdSchema,
1459
1502
  createdAt: continuableCatalogSchema.shape.childCreatedAt
1460
1503
  })]));
1461
- const stateSchema = z.object({
1462
- inheritedEventCount: z.number().int().nonnegative(),
1504
+ const stateSchema = z$1.object({
1505
+ inheritedEventCount: z$1.number().int().nonnegative(),
1463
1506
  head: chunkedListSchema(eventDataSchema).optional()
1464
1507
  }).strict();
1465
1508
  /**
@@ -1628,10 +1671,10 @@ var SubagentContinuationManager = class {
1628
1671
  ctx;
1629
1672
  host;
1630
1673
  activations;
1631
- constructor(ctx, host) {
1674
+ constructor(ctx, host, maxActiveSubagents) {
1632
1675
  this.ctx = ctx;
1633
1676
  this.host = host;
1634
- this.activations = new ContinuableActivationRegistry(ctx, (provider, childId, parent) => host.observeActivation(provider, childId, parent));
1677
+ this.activations = new ContinuableActivationRegistry(ctx, (provider, childId, parent) => host.observeActivation(provider, childId, parent), maxActiveSubagents);
1635
1678
  }
1636
1679
  /**
1637
1680
  * Start one continuable background child and resolve at initial inbox acceptance.
@@ -2317,12 +2360,12 @@ function sessionQueryCode(error) {
2317
2360
  *
2318
2361
  * @module @deepseek-ai/dsh-subagent/projection
2319
2362
  */
2320
- const activeIntervalSchema = z.object({
2321
- since: z.number().int().nonnegative(),
2322
- through: z.number().int().nonnegative()
2363
+ const activeIntervalSchema = z$1.object({
2364
+ since: z$1.number().int().nonnegative(),
2365
+ through: z$1.number().int().nonnegative()
2323
2366
  }).strict();
2324
- const projectionSchema = z.object({
2325
- settledMs: z.number().int().nonnegative(),
2367
+ const projectionSchema = z$1.object({
2368
+ settledMs: z$1.number().int().nonnegative(),
2326
2369
  active: activeIntervalSchema.optional()
2327
2370
  }).strict().transform(({ settledMs, active }) => ({
2328
2371
  settledMs,
@@ -2338,11 +2381,11 @@ const projectionSchema = z.object({
2338
2381
  */
2339
2382
  const subagentTimingProjectionDefinition = {
2340
2383
  key: "subagentTiming",
2341
- stateSchema: z.object({
2342
- settledMs: z.number().int().nonnegative(),
2384
+ stateSchema: z$1.object({
2385
+ settledMs: z$1.number().int().nonnegative(),
2343
2386
  active: activeIntervalSchema.optional(),
2344
- pendingTurnStart: z.number().int().nonnegative().optional(),
2345
- descriptorSeen: z.boolean()
2387
+ pendingTurnStart: z$1.number().int().nonnegative().optional(),
2388
+ descriptorSeen: z$1.boolean()
2346
2389
  }).strict(),
2347
2390
  init: () => ({
2348
2391
  descriptorSeen: false,
@@ -2401,17 +2444,17 @@ const subagentTimingProjectionDefinition = {
2401
2444
  },
2402
2445
  stateVersion: 2
2403
2446
  };
2404
- const identityValueSchema = z.discriminatedUnion("mode", [z.object({
2405
- mode: z.literal("one-shot"),
2406
- label: z.string().optional(),
2407
- seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
2408
- }).strict(), z.object({
2409
- mode: z.literal("continuable"),
2410
- label: z.string(),
2411
- seq: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
2447
+ const identityValueSchema = z$1.discriminatedUnion("mode", [z$1.object({
2448
+ mode: z$1.literal("one-shot"),
2449
+ label: z$1.string().optional(),
2450
+ seq: z$1.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
2451
+ }).strict(), z$1.object({
2452
+ mode: z$1.literal("continuable"),
2453
+ label: z$1.string(),
2454
+ seq: z$1.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER).transform(SessionSeq)
2412
2455
  }).strict()]);
2413
2456
  const identitySchema = identityValueSchema.nullable();
2414
- const identityStateSchema = z.object({ identity: identityValueSchema.optional() }).strict();
2457
+ const identityStateSchema = z$1.object({ identity: identityValueSchema.optional() }).strict();
2415
2458
  /** Interpret one `subagent/descriptor` event's identity; no value when the payload cannot be trusted. */
2416
2459
  function descriptorIdentity(event) {
2417
2460
  let descriptor;
@@ -2841,7 +2884,12 @@ let SubagentRuntime = (() => {
2841
2884
  value: _metadata
2842
2885
  });
2843
2886
  }
2844
- providers = (__runInitializers(this, _instanceExtraInitializers), /* @__PURE__ */ new Map());
2887
+ static Config = z.object({
2888
+ maxDepth: z.number().step(1).min(0).max(Number.MAX_SAFE_INTEGER).default(1),
2889
+ maxActiveSubagents: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(8)
2890
+ });
2891
+ settingsSource = __runInitializers(this, _instanceExtraInitializers);
2892
+ providers = /* @__PURE__ */ new Map();
2845
2893
  continuations;
2846
2894
  /**
2847
2895
  * The contained lifecycle-edge publisher. Built here because scoped dispatch
@@ -2849,14 +2897,27 @@ let SubagentRuntime = (() => {
2849
2897
  * composes into the carrier.
2850
2898
  */
2851
2899
  emitLifecycle;
2852
- constructor(ctx) {
2900
+ constructor(ctx, config) {
2853
2901
  super(ctx, "subagents");
2902
+ assertSubagentMaxDepth(config.maxDepth);
2903
+ this.settingsSource = () => config;
2904
+ ctx.inject(["settings"], (settingsCtx) => {
2905
+ settingsCtx.settings.installSection(ctx, "subagent", SubagentRuntime.Config, config, {
2906
+ validate: (value) => {
2907
+ assertSubagentMaxDepth(value.maxDepth);
2908
+ },
2909
+ setSource: (source) => {
2910
+ this.settingsSource = source;
2911
+ },
2912
+ onChange: () => {}
2913
+ });
2914
+ });
2854
2915
  this.emitLifecycle = createLifecycleEmitter(this.ctx, (parent) => scopeTarget(this, parent));
2855
2916
  ctx.inject(["agents"], (childCtx) => {
2856
2917
  const manager = new SubagentContinuationManager(childCtx, {
2857
2918
  prepareContinuable: (name, request) => this.prepareContinuable(name, request),
2858
2919
  observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent)
2859
- });
2920
+ }, () => this.settingsSource().maxActiveSubagents);
2860
2921
  this.continuations = manager;
2861
2922
  childCtx.effect(() => () => {
2862
2923
  /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
@@ -2870,6 +2931,15 @@ let SubagentRuntime = (() => {
2870
2931
  });
2871
2932
  }
2872
2933
  /**
2934
+ * Resolve a delegation tool's depth policy against the current user setting.
2935
+ * @param configured - Explicit tool limit, or provider-managed for external delegation.
2936
+ * @returns The numeric limit, or undefined when the provider owns depth enforcement.
2937
+ */
2938
+ resolveMaxDepth(configured) {
2939
+ if (configured === "provider-managed") return void 0;
2940
+ return configured ?? this.settingsSource().maxDepth;
2941
+ }
2942
+ /**
2873
2943
  * Establish one durable continuable child and deliver its initial prompt.
2874
2944
  * Resolves when the child's inbox accepts that prompt, without waiting for the
2875
2945
  * turn to start or for the message to reach the Session log; any earlier
@@ -2900,12 +2970,12 @@ let SubagentRuntime = (() => {
2900
2970
  }
2901
2971
  /**
2902
2972
  * Deliver one host-protocol message to a direct continuable child.
2903
- * Symbol-keyed so host adapters can preserve their own provenance without
2973
+ * Symbol-keyed so host adapters can preserve their own source descriptors without
2904
2974
  * widening the public Service Definition or impersonating an Agent sender.
2905
2975
  * @param parent - exact live direct parent authorizing delivery.
2906
2976
  * @param childId - durable direct-child session id.
2907
2977
  * @param content - host-authored content to deliver.
2908
- * @param source - durable host-protocol provenance.
2978
+ * @param source - durable host-protocol source descriptor.
2909
2979
  * @param signal - caller cancellation before inbox acceptance.
2910
2980
  * @param delivery - Queue as a distinct turn or Steer at the nearest step.
2911
2981
  * @returns the accepted message's inbox id.
@@ -3029,6 +3099,7 @@ let SubagentRuntime = (() => {
3029
3099
  * nearest step and retains the Agent loop's best-effort fallback semantics.
3030
3100
  * Image parts are admitted and persisted through the attachment store
3031
3101
  * before delivery, and the child's model must accept image input.
3102
+ * Cold resume at capacity rejects with `subagent/delivery-unavailable`.
3032
3103
  * @param request - durable address, delivery, minted identity, content, and optional browser zone.
3033
3104
  * @param signal - carrier cancellation, owning the call until inbox acceptance.
3034
3105
  * @returns the accepted message's inbox identity.