diff --git a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.i18n.yaml index c809de11b4..4312f50471 100644 --- a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.i18n.yaml @@ -6,20 +6,20 @@ en: 4784b23b16ce0d58 zh: 7fb5755eebab37b0 /agent-note-adjacent-agents-share-one-steer-send-message-operation/problem: - en: c988b8a3be4e6f19 - zh: b58011fd8d2e878e + en: 5dc4c218f648c27e + zh: b48531fac94b25db /agent-note-adjacent-agents-share-one-steer-send-message-operation/decision: - en: 666b95a49f9ad05c - zh: 3d5ad7c8986428b8 + en: f4789878ac0e79b2 + zh: 68eb68d34cba810d /agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/one-model-tool-and-one-return-instruction: - en: 4b0a25389324fa91 - zh: c2cdd9f7e40ef43b + en: 62284bd7dd804188 + zh: b3a8dfebffc01bdb /agent-note-adjacent-agents-share-one-steer-send-message-operation/decision/complete-removal-and-reintroduction-condition: - en: 5a9ec6478cf7430c - zh: f4c2f067cc72040e + en: 8c6d355d91ec83d2 + zh: fcc74d1e6ef477bf /agent-note-adjacent-agents-share-one-steer-send-message-operation/alternatives-considered: - en: 1f62d5d938144666 - zh: dec0f30228c5b7a7 + en: 7386e88e7051dce7 + zh: dcea5b9e30374be3 /agent-note-adjacent-agents-share-one-steer-send-message-operation/consequences: - en: d55de74742c3af4b - zh: d894f9a33c8e4691 + en: 9c23dd6ace029ec3 + zh: f08ef3f142534843 diff --git a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md index c1a944bc46..45a36f1428 100644 --- a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md +++ b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.md @@ -10,23 +10,19 @@ The adjacency, discovery, and parent-lifetime restrictions below are superseded Continuable Agents originally used direction-specific model controls. A parent called `send_message({ subagent_id, message })`, which delegated to a FIFO `followup` service operation. A child instead received a child-scoped `report({ output })` tool, a `tool:report` system-prompt section, and deployment-selected quiet or waking delivery. The tools described one adjacent-Agent operation through different schemas, service paths, source attribution, and scheduling. -A continuable child owns its own Session, so its parent does not automatically receive the child's transcript, tool output, or reasoning. The return path must therefore remain explicit and repeatable: a child may send progress before it finishes, remain available after sending, or fail before it can cooperate. Turning every final assistant message into an implicit result would conflate turn completion with model-selected communication and would not cover abnormal endings. +A continuable child owns its own Session, so its parent does not automatically receive the child's transcript, tool output, or reasoning. Explicit messages let a child send actionable findings before finishing. The runtime separately delivers terminal output in a settlement notice when the direct creator is still online. The child-only tool and system-prompt section also preceded every inherited fork turn. They made a continuable fork child's request head differ from its parent's before the history that fork exists to reuse, forcing the provider to prefill the entire copied transcript again. ## Decision -`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` is the only public model-authored message operation. The continuation manager accepts only the exact live sender and a target on one adjacent edge: +`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` is the only public model-authored message operation. The continuation manager accepts only the exact live sender and a target in the same root tree; [tree-wide communication](2026-10-16-tree-wide-agent-communication.md) owns authorization and discovery. -- parent to direct continuable child, authorized by the child's durable `SessionHeader.parentSession`; -- resident continuable child to its exact live direct parent, authorized by the child's Activation; -- a resident continuable child to a continuable sibling of the same live parent, authorized by that parent’s child authority ([subagent teammates](2026-10-15-agent-teams-as-subagents.md)). +Self-targets, stale Agent objects, unknown targets, and external executions are not alternate routes. The operation has no caller-supplied source, delivery mode, offline root mailbox, or provider dispatch. -Self-targets, ancestors beyond one edge, stale Agent objects, unknown targets, and one-shot children are not alternate routes. The operation has no caller-supplied source, delivery mode, offline parent mailbox, or provider dispatch. +Every accepted message uses `Agent.steer()`. A running target receives it at the nearest step boundary; an idle target starts a turn. An absent target is cold-resumed under the root before the same Steer delivery; its creation ancestors can remain offline. The manager retains waking-send accounting so a continuation-managed target cannot settle between synchronous inbox insertion and driver admission. -Every accepted message uses `Agent.steer()`. A running target receives it at the nearest step boundary; an idle target starts a turn. An absent direct child is cold-resumed through the existing continuation lifecycle before the same Steer delivery. The manager retains waking-send accounting so a continuation-managed target cannot settle between synchronous inbox insertion and driver admission. - -Every direction uses one durable source. The service derives `senderSessionId` from the exact live sender and frames content as `Agent sent a message:`, including sibling delivery under the parent’s authority. +Every direction uses one durable source. The service derives `senderSessionId` from the exact live sender and frames content as `Agent sent a message:`. ```ts import type { SessionId } from '@deepseek-ai/dsh-session' @@ -49,15 +45,15 @@ interface SendMessageInput { } ``` -Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. An independently selected preset may omit the tool, a historical descriptor filter may remove it on resume, and a scoped replacement may provide different semantics; neither case receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id and the instruction to send one self-contained result before finishing, plus earlier actionable findings, to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix. +Parents and children inherit the same definition in the same registry order. The standard definition carries a process-stable internal identity that a scoped same-name tool does not satisfy. An independently selected preset may omit the tool, a historical descriptor filter may remove it on resume, and a scoped replacement may provide different semantics; none receives the standard call instruction. When the standard tool remains visible, the continuation manager appends the JSON-encoded direct parent id, automatic final-answer delivery conditions, and guidance to send earlier actionable findings to the child's initial user task. For a fork child this task follows the inherited completed-turn prefix; no child-only system-prompt section or tool schema precedes that prefix. -The instruction is guidance, not settlement enforcement. Sending does not end the child's turn, zero or several calls remain mechanically valid, and the runtime never rejects a child for staying silent. The manager-owned `subagent-settled` notice remains unconditional and separately attributed because it records how an Activation ended and preserves terminal output when the child cannot cooperate. +The instruction is guidance, not settlement enforcement. Sending does not end the child's turn, zero or several calls remain mechanically valid, and the runtime never rejects a child for staying silent. The separately attributed `subagent-settled` notice records the Activation's outcome and terminal output only when its direct creator is still online and delivery is `parent`. Human browser prompts are not model-authored Agent messages. The remote prompt path keeps a private Queue delivery so each human prompt remains a distinct turn. Interrupt behavior and settlement delivery remain independent. ### Complete removal and reintroduction condition -The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema, `tool:report` prompt section, `reportDelivery` configuration, report-specific message source, catalog entries, composition rows, and supported-behavior snapshots are absent. The unified tool gives up the recipient-free child shortcut and the old ability for a structural return tool to survive an explicit child allow-list. Those capabilities return only if a concrete use case requires semantics that an adjacent `agent_id` and fixed Steer cannot express; reintroducing them requires a distinct model operation and prefix-cost evidence, not an alias over `sendMessage()`. +The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema, `tool:report` prompt section, `reportDelivery` configuration, report-specific message source, catalog entries, composition rows, and supported-behavior snapshots are absent. The unified tool gives up the recipient-free child shortcut and the old ability for a structural return tool to survive an explicit child allow-list. Those capabilities return only if a concrete use case requires semantics that a tree member’s `agent_id` and fixed Steer cannot express; reintroducing them requires a distinct model operation and prefix-cost evidence, not an alias over `sendMessage()`. ## Alternatives considered @@ -67,16 +63,16 @@ The standalone `@deepseek-ai/dsh-tool-subagent-report` package, `report` schema, **Make `report` global.** Roots, one-shot children, remote children, and agentless callers cannot derive a report recipient. Advertising it globally would make schema visibility disagree with authority, while `send_message` already makes the recipient explicit. -**Turn every child final message into an implicit send.** A long-lived child may have nothing useful to send in one turn and several findings in another. Automatic delivery would merge model-authored communication with the runtime's settlement account and could not replace the unconditional notice on errors, cancellation, or token exhaustion. +**Treat the final answer as a model-authored send.** A settlement notice must preserve terminal output and abnormal endings even when the child cannot cooperate. It keeps runtime attribution distinct from findings the child explicitly chose to send. -**Rely only on the tool description.** A tool description helps after the model considers that tool; the failure mode is a child that believes it is finished without considering any return call. Initial-task guidance reaches that decision without changing the inherited system or tool prefix. +**Rely only on the tool description.** A tool description helps after the model considers that tool; initial-task guidance also explains which earlier findings should reach the parent and what the final answer must contain, without changing the inherited system or tool prefix. **Keep quiet delivery as deployment policy.** A quiet model-authored message can be accepted while an idle target never reads it. Fixed Steer gives both directions one delivery meaning and preserves accepted order with later settlement notices. ## Consequences - Model consumers expose one `send_message({ agent_id, message })` definition to parents and children, with no model-selected Queue versus Steer parameter. -- The continuation manager remains the sole owner of adjacency authorization, residency, cold resume, waking admission, and teardown races. +- The continuation manager remains the sole owner of tree authorization, residency, cold resume, waking admission, and teardown races. - Accepted messages may extend a running target's current turn; messages waiting together share next-step FIFO ordering. - Caller cancellation owns work only until inbox acceptance and does not retract an accepted message or dispose the target. - The initial task carries JSON-encoded dynamic parent addressing after a fork prefix, while the request-head system prompt and tool ordering remain reusable. diff --git a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.zh.md b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.zh.md index c41e4eb143..1bf1b5dbbc 100644 --- a/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-27-adjacent-agent-steer-messaging.zh.md @@ -10,23 +10,19 @@ Status: implemented 可继续 Agent 最初使用方向专属的模型控制。parent 调用 `send_message({ subagent_id, message })`,委托给 FIFO `followup` 服务操作。child 则获得 child 作用域的 `report({ output })` 工具、`tool:report` 系统提示词 section,以及由部署选择的静默或唤醒投递。两个工具用不同 schema、服务路径、来源与调度描述同一个相邻 Agent 操作。 -可继续 child 拥有自己的 Session,因此 parent 不会自动收到 child 的 transcript(文本记录)、工具输出或推理。返回路径必须保持显式且可重复:child 可以在结束前发送进度、发送后仍保持可用,也可能在来得及配合前失败。把每条最终 assistant 消息变成隐式结果会混淆轮次完成与模型选择的通信,而且无法覆盖异常结束。 +可继续 child 拥有自己的 Session,因此 parent 不会自动收到 child 的 transcript(文本记录)、工具输出或推理。显式消息让 child 在结束前发送可操作的发现。直接创建者仍在线时,runtime 另行通过结算通知投递终态输出。 child 专属工具与系统提示词 section 还位于每个继承 fork 轮次之前。它们让可继续 fork child 的请求头在 fork 旨在复用的历史之前就与 parent 不同,迫使提供方重新预填充整份复制 transcript。 ## 决策 -`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` 是唯一公开的模型编写消息操作。继续执行管理器只接受确切在线 sender 与一条相邻边上的目标: +`SubagentRuntime.sendMessage(sender, targetId, content, { signal })` 是唯一公开的模型编写消息操作。继续执行管理器只接受确切在线 sender 与同一根树中的目标;[整树通信](2026-10-16-tree-wide-agent-communication.zh.md)负责授权与发现规则。 -- parent 到直接可继续 child,由 child 的持久化 `SessionHeader.parentSession` 授权; -- 驻留的可继续 child 到其确切在线直接 parent,由 child 的 Activation 授权; -- 驻留的可继续子级向同一在线父级的可继续同级发消息,由该父级对子级的权限授权([子代理队友](2026-10-15-agent-teams-as-subagents.zh.md))。 +自身目标、过期 Agent 对象、未知目标和外部执行不是替代路由。操作不接受调用方提供的 source、投递模式、离线根邮箱或提供方分发。 -自身目标、跨越一层的祖先、过期 Agent 对象、未知目标和一次性子级不是替代路由。操作不接受调用方提供的 source、投递模式、离线父级邮箱或提供方分发。 +每条被接受的消息都使用 `Agent.steer()`。运行中目标在最近 step 边界接收消息;空闲目标启动轮次。不在线目标先在根下冷恢复,再接受同一 Steer 投递;其创建祖先可以保持离线。管理器保留唤醒发送记账,因此受继续执行管理的目标不会在同步 inbox 插入与 driver 准入之间结算。 -每条被接受的消息都使用 `Agent.steer()`。运行中目标在最近 step 边界接收消息;空闲目标启动轮次。缺失的直接 child 会先通过现有继续执行生命周期冷恢复,再接受同一 Steer 投递。管理器保留唤醒发送记账,因此受继续执行管理的目标不会在同步 inbox 插入与 driver 准入之间结算。 - -所有方向使用同一种持久 source。服务从确切在线发送方推导 `senderSessionId`,并以 `Agent sent a message:` 包装内容,使用父级权限的同级投递也一样。 +所有方向使用同一种持久 source。服务从确切在线发送方推导 `senderSessionId`,并以 `Agent sent a message:` 包装内容。 ```ts import type { SessionId } from '@deepseek-ai/dsh-session' @@ -49,15 +45,15 @@ interface SendMessageInput { } ``` -parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。独立选择的 preset 可以省略该工具,历史 descriptor 过滤器可在恢复时移除该工具,作用域替代工具也可以提供不同语义;两种情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、结束前发送一份自包含结果的指令,以及更早发送可操作发现的指令追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。 +parent 与 child 以相同注册表顺序继承相同定义。标准定义携带进程稳定的内部身份,同名的作用域工具不满足该身份。独立选择的 preset 可以省略该工具,历史 descriptor 过滤器可在恢复时移除该工具,作用域替代工具也可以提供不同语义;这些情况都不会收到标准调用指令。当标准工具仍可见时,继续执行管理器会把经过 JSON 编码的直接 parent id、最终回答的自动投递条件,以及提前发送可操作发现的指导追加到 child 初始用户任务。对 fork child 而言,该任务位于继承的已完成轮次前缀之后;没有 child 专属系统提示词 section 或工具 schema 位于此前缀之前。 -该指令是指导,不是结算强制。发送不会结束 child 轮次,机制仍允许零次或多次调用,runtime 绝不会因 child 保持沉默而拒绝它。由管理器负责的 `subagent-settled` 通知仍无条件发送并采用独立来源,因为它记录 Activation 如何结束,并在 child 无法配合时保留终态输出。 +该指令是指导,不是结算强制。发送不会结束 child 轮次,机制仍允许零次或多次调用,runtime 绝不会因 child 保持沉默而拒绝它。采用独立来源的 `subagent-settled` 通知仅在直接创建者仍在线且投递方式为 `parent` 时,记录 Activation 的结果与终态输出。 浏览器中的人类提示不是模型编写的 Agent 消息。远程提示路径保留私有 Queue 投递,使每条人类提示保持为独立轮次。中断行为与结算投递保持独立。 ### 完整移除与重新引入条件 -独立的 `@deepseek-ai/dsh-tool-subagent-report` 包、`report` schema、`tool:report` 提示词 section、`reportDelivery` 配置、report 专属消息来源、目录项、组合行和受支持行为快照均已不存在。统一工具放弃了无需接收方的 child 快捷方式,也放弃了让结构性返回工具绕过显式 child allow-list 的旧能力。只有具体用例需要相邻 `agent_id` 与固定 Steer 无法表达的语义时,这些能力才会重新出现;重新引入需要独立的模型操作与前缀成本证据,而非 `sendMessage()` 之上的别名。 +独立的 `@deepseek-ai/dsh-tool-subagent-report` 包、`report` schema、`tool:report` 提示词 section、`reportDelivery` 配置、report 专属消息来源、目录项、组合行和受支持行为快照均已不存在。统一工具放弃了无需接收方的 child 快捷方式,也放弃了让结构性返回工具绕过显式 child allow-list 的旧能力。只有具体用例需要树成员的 `agent_id` 与固定 Steer 无法表达的语义时,这些能力才会重新出现;重新引入需要独立的模型操作与前缀成本证据,而非 `sendMessage()` 之上的别名。 ## 考虑过的替代方案 @@ -67,16 +63,16 @@ parent 与 child 以相同注册表顺序继承相同定义。标准定义携带 **让 `report` 全局可见。** 根 Agent、一次性 child、远程 child 与无 Agent 调用方无法推导 report 接收方。全局宣传它会让 schema 可见性与权限不一致,而 `send_message` 已显式给出接收方。 -**把每条 child 最终消息变成隐式发送。** 长期运行的 child 可能在某个轮次没有值得发送的内容,在另一个轮次却有多条发现。自动投递会混合模型编写通信与 runtime 结算说明,而且无法替代错误、取消或 token 耗尽时的无条件通知。 +**将最终回答当作模型编写的发送。** 即使 child 无法配合,结算通知仍须保留终态输出与异常结束信息。它将 runtime 来源与 child 显式选择发送的发现区分开。 -**只依赖工具描述。** 工具描述会在模型考虑该工具后提供帮助;失败模式是 child 认为自己已经完成而根本没有考虑返回调用。初始任务指导能触及该决策,又不会改变继承的系统或工具前缀。 +**只依赖工具描述。** 工具描述会在模型考虑该工具后提供帮助;初始任务指导还说明哪些早期发现应发送给 parent,以及最终回答须包含什么,又不会改变继承的系统或工具前缀。 **保留静默投递作为部署策略。** 静默的模型编写消息可能被接受,但空闲目标永远不会读取它。固定 Steer 为两个方向提供一种投递含义,并保持与后续结算通知的接受顺序。 ## 后果 - 模型 Consumer 向 parent 与 child 公开一个 `send_message({ agent_id, message })` 定义,不提供模型选择的 Queue 与 Steer 参数。 -- 继续执行管理器仍是相邻关系授权、驻留、冷恢复、唤醒准入与拆卸竞态的唯一所有者。 +- 继续执行管理器仍是整树授权、驻留、冷恢复、唤醒准入与拆卸竞态的唯一所有者。 - 被接受的消息可以延长运行中目标的当前轮次;一起等待的消息共享 next-step FIFO 顺序。 - 调用方取消只在 inbox 接受前掌管工作,不会撤回已接受消息或 dispose(资源释放)目标。 - 初始任务在 fork 前缀之后携带经过 JSON 编码的动态 parent 地址,而请求头系统提示词与工具顺序保持可复用。 diff --git a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts index f0150c6412..8fcc3181b6 100644 --- a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts @@ -1221,7 +1221,6 @@ describe('headless stream-json snapshots', () => { } projection = { sessions: logs.length, - taskNoticeEntered: messageSources.includes('team-task-change'), workflowStopReason: (rows.find(row => row.type === 'tool-workflow/run-end')?.data as JsonObject)?.stopReason, identities, teammates: [...teammates].sort(), @@ -1266,7 +1265,6 @@ describe('headless stream-json snapshots', () => { "messageEntered": true, "nextStepInbox": true, }, - "taskNoticeEntered": false, "tasks": [ { "revision": 3, diff --git a/apps/web/tests/agent-team-panel.e2e.ts b/apps/web/tests/agent-team-panel.e2e.ts index b4de20c924..c406eef8c9 100644 --- a/apps/web/tests/agent-team-panel.e2e.ts +++ b/apps/web/tests/agent-team-panel.e2e.ts @@ -133,7 +133,7 @@ describe('web e2e: Agent Teams panel', () => { taskId: task.id, expectedRevision: task.revision, action: 'claim', }) await action.getByText('In progress', { exact: true }).waitFor() - await action.getByText('Owner: Open the task board.', { exact: true }).waitFor() + await action.getByText('Owner: Lead', { exact: true }).waitFor() expect(await action.getByRole('button', { name: /^(New task|Edit|Complete|Reopen|Delete)$/u }).count()).toBe(0) expect(await action.locator('input, select, textarea').count()).toBe(0) diff --git a/apps/web/tests/assembled-remote.ts b/apps/web/tests/assembled-remote.ts index be74dc87b6..c18e9155e3 100644 --- a/apps/web/tests/assembled-remote.ts +++ b/apps/web/tests/assembled-remote.ts @@ -154,7 +154,6 @@ export function createAssembledRemote(options: AssembledRemoteOptions = {}): Ass 'agentPresets/list': structuredClone(fixture.agentPresets), 'commands/list': structuredClone(fixture.commands), 'settings/openSettingsDocument': ok({ opened: true }), - 'subagents/list': ok({ entries: [], parentAvailable: true }), 'terminal/list': ok([]), 'skills/list': ok({ skills: [] }), 'session/canOpenWorkspacePath': ok(true), diff --git a/apps/web/tests/snapshots/agent-team-panel/resize.expected.md b/apps/web/tests/snapshots/agent-team-panel/resize.expected.md index 8fb6beac3a..6e420e796f 100644 --- a/apps/web/tests/snapshots/agent-team-panel/resize.expected.md +++ b/apps/web/tests/snapshots/agent-team-panel/resize.expected.md @@ -4,7 +4,7 @@ - strong: Agent task - text: In progress - paragraph: Created by the root agent - - text: "task-1Owner: Open the task board.Write scopes: src/web" + - text: "task-1Owner: LeadWrite scopes: src/web" - article: - strong: Responsive task - text: Pending diff --git a/apps/web/tests/snapshots/agent-team-panel/task.expected.md b/apps/web/tests/snapshots/agent-team-panel/task.expected.md index 825d14eaed..160bcd4937 100644 --- a/apps/web/tests/snapshots/agent-team-panel/task.expected.md +++ b/apps/web/tests/snapshots/agent-team-panel/task.expected.md @@ -4,4 +4,4 @@ - strong: Agent task - text: In progress - paragraph: Created by the root agent - - text: "task-1Owner: Open the task board.Write scopes: src/web" + - text: "task-1Owner: LeadWrite scopes: src/web" diff --git a/docs/capability-seams.i18n.yaml b/docs/capability-seams.i18n.yaml index 573a76f8b7..d96e81e3de 100644 --- a/docs/capability-seams.i18n.yaml +++ b/docs/capability-seams.i18n.yaml @@ -6,5 +6,5 @@ en: 41d772192075e133 zh: 5ef3da69b4de1d8a /capability-seams-and-core-services: - en: f209321ab0da4f90 - zh: 4af5c135eec51ce1 + en: ced90e97b121e70a + zh: a5e53b5352002b7f diff --git a/docs/capability-seams.md b/docs/capability-seams.md index 865162e7df..5a27e3c5a7 100644 --- a/docs/capability-seams.md +++ b/docs/capability-seams.md @@ -699,7 +699,7 @@ flowchart LR | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; there is no model-facing compact tool. | | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/experimental/tool-ralph) | - | Providers prepare local children or execute external tasks; one activation manager owns both lifecycles. The delegation tool selects a backend, control tools deliver local follow-ups, and workflows await activation results. | | `ctx.speechToText` | `seam` | [`experimental-speech-to-text`](../packages/experimental/speech-to-text) | [`experimental-speech-to-text-sensevoice`](../packages/experimental/speech-to-text-sensevoice) | [`experimental-api-speech-to-text`](../packages/experimental/api-speech-to-text) | - | Routes explicit recognizers; the browser uses the authenticated Remote and keeps transcripts in the draft until submission. | -| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | Owns the implicit-root roster, direct peer inbox delivery, shared task DAG, and continuable-child lifecycle; tool-agent-team contributes model controls. | +| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | Owns the shared task DAG and wait signals for a subagent tree; tool-agent-team contributes task controls. | | `ctx.claudeCodeMods` | `core` | [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | - | - | - | Loads mods that defineMod plugins add, raises their hook chains from harness extension points, and draws the band above the prompt over its Remote. | | `ctx.inspector` | `core` | `inspector` | - | - | - | Owns the Worker-hosted CDP target and the transport-independent Host and Client observation and Cordis-tree query API. | | `ctx.jobs` | `seam` | [`jobs`](../packages/jobs/jobs) | [`jobs-local`](../packages/jobs/jobs-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh), [`tool-terminal`](../packages/experimental/tool-terminal), [`tool-jobs`](../packages/jobs/tool-jobs), [`api-job-controller`](../packages/api/job-controller) | - | Producers (background bash/pwsh and PTY sends) register running work; record-declaring jobs additionally stream raw output for non-consuming observers; tool-jobs is the model-facing controller that reads, lists, and kills it; jobs-local is the process-local registry. | diff --git a/docs/capability-seams.zh.md b/docs/capability-seams.zh.md index 395986ab96..750915cefe 100644 --- a/docs/capability-seams.zh.md +++ b/docs/capability-seams.zh.md @@ -701,7 +701,7 @@ flowchart LR | `ctx.compaction` | `seam` | [`compaction`](../packages/compaction/compaction) | [`compaction-basic`](../packages/compaction/compaction-basic) | [`compaction-basic`](../packages/compaction/compaction-basic) | - | 基础后端消费步骤后的压力事件和请求错误恢复事件;不存在面向模型的压缩工具。 | | `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process), [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process), [`subagent-acp`](../packages/subagent/subagent-acp), [`subagent-codex`](../packages/subagent/subagent-codex), [`subagent-claude-code`](../packages/subagent/subagent-claude-code), [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control), [`tool-ralph`](../packages/experimental/tool-ralph) | - | 提供方准备本地子级或执行外部任务;一个 activation 管理器持有两类生命周期。委派工具选择后端,控制工具投递本地后续消息,工作流等待 activation 结果。 | | `ctx.speechToText` | `seam` | [`experimental-speech-to-text`](../packages/experimental/speech-to-text) | [`experimental-speech-to-text-sensevoice`](../packages/experimental/speech-to-text-sensevoice) | [`experimental-api-speech-to-text`](../packages/experimental/api-speech-to-text) | - | 路由显式选择的识别器;浏览器使用带认证的 Remote,并在提交前将转写保留在草稿中。 | -| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | 负责隐式 Root roster、直接 peer inbox 投递、共享任务 DAG 与 continuable child 生命周期;tool-agent-team 提供模型控制工具。 | +| `ctx.agentTeams` | `core` | [`experimental-agent-team`](../packages/experimental/agent-team) | - | [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | - | 负责子代理树共享的任务 DAG 和等待信号;tool-agent-team 提供任务控制工具。 | | `ctx.claudeCodeMods` | `core` | [`experimental-claude-code-mods`](../packages/experimental/claude-code-mods) | - | - | - | 加载 defineMod 插件添加的模组,从 harness 扩展点触发其钩子链,并通过其 Remote 绘制提示框上方的横幅。 | | `ctx.inspector` | `core` | `inspector` | - | - | - | 负责 Worker 托管的 CDP target,以及独立于传输的 Host 和 Client observation 与 Cordis tree query API。 | | `ctx.jobs` | `seam` | [`jobs`](../packages/jobs/jobs) | [`jobs-local`](../packages/jobs/jobs-local) | [`tool-bash`](../packages/shell/tool-bash), [`tool-pwsh`](../packages/shell/tool-pwsh), [`tool-terminal`](../packages/experimental/tool-terminal), [`tool-jobs`](../packages/jobs/tool-jobs), [`api-job-controller`](../packages/api/job-controller) | - | 生产方(后台 bash/pwsh 与 PTY 发送)登记正在运行的工作;声明 record 的 job 还为非消费观察者流式提供原始输出;tool-jobs 是面向模型的控制器,用于读取、列出和终止这些工作;jobs-local 是进程本地注册表。 | diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 78474c94c3..d0fc1afcd3 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -847,7 +847,7 @@ export interface Config { ## `@deepseek-ai/dsh-experimental-agent-team` - `inject`: `agents` · `sessions` · `sessionProjections` · `subagents` -- `source`: [`packages/experimental/agent-team/src/types.ts:158`](../packages/experimental/agent-team/src/types.ts) +- `source`: [`packages/experimental/agent-team/src/types.ts:143`](../packages/experimental/agent-team/src/types.ts) ```ts config-catalog /** Team-service deployment limits and initial mode. */ diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 4e029a324a..2cc4646deb 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -849,7 +849,7 @@ export interface Config { ## `@deepseek-ai/dsh-experimental-agent-team` - `inject`: `agents` · `sessions` · `sessionProjections` · `subagents` -- `source`: [`packages/experimental/agent-team/src/types.ts:158`](../packages/experimental/agent-team/src/types.ts) +- `source`: [`packages/experimental/agent-team/src/types.ts:143`](../packages/experimental/agent-team/src/types.ts) ```ts config-catalog /** Team-service deployment limits and initial mode. */ diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml index 8dd46900bb..0124df99db 100644 --- a/docs/persistence-catalog.i18n.yaml +++ b/docs/persistence-catalog.i18n.yaml @@ -225,20 +225,20 @@ en: a640800a38b3c874 zh: a640800a38b3c874 /session-persistence-event-catalog/events/team/team-member-log-only: - en: 00b5c6ff294441b1 - zh: 9c504296c9496bf6 + en: 26f2747021931cbc + zh: 64c1074a5ebea28b /session-persistence-event-catalog/events/team/team-message-delivered-log-only: - en: bb844a0632dba9f7 - zh: 0c2d2944035ac8ed + en: 84974faad49f192b + zh: 62673037182c42fe /session-persistence-event-catalog/events/team/team-message-queued-log-only: - en: 04a92845cb7624a0 - zh: b0f2b8ea4db2f4b7 + en: b0832ab3266150dd + zh: 91edfb2003526f28 /session-persistence-event-catalog/events/team/team-mode-log-only: - en: 7d5e9377d1f1ee7f - zh: 0479cdd8a0a1f285 + en: f43fc33db1215455 + zh: e1930e95c21e80bf /session-persistence-event-catalog/events/team/team-task-log-only: - en: d417a3e158c16f03 - zh: eb8c7e715ef816d8 + en: 661941f6e95dbf7e + zh: e46db19e05a0b839 /session-persistence-event-catalog/events/todo: en: 308d4bb4b6b96e35 zh: 308d4bb4b6b96e35 @@ -1257,11 +1257,11 @@ en: fa7208409888557c zh: 01f948c369fd2973 /session-persistence-event-catalog/resolved-persistence-types/teammessagesnapshot: - en: 2913ea413d412e7d - zh: a204cfc70f17b093 + en: 216b8c901ee69d3f + zh: b80bb7aa936a1b69 /session-persistence-event-catalog/resolved-persistence-types/teammessagesource: - en: 54a77ee0aabf713d - zh: f9eba937d9eb9ec6 + en: e0025c306befe6d3 + zh: 59bc543e134f0dbf /session-persistence-event-catalog/resolved-persistence-types/teamtasksnapshot: en: fba012d93cb1a16a zh: e3b0461d6e251a59 @@ -1488,8 +1488,8 @@ en: 25423119a1117143 zh: 31bc9f1a811c4fa9 /session-persistence-event-catalog/resolved-persistence-types/enabled: - en: 73d98f96c8ca2231 - zh: 2acf8d22f6111919 + en: c6d07a63322218bd + zh: 81e7bc6a00be9fe2 /session-persistence-event-catalog/resolved-persistence-types/end-start: en: 4b7a8793663d3fef zh: 249459c825d330c3 @@ -1878,17 +1878,17 @@ en: 3216b23c131d0070 zh: 73ca171c6014f16e /session-persistence-event-catalog/resolved-persistence-types/member-teamid-version: - en: 0f72e88b18df88a9 - zh: 694ececda444127a + en: 810c6419e14d766c + zh: e61251e312c3f2fa /session-persistence-event-catalog/resolved-persistence-types/message-step-turn: en: dcaa676f106cfdfd zh: e3941fea97c943fb /session-persistence-event-catalog/resolved-persistence-types/message-teamid-version: - en: 985b8c14a9de5143 - zh: 9fb608f621a1c7c6 + en: 687d411f1e2b2d39 + zh: 894676da06004a08 /session-persistence-event-catalog/resolved-persistence-types/messageid-targetid-teamid-version: - en: 4f2287a8e249cde0 - zh: cbc8d71664eb7c2a + en: 6fdb9c1e7b6f4873 + zh: 28648d39ba328110 /session-persistence-event-catalog/resolved-persistence-types/mode-source: en: 3028b222670ce5ad zh: cae4c9437e560d61 @@ -1920,8 +1920,8 @@ en: 5360af614fe753e3 zh: 96427c489cc71de6 /session-persistence-event-catalog/resolved-persistence-types/task-teamid-version: - en: 625d7212ab467b26 - zh: b31f90398f104239 + en: a7b642bca3cb3c1e + zh: 12719763e48ca0ce /session-persistence-event-catalog/resolved-persistence-types/todos: en: 8ab873d30e062fd5 zh: a138832c6e299ebb diff --git a/docs/persistence-catalog.md b/docs/persistence-catalog.md index fc36d0f384..2f3d10c6f5 100644 --- a/docs/persistence-catalog.md +++ b/docs/persistence-catalog.md @@ -1013,7 +1013,7 @@ Source: [`packages/core/session/src/types.ts:331`](../packages/core/session/src/ Types: [TeamId](subsystems/agent-team.md) · [TeamMemberSnapshot](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:201`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:186`](../packages/experimental/agent-team/src/types.ts) @@ -1031,7 +1031,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:201`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamMessageId](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:207`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:192`](../packages/experimental/agent-team/src/types.ts) @@ -1044,7 +1044,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:207`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamMessageSnapshot](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:205`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:190`](../packages/experimental/agent-team/src/types.ts) @@ -1055,7 +1055,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:205`](../packages/experi 'team/mode': { enabled: boolean } ``` -Source: [`packages/experimental/agent-team/src/types.ts:199`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:184`](../packages/experimental/agent-team/src/types.ts) @@ -1068,7 +1068,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:199`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamTaskSnapshot](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:203`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:188`](../packages/experimental/agent-team/src/types.ts) ### `todo/*` @@ -4996,7 +4996,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:50`](../packages/experi SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e` -Sources: [`packages/experimental/agent-team/src/types.ts:122`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:107`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -5016,7 +5016,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:122`](../packages/exper SHA-256: `2152caf1a71e588e4df2358a806dea24936c1c47131134f01a12939d12cdc0c0` -Sources: [`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:116`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -6211,7 +6211,7 @@ Sources: [`packages/experimental/hook-protocol/src/types.ts:19`](../packages/exp SHA-256: `c2a13242efc3bea4b36a843ddf1524ff4d33c765a71b582fa1fdbfa4fa148434` -Sources: [`packages/experimental/agent-team/src/types.ts:199`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:184`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7744,7 +7744,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:61`](../packages/web SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1` -Sources: [`packages/experimental/agent-team/src/types.ts:201`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:186`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7772,7 +7772,7 @@ Sources: [`packages/core/session/src/types.ts:331`](../packages/core/session/src SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` -Sources: [`packages/experimental/agent-team/src/types.ts:205`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:190`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7786,7 +7786,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:205`](../packages/exper SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1` -Sources: [`packages/experimental/agent-team/src/types.ts:207`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:192`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7932,7 +7932,7 @@ Sources: [`packages/compaction/compaction-image-offload/src/projection.ts:25`](. SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c` -Sources: [`packages/experimental/agent-team/src/types.ts:203`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:188`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| diff --git a/docs/persistence-catalog.zh.md b/docs/persistence-catalog.zh.md index 77d9687dfb..ff9ce97949 100644 --- a/docs/persistence-catalog.zh.md +++ b/docs/persistence-catalog.zh.md @@ -1015,7 +1015,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMemberSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:201`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:186`](../packages/experimental/agent-team/src/types.ts) @@ -1033,7 +1033,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageId](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:207`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:192`](../packages/experimental/agent-team/src/types.ts) @@ -1046,7 +1046,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:205`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:190`](../packages/experimental/agent-team/src/types.ts) @@ -1057,7 +1057,7 @@ export type SessionEvent = { 'team/mode': { enabled: boolean } ``` -来源:[`packages/experimental/agent-team/src/types.ts:199`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:184`](../packages/experimental/agent-team/src/types.ts) @@ -1070,7 +1070,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamTaskSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:203`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:188`](../packages/experimental/agent-team/src/types.ts) ### `todo/*` @@ -4998,7 +4998,7 @@ SHA-256: `6598cd6da253948d6b207c6374a45335804c79b576866d6215649c2d1bc85e25` SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e` -来源:[`packages/experimental/agent-team/src/types.ts:122`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:107`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -5018,7 +5018,7 @@ SHA-256: `2eafefdc6f79a4922d68a787a09eaf4dd2b2369c3777fba4c05e800b5678ac8e` SHA-256: `2152caf1a71e588e4df2358a806dea24936c1c47131134f01a12939d12cdc0c0` -来源:[`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:116`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -6213,7 +6213,7 @@ SHA-256: `119abe2b9c0b2f46ecb31d82b9acba4d4b30e049f655e2475e8b4ae12d5a9e25` SHA-256: `c2a13242efc3bea4b36a843ddf1524ff4d33c765a71b582fa1fdbfa4fa148434` -来源:[`packages/experimental/agent-team/src/types.ts:199`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:184`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7746,7 +7746,7 @@ SHA-256: `9c001ecb5e1d3c3894410db2a1e383842e5f8454fb52182338686158abc1b3ee` SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1` -来源:[`packages/experimental/agent-team/src/types.ts:201`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:186`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7774,7 +7774,7 @@ SHA-256: `6bc8e51124d0874bc89e1d0ceff381a9c3cd0fc240667d69f841ce4f60f65fe5` SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` -来源:[`packages/experimental/agent-team/src/types.ts:205`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:190`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7788,7 +7788,7 @@ SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1` -来源:[`packages/experimental/agent-team/src/types.ts:207`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:192`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7934,7 +7934,7 @@ SHA-256: `477614bca595a9e7adb5a1870be446b9a8684f3ba8950259d6289f2d3b74f028` SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c` -来源:[`packages/experimental/agent-team/src/types.ts:203`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:188`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| diff --git a/docs/persistence-schema.json b/docs/persistence-schema.json index a003c731a4..f5d11c197c 100644 --- a/docs/persistence-schema.json +++ b/docs/persistence-schema.json @@ -22662,7 +22662,7 @@ "digest": "0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:207" + "packages/experimental/agent-team/src/types.ts:192" ] }, { @@ -23042,7 +23042,7 @@ "packages/experimental/agent-team/src/types.ts#TeamMessageSource" ], "sources": [ - "packages/experimental/agent-team/src/types.ts:131" + "packages/experimental/agent-team/src/types.ts:116" ] }, { @@ -23222,7 +23222,7 @@ "packages/experimental/agent-team/src/types.ts#TeamMessageSnapshot" ], "sources": [ - "packages/experimental/agent-team/src/types.ts:122" + "packages/experimental/agent-team/src/types.ts:107" ] }, { @@ -24989,7 +24989,7 @@ "digest": "a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:203" + "packages/experimental/agent-team/src/types.ts:188" ] }, { @@ -25236,7 +25236,7 @@ "digest": "b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:201" + "packages/experimental/agent-team/src/types.ts:186" ] }, { @@ -25501,7 +25501,7 @@ "digest": "c2a13242efc3bea4b36a843ddf1524ff4d33c765a71b582fa1fdbfa4fa148434", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:199" + "packages/experimental/agent-team/src/types.ts:184" ] }, { @@ -25957,7 +25957,7 @@ "digest": "ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:205" + "packages/experimental/agent-team/src/types.ts:190" ] }, { diff --git a/docs/subsystems/README.i18n.yaml b/docs/subsystems/README.i18n.yaml index ec9d159bbb..fd13bb4d00 100644 --- a/docs/subsystems/README.i18n.yaml +++ b/docs/subsystems/README.i18n.yaml @@ -3,5 +3,5 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/README.md /subsystems: - en: 1d4818dbb07391a7 - zh: f1e27ce66c181295 + en: 857da1eede1f5523 + zh: 93fc3928f4eb43b5 diff --git a/docs/subsystems/README.md b/docs/subsystems/README.md index c2a47fb70b..92ac9554ce 100644 --- a/docs/subsystems/README.md +++ b/docs/subsystems/README.md @@ -50,7 +50,7 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures | [subagent.md](subagent.md) | the subagent seam: the named-provider registry, `SubagentStartRequest`/`Result`/`Run`, the start-time-vs-runtime capability split | | [voice-input.md](voice-input.md) | experimental named recognizers, transient audio and revision-guarded draft insertion | | [translation.md](translation.md) | experimental anonymous text translation and explicit provider requests | -| [agent-team.md](agent-team.md) | Agent Teams: implicit Lead identity, named continuable teammates, direct peer inbox delivery, and shared task DAG | +| [agent-team.md](agent-team.md) | Agent Teams: shared task DAG, task ownership, and wait signals for a subagent tree | | [claude-code-mods.md](claude-code-mods.md) | Claude Code mods through the experimental bridge: every event, `$` member, chain rule, drawing feature, and example mod that differs from Claude Code | | [web.md](web.md) | the web access seam: `WebSearchRequest`/`Result`, `WebFetchRequest`/`Result`, `WebFetchBody`, provider availability, `WebError` | | [spill.md](spill.md) | the spill storage seam: `SaveTextSpill`, `SpillOwner`/`SpillSource`, `SpillRef`, the branded `SpillLocator` | diff --git a/docs/subsystems/README.zh.md b/docs/subsystems/README.zh.md index a1380713bc..6e9fa95917 100644 --- a/docs/subsystems/README.zh.md +++ b/docs/subsystems/README.zh.md @@ -50,7 +50,7 @@ | [subagent.md](subagent.zh.md) | subagent seam:命名提供方注册表、`SubagentStartRequest`/`Result`/`Run`、启动时与运行时能力拆分 | | [voice-input.md](voice-input.zh.md) | 实验性具名语音识别、临时音频和受版本保护的草稿插入 | | [translation.md](translation.zh.md) | 实验性免登录文本翻译与显式 Provider 请求 | -| [agent-team.md](agent-team.zh.md) | Agent Teams:隐式 Lead 身份、具名 continuable teammate、直接 peer inbox 投递 与共享任务 DAG | +| [agent-team.md](agent-team.zh.md) | Agent Teams:子代理树共享的任务 DAG、任务归属与等待信号 | | [claude-code-mods.md](claude-code-mods.zh.md) | 经实验性桥接运行的 Claude Code 模组:与 Claude Code 不同的每个事件、`$` 成员、链规则、绘制特性与示例模组 | | [web.md](web.zh.md) | Web 访问 seam:`WebSearchRequest`/`Result`、`WebFetchRequest`/`Result`、`WebFetchBody`、提供方可用性、`WebError` | | [spill.md](spill.zh.md) | spill 存储 seam:`SaveTextSpill`、`SpillOwner`/`SpillSource`、`SpillRef`、品牌类型 `SpillLocator` | diff --git a/docs/subsystems/agent-team.i18n.yaml b/docs/subsystems/agent-team.i18n.yaml index c5b4c07580..a9b8b32a30 100644 --- a/docs/subsystems/agent-team.i18n.yaml +++ b/docs/subsystems/agent-team.i18n.yaml @@ -15,8 +15,8 @@ en: c70f0fd199fc7db5 zh: c6b77cb5a5cfd1c2 /agent-teams/web-projection: - en: d076163181d79c70 - zh: f83446b2eb5830aa + en: 843d35c30662189e + zh: 52f1dab26c76a86e /agent-teams/replay: en: 81d0de978aa92363 zh: a5bacfd4051afe51 diff --git a/docs/subsystems/agent-team.md b/docs/subsystems/agent-team.md index 943bc1e5f7..39b016d01b 100644 --- a/docs/subsystems/agent-team.md +++ b/docs/subsystems/agent-team.md @@ -77,23 +77,7 @@ interface TeamTaskSnapshot { `agentTeamMode.enabled` exposes the root Session’s saved selection. New Sessions default to false; `team/mode` records explicit selections. Historical tasks imply enabled until an explicit selection. Only the root can call `setEnabled`; descendants share that mode. Mode writes serialize with task mutations, synchronize tools before flushing, and wake current waits after flushing. A failed flush leaves the appended selection active; retrying the same selection flushes it without a duplicate event. -The root Session publishes non-deleted task views through `SessionProjectionMap.agentTeam`. `failure` reports a rejected persisted Team record beside the last valid state. Historical `members` fields remain decodable but supply neither assignment authority nor a second browser roster. The task panel labels owners from existing Session and catalog data with id fallback. - -```ts type-equiv -/** - * One roster row published through the `agentTeam` Session projection: the - * Lead and each historical roster member. Current children come from `subagentCatalog`. - */ -interface TeamMemberProjection { - readonly id: SessionId - /** Delegated responsibility; absent for the Lead. Never used as an address. */ - readonly description?: string - readonly role: 'lead' | 'teammate' - /** The Lead and continuable children are always `active`. Turn activity comes from Session status. */ - readonly phase: TeamMemberPhase - readonly error?: string -} -``` +The root Session publishes non-deleted task views through `SessionProjectionMap.agentTeam`. `failure` reports a rejected persisted Team record beside the last valid state. Historical member fields remain in persisted projection state but are omitted from the browser view. The task panel labels the root as Lead and other owners from catalog labels, Session titles, or ids. ```ts type-equiv /** Runtime-enriched task view returned to tools and hosts. */ @@ -115,10 +99,9 @@ interface TeamTaskView { /** * Durable Team state published to browser clients through the Lead Session's * `agentTeam` projection. `failure` names the first rejected persisted Team - * record; members and tasks then stay at the last valid state. + * record; tasks then stay at the last valid state. */ interface TeamProjection { - readonly members: TeamMemberProjection[] readonly tasks: TeamTaskView[] readonly failure?: string } @@ -167,9 +150,11 @@ async setEnabled(caller: Agent, enabled: boolean, signal?: AbortSignal): Promise * Create one unowned pending task in the Team Lead log. * @param caller - exact live Team member creating the task. * @param request - task text, blockers, and advisory write scopes. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the revision-one task view. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ -async createTask(caller: Agent, request: CreateTeamTaskRequest): Promise +async createTask(caller: Agent, request: CreateTeamTaskRequest, signal?: AbortSignal): Promise /** * Return one task, including a deleted tombstone. @@ -190,9 +175,11 @@ listTasks(caller: Agent): TeamTaskView[] * Compare-and-set one authorized task transition. * @param caller - exact live Team member authorizing the mutation. * @param request - task identity, expected revision, action, and action fields. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the committed next task revision. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ -async updateTask(caller: Agent, request: UpdateTeamTaskRequest): Promise +async updateTask(caller: Agent, request: UpdateTeamTaskRequest, signal?: AbortSignal): Promise ``` Types: [Agent](core.md) diff --git a/docs/subsystems/agent-team.zh.md b/docs/subsystems/agent-team.zh.md index e04d67a979..8b0b1634b6 100644 --- a/docs/subsystems/agent-team.zh.md +++ b/docs/subsystems/agent-team.zh.md @@ -77,23 +77,7 @@ interface TeamTaskSnapshot { `agentTeamMode.enabled` 提供根 Session 保存的选择。新 Session 默认为 false;`team/mode` 记录显式选择。历史任务在显式选择前视为开启。仅根可调用 `setEnabled`;后代共享该模式。模式写入与任务修改串行,刷盘前同步工具,刷盘后唤醒当前等待。刷盘失败时已追加的选择仍然生效;重试相同选择会再次刷盘,不重复追加事件。 -根 Session 通过 `SessionProjectionMap.agentTeam` 发布未删除任务视图。`failure` 在最后有效状态旁报告被拒绝的持久化 Team 记录。历史 `members` 字段仍可解码,但既不提供分配权限,也不提供第二个浏览器名册。任务面板从现有 Session 和目录数据标注所有者,缺失时使用 id。 - -```ts type-equiv -/** - * One roster row published through the `agentTeam` Session projection: the - * Lead and each historical roster member. Current children come from `subagentCatalog`. - */ -interface TeamMemberProjection { - readonly id: SessionId - /** Delegated responsibility; absent for the Lead. Never used as an address. */ - readonly description?: string - readonly role: 'lead' | 'teammate' - /** The Lead and continuable children are always `active`. Turn activity comes from Session status. */ - readonly phase: TeamMemberPhase - readonly error?: string -} -``` +根 Session 通过 `SessionProjectionMap.agentTeam` 发布未删除任务视图。`failure` 在最后有效状态旁报告被拒绝的持久化 Team 记录。历史成员字段保留在持久化投影状态中,但不发送到浏览器。任务面板将根标记为主代理,其他负责人依次使用目录标签、Session 标题或 id。 ```ts type-equiv /** Runtime-enriched task view returned to tools and hosts. */ @@ -115,10 +99,9 @@ interface TeamTaskView { /** * Durable Team state published to browser clients through the Lead Session's * `agentTeam` projection. `failure` names the first rejected persisted Team - * record; members and tasks then stay at the last valid state. + * record; tasks then stay at the last valid state. */ interface TeamProjection { - readonly members: TeamMemberProjection[] readonly tasks: TeamTaskView[] readonly failure?: string } @@ -167,9 +150,11 @@ async setEnabled(caller: Agent, enabled: boolean, signal?: AbortSignal): Promise * Create one unowned pending task in the Team Lead log. * @param caller - exact live Team member creating the task. * @param request - task text, blockers, and advisory write scopes. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the revision-one task view. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ -async createTask(caller: Agent, request: CreateTeamTaskRequest): Promise +async createTask(caller: Agent, request: CreateTeamTaskRequest, signal?: AbortSignal): Promise /** * Return one task, including a deleted tombstone. @@ -190,9 +175,11 @@ listTasks(caller: Agent): TeamTaskView[] * Compare-and-set one authorized task transition. * @param caller - exact live Team member authorizing the mutation. * @param request - task identity, expected revision, action, and action fields. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the committed next task revision. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ -async updateTask(caller: Agent, request: UpdateTeamTaskRequest): Promise +async updateTask(caller: Agent, request: UpdateTeamTaskRequest, signal?: AbortSignal): Promise ``` Types: [Agent](core.zh.md) diff --git a/docs/subsystems/subagent.i18n.yaml b/docs/subsystems/subagent.i18n.yaml index 8d7bab8718..7191ee85cf 100644 --- a/docs/subsystems/subagent.i18n.yaml +++ b/docs/subsystems/subagent.i18n.yaml @@ -12,8 +12,8 @@ en: 6f87231d18d82916 zh: 9f064b23058f8766 /subagents/messages-and-interruption: - en: b1debfea952d2e4e - zh: fff0a6db056d7bb6 + en: 6ff748d66293c367 + zh: b9f8a7183a27e7a1 /subagents/durable-enumeration: en: 30c3607a969696ce zh: e6359cdf22b1e235 diff --git a/docs/subsystems/subagent.md b/docs/subsystems/subagent.md index 3fcc0da6a6..c34ddc2b3d 100644 --- a/docs/subsystems/subagent.md +++ b/docs/subsystems/subagent.md @@ -177,7 +177,7 @@ interface ContinuableCreateSpec { ## Messages and interruption -sendMessage requires the exact live root or resident continuable sender and another member’s Session id. Delivery preserves the actual sender, verifies the target’s durable branch, and restores cold continuable ancestors under their recorded parents without delivering the target’s message to them. Running targets receive Steer; inactive targets start a turn. External executions reject follow-ups. Interruption still requires ancestor authority. Local and external executions report final text, structured output, diagnostics, and status through parent settlement notices; caller delivery suppresses those notices. drainDescendants and drainChildren await owned teardown. +`sendMessage` requires the exact live sender and a target Session id in the same root tree. Root and continuable members can message across branches; admission revalidates the sender and root. Running targets receive Steer; only an absent target cold-resumes, while its creation ancestors may remain offline. External executions reject follow-ups. Local and external executions report final text, structured output, diagnostics, and status to their direct creator only while it is online; caller delivery suppresses those notices. `drainDescendants` and `drainChildren` await owned teardown. ```ts type-equiv /** Durable attribution for one model-authored message between Agents. */ @@ -210,7 +210,7 @@ interface SubagentSettledMessageSource { ``` ```ts type-equiv -/** Options for one model-authored message between adjacent Agents. */ +/** Options for one model-authored message within a subagent tree. */ interface SubagentSendMessageOptions { /** Caller cancellation, owning the operation only until inbox acceptance. */ readonly signal: AbortSignal diff --git a/docs/subsystems/subagent.zh.md b/docs/subsystems/subagent.zh.md index 7de0470927..94073cd69e 100644 --- a/docs/subsystems/subagent.zh.md +++ b/docs/subsystems/subagent.zh.md @@ -177,7 +177,7 @@ interface ContinuableCreateSpec { ## 消息与中断 -sendMessage 要求确切在线的根或驻留可继续发送方,以及另一成员的 Session id。投递保留实际发送方,验证目标持久分支,并按记录的父级恢复冷的可继续祖先,不向祖先投递目标消息。运行中目标接收 Steer;空闲目标开始轮次。外部执行拒绝后续输入。中断仍要求祖先权限。本地和外部执行通过父级结算通知报告最终文本、结构化输出、诊断和状态;caller 投递抑制通知。drainDescendants 和 drainChildren 等待所属资源释放。 +`sendMessage` 要求确切在线发送方和同一根树中的目标 Session id。根和可继续成员可跨分支发消息;接纳时重新校验发送方和根。运行中目标接收 Steer;只冷恢复不在线的目标,其创建祖先可以保持离线。外部执行拒绝后续输入。本地和外部执行仅向仍在线的直接创建者报告最终文本、结构化输出、诊断和状态;caller 投递抑制这些通知。`drainDescendants` 和 `drainChildren` 等待所属资源释放。 ```ts type-equiv /** Durable attribution for one model-authored message between Agents. */ @@ -210,7 +210,7 @@ interface SubagentSettledMessageSource { ``` ```ts type-equiv -/** Options for one model-authored message between adjacent Agents. */ +/** Options for one model-authored message within a subagent tree. */ interface SubagentSendMessageOptions { /** Caller cancellation, owning the operation only until inbox acceptance. */ readonly signal: AbortSignal diff --git a/docs/tool-catalog.i18n.yaml b/docs/tool-catalog.i18n.yaml index 5526e18851..8400699325 100644 --- a/docs/tool-catalog.i18n.yaml +++ b/docs/tool-catalog.i18n.yaml @@ -255,8 +255,8 @@ en: aaccc4f194398018 zh: d918c1346a87a4b1 /tool-schema-catalog/deepseek-ai-dsh-tool-subagent-control/wait-agent: - en: 554cf676a9ae0b13 - zh: a311de7201f49d3c + en: f3cf5a0c56ddc07a + zh: 7f6754d71e1b4ae4 /tool-schema-catalog/deepseek-ai-dsh-tool-jobs: en: 9be673b1604d0114 zh: 9be673b1604d0114 diff --git a/docs/tool-catalog.md b/docs/tool-catalog.md index cb69022d6f..08411c3a4e 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -2240,7 +2240,7 @@ Source: [`packages/subagent/tool-subagent-control/src/index.ts`](../packages/sub ### `wait_agent` -Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. +Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. ```json { diff --git a/docs/tool-catalog.zh.md b/docs/tool-catalog.zh.md index 4a1517da1e..de03480c75 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -2246,7 +2246,7 @@ subagent_oar 工具在为外部提供方配置的别名之间选择。本目录 ### `wait_agent` -等待自己收件箱中的新输入,包括 Agent 消息、完成通知和用户引导。已有待处理输入时立即返回。 +等待下一步可处理的输入,包括 Agent 消息、完成通知和用户引导。已有此类待处理输入时立即返回。 ```json { diff --git a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.i18n.yaml index 4da1c3b48c..12f2359dd7 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.i18n.yaml +++ b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.i18n.yaml @@ -9,8 +9,8 @@ en: 8ff32d071efaba78 zh: 4bc6f62f6863fca8 /agent-teams-uses-subagents-and-hierarchical-names/change: - en: 7b655d4100173cdb - zh: 4b1d411be9f7f0c7 + en: 1e283d01c8105e8b + zh: a8dc1f3a3b05d4de /agent-teams-uses-subagents-and-hierarchical-names/migration: - en: 562c96b50e068594 - zh: 8ef24a2722d42f3f + en: cdb4e1cb7eaa3f40 + zh: 1a665fa3c61258ed diff --git a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md index 39e07827c8..af4ba09004 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md +++ b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md @@ -9,17 +9,17 @@ English | [中文](guide.zh.md) ## Change -Agent Teams uses subagent tools. Full paths rooted at `lead` identify recipients; descriptions label responsibilities. Creation accepts an optional sibling-unique name segment; omission selects the first unused `agent-N`, starting at `agent-1`. +Full paths rooted at `lead` identify recipients; descriptions label responsibilities. Creation accepts an optional sibling-unique name segment; omission selects the first unused `agent-N`, starting at `agent-1`. - `spawn_teammate` and the Team-scoped `send_message`, `list_agents`, and `interrupt_agent` are removed. The standard control tools use `name`, not `target`. Members can message across branches of their root tree; interruption remains ancestor-only. -- `list_agents` takes no arguments and lists the entire tree. Remove scripted `scope` arguments. Rows use `kind: root` or `kind: child`; descendants include parent paths and root-relative depth. The root can assign tasks to itself or any discovered continuable descendant. +- `list_agents` takes no arguments and lists the entire tree. Remove scripted `scope` arguments. Rows use `kind: root|child|diagnostic`; descendants include parent paths and root-relative depth. The root can assign tasks to itself or any discovered continuable descendant. - Team adds four task tools, policy, and required identity. `wait_agent` moves to ordinary subagent controls and waits for the caller’s next-step inbox, including pending input. Status changes and unrelated inboxes do not wake it; `noProgress` is removed. Timeout bounds and default are unchanged. - `agent-team` rejects `maxMembers`, `maxMessageBytes`, and `disposalTimeoutMs`; `tool-agent-team` rejects `interrupt`, `freshProvider`, and `forkProvider`. -- `ctx.agentTeams` removes creation, messaging, interruption, roster, membership, and wait APIs. `TeamMembership` and `TeamMembers` are removed; use subagent root resolution and discovery. Service task ownership retains `ownerId`; model tool results use `ownerName`. +- `ctx.agentTeams` removes creation, messaging, interruption, roster, membership, and wait APIs. `TeamMembership`, `TeamMemberView`, spawn/send types, and `TeamWaitResult` are removed; use subagent root resolution and discovery. Service task ownership retains `ownerId`; model tool results use `ownerName`. - Runtime instances share root ownership; creation ancestry is unchanged. Member settlement does not dispose descendants. Messages restore only their target. Root closure closes the tree; members obtain results explicitly. - Cold resume preserves saved presets and permissions; missing presets fail restoration. Lifecycle dispatch uses the root scope. Browser continuation requires the root online. - Historical Team rosters and mailboxes remain readable; their names grant no recipient or task-owner authority. Parent catalog entries carry an optional `name` segment; historical unnamed children use `legacy:` path segments. -- The Web Team panel shows tasks; subagent navigation browses members. Historical `agentTeam.members` grants no authority. Task commits end current waits across the tree; changes outside a wait cannot wake later waits. +- The Web Team panel shows tasks; subagent navigation browses members. Wire `agentTeam.members` and `TeamMemberProjection` are removed. Task commits end current waits across the tree; changes outside a wait cannot wake later waits. ## Migration @@ -28,4 +28,4 @@ Agent Teams uses subagent tools. Full paths rooted at `lead` identify recipients 3. Use `send_message({ name, message })` and `interrupt_agent({ name })`; discover member paths with `list_agents`. 4. Task `owner` fields accept paths; results expose `ownerName`. Use `lead` for the root. Host operations retain Session ids. `startActivation()` accepts optional `name` and returns the full path in `activation.name`; retain `childId` for id-based operations. 5. Every activation enters its parent catalog, including external `delivery: 'caller'` work. Discovery and the Web child list therefore include those entries; delivery only controls result routing. Verify scripts that count children. -6. Replace removed services with `ctx.subagents`. Verify member navigation, nested-descendant assignment, and task-board wakeups during active waits. Re-read tasks after waiting. +6. Replace removed services with `ctx.subagents`. SDK `session/resume` rejects subagent Sessions; resume the root. Verify navigation, task assignment and active-wait wakeups; re-read tasks after waiting. diff --git a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md index a2a3ed616d..fc83115120 100644 --- a/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md +++ b/docs/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md @@ -9,17 +9,17 @@ description: "Agent Teams 使用标准 subagent 工具和分层名字;移除 T ## 变更 -Agent Teams 使用子代理工具。以 `lead` 为根的完整路径标识接收者;描述标注职责。创建接受可选的同级唯一名字片段;省略时从 `agent-1` 开始选择第一个未占用的 `agent-N`。 +以 `lead` 为根的完整路径标识接收者;描述标注职责。创建接受可选的同级唯一名字片段;省略时从 `agent-1` 开始选择第一个未占用的 `agent-N`。 - 移除 `spawn_teammate` 以及 Team 作用域的 `send_message`、`list_agents` 和 `interrupt_agent`。标准控制工具使用 `name` 而非 `target`。同一根下的成员可以通过完整路径跨分支互发消息;中断仍只允许祖先执行。 -- `list_agents` 不接受参数,列出整棵树。移除脚本中的 `scope` 参数。行使用 `kind: root` 或 `kind: child`;后代包含父路径和相对根的深度。根可将任务分配给自身或任意已发现的可继续执行后代。 +- `list_agents` 不接受参数,列出整棵树。移除脚本中的 `scope` 参数。行使用 `kind: root|child|diagnostic`;后代包含父路径和相对根的深度。根可将任务分配给自身或任意已发现的可继续执行后代。 - Team 添加四个任务工具、任务策略和必需身份。`wait_agent` 移至普通 subagent 控制工具,等待调用者的下一步收件箱,包括待处理输入。状态变化和其他收件箱不会唤醒它;移除 `noProgress`。超时范围和默认值不变。 - `agent-team` 拒绝 `maxMembers`、`maxMessageBytes` 和 `disposalTimeoutMs`;`tool-agent-team` 拒绝 `interrupt`、`freshProvider` 和 `forkProvider`。 -- `ctx.agentTeams` 移除创建、通信、中断、名册、成员关系和等待 API。移除 `TeamMembership` 和 `TeamMembers`;改用 subagent 根解析和发现。服务任务归属保留 `ownerId`;模型工具结果使用 `ownerName`。 +- `ctx.agentTeams` 移除创建、通信、中断、名册、成员关系和等待 API。移除 `TeamMembership`、`TeamMemberView`、spawn/send 类型及 `TeamWaitResult`;改用 subagent 根解析和发现。服务任务归属保留 `ownerId`;模型工具结果使用 `ownerName`。 - 运行时实例共享根所有权,不改变创建祖先关系。成员收尾不释放后代。消息仅恢复目标。根关闭时 host 关闭整棵树;成员显式获取所需结果。 - 冷恢复保留已保存的 preset 和权限;缺失 preset 会导致恢复失败。生命周期分派使用根作用域。浏览器继续执行需要根在线。 - 历史 Team 名册和邮箱记录仍可读取。其中的名字仅作为历史数据,不用于解析消息收件人或任务所有者。父级目录条目携带可选的 `name` 片段;历史未命名子级使用 `legacy:` 路径片段。 -- Web Team 面板仅显示任务;普通 subagent 导航负责浏览成员。历史 `agentTeam.members` 不授予权限。任务提交结束同树当前等待;等待之外的变更不能唤醒后续等待。 +- Web Team 面板仅显示任务;普通 subagent 导航负责浏览成员。移除 wire 的 `agentTeam.members` 和 `TeamMemberProjection`。任务提交结束同树当前等待;等待之外的变更不能唤醒后续等待。 ## 迁移 @@ -28,4 +28,4 @@ Agent Teams 使用子代理工具。以 `lead` 为根的完整路径标识接收 3. 使用 `send_message({ name, message })` 和 `interrupt_agent({ name })`;通过 `list_agents` 查询成员路径。 4. 任务的 `owner` 字段接受路径,结果提供 `ownerName`。根使用 `lead`。Host 操作保留 Session id。`startActivation()` 接受可选的 `name`,通过 `activation.name` 返回完整路径;按 id 操作时保留 `childId`。 5. 所有 activation 都进入父级 catalog,包括外部 `delivery: 'caller'` 工作。发现接口和 Web 子级列表因此包含这些条目;delivery 只控制结果投递。检查统计子级的脚本。 -6. 将已移除服务替换为 `ctx.subagents`。验证成员导航、嵌套后代分配,以及当前等待期间的任务板唤醒。等待结束后重新读取任务。 +6. 将已移除服务替换为 `ctx.subagents`。SDK `session/resume` 拒绝子级 Session;请恢复根。验证导航、任务分配和当前等待的唤醒;等待后重新读取任务。 diff --git a/packages/client/ui-subagent/src/client/index.ts b/packages/client/ui-subagent/src/client/index.ts index 5bd710963e..5fec53dd43 100644 --- a/packages/client/ui-subagent/src/client/index.ts +++ b/packages/client/ui-subagent/src/client/index.ts @@ -78,8 +78,7 @@ export function apply(ctx: ClientContext): void { () => ctx.slots.register({ name: 'conversation.session.header.actions', id: 'subagent-catalog', - // Between the preset label (-10) and background tasks (20), after Team - // navigation (-20). + // Between the preset label (-10) and background tasks (20). order: 10, locale: NS, inject: catalogActions, diff --git a/packages/experimental/agent-team/README.i18n.yaml b/packages/experimental/agent-team/README.i18n.yaml index 4d5796e550..3db2bc7fac 100644 --- a/packages/experimental/agent-team/README.i18n.yaml +++ b/packages/experimental/agent-team/README.i18n.yaml @@ -63,14 +63,14 @@ en: 027a7ccc996435f5 zh: 3b95dc637691c03b /deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/durability-model: - en: f39327bef33faad4 - zh: ae6d090ff0cc5b9e + en: 6a157930f0bef14a + zh: c29b976450c1fded /deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/disposal: en: a8baf51f2fcd6d49 zh: 357c8db36823fe0e /deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/browser-projection: - en: bfa8138fd4a1ca42 - zh: 5b369b4e2d802a1a + en: 11d31cdabfbe8942 + zh: faab17cdeb9a0c8c /deepseek-ai-dsh-experimental-agent-team/further-exploration: en: 91d96c24ad22c546 zh: 66f519175fefd944 diff --git a/packages/experimental/agent-team/README.md b/packages/experimental/agent-team/README.md index 5ea7fb206c..7d6012ba6a 100644 --- a/packages/experimental/agent-team/README.md +++ b/packages/experimental/agent-team/README.md @@ -136,7 +136,7 @@ After a successful append and flush, Team emits an Agent-scoped in-memory signal `Service.init` awaits registration of the `agentTeam` projection before publishing service readiness. The journal retains the returned handle and reads task state through `stateOf(session)`; callers use the Team service rather than registering another reader. -Task mutations append `team/task` to the exact live root Session and flush before emitting wait signals. Task records remain log-only; signals are not messages or persisted events. The wait tool records its result through the normal tool pipeline. No committed task or historical payload generation is rewritten. +Task mutations honor cancellation before appending `team/task` to the exact live root Session, then flush before emitting wait signals. Cancellation cannot retract an appended record. A flush failure raises `TEAM_TASK_PERSISTENCE_UNCONFIRMED` with the task id and revision: the task is already visible, but durability is unknown. Read that task before retrying. Task records remain log-only; signals are not messages or persisted events. The wait tool records its result through the normal tool pipeline. No committed task or historical payload generation is rewritten. Mailbox projection and checkpoint admission preserve decoded JSON fields outside locally declared validators, including an own `__proto__` key. Historical payload generations remain unchanged; projection caches rebuild from the Session log when their cache version changes. @@ -146,7 +146,7 @@ Unloading the service unregisters the projection. The retained handle remains re ### Browser projection -The `agentTeam` projection publishes non-deleted task views and any failure beside the last valid state. Historical member fields remain for decoding compatibility and grant no runtime authority. A failed projection rejects subsequent Team reads and mutations without appending further Team events. The [subsystem reference](../../../docs/subsystems/agent-team.md#web-projection) defines the wire types. +The `agentTeam` projection publishes non-deleted task views and any failure beside the last valid state. Historical member fields remain in persisted projection state for decoding compatibility, are omitted from the browser view, and grant no runtime authority. A failed projection rejects subsequent Team reads and mutations without appending further Team events. The [subsystem reference](../../../docs/subsystems/agent-team.md#web-projection) defines the wire types. The [Web UI](../client-ui-agent-team/README.md) displays tasks only. Ordinary subagent navigation owns the member list; task owner labels reuse Session and catalog data with an id fallback. diff --git a/packages/experimental/agent-team/README.zh.md b/packages/experimental/agent-team/README.zh.md index caa7f18142..42e878d1a1 100644 --- a/packages/experimental/agent-team/README.zh.md +++ b/packages/experimental/agent-team/README.zh.md @@ -136,7 +136,7 @@ subagent 服务通过 `subagents.rootOf()` 解析当前在线调用者的根。 `Service.init` 等待 `agentTeam` 投影注册完成后才发布服务就绪状态。日志持有返回的 handle,通过 `stateOf(session)` 读取任务状态;调用者使用 Team 服务,不另行注册 reader。 -任务修改向当前在线根 Session 追加 `team/task`,并在发出等待信号前刷盘。任务记录仅保留在日志中;信号不是消息,也不是持久化事件。等待工具通过普通工具流水线记录结果。不重写已提交任务或历史载荷代次。 +任务修改在向当前在线根 Session 追加 `team/task` 前响应取消,随后刷盘并发出等待信号。取消不能撤回已追加的记录。刷盘失败以 `TEAM_TASK_PERSISTENCE_UNCONFIRMED` 报告任务 id 和修订号:任务已可见,但持久性未知。重试前先读取该任务。任务记录仅保留在日志中;信号不是消息,也不是持久化事件。等待工具通过普通工具流水线记录结果。不重写已提交任务或历史载荷代次。 邮箱投影和检查点接纳保留本地声明校验器之外的已解码 JSON 字段,包括自身的 `__proto__` 键。历史载荷代次保持不变;投影缓存版本变化时从 Session 日志重建。 @@ -146,7 +146,7 @@ subagent 服务通过 `subagents.rootOf()` 解析当前在线调用者的根。 ### 浏览器投影 -`agentTeam` 投影发布未删除任务视图,并在最后有效状态旁报告失败。历史成员字段为解码兼容性保留,不授予运行时权限。投影失败后,后续 Team 读取和修改均报错,不再追加 Team 事件。[子系统参考](../../../docs/subsystems/agent-team.zh.md#web-projection)定义线上的类型。 +`agentTeam` 投影发布未删除任务视图,并在最后有效状态旁报告失败。历史成员字段保留在持久化投影状态中以供解码,不发送到浏览器,也不授予运行时权限。投影失败后,后续 Team 读取和修改均报错,不再追加 Team 事件。[子系统参考](../../../docs/subsystems/agent-team.zh.md#web-projection)定义线上的类型。 [Web UI](../client-ui-agent-team/README.zh.md)仅展示任务。普通 subagent 导航负责成员列表;任务所有者标签复用 Session 和目录数据,缺失时使用 id。 diff --git a/packages/experimental/agent-team/locale/en.json b/packages/experimental/agent-team/locale/en.json index f82366348e..008ecd360b 100644 --- a/packages/experimental/agent-team/locale/en.json +++ b/packages/experimental/agent-team/locale/en.json @@ -1,6 +1,6 @@ { "meta": { "title": "Team Collaboration", - "description": "Track Team members and manage the shared task board." + "description": "Manage the shared task board for a subagent tree." } } diff --git a/packages/experimental/agent-team/locale/zh.json b/packages/experimental/agent-team/locale/zh.json index fabdda566a..bd6301f19f 100644 --- a/packages/experimental/agent-team/locale/zh.json +++ b/packages/experimental/agent-team/locale/zh.json @@ -1,6 +1,6 @@ { "meta": { "title": "团队协作", - "description": "跟踪团队成员并管理共享任务看板。" + "description": "管理子代理树的共享任务看板。" } } diff --git a/packages/experimental/agent-team/package.json b/packages/experimental/agent-team/package.json index 569590017d..68da2fa2ff 100644 --- a/packages/experimental/agent-team/package.json +++ b/packages/experimental/agent-team/package.json @@ -1,6 +1,6 @@ { "name": "@deepseek-ai/dsh-experimental-agent-team", - "description": "Implicit-root Agent Teams roster of subagents, shared task DAG, and change waits", + "description": "Shared task DAG and change waits for subagent trees", "version": "0.2.1-rc.2", "publishConfig": { "access": "public" @@ -58,7 +58,6 @@ "@deepseek-ai/dsh-agent-loop-testkit": "workspace:*", "@deepseek-ai/dsh-llm": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", - "@deepseek-ai/dsh-session-persistence": "workspace:*", "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*", "@deepseek-ai/dsh-session-projection": "workspace:*", "@deepseek-ai/dsh-session-query": "workspace:*", diff --git a/packages/experimental/agent-team/src/client.ts b/packages/experimental/agent-team/src/client.ts index d117679270..1650a046ec 100644 --- a/packages/experimental/agent-team/src/client.ts +++ b/packages/experimental/agent-team/src/client.ts @@ -1,8 +1,7 @@ -/** Client-safe Agent Teams roster and task views plus the `agentTeam` projection vocabulary. */ +/** Client-safe Agent Teams task views plus the `agentTeam` projection vocabulary. */ export type { TeamMemberPhase, - TeamMemberProjection, TeamProjection, TeamModeProjection, TeamTaskId, diff --git a/packages/experimental/agent-team/src/index.ts b/packages/experimental/agent-team/src/index.ts index f6aeb467ca..994c39d6ac 100644 --- a/packages/experimental/agent-team/src/index.ts +++ b/packages/experimental/agent-team/src/index.ts @@ -152,10 +152,12 @@ export class TeamService extends Service { * Create one unowned pending task in the Team Lead log. * @param caller - exact live Team member creating the task. * @param request - task text, blockers, and advisory write scopes. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the revision-one task view. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ - async createTask(caller: Agent, request: CreateTeamTaskRequest): Promise { - return await this.tasks.create(caller, this.ctx.subagents.rootOf(caller), request) + async createTask(caller: Agent, request: CreateTeamTaskRequest, signal?: AbortSignal): Promise { + return await this.tasks.create(caller, this.ctx.subagents.rootOf(caller), request, signal) } /** @@ -181,10 +183,12 @@ export class TeamService extends Service { * Compare-and-set one authorized task transition. * @param caller - exact live Team member authorizing the mutation. * @param request - task identity, expected revision, action, and action fields. + * @param signal - cancellation before the task event is appended; omitted means no cancellation. * @returns the committed next task revision. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying. */ - async updateTask(caller: Agent, request: UpdateTeamTaskRequest): Promise { - return await this.tasks.update(caller, this.ctx.subagents.rootOf(caller), request) + async updateTask(caller: Agent, request: UpdateTeamTaskRequest, signal?: AbortSignal): Promise { + return await this.tasks.update(caller, this.ctx.subagents.rootOf(caller), request, signal) } } diff --git a/packages/experimental/agent-team/src/journal.ts b/packages/experimental/agent-team/src/journal.ts index d737fcd6e8..f19696c735 100644 --- a/packages/experimental/agent-team/src/journal.ts +++ b/packages/experimental/agent-team/src/journal.ts @@ -7,6 +7,7 @@ import type { SessionId } from '@deepseek-ai/dsh-session' import type { TeamProjectionState, TeamState } from './projection.ts' import { TeamId } from './types.ts' +import { TeamError } from './error.ts' import type { TeamTaskSnapshot } from './types.ts' /** Owns per-Lead transaction order and committed Team event publication. */ @@ -58,11 +59,19 @@ export class TeamJournal { * Commit a task before notifying current waits in its tree. * @param root - live tree root whose Session stores the task. * @param task - complete committed task revision. - * @throws when persistence fails. + * @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED after append when durability cannot be confirmed; read the task before retrying. */ async appendAndFlush(root: Agent, task: TeamTaskSnapshot): Promise { root.session.append('team/task', { version: 2, teamId: TeamId(root.id), task }) - await this.ctx.sessions.flush(root.session) + try { + await this.ctx.sessions.flush(root.session) + } catch (error: unknown) { + throw new TeamError( + `task "${task.id}" revision ${task.revision} is visible, but persistence was not confirmed; read the task before retrying`, + 'TEAM_TASK_PERSISTENCE_UNCONFIRMED', + { cause: error }, + ) + } this.onCommit(root) } } diff --git a/packages/experimental/agent-team/src/projection.ts b/packages/experimental/agent-team/src/projection.ts index c7bcf7fa78..4bc4b30275 100644 --- a/packages/experimental/agent-team/src/projection.ts +++ b/packages/experimental/agent-team/src/projection.ts @@ -7,7 +7,6 @@ import type { SessionEvent, SessionEventMap, SessionId } from '@deepseek-ai/dsh- import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection' import type { TeamId, - TeamMemberProjection, TeamMemberSnapshot, TeamMessageId, TeamMessageSnapshot, @@ -304,14 +303,6 @@ function applyCurrentTeamEvent(state: TeamProjectionState, event: TeamSessionEve } } -const teamMemberProjectionSchema = z.object({ - id: sessionIdSchema, - description: z.string().optional(), - role: z.enum(['lead', 'teammate']), - phase: z.enum(['provisioning', 'active', 'failed']), - error: z.string().optional(), -}).strict() as z.ZodType - const teamTaskViewSchema = z.object({ id: teamTaskIdSchema, revision: positiveSafeInteger, @@ -326,28 +317,15 @@ const teamTaskViewSchema = z.object({ }).strict() as z.ZodType const teamProjectionSchema = z.object({ - members: z.array(teamMemberProjectionSchema), tasks: z.array(teamTaskViewSchema), failure: z.string().optional(), }).strict() as z.ZodType -/** Client views keyed by the member and task collections they were derived from. */ -const teamProjectionViews = new WeakMap>() +/** Client views keyed by their task collection; historical roster and mailbox changes publish nothing. */ +const teamProjectionViews = new WeakMap() function buildTeamProjection(state: TeamProjectionState): TeamProjection { - const rootId = brandString(state.id) - const members: TeamMemberProjection[] = [{ id: rootId, role: 'lead', phase: 'active' }] - for (const member of state.members) { - members.push({ - id: member.id, - description: member.description, - role: 'teammate', - phase: member.phase, - ...member.error === undefined ? {} : { error: member.error }, - }) - } return { - members, tasks: state.tasks .filter(task => task.status !== 'deleted') .map(task => projectTaskView(state, task)), @@ -356,36 +334,31 @@ function buildTeamProjection(state: TeamProjectionState): TeamProjection { } /** - * Durable client view of one Team state. Mailbox-only state changes reuse the + * Durable client view of one Team state. Historical roster and mailbox changes reuse the * previous view reference, so the live drive publishes nothing for them. * A failure is terminal: later events retain the failed state reference and * do not republish its view. * @param state - current Team state. - * @returns the roster and non-deleted task board, plus any projection failure. + * @returns the non-deleted task board, plus any projection failure. */ export function teamProjectionView(state: TeamProjectionState): TeamProjection { if (state.failure !== undefined) return buildTeamProjection(state) - let byTasks = teamProjectionViews.get(state.members) - if (byTasks === undefined) { - byTasks = new WeakMap() - teamProjectionViews.set(state.members, byTasks) - } - let view = byTasks.get(state.tasks) + let view = teamProjectionViews.get(state.tasks) if (view === undefined) { view = buildTeamProjection(state) - byTasks.set(state.tasks, view) + teamProjectionViews.set(state.tasks, view) } return view } /** * Team projection selected by the projected Session identity; the wire view - * carries the Lead, historical roster, and task board. Current subagents are + * carries the task board. Current subagents are * supplied separately by the existing `subagentCatalog` projection. */ export const teamProjectionDefinition = { key: 'agentTeam', - stateVersion: 6, + stateVersion: 4, stateSchema: teamProjectionEntrySchema, init: (header, _inheritedEventCount) => emptyTeamState(header.id), apply: applyProjectionEvent, diff --git a/packages/experimental/agent-team/src/task-board.ts b/packages/experimental/agent-team/src/task-board.ts index 5ad261d95c..73d90f6ebb 100644 --- a/packages/experimental/agent-team/src/task-board.ts +++ b/packages/experimental/agent-team/src/task-board.ts @@ -50,10 +50,12 @@ export class TeamTaskBoard { * @param caller - exact live Agent creating the task. * @param root - live subagent tree root owning the board. * @param request - task text, blockers, and advisory write scopes. + * @param signal - cancellation before append; omitted means no cancellation. * @returns the revision-one task view. */ - async create(caller: Agent, root: Agent, request: CreateTeamTaskRequest): Promise { + async create(caller: Agent, root: Agent, request: CreateTeamTaskRequest, signal?: AbortSignal): Promise { return this.journal.transact(root.id, async () => { + signal?.throwIfAborted() const state = this.state(root) const active = state.tasks.filter(task => task.status !== 'deleted').length if (active >= this.maxTasks) { @@ -73,6 +75,7 @@ export class TeamTaskBoard { writeScopes: this.writeScopes(request.writeScopes ?? []), } this.assertTaskGraph(state, task) + // The transaction queue may outlive its caller. this.ctx.subagents.rootOf(caller) await this.journal.appendAndFlush(root, task) return projectTaskView(state, task) @@ -109,14 +112,17 @@ export class TeamTaskBoard { * @param caller - exact live Team member authorizing the mutation. * @param root - live subagent tree root owning the board. * @param request - task identity, expected revision, action, and action fields. + * @param signal - cancellation before append; omitted means no cancellation. * @returns the committed next task revision. */ async update( caller: Agent, root: Agent, request: UpdateTeamTaskRequest, + signal?: AbortSignal, ): Promise { return this.journal.transact(root.id, async () => { + signal?.throwIfAborted() const state = this.state(root) const current = state.tasks.find(task => task.id === request.taskId) if (current === undefined) throw new TeamError(`team task "${request.taskId}" not found`, 'TEAM_TASK_NOT_FOUND') @@ -190,7 +196,7 @@ export class TeamTaskBoard { break } if (!taskReady(state, current)) throw new TeamError(`team task "${current.id}" is blocked`, 'TEAM_TASK_BLOCKED') - next = { ...current, status: 'in_progress', ownerId: await this.resolveAssignee(root, request.owner) } + next = { ...current, status: 'in_progress', ownerId: await this.resolveAssignee(root, request.owner, signal) } break } case 'delete': { @@ -212,6 +218,8 @@ export class TeamTaskBoard { revision: current.revision + 1, } this.assertTaskGraph(state, task) + signal?.throwIfAborted() + // Queueing and assignee discovery may outlive the caller. this.ctx.subagents.rootOf(caller) await this.journal.appendAndFlush(root, task) return projectTaskView(state, task) @@ -219,9 +227,9 @@ export class TeamTaskBoard { } /** Resolve a task owner through the same durable tree used by Agent discovery. */ - private async resolveAssignee(root: Agent, target: SessionId): Promise { + private async resolveAssignee(root: Agent, target: SessionId, signal?: AbortSignal): Promise { if (target === root.id) return target - const descendants = await this.ctx.subagents.listDescendants(root.id) + const descendants = await this.ctx.subagents.listDescendants(root.id, signal) if (descendants.some(entry => entry.kind !== 'diagnostic' && entry.mode === 'continuable' && entry.id === target)) return target throw new TeamError(`agent "${target}" is not a continuable descendant of this task board's root`, 'TEAM_MEMBER_NOT_FOUND') } diff --git a/packages/experimental/agent-team/src/types.ts b/packages/experimental/agent-team/src/types.ts index 1804552512..429da7ad7c 100644 --- a/packages/experimental/agent-team/src/types.ts +++ b/packages/experimental/agent-team/src/types.ts @@ -86,27 +86,12 @@ export interface TeamTaskView { readonly writeScopeWarnings: string[] } -/** - * One roster row published through the `agentTeam` Session projection: the - * Lead and each historical roster member. Current children come from `subagentCatalog`. - */ -export interface TeamMemberProjection { - readonly id: SessionId - /** Delegated responsibility; absent for the Lead. Never used as an address. */ - readonly description?: string - readonly role: 'lead' | 'teammate' - /** The Lead and continuable children are always `active`. Turn activity comes from Session status. */ - readonly phase: TeamMemberPhase - readonly error?: string -} - /** * Durable Team state published to browser clients through the Lead Session's * `agentTeam` projection. `failure` names the first rejected persisted Team - * record; members and tasks then stay at the last valid state. + * record; tasks then stay at the last valid state. */ export interface TeamProjection { - readonly members: TeamMemberProjection[] readonly tasks: TeamTaskView[] readonly failure?: string } diff --git a/packages/experimental/agent-team/tests/persistence.spec.ts b/packages/experimental/agent-team/tests/persistence.spec.ts index 9744455ef5..b258d48657 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -157,9 +157,6 @@ for (const backend of backends) { }) const projections = second.ctx.sessionProjections.snapshot(rootHandle.agent.session).values expect(projections.agentTeam).toMatchObject({ - members: [ - { id: rootId, role: 'lead', phase: 'active' }, - ], tasks: [{ id: created.id, subject: 'carry over' }], }) expect(projections.subagentCatalog).toMatchObject([ @@ -211,12 +208,7 @@ for (const backend of backends) { pendingMessages: [message], }) const projections = second.ctx.sessionProjections.snapshot(rootHandle.agent.session).values - expect(projections.agentTeam).toMatchObject({ - members: [ - { id: rootId, role: 'lead' }, - { id: childId, description: 'historical mailbox target', role: 'teammate', phase: 'active' }, - ], - }) + expect(projections.agentTeam).toEqual({ tasks: [] }) expect(second.ctx.agents.get(childId)).toBeUndefined() expect(second.adapter.requests).toEqual([]) diff --git a/packages/experimental/agent-team/tests/projection-events.spec.ts b/packages/experimental/agent-team/tests/projection-events.spec.ts index bc7e0a4ecc..40cc2ae1c0 100644 --- a/packages/experimental/agent-team/tests/projection-events.spec.ts +++ b/packages/experimental/agent-team/tests/projection-events.spec.ts @@ -439,29 +439,7 @@ describe('Agent Teams projection events', () => { const state = project(ROOT, []) const next = teamProjectionDefinition.apply(state, catalog('child', 0)) expect(next).toBe(state) - expect(teamProjectionView(next).members).toEqual([{ id: ROOT, role: 'lead', phase: 'active' }]) - }) - - it('rebuilds version-5 checkpoints without retaining their copied catalog', async () => { - const ctx = new Context() - onTestFinished(() => ctx.fiber.dispose()) - await ctx.plugin(SessionProjectionRegistry) - await ctx.sessionProjections.register(teamProjectionDefinition) - const events = [catalog('child', 0), event('team/task', { version: 2, teamId: TEAM, task: task() }, SessionSeq(1))] - const oldState = { - ...project(ROOT, events), inheritedEventCount: 0, - children: [{ id: SessionId('child'), description: 'child task' }], - } - const restored = ctx.sessionProjections.restore( - { agentTeam: { ver: 5, seq: SessionSeq(1), val: oldState } }, - events, SessionLogOffset(0), - { version: SESSION_FORMAT_VERSION, id: ROOT, createdAt: 0, isSeeded: false }, - SessionLogOffset(0), - ) - const state = teamProjectionDefinition.stateSchema.parse(restored.checkpoint['agentTeam']!.val) - expect(state.tasks).toEqual([task()]) - expect(state).not.toHaveProperty('children') - expect(state).not.toHaveProperty('inheritedEventCount') + expect(teamProjectionView(next)).toEqual({ tasks: [] }) }) describe('copy-on-write state and client view', () => { @@ -533,10 +511,6 @@ describe('Agent Teams projection events', () => { const view = teamProjectionView(state) expect(teamProjectionDefinition.wire.viewSchema.parse(view)).toEqual(view) expect(view).toEqual({ - members: [ - { id: ROOT, role: 'lead', phase: 'active' }, - { id: CHILD, description: 'worker', role: 'teammate', phase: 'active' }, - ], tasks: [ { id: 'task-1', revision: 2, subject: 'subject', description: 'description', status: 'in_progress', @@ -556,26 +530,17 @@ describe('Agent Teams projection events', () => { const withMember = teamProjectionDefinition.apply(withMail, event('team/member', { version: 2, teamId: TEAM, member: member({ id: SessionId('child-b'), name: 'worker-b' }), }, SessionSeq(7))) - const memberView = teamProjectionView(withMember) - expect(memberView).not.toBe(view) - expect(memberView.members.at(-1)).toEqual({ - id: 'child-b', description: 'worker', role: 'teammate', phase: 'provisioning', - }) - - const failedMember = event('team/member', { - version: 2, teamId: TEAM, member: member({ id: SessionId('child-b'), name: 'worker-b', phase: 'failed', error: 'provider failed' }), - }, SessionSeq(8)) - expect(teamProjectionView(teamProjectionDefinition.apply(withMember, failedMember)).members.at(-1)) - .toMatchObject({ phase: 'failed', error: 'provider failed' }) + expect(teamProjectionView(withMember)).toBe(view) + expect(withMember.members).toHaveLength(2) }) - it('publishes the failure with the last valid roster and tasks', () => { + it('publishes the failure with the last valid tasks', () => { const state = project(ROOT, [memberEvent, taskEvent, event('team/task', { version: 1 as 2, teamId: TEAM, task: task(), }, SessionSeq(2))]) const view = teamProjectionView(state) expect(view.failure).toMatch(/unsupported Agent Teams event version 1/) - expect(view.members).toHaveLength(2) + expect(view).not.toHaveProperty('members') expect(view.tasks).toHaveLength(1) expect(teamProjectionDefinition.wire.viewSchema.parse(view)).toEqual(view) }) @@ -593,7 +558,6 @@ describe('Agent Teams projection events', () => { const lead = ctx.sessions.create(ROOT) const other = ctx.sessions.create(SessionId('other-root')) expect(ctx.sessionProjections.snapshot(lead).values['agentTeam']).toEqual({ - members: [{ id: ROOT, role: 'lead', phase: 'active' }], tasks: [], }) @@ -613,7 +577,7 @@ describe('Agent Teams projection events', () => { expect(published).toHaveLength(2) expect(published[1]).toMatchObject({ session: other.id, - value: { members: [{ id: other.id, role: 'lead', phase: 'active' }] }, + value: { tasks: [{ id: 'task-1' }] }, }) expect(ctx.sessionProjections.snapshot(lead).values['agentTeam']).toMatchObject({ tasks: [{ id: 'task-1' }], diff --git a/packages/experimental/agent-team/tests/team.spec.ts b/packages/experimental/agent-team/tests/team.spec.ts index d253db6040..0874e65b14 100644 --- a/packages/experimental/agent-team/tests/team.spec.ts +++ b/packages/experimental/agent-team/tests/team.spec.ts @@ -244,7 +244,9 @@ describe('Tree-owned task boards', () => { expect([first.id, second.id]).toEqual([TeamTaskId('task-1'), TeamTaskId('task-2')]) }) - it.each(['create', 'claim'] as const)('rejects a queued %s after its caller is disposed', async (action) => { + it.each([ + ['create', 'disposed'], ['claim', 'disposed'], ['create', 'cancelled'], ['claim', 'cancelled'], + ] as const)('rejects a queued %s after its caller is %s', async (action, stopped) => { const { ctx, lead } = await setup([]) const child = await ctx.agents.create({ sessionId: SessionId('queued-caller'), parentAgent: lead, agentOptions: { provider: 'mock', model: 'mock' }, @@ -261,11 +263,13 @@ describe('Tree-owned task boards', () => { const creating = ctx.agentTeams.createTask(lead, { subject: 'blocking', description: 'holds the transaction queue' }) try { await entered.promise + const controller = new AbortController() const queued = action === 'create' - ? ctx.agentTeams.createTask(child.agent, { subject: 'disposed', description: 'must not be committed' }) - : ctx.agentTeams.updateTask(child.agent, { taskId: task.id, expectedRevision: task.revision, action: 'claim' }) - const rejected = expect(queued).rejects.toMatchObject({ code: 'UNAUTHORIZED' }) - await child.dispose() + ? ctx.agentTeams.createTask(child.agent, { subject: 'stopped', description: 'must not be committed' }, controller.signal) + : ctx.agentTeams.updateTask(child.agent, { taskId: task.id, expectedRevision: task.revision, action: 'claim' }, controller.signal) + const rejected = expect(queued).rejects.toMatchObject(stopped === 'disposed' ? { code: 'UNAUTHORIZED' } : { name: 'AbortError' }) + if (stopped === 'disposed') await child.dispose() + else controller.abort() release.resolve(undefined) await creating await rejected @@ -697,7 +701,13 @@ describe('Task-board wait notifications', () => { expect(notified).toEqual([lead, child]) notified.length = 0 vi.spyOn(ctx.sessions, 'flush').mockRejectedValueOnce(new Error('storage unavailable')) - await expect(ctx.agentTeams.createTask(lead, { subject: 'two', description: 'failed flush' })).rejects.toThrow('storage unavailable') + await expect(ctx.agentTeams.createTask(lead, { subject: 'two', description: 'failed flush' })).rejects.toMatchObject({ + code: 'TEAM_TASK_PERSISTENCE_UNCONFIRMED', + message: 'task "task-2" revision 1 is visible, but persistence was not confirmed; read the task before retrying', + cause: new Error('storage unavailable'), + }) + expect(ctx.agentTeams.getTask(lead, TeamTaskId('task-2'))).toMatchObject({ subject: 'two', revision: 1 }) + expect(ctx.agentTeams.listTasks(lead)).toHaveLength(2) expect(notified).toEqual([]) await expect(ctx.agentTeams.updateTask(lead, { taskId: TeamTaskId('task-1'), expectedRevision: 99, action: 'claim', diff --git a/packages/experimental/client-ui-agent-team/README.i18n.yaml b/packages/experimental/client-ui-agent-team/README.i18n.yaml index f9641f463c..cfca3b5a21 100644 --- a/packages/experimental/client-ui-agent-team/README.i18n.yaml +++ b/packages/experimental/client-ui-agent-team/README.i18n.yaml @@ -18,8 +18,8 @@ en: c86ee20fce90cf0c zh: 52127ab35418f56a /deepseek-ai-dsh-experimental-client-ui-agent-team/use-this-package/inspect-the-task-board: - en: 5f3dc9476b3dcd25 - zh: c77cbab2c4250f3b + en: d565ccd7b37d24c5 + zh: c9e780a9972c9373 /deepseek-ai-dsh-experimental-client-ui-agent-team/understand-the-implementation: en: 395ed32cf10bbb58 zh: a96230289ec9dcb3 diff --git a/packages/experimental/client-ui-agent-team/README.md b/packages/experimental/client-ui-agent-team/README.md index a6c2223c00..3df47f0356 100644 --- a/packages/experimental/client-ui-agent-team/README.md +++ b/packages/experimental/client-ui-agent-team/README.md @@ -33,7 +33,7 @@ Enable this package through [`@deepseek-ai/dsh-experimental-agent-team-profile`] Ready pending tasks use idle, blocked pending tasks use warning, in-progress tasks use ongoing, and completed tasks use done. -The read-only board shows task identity, owner label, blockers, readiness, advisory write scopes, and overlap warnings. Owner labels reuse existing Session and subagent catalog data, falling back to the Session id. Long descriptions can expand. The heading shows the task count and an empty board has a short explanation. Agents mutate tasks through tools; the panel provides no mutation controls. A projection failure appears above the last valid tasks. +The read-only board shows task identity, owner label, blockers, readiness, advisory write scopes, and overlap warnings. The root owner is labeled Lead; other owners use the subagent catalog label, then the Session title, then the Session id. Long descriptions can expand. The heading shows the task count and an empty board has a short explanation. Agents mutate tasks through tools; the panel provides no mutation controls. A projection failure appears above the last valid tasks. ----- diff --git a/packages/experimental/client-ui-agent-team/README.zh.md b/packages/experimental/client-ui-agent-team/README.zh.md index 59a2de6918..a514b22a22 100644 --- a/packages/experimental/client-ui-agent-team/README.zh.md +++ b/packages/experimental/client-ui-agent-team/README.zh.md @@ -33,7 +33,7 @@ kind: "package-reference" 可开始的 pending 任务使用 idle 灰点,被依赖阻塞的 pending 任务使用 warning 橙点,in-progress 任务使用 ongoing loading,completed 任务使用 done 绿点。 -只读任务板显示任务身份、所有者标签、阻塞项、就绪状态、建议写入范围和重叠警告。所有者标签复用现有 Session 和 subagent 目录数据,缺失时使用 Session id。长描述可展开。标题显示任务数量,空任务板提供简短说明。Agent 通过工具修改任务;面板不提供修改控件。投影失败显示在最后有效任务上方。 +只读任务板显示任务身份、所有者标签、阻塞项、就绪状态、建议写入范围和重叠警告。根负责人显示为“主代理”;其他负责人优先使用 subagent 目录标签,其次为 Session 标题,最后为 Session id。长描述可展开。标题显示任务数量,空任务板提供简短说明。Agent 通过工具修改任务;面板不提供修改控件。投影失败显示在最后有效任务上方。 ----- diff --git a/packages/experimental/client-ui-agent-team/package.json b/packages/experimental/client-ui-agent-team/package.json index 8b2b15db4f..1ca7f4ded1 100644 --- a/packages/experimental/client-ui-agent-team/package.json +++ b/packages/experimental/client-ui-agent-team/package.json @@ -53,7 +53,6 @@ "@deepseek-ai/dsh-client-ui-conversation": "workspace:*", "@deepseek-ai/dsh-client-ui-primitives": "workspace:*", "@deepseek-ai/dsh-client-ui-renderer": "workspace:*", - "@deepseek-ai/dsh-client-ui-session": "workspace:*", "@deepseek-ai/dsh-client-ui-slots": "workspace:*", "@deepseek-ai/dsh-experimental-agent-team": "workspace:*", "@deepseek-ai/dsh-session": "workspace:*", diff --git a/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx b/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx index b32e5d0423..88d3d0c163 100644 --- a/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx +++ b/packages/experimental/client-ui-agent-team/src/client/TeamAction.tsx @@ -50,10 +50,11 @@ function TaskCard({ task, rootId, useSessions, t }: { }) { const owner = useSessions((state) => { if (task.ownerId === undefined) return undefined + if (task.ownerId === rootId) return t('lead') const summary = state.byId[task.ownerId] const parentId = summary?.parentId ?? rootId const catalog = state.projectionsBySession[parentId]?.values.subagentCatalog - return summary?.title ?? catalog?.find(child => child.id === task.ownerId)?.label ?? summary?.displayTitle ?? task.ownerId + return catalog?.find(child => child.id === task.ownerId)?.label ?? summary?.title ?? summary?.displayTitle ?? task.ownerId }) const [expanded, setExpanded] = useState(false) const [clamped, setClamped] = useState(false) diff --git a/packages/experimental/client-ui-agent-team/src/client/locales.ts b/packages/experimental/client-ui-agent-team/src/client/locales.ts index bf1b023fa0..5c4f8fac1f 100644 --- a/packages/experimental/client-ui-agent-team/src/client/locales.ts +++ b/packages/experimental/client-ui-agent-team/src/client/locales.ts @@ -14,7 +14,8 @@ export const zh = { failure: '任务记录无效:{message}', empty: '暂无共享任务,可以通过对话创建', tasks: '共享任务', - owner: 'Owner', + owner: '负责人', + lead: '主代理', unowned: '未分配', blockedBy: '依赖', writeScopes: '写入范围', @@ -42,6 +43,7 @@ export const en = { empty: 'No shared tasks yet. Create them through the conversation.', tasks: 'Shared tasks', owner: 'Owner', + lead: 'Lead', unowned: 'Unowned', blockedBy: 'Blocked by', writeScopes: 'Write scopes', diff --git a/packages/experimental/client-ui-agent-team/tests/team-action.client.spec.tsx b/packages/experimental/client-ui-agent-team/tests/team-action.client.spec.tsx index 65d33394a6..8c8cd15145 100644 --- a/packages/experimental/client-ui-agent-team/tests/team-action.client.spec.tsx +++ b/packages/experimental/client-ui-agent-team/tests/team-action.client.spec.tsx @@ -39,7 +39,7 @@ const task: TeamTask = { ready: false, writeScopeWarnings: ['write scopes overlap with task-2'], } -const team: TeamProjection = { members: [], tasks: [task] } +const team: TeamProjection = { tasks: [task] } type Projections = SessionListState['projectionsBySession'] @@ -123,13 +123,11 @@ describe('TeamAction', () => { }) it('shows the task board without a roster or a subagent catalog', () => { - const b = bench({ projections: { [SESSION]: { state: 'ready', error: null, values: { agentTeamMode: { enabled: true }, agentTeam: { - ...team, - members: [{ id: WORKER, role: 'teammate', phase: 'failed', description: 'historical member', error: 'old failure' }], - } } } } }) + const b = bench({ byId: { [SESSION]: summary(SESSION, 'Open the task board.') } }) render() openPanel() expect(screen.getByText(task.subject)).toBeTruthy() + expect(screen.getByText('负责人: 主代理')).toBeTruthy() expect(screen.getByRole('heading', { name: '共享任务1' })).toBeTruthy() expect(screen.queryByText('historical member')).toBeNull() expect(screen.queryByText('old failure')).toBeNull() @@ -137,7 +135,7 @@ describe('TeamAction', () => { expect(screen.queryByRole('button', { name: /打开成员会话/u })).toBeNull() }) - it('updates owner labels from Session titles and direct-parent catalogs', () => { + it('prefers direct-parent catalog labels and falls back to Session titles', () => { const b = bench({ byId: { [SESSION]: summary(SESSION), @@ -155,13 +153,15 @@ describe('TeamAction', () => { }) render() openPanel() - expect(screen.getByText('Owner: Nested implementer')).toBeTruthy() + expect(screen.getByText('负责人: Nested implementer')).toBeTruthy() act(() => { b.sessions.update((draft) => { draft.byId[NESTED] = { ...draft.byId[NESTED]!, title: 'Runtime work' } }) }) - expect(screen.getByText('Owner: Runtime work')).toBeTruthy() + expect(screen.getByText('负责人: Nested implementer')).toBeTruthy() + act(() => { b.sessions.update((draft) => { draft.projectionsBySession = { [SESSION]: draft.projectionsBySession[SESSION]! } }) }) + expect(screen.getByText('负责人: Runtime work')).toBeTruthy() act(() => { b.sessions.set({ ...b.sessions.getSnapshot(), byId: { [SESSION]: summary(SESSION), [WORKER]: summary(WORKER) }, }) }) - expect(screen.getByText(`Owner: ${NESTED}`)).toBeTruthy() + expect(screen.getByText(`负责人: ${NESTED}`)).toBeTruthy() }) it('uses a root catalog label when the owner has no Session summary', () => { @@ -171,9 +171,9 @@ describe('TeamAction', () => { } } } }) render() openPanel() - expect(screen.getByText('Owner: Current responsibility')).toBeTruthy() + expect(screen.getByText('负责人: Current responsibility')).toBeTruthy() setProjection(b.sessions, SESSION, { ...team, tasks: [{ ...task, ownerId: WORKER }] }) - expect(screen.getByText(`Owner: ${WORKER}`)).toBeTruthy() + expect(screen.getByText(`负责人: ${WORKER}`)).toBeTruthy() }) it('applies later projection frames without a refresh action', () => { @@ -204,7 +204,7 @@ describe('TeamAction', () => { expect(screen.queryByRole('status')).toBeNull() setProjectionSnapshot(b.sessions, SESSION, { state: 'idle', error: null, values: { agentTeamMode: { enabled: true } } }) expect(screen.getByRole('status').textContent).toBe(zh.unavailable) - setProjection(b.sessions, SESSION, { members: [], tasks: [] }) + setProjection(b.sessions, SESSION, { tasks: [] }) expect(screen.getByText(zh.empty)).toBeTruthy() }) @@ -254,7 +254,7 @@ describe('TeamAction', () => { byId: { ...current.byId, ['unrelated' as SessionId]: summary('unrelated' as SessionId) }, projectionsBySession: { ...current.projectionsBySession, - ['unrelated' as SessionId]: { state: 'ready', error: null, values: { agentTeamMode: { enabled: true }, agentTeam: { members: [], tasks: [] } } }, + ['unrelated' as SessionId]: { state: 'ready', error: null, values: { agentTeamMode: { enabled: true }, agentTeam: { tasks: [] } } }, }, }) }) @@ -287,8 +287,8 @@ describe('TeamAction', () => { openPanel() expect(screen.getByText(zh.ready)).toBeTruthy() expect(screen.getByText(zh.blocked)).toBeTruthy() - expect(screen.getAllByText('Owner: 未分配')).toHaveLength(2) - expect(screen.getByText(`Owner: ${WORKER}`)).toBeTruthy() + expect(screen.getAllByText('负责人: 未分配')).toHaveLength(2) + expect(screen.getByText(`负责人: ${WORKER}`)).toBeTruthy() const tasks = [...document.querySelectorAll('article')] expect(tasks.map(card => card.querySelector('[data-state]')?.getAttribute('data-state'))) .toEqual(['idle', 'warning', 'done']) diff --git a/packages/experimental/client-ui-agent-team/tsconfig.json b/packages/experimental/client-ui-agent-team/tsconfig.json index d2942f104b..f19f9a7052 100644 --- a/packages/experimental/client-ui-agent-team/tsconfig.json +++ b/packages/experimental/client-ui-agent-team/tsconfig.json @@ -16,7 +16,6 @@ { "path": "../../client/ui-conversation/tsconfig.client.json" }, { "path": "../../client/ui-primitives" }, { "path": "../../client/ui-renderer" }, - { "path": "../../client/ui-session" }, { "path": "../../client/ui-slots" }, { "path": "../../core/session" } ] diff --git a/packages/experimental/tool-agent-team/src/index.ts b/packages/experimental/tool-agent-team/src/index.ts index 8f73666c30..16ea0ca626 100644 --- a/packages/experimental/tool-agent-team/src/index.ts +++ b/packages/experimental/tool-agent-team/src/index.ts @@ -154,7 +154,7 @@ function install(agent: Agent, ctx: Context): () => void { description: args.description, ...args.blocked_by === undefined ? {} : { blockedBy: args.blocked_by.map(TeamTaskId) }, ...args.write_scopes === undefined ? {} : { writeScopes: args.write_scopes }, - }) + }, exec.signal) return (await namedTasks(ctx, caller, [task], exec.signal))[0] as NamedTaskView }, }))) @@ -238,7 +238,7 @@ function install(agent: Agent, ctx: Context): () => void { ...args.blocked_by === undefined ? {} : { blockedBy: args.blocked_by.map(TeamTaskId) }, ...args.write_scopes === undefined ? {} : { writeScopes: args.write_scopes }, ...owner === undefined ? {} : { owner }, - }) + }, exec.signal) return (await namedTasks(ctx, caller, [task], exec.signal))[0] as NamedTaskView }, }))) diff --git a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts index 000ae1ca42..d3863f5400 100644 --- a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts +++ b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts @@ -172,6 +172,36 @@ async function interrupt(ctx: Context, lead: Agent, target: SessionId): Promise< } describe('dsh-tool-team', () => { + it.each([true, false])('cancels reassignment without appending when discovery observes cancellation: %s', async (observesCancellation) => { + const { ctx, lead } = await setup(['hang']) + const childId = await spawn(ctx, lead, 'worker', 'stay running') + await waitRunning(ctx, childId) + const task = await ctx.agentTeams.createTask(lead, { subject: 'work', description: 'assign work' }) + const entered = Promise.withResolvers() + const release = Promise.withResolvers() + const discover = ctx.subagents.listDescendants.bind(ctx.subagents) + const lookup = vi.spyOn(ctx.subagents, 'listDescendants').mockImplementationOnce(async (...args) => { + entered.resolve(undefined) + await release.promise + return discover(args[0], observesCancellation ? args[1] : undefined) + }) + const controller = new AbortController() + const result = execute(ctx, lead, 'team_task_update', { + task_id: task.id, expected_revision: task.revision, action: 'reassign', owner: ctx.subagents.nameOf(ctx.agents.get(childId)!), + }, controller.signal) + try { + await entered.promise + expect(lookup.mock.calls[0]?.[1]).toBeInstanceOf(AbortSignal) + controller.abort(new Error('stop assignment')) + } finally { + release.resolve(undefined) + } + expect(await result).toMatchObject({ isError: true }) + expect(ctx.agentTeams.getTask(lead, task.id)).toEqual(task) + await using stored = await ctx.sessionPersistence.open(lead.id, 'read') + expect((await stored.read()).events.filter(event => event.type === 'team/task')).toHaveLength(1) + }) + it('adds task tools, policy, and scoped identity to the shared subagent controls', async () => { const { ctx, lead } = await setup(['hang', 'hang']) const leadAssembly = await assembly(ctx, lead) @@ -621,7 +651,9 @@ describe('dsh-tool-team', () => { vi.spyOn(ctx.sessions, 'flush').mockRejectedValueOnce(new Error('storage unavailable')) const failed = await execute(ctx, lead, 'team_task_create', { subject: 'failed', description: 'flush fails' }) expect(failed.isError).toBe(true) - expect(text(failed)).toContain('storage unavailable') + expect(text(failed)).toContain('task "task-2" revision 1 is visible, but persistence was not confirmed') + expect(text(failed)).toContain('read the task before retrying') + expect(ctx.agentTeams.listTasks(lead).map(task => task.subject)).toEqual(['committed', 'failed']) await vi.advanceTimersByTimeAsync(10_000) expect(await afterCommit.result).toMatchObject({ isError: false, value: { timedOut: true } }) }) @@ -686,7 +718,7 @@ describe('dsh-tool-team', () => { expect(lead.session.ownEvents().filter(event => event.type === 'team/mode')).toHaveLength(1) }) - it('installs Team scope before a cold-resumed teammate request' , async () => { + it('installs Team scope before a cold-resumed teammate request', async () => { const { ctx, lead, adapter } = await setup([textResponse('first'), textResponse('lead received settlement'), 'hang']) const childId = await spawn(ctx, lead, 'cold-worker', 'finish once') await waitNoAgent(ctx, childId) @@ -746,7 +778,7 @@ describe('dsh-tool-team', () => { await expect(ctx.agents.create({ sessionId: SessionId('lookup-failure'), agentOptions: { provider: 'mock', model: 'mock' } })).rejects.toBe(failure) }) - it('reports unavailable identity without inventing a parent id' , async () => { + it('reports unavailable identity without inventing a parent id', async () => { const { ctx, lead } = await setup([]) vi.spyOn(ctx.subagents, 'rootOf').mockImplementation(() => { throw new SubagentError('Team root is unavailable', 'UNAUTHORIZED') diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 618123bdeb..3e87bc1c21 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -348,10 +348,11 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ throws: ['when called by a descendant or persistence fails. An appended selection remains active; retrying the same selection synchronizes consumers and flushes without a duplicate event.'], }, { - signature: 'async createTask(caller: Agent, request: CreateTeamTaskRequest): Promise', + signature: 'async createTask(caller: Agent, request: CreateTeamTaskRequest, signal?: AbortSignal): Promise', description: 'Create one unowned pending task in the Team Lead log.', - parameters: [{ name: 'caller', description: 'exact live Team member creating the task.' }, { name: 'request', description: 'task text, blockers, and advisory write scopes.' }], + parameters: [{ name: 'caller', description: 'exact live Team member creating the task.' }, { name: 'request', description: 'task text, blockers, and advisory write scopes.' }, { name: 'signal', description: 'cancellation before the task event is appended; omitted means no cancellation.' }], returns: 'the revision-one task view.', + throws: ['TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying.'], }, { signature: 'getTask(caller: Agent, id: TeamTaskId): TeamTaskView', @@ -366,10 +367,11 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ returns: 'detached current task views.', }, { - signature: 'async updateTask(caller: Agent, request: UpdateTeamTaskRequest): Promise', + signature: 'async updateTask(caller: Agent, request: UpdateTeamTaskRequest, signal?: AbortSignal): Promise', description: 'Compare-and-set one authorized task transition.', - parameters: [{ name: 'caller', description: 'exact live Team member authorizing the mutation.' }, { name: 'request', description: 'task identity, expected revision, action, and action fields.' }], + parameters: [{ name: 'caller', description: 'exact live Team member authorizing the mutation.' }, { name: 'request', description: 'task identity, expected revision, action, and action fields.' }, { name: 'signal', description: 'cancellation before the task event is appended; omitted means no cancellation.' }], returns: 'the committed next task revision.', + throws: ['TEAM_TASK_PERSISTENCE_UNCONFIRMED when the task is visible but durability is unknown; read it before retrying.'], }, ], }, diff --git a/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md b/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md index 9a6fe8aaf5..21541aba1b 100644 --- a/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md +++ b/packages/preset/agent-preset/skills/cordis-composition-reference/references/packages.md @@ -184,7 +184,7 @@ Every package below exports a Cordis plugin that a bundle patch can name in a Lo | Package | Config | Description | |---|---|---| -| `@deepseek-ai/dsh-experimental-agent-team` | yes | Implicit-root Agent Teams roster of subagents, shared task DAG, and change waits | +| `@deepseek-ai/dsh-experimental-agent-team` | yes | Shared task DAG and change waits for subagent trees | | `@deepseek-ai/dsh-experimental-api-speech-to-text` | yes | Authenticated experimental speech transcription for browser clients | | `@deepseek-ai/dsh-experimental-auto-review` | no | Per-tool LLM authorization review for the DeepSeek Harness Auto permission preset | | `@deepseek-ai/dsh-experimental-browser-use-chrome-devtools-mcp` | yes | Experimental per-Session Chromium browser tools through chrome-devtools-mcp | diff --git a/packages/sdk/server/README.i18n.yaml b/packages/sdk/server/README.i18n.yaml index acbd3dfbba..62fe0361dd 100644 --- a/packages/sdk/server/README.i18n.yaml +++ b/packages/sdk/server/README.i18n.yaml @@ -15,8 +15,8 @@ en: d152484eb41ac6b4 zh: 09388d293f9be9cb /deepseek-ai-dsh-sdk-jsonrpc-server/use-this-package: - en: e136ff3a0b42cc22 - zh: f6c4df5bbafa335f + en: 33f2a87ce05140ae + zh: a3c690ddf4fc43c5 /deepseek-ai-dsh-sdk-jsonrpc-server/use-this-package/wiring: en: 47a33e9397e36e15 zh: e6d173820f9ea6e5 diff --git a/packages/sdk/server/README.md b/packages/sdk/server/README.md index ca7d6773fc..f9a30c51f0 100644 --- a/packages/sdk/server/README.md +++ b/packages/sdk/server/README.md @@ -25,7 +25,7 @@ English | [中文](README.zh.md) ## Use this package -`session/resume` accepts a persisted `{ sessionId }` after initialization and returns `{}` once its Agent is ready. It loads history through `agents.resume()`, repairs an interrupted final turn, and preserves the stored origin and effective directory while applying the initialized model route. It sends no new input. Missing, unreadable, incompatible, or occupied sessions reject without creating a replacement. Prompts for an identity currently being restored wait for that restoration; shutdown drains pending restoration and releases its Agent. Historical events are retained in the log rather than streamed again as new notifications. +`session/resume` accepts a persisted `{ sessionId }` after initialization and returns `{}` once its Agent is ready. It loads history through `agents.resume()`, repairs an interrupted final turn, and preserves the stored origin and effective directory while applying the initialized model route. It sends no new input. Subagent sessions reject before loading or repairing history; resume their root session instead. Missing, unreadable, incompatible, or occupied sessions reject without creating a replacement. Prompts for an identity currently being restored wait for that restoration; shutdown drains pending restoration and releases its Agent. Historical events are retained in the log rather than streamed again as new notifications. `session/working-directory/get` accepts `{ sessionId }`; `session/working-directory/set` accepts `{ sessionId, path }`. Both return `{ cwd }` and create an unknown Session without starting a model turn. Reads recover missing directories through the working-directory service; changes preserve origin metadata and permissions, and reach the model through logged user context. diff --git a/packages/sdk/server/README.zh.md b/packages/sdk/server/README.zh.md index fa85f9bbfd..8405e79d19 100644 --- a/packages/sdk/server/README.zh.md +++ b/packages/sdk/server/README.zh.md @@ -25,7 +25,7 @@ kind: "package-reference" ## 使用本包 -`session/resume` 在初始化后接受持久化的 `{ sessionId }`,待其 Agent 就绪后返回 `{}`。它通过 `agents.resume()` 加载历史、修复最后一个被中断的轮次,并保留存储的起始目录和当前有效目录,同时应用初始化时选定的模型路由。它不发送新输入。会话缺失、不可读、不兼容或被占用时,请求失败且不创建替代会话。对正在恢复的标识发送提示词时,会等待恢复完成;shutdown 会等待进行中的恢复并释放其 Agent。历史事件保留在日志中,不会作为新通知重新发送。 +`session/resume` 在初始化后接受持久化的 `{ sessionId }`,待其 Agent 就绪后返回 `{}`。它通过 `agents.resume()` 加载历史、修复最后一个被中断的轮次,并保留存储的来源类型和当前有效目录,同时应用初始化时选定的模型路由。它不发送新输入。子代理会话在加载或修复历史前被拒绝;应恢复其根会话。会话缺失、不可读、不兼容或被占用时,请求失败且不创建替代会话。对正在恢复的标识发送提示词时,会等待恢复完成;shutdown 会等待进行中的恢复并释放其 Agent。历史事件保留在日志中,不会作为新通知重新发送。 `session/working-directory/get` 接收 `{ sessionId }`;`session/working-directory/set` 接收 `{ sessionId, path }`。两者均返回 `{ cwd }`,并可创建未知 Session,而不启动模型轮次。读取通过工作目录服务恢复缺失目录;修改保留起始目录元数据与权限,并通过已记录的用户上下文告知模型。 diff --git a/packages/sdk/server/src/server.ts b/packages/sdk/server/src/server.ts index fe9c8243b7..66e07320a0 100644 --- a/packages/sdk/server/src/server.ts +++ b/packages/sdk/server/src/server.ts @@ -207,7 +207,7 @@ export class HarnessSdkJsonRpcServer { /** * Restore a persisted session and repair its interrupted final turn without sending input. * Uses the initialized model route while preserving the stored session identity and directory. - * Rejects missing, unreadable, or already owned sessions; never creates a replacement. + * Rejects subagent, missing, unreadable, or already owned sessions; never creates a replacement. * @param params - persisted session identity in the configured persistence store. * @returns an empty result after the Agent is ready for prompts. */ @@ -388,6 +388,9 @@ export class HarnessSdkJsonRpcServer { } private async createSession(sessionId: string, resume: boolean): Promise { + if (resume && (await this.ctx.get('sessionPersistence')?.stat(brandString(sessionId)))?.header.origin === 'subagent') { + throw new Error(`subagent session "${sessionId}" cannot be resumed directly; resume its root session instead`) + } // No preset composition: this server's compositions keep the model-facing // rows in the host plane, so this agent reads them from the global layer. A // deployment that configures a roster has to join one here first diff --git a/packages/sdk/server/tests/resume.spec.ts b/packages/sdk/server/tests/resume.spec.ts index 4b1710e6b9..63ab1f5ab4 100644 --- a/packages/sdk/server/tests/resume.spec.ts +++ b/packages/sdk/server/tests/resume.spec.ts @@ -49,6 +49,25 @@ async function initialized(root: string) { } describe('SDK persisted session resume', () => { + it('rejects independent subagent restoration before opening or repairing its log', async () => { + const root = await storageRoot() + const { ctx, server } = await initialized(root) + const child = await ctx.agents.create({ + sessionId: SessionId('saved-child'), + meta: { origin: 'subagent', parentSession: SessionId('creator'), cwd: root }, + }) + child.agent.session.append('turn/start', { turn: 1 }) + await ctx.sessions.flush(child.agent.session) + await child.dispose() + const resume = vi.spyOn(ctx.agents, 'resume') + for (let attempt = 0; attempt < 2; attempt++) { + await expect(server.resume({ sessionId: 'saved-child' })).rejects.toThrow('resume its root session instead') + } + expect(resume).not.toHaveBeenCalled() + expect(ctx.agents.get(SessionId('saved-child'))).toBeUndefined() + await expect(server.shutdown()).resolves.toEqual({}) + }) + it('restores a non-main identity, preserves history and origin, and appends a new turn', async () => { const root = await storageRoot() const first = await initialized(root) diff --git a/packages/subagent/subagent/README.i18n.yaml b/packages/subagent/subagent/README.i18n.yaml index 85ea2aad4b..f9fc861f65 100644 --- a/packages/subagent/subagent/README.i18n.yaml +++ b/packages/subagent/subagent/README.i18n.yaml @@ -36,8 +36,8 @@ en: 4c91b1d897ff73fa zh: 0c65fb3bb7de1244 /deepseek-ai-dsh-subagent/use-this-package/failure-and-recovery: - en: 399eb5360009a386 - zh: 366eb6dd5a304c3f + en: 896bf2515524c01f + zh: 1f1413710e58e4f6 /deepseek-ai-dsh-subagent/understand-the-implementation: en: 74d91b364b23f88d zh: 19cc558105efe8df diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md index a55b882c35..a742e02b5e 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -73,7 +73,7 @@ In-process spawn accepts an independent `agentPreset`; fork and external backend ### Failure and recovery -Starting without the `agents` service rejects with `CONTINUATION_UNAVAILABLE`; an available runtime rejects a missing or replaced parent with `UNAUTHORIZED`. Providers implementing neither execution method are rejected at registration with `UNSUPPORTED_CAPABILITY`; unsupported requested capabilities reject creation with the same code. The caller signal cancels only unpublished work; after publication, `dispose()` cancels the exact activation and waits for its resource cleanup. Creation descendants continue independently; closing the Team root stops all members and awaits pending startup. `result` settles after teardown and notifications, carrying output, optional structured data, and a stop reason; capture failures reject it after teardown. Cleanup failures reject `dispose()` and mark the lifecycle end as failed; the result promise and parent notification retain the captured execution result. +Starting without the `agents` service rejects with `CONTINUATION_UNAVAILABLE`; an available runtime rejects a missing or replaced parent with `UNAUTHORIZED`. Providers implementing neither execution method are rejected at registration with `UNSUPPORTED_CAPABILITY`; unsupported requested capabilities reject creation with the same code. The caller signal cancels only unpublished work; after publication, `dispose()` cancels the exact activation and waits for its resource cleanup. Creation descendants continue independently; hosts must await `drainDescendants([root])` before disposing the root handle to stop all members and await pending startup. `result` settles after teardown and notifications, carrying output, optional structured data, and a stop reason; capture failures reject it after teardown. Cleanup failures reject `dispose()` and mark the lifecycle end as failed; the result promise and parent notification retain the captured execution result. ----- diff --git a/packages/subagent/subagent/README.zh.md b/packages/subagent/subagent/README.zh.md index 6da667807b..fdc0548d9e 100644 --- a/packages/subagent/subagent/README.zh.md +++ b/packages/subagent/subagent/README.zh.md @@ -73,7 +73,7 @@ agent 名字是不可变的路径段,只在同一父级的直接子级中唯 ### 失败与恢复 -缺少 `agents` 服务时,启动请求以 `CONTINUATION_UNAVAILABLE` 拒绝;运行时可用但父级缺失或被替换时,以 `UNAUTHORIZED` 拒绝。未实现任何执行方法的提供方会在注册时以 `UNSUPPORTED_CAPABILITY` 被拒绝;请求不受支持的能力会以同一码使创建失败。调用方信号仅取消尚未发布的工作;发布后通过 `dispose()` 取消该次 activation,并等待其资源清理完成。创建后代独立继续运行;关闭 Team 根会停止所有成员并等待正在启动的工作。`result` 在清理与通知结束后完成,携带输出、可选结构化数据与停止原因;结果捕获故障在清理后使其拒绝。清理失败使 `dispose()` 拒绝,并将生命周期结束事件标记为失败;结果 promise 与父级通知保留已捕获的执行结果。 +缺少 `agents` 服务时,启动请求以 `CONTINUATION_UNAVAILABLE` 拒绝;运行时可用但父级缺失或被替换时,以 `UNAUTHORIZED` 拒绝。未实现任何执行方法的提供方会在注册时以 `UNSUPPORTED_CAPABILITY` 被拒绝;请求不受支持的能力会以同一码使创建失败。调用方信号仅取消尚未发布的工作;发布后通过 `dispose()` 取消该次 activation,并等待其资源清理完成。创建后代独立继续运行;宿主必须在释放根句柄前等待 `drainDescendants([root])`,以停止所有成员并等待正在启动的工作。`result` 在清理与通知结束后完成,携带输出、可选结构化数据与停止原因;结果捕获故障在清理后使其拒绝。清理失败使 `dispose()` 拒绝,并将生命周期结束事件标记为失败;结果 promise 与父级通知保留已捕获的执行结果。 ----- diff --git a/packages/subagent/subagent/src/continuation-messages.ts b/packages/subagent/subagent/src/continuation-messages.ts index 5fd4649f3c..d4b4a6d7a4 100644 --- a/packages/subagent/subagent/src/continuation-messages.ts +++ b/packages/subagent/subagent/src/continuation-messages.ts @@ -43,7 +43,7 @@ declare module '@deepseek-ai/dsh-llm' { } } -/** Build durable attribution for one adjacent-Agent message. */ +/** Build durable attribution for one Agent message. */ function agentMessageSource(sender: Agent): AgentMessageSource { return { kind: 'agent-message', @@ -53,7 +53,7 @@ function agentMessageSource(sender: Agent): AgentMessageSource { } /** - * Build the model-visible and durable representation of one adjacent-Agent message. + * Build the model-visible and durable representation of one Agent message. * @param sender - exact live Agent that authored the message. * @param content - model-visible message blocks supplied by the sender. * @param senderName - full agent path shown to the recipient. @@ -74,7 +74,7 @@ export function createAgentMessage( } /** - * Append adjacent-Agent return guidance to a continuable child's initial task. + * Append parent communication guidance to a continuable child's initial task. * @param parentName - name of the direct parent. * @param childName - name assigned to this child. * @param prompt - initial model-visible task blocks. diff --git a/packages/subagent/subagent/src/types.ts b/packages/subagent/subagent/src/types.ts index 10a5b4497c..29f6ba3993 100644 --- a/packages/subagent/subagent/src/types.ts +++ b/packages/subagent/subagent/src/types.ts @@ -71,7 +71,7 @@ export type SubagentInterruptAuthority = | { readonly kind: 'user'; readonly parentSessionId: SessionId } | { readonly kind: 'ancestor'; readonly agent: Agent } -/** Options for one model-authored message between adjacent Agents. */ +/** Options for one model-authored message within a subagent tree. */ export interface SubagentSendMessageOptions { /** Caller cancellation, owning the operation only until inbox acceptance. */ readonly signal: AbortSignal diff --git a/packages/subagent/tool-subagent-control/README.i18n.yaml b/packages/subagent/tool-subagent-control/README.i18n.yaml index 4f1f0fd83a..4b76cc62aa 100644 --- a/packages/subagent/tool-subagent-control/README.i18n.yaml +++ b/packages/subagent/tool-subagent-control/README.i18n.yaml @@ -36,8 +36,8 @@ en: c5cdf01a78c990f2 zh: 53c180d726ce4502 /deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/design-concept: - en: 0b3853eaa8859d72 - zh: 16358081c7362589 + en: 397401e9305cf11e + zh: b3b342fa3f05cf32 /deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/delivery-and-signal-ownership: en: 81632e94d10ce33f zh: 36958764906bdb68 diff --git a/packages/subagent/tool-subagent-control/README.md b/packages/subagent/tool-subagent-control/README.md index f6206acca4..ff40b5ceb9 100644 --- a/packages/subagent/tool-subagent-control/README.md +++ b/packages/subagent/tool-subagent-control/README.md @@ -71,7 +71,7 @@ This section explains what the tools delegate to the subagent service; the obser ### Design concept -Messaging, interruption, and discovery delegate to `ctx.subagents`; residency, cold resume, and authorization belong to that service. The wait tool subscribes to the exact caller’s inbox and wait signals before checking pending next-step input, and removes its listeners and timer on every settlement. +Messaging, interruption, and discovery delegate to `ctx.subagents`; residency, cold resume, and authorization belong to that service. The wait tool subscribes to the exact caller’s inbox and wait signals before starting the timer and checking pending next-step input, and removes its listeners and timer on every settlement. ### Delivery and signal ownership diff --git a/packages/subagent/tool-subagent-control/README.zh.md b/packages/subagent/tool-subagent-control/README.zh.md index 3a51ac2679..ad5b232922 100644 --- a/packages/subagent/tool-subagent-control/README.zh.md +++ b/packages/subagent/tool-subagent-control/README.zh.md @@ -71,7 +71,7 @@ kind: "package-reference" ### 设计理念 -通信、中断和发现委派给 `ctx.subagents`;驻留、冷恢复和授权属于该服务。等待工具在检查待处理下一步输入前订阅当前调用者的收件箱和等待信号,并在每次结束时移除监听器和定时器。 +通信、中断和发现委派给 `ctx.subagents`;驻留、冷恢复和授权属于该服务。等待工具在启动定时器并检查待处理下一步输入前订阅当前调用者的收件箱和等待信号,并在每次结束时移除监听器和定时器。 ### 投递与信号所有权 diff --git a/packages/subagent/tool-subagent-control/src/wait-agent.ts b/packages/subagent/tool-subagent-control/src/wait-agent.ts index 102bc6db18..823204fefe 100644 --- a/packages/subagent/tool-subagent-control/src/wait-agent.ts +++ b/packages/subagent/tool-subagent-control/src/wait-agent.ts @@ -42,7 +42,6 @@ function waitForInput( const onAbort = (): void => { finish(() => { reject(signal.reason) }) } // oxlint-disable-next-line typescript/prefer-promise-reject-errors -- This plugin aborts its lifetime only with AgentWaitError. const onUnload = (): void => { finish(() => { reject(lifetime.reason) }) } - const timer = setTimeout(() => { finish(() => { resolve({ timedOut: true }) }) }, timeoutMs) const offInbox = ctx.on('agent/inbox/inserted', ({ agent: recipient }) => { if (recipient === agent) onInput() }) @@ -58,6 +57,7 @@ function waitForInput( }) signal.addEventListener('abort', onAbort, { once: true }) lifetime.addEventListener('abort', onUnload, { once: true }) + const timer = setTimeout(() => { finish(() => { resolve({ timedOut: true }) }) }, timeoutMs) // Subscribe before reading pending input so an arrival cannot fall between them. if (signal.aborted) onAbort() else onInput() @@ -75,7 +75,7 @@ export function registerWaitAgent(ctx: Context): void { }, 'subagent-control.waits()') ctx.tools.register(defineTool({ name: 'wait_agent', - description: 'Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.', + description: 'Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.', parameters: { timeout_ms: { type: 'integer', diff --git a/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts b/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts index 80846d7a93..fe1b39a161 100644 --- a/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts +++ b/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts @@ -121,11 +121,12 @@ describe('wait_agent', () => { async (settlement) => { const { ctx, agent, handle, fiber } = await setup() vi.useFakeTimers() - const listeners = () => ctx.events.dispatch('emit', [agentCarrier(agent), 'agent/wait-notified', { agent }]).length + const listeners = () => ['agent/inbox/inserted', 'agent/wait-notified', 'agent/disposed'].map(name => + ctx.events.dispatch('emit', [agentCarrier(agent), name, { agent }]).length) const baseline = listeners() const controller = new AbortController() const { result } = await startWait(ctx, agent, { timeout_ms: 10_000 }, controller.signal) - expect(listeners()).toBe(baseline + 1) + expect(listeners()).toEqual(baseline.map(count => count + 1)) switch (settlement) { case 'input': agent.inject(input()); break case 'notification': emitAgentEvent(ctx, agent, 'agent/wait-notified', {}); break @@ -135,7 +136,7 @@ describe('wait_agent', () => { case 'unload': await fiber.dispose(); break } await result - expect(listeners()).toBe(baseline) + expect(listeners()).toEqual(baseline) expect(vi.getTimerCount()).toBe(0) }, ) @@ -152,6 +153,23 @@ describe('wait_agent', () => { expect(agent.inbox.nextStep.map(item => item.id)).toEqual([message.id]) }) + it('leaves no timer when plugin disposal rejects listener registration', async () => { + const { ctx, agent, fiber } = await setup() + vi.useFakeTimers() + const definition = ctx.tools.get('wait_agent')! + const execute = definition.execute.bind(definition) + let disposal = Promise.resolve() + vi.spyOn(definition, 'execute').mockImplementation((args, exec) => { + // Disposal invalidates the context before its deferred cleanup aborts waits. + disposal = Promise.resolve(fiber.dispose()) + return execute(args, exec) + }) + const { result } = await startWait(ctx, agent) + expect(await result).toMatchObject({ isError: true, error: { message: 'cannot create effect on inactive context' } }) + await disposal + expect(vi.getTimerCount()).toBe(0) + }) + it.each([ [{}, 30_000], [{ timeout_ms: 10_000 }, 10_000], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1fde9cab8..de5d29bf1c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7634,9 +7634,6 @@ importers: '@deepseek-ai/dsh-session': specifier: workspace:* version: link:../../core/session - '@deepseek-ai/dsh-session-persistence': - specifier: workspace:* - version: link:../../session/session-persistence '@deepseek-ai/dsh-session-persistence-jsonl': specifier: workspace:* version: link:../../session/session-persistence-jsonl @@ -8162,9 +8159,6 @@ importers: '@deepseek-ai/dsh-client-ui-renderer': specifier: workspace:* version: link:../../client/ui-renderer - '@deepseek-ai/dsh-client-ui-session': - specifier: workspace:* - version: link:../../client/ui-session '@deepseek-ai/dsh-client-ui-slots': specifier: workspace:* version: link:../../client/ui-slots diff --git a/python/sdk/tests/test_client.py b/python/sdk/tests/test_client.py index 6298403a82..47ce2f6f1a 100644 --- a/python/sdk/tests/test_client.py +++ b/python/sdk/tests/test_client.py @@ -641,7 +641,9 @@ def test_subtree_keeps_descendant_completion_after_creator_finishes() -> None: ]: client._handle_message({"jsonrpc": "2.0", "method": method, "params": {"parentSessionId": parent, "childSessionId": child}}) - assert subtree.next().payload == {"parentSessionId": parent, "childSessionId": child} + received: list[Notification] = [] + subtree.drain(received.append) + assert [item.payload for item in received] == [{"parentSessionId": parent, "childSessionId": child}] def test_session_subscription_preserves_reused_child_ancestry_after_late_finish() -> None: diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts index 36f148906f..17d451872e 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -707,7 +707,6 @@ export const LINK_MAP: Readonly> = { TeamTaskId: 'agent-team.md', TeamTaskView: 'agent-team.md', TeamProjection: 'agent-team.md', - TeamMemberProjection: 'agent-team.md', UpdateTeamTaskRequest: 'agent-team.md', ModDefinition: 'claude-code-mods.md', SurfaceSnapshot: 'claude-code-mods.md', diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts index 7b4b9df647..bdaed476cb 100644 --- a/scripts/gen-doc-graphs.ts +++ b/scripts/gen-doc-graphs.ts @@ -796,7 +796,7 @@ const SERVICE_ROLES: ServiceRole[] = [ title: 'Agent Teams coordination domain', mode: 'core', consumers: ['experimental-tool-agent-team'], - note: 'Owns the implicit-root roster, direct peer inbox delivery, shared task DAG, and continuable-child lifecycle; tool-agent-team contributes model controls.', + note: 'Owns the shared task DAG and wait signals for a subagent tree; tool-agent-team contributes task controls.', }, { key: 'claudeCodeMods', diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json index 53fd5a6f73..b91d3d0951 100644 --- a/scripts/type-equiv.manifest.json +++ b/scripts/type-equiv.manifest.json @@ -432,11 +432,6 @@ "symbol": "ScheduleDeleteResult", "source": "packages/schedule/schedule/src/types.ts" }, - { - "doc": "docs/subsystems/agent-team.md", - "symbol": "TeamMemberProjection", - "source": "packages/experimental/agent-team/src/types.ts" - }, { "doc": "docs/subsystems/agent-team.md", "symbol": "TeamTaskView", diff --git a/snapshots/sdk/bash-tool/tool-schemas.expected.json b/snapshots/sdk/bash-tool/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/bash-tool/tool-schemas.expected.json +++ b/snapshots/sdk/bash-tool/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json b/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json b/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json b/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json b/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json index 5f36a6ca11..01ba1050cd 100644 --- a/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json +++ b/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json @@ -530,7 +530,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.1.expected.json b/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.expected.json b/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.expected.json index eade1d7c8b..fb2b91d808 100644 --- a/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.expected.json +++ b/snapshots/sdk/subagent-dsh-sdk-dynamic-route/tool-schemas.expected.json @@ -501,7 +501,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json b/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json b/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/team-session-mode/snapshot.yml b/snapshots/sdk/team-session-mode/snapshot.yml index d7ae8ead04..0a81b3fe57 100644 --- a/snapshots/sdk/team-session-mode/snapshot.yml +++ b/snapshots/sdk/team-session-mode/snapshot.yml @@ -6,5 +6,6 @@ recording: authored header: class: sdk-team-session-mode pin: true + toolSchemasSource: session/team-session-mode changes: 1 promptChanges: 1 diff --git a/snapshots/sdk/team-session-mode/tool-schemas.expected.json b/snapshots/sdk/team-session-mode/tool-schemas.expected.json deleted file mode 100644 index 0f2b37b2e9..0000000000 --- a/snapshots/sdk/team-session-mode/tool-schemas.expected.json +++ /dev/null @@ -1,1439 +0,0 @@ -{ - "initial": [ - { - "name": "bash", - "description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell; pass `workdir` instead of using `cd`. Managed `$DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Provide `description` before `command` in the arguments. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. An unset variable expands to an empty string, so guard variables in such paths with `${VAR:?}`. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under mode]`, a policy denial: do not retry another way.", - "parameters": { - "type": "object", - "properties": { - "description": { - "type": "string", - "description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"." - }, - "command": { - "type": "string", - "description": "The bash command to execute." - }, - "timeoutMs": { - "type": "number", - "description": "Timeout in milliseconds. The executor applies its configured default and cap; on expiry the command moves to the background as a job instead of being killed." - }, - "workdir": { - "type": "string", - "description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it." - }, - "run_in_background": { - "type": "boolean", - "description": "Run in the background and return a job id immediately (collect with job_output, stop with job_kill). No timeout applies." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact command the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "description", - "command" - ] - } - }, - { - "name": "create_goal", - "description": "Create a persisted goal that keeps this session working across automatic continuation rounds. Use it when the direct human request is a long-running objective, even if the user did not say \"goal\"; not for single-turn work.", - "parameters": { - "type": "object", - "properties": { - "objective": { - "type": "string", - "description": "The concrete completion objective inferred from the direct human request." - }, - "max_goal_rounds": { - "type": "number", - "description": "Optional positive safe-integer limit on automatic continuation rounds." - } - }, - "required": [ - "objective" - ] - } - }, - { - "name": "edit", - "description": "Edit an existing UTF-8 text file by replacing literal text.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to edit, resolved by the filesystem backend. Provide `file_path` before `old_string` and `new_string` in the arguments." - }, - "old_string": { - "type": "string", - "description": "Literal text to replace." - }, - "new_string": { - "type": "string", - "description": "Literal replacement text. Use an empty string to delete the match." - }, - "replace_all": { - "type": "boolean", - "description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact operation the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "file_path", - "old_string", - "new_string" - ] - } - }, - { - "name": "exit_plan_mode", - "description": "Use only in plan mode. Present your plan for the user's review and, on approval, leave plan mode. The user may approve (carry out the plan from your next step) or keep planning — their feedback comes back in the tool result; revise and present again.", - "parameters": { - "type": "object", - "properties": { - "plan": { - "type": "string", - "description": "The complete plan, as markdown, starting with a # heading that names it." - } - }, - "required": [ - "plan" - ] - } - }, - { - "name": "get_goal", - "description": "Read the current session goal, including the id and revision that update_goal requires.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "glob", - "description": "Find files, not directories, whose paths match a glob pattern, including hidden and ignored files. Returns up to 100 paths in modification-time order; a larger result keeps the first paths and reports where the complete list was saved.", - "parameters": { - "type": "object", - "properties": { - "pattern": { - "type": "string", - "description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\"). A pattern with no \"/\" matches the basename at any depth, so \"*\" and \"*.ts\" both search the whole tree; include a separator to anchor the depth." - }, - "path": { - "type": "string", - "description": "Directory to search in. Defaults to the session workspace; a relative path resolves against it." - } - }, - "required": [ - "pattern" - ] - } - }, - { - "name": "grep", - "description": "Search file contents with a ripgrep regular expression. Returns matching lines with line numbers, grouped by file. Returns up to 250 matches; a larger result reports where the complete match list was saved.", - "parameters": { - "type": "object", - "properties": { - "pattern": { - "type": "string", - "description": "Regular expression to search for (ripgrep syntax)." - }, - "path": { - "type": "string", - "description": "File or directory to search. Defaults to the session workspace; a relative path resolves against it." - }, - "include": { - "type": "string", - "description": "One glob filter for which files to search (e.g. \"*.ts\", \"*.{js,jsx}\"). Not a list; negation is not supported." - } - }, - "required": [ - "pattern" - ] - } - }, - { - "name": "interrupt_agent", - "description": "Ask a subagent to stop its current work. This call returns without waiting for it to stop. You can continue a local subagent's conversation later with send_message. External executions stop permanently and cannot receive follow-ups. Subagents it started will keep running.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The full path (such as lead/explore/alice) of an agent created under you: your direct child or a deeper descendant." - } - }, - "required": [ - "name" - ] - } - }, - { - "name": "job_kill", - "description": "Request cancellation of a running background job.", - "parameters": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "description": "Job id returned by the tool that started the background work." - }, - "reason": { - "type": "string", - "description": "Optional short reason, recorded in the log and forwarded to the job." - } - }, - "required": [ - "job_id" - ] - } - }, - { - "name": "job_list", - "description": "List your background jobs (running and finished) with their ids, kinds, and statuses.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "job_output", - "description": "Read a background job: output since the previous read for stream jobs, or the result of a finished final-output job.", - "parameters": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "description": "Job id returned by the tool that started the background work." - }, - "wait": { - "type": "boolean", - "description": "Block until the job finishes or the timeout expires; a timed-out wait leaves the job running. Defaults to false." - }, - "timeout_ms": { - "type": "number", - "description": "Max wait in milliseconds with wait: true. Defaults to and is capped by configuration." - } - }, - "required": [ - "job_id" - ] - } - }, - { - "name": "list_agents", - "description": "List your entire Team: the root agent and all reachable continuable descendants, including yourself. Rows carry full agent paths, descriptions, and status; descendant rows carry parent paths and depth from the root. You can message any other Team member listed as running or inactive. You can interrupt only your own descendants. running means it is working; inactive means it is not currently working. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "read", - "description": "Read a UTF-8 text file and return line-numbered content.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to read, resolved by the filesystem backend." - }, - "offset": { - "type": "number", - "description": "1-based first line to return. Defaults to 1." - }, - "limit": { - "type": "number", - "description": "Maximum number of lines to return. Defaults to 2000." - } - }, - "required": [ - "file_path" - ] - } - }, - { - "name": "read_image", - "description": "Read a PNG/JPEG/WebP/GIF file and return the image itself. Large images are downscaled automatically; do not install image libraries or create thumbnails to inspect an image.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to the image file, resolved by the filesystem backend." - } - }, - "required": [ - "file_path" - ] - } - }, - { - "name": "send_message", - "description": "Send a message to an agent. A working agent receives it at its next step; an idle agent starts a new turn with it. Returns delivery confirmation, not the agent's answer.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The full path (such as lead/reviewer) of another member of your Team, as shown by list_agents." - }, - "message": { - "type": "string", - "description": "The message to deliver to the agent." - } - }, - "required": [ - "name", - "message" - ] - } - }, - { - "name": "skill", - "description": "Load the full instructions for a skill. Call it before acting on a task that names or clearly matches a skill in the session skill catalog.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The exact skill name from the available skills list." - } - }, - "required": [ - "name" - ] - } - }, - { - "name": "subagent", - "description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its name. If you are still online when it finishes, the runtime automatically delivers its completion notice and any final answer. Use `send_message` to steer it while running or continue its conversation after it finishes.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Optional name unique among your direct children. Omission selects the first unused agent-N, starting at agent-1. Use one segment starting with a lower-case letter, followed by lower-case letters, digits, underscores, or hyphens. Names remain taken after agents finish. The result returns the full path, such as lead/reviewer." - }, - "cwd": { - "type": "string", - "description": "Initial child working directory. Relative paths use your current directory; omitted inherits it. Later directory changes in either agent are independent." - }, - "description": { - "type": "string", - "description": "A short (3-5 word) description of the delegated task, for display." - }, - "prompt": { - "type": "string", - "description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs." - } - }, - "required": [ - "description", - "prompt" - ] - } - }, - { - "name": "subagent_fork", - "description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn). Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its name. If you are still online when it finishes, the runtime automatically delivers its completion notice and any final answer. Use `send_message` to steer it while running or continue its conversation after it finishes.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Optional name unique among your direct children. Omission selects the first unused agent-N, starting at agent-1. Use one segment starting with a lower-case letter, followed by lower-case letters, digits, underscores, or hyphens. Names remain taken after agents finish. The result returns the full path, such as lead/reviewer." - }, - "cwd": { - "type": "string", - "description": "Initial child working directory. Relative paths use your current directory; omitted inherits it. Later directory changes in either agent are independent." - }, - "description": { - "type": "string", - "description": "A short (3-5 word) description of the delegated task, for display." - }, - "prompt": { - "type": "string", - "description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new." - } - }, - "required": [ - "description", - "prompt" - ] - } - }, - { - "name": "team_task_create", - "description": "Create one unowned pending task on the shared Team task board.", - "parameters": { - "type": "object", - "properties": { - "subject": { - "type": "string", - "description": "Concise task title." - }, - "description": { - "type": "string", - "description": "Complete task details and acceptance criteria." - }, - "blocked_by": { - "type": "array", - "description": "Task ids that must complete first.", - "items": { - "type": "string" - } - }, - "write_scopes": { - "type": "array", - "description": "Advisory workspace-relative file or directory prefixes this task expects to modify.", - "items": { - "type": "string" - } - } - }, - "required": [ - "subject", - "description" - ] - } - }, - { - "name": "team_task_get", - "description": "Read the complete latest value of one shared task before changing or executing it.", - "parameters": { - "type": "object", - "properties": { - "task_id": { - "type": "string", - "description": "Shared task id." - } - }, - "required": [ - "task_id" - ] - } - }, - { - "name": "team_task_list", - "description": "List shared tasks, including readiness, owner, revision, blockers, and write-scope warnings.", - "parameters": { - "type": "object", - "properties": { - "status": { - "type": "string", - "description": "Optional exact status filter.", - "enum": [ - "pending", - "in_progress", - "completed" - ] - }, - "owner": { - "type": "string", - "description": "Optional exact ownerName filter; use unowned for tasks without an owner." - }, - "ready": { - "type": "boolean", - "description": "Optional readiness filter." - }, - "cursor": { - "type": "integer", - "description": "Zero-based result offset. Defaults to 0." - }, - "limit": { - "type": "integer", - "description": "Number of rows, 1 through 100. Defaults to 50." - } - } - } - }, - { - "name": "team_task_update", - "description": "Compare-and-set a shared task action using the latest revision from team_task_get or team_task_list.", - "parameters": { - "type": "object", - "properties": { - "task_id": { - "type": "string", - "description": "Shared task id." - }, - "expected_revision": { - "type": "integer", - "description": "Current task revision used as the CAS precondition." - }, - "action": { - "type": "string", - "description": "Task transition to apply.", - "enum": [ - "claim", - "release", - "edit", - "set_dependencies", - "complete", - "reopen", - "reassign", - "delete" - ] - }, - "subject": { - "type": "string", - "description": "Replacement title for edit." - }, - "description": { - "type": "string", - "description": "Replacement details for edit." - }, - "blocked_by": { - "type": "array", - "description": "Complete blocker list for set_dependencies.", - "items": { - "type": "string" - } - }, - "write_scopes": { - "type": "array", - "description": "Replacement advisory write scopes for edit.", - "items": { - "type": "string" - } - }, - "owner": { - "type": "string", - "description": "Full agent path of the root or any continuable descendant for root-only reassign; omit to unassign." - } - }, - "required": [ - "task_id", - "expected_revision", - "action" - ] - } - }, - { - "name": "todo_write", - "description": "Record and update a task list to plan multi-step work and show progress; skip it for trivial single-step tasks. Add one todo per concrete step before you start. While work remains, keep the todos being worked on `in_progress`, several only when work runs in parallel. Mark each todo `completed` as soon as it is done.", - "parameters": { - "type": "object", - "properties": { - "todos": { - "type": "array", - "description": "The COMPLETE task list, replacing any previous list.", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - "description": "What the task is — a short imperative line." - }, - "status": { - "type": "string", - "description": "pending (not started) | in_progress (now) | completed (done).", - "enum": [ - "pending", - "in_progress", - "completed" - ] - } - }, - "required": [ - "content", - "status" - ] - } - } - }, - "required": [ - "todos" - ] - } - }, - { - "name": "update_goal", - "description": "Update the current goal.", - "parameters": { - "type": "object", - "properties": { - "goal_id": { - "type": "string", - "description": "Exact id returned by get_goal." - }, - "revision": { - "type": "number", - "description": "Exact positive revision returned by get_goal." - }, - "action": { - "type": "string", - "description": "edit, pause, and resume require a direct top-level human request. complete and blocked are also allowed during an automatic continuation of this goal; blocked is rejected before the configured minimum round count.", - "enum": [ - "edit", - "pause", - "resume", - "complete", - "blocked" - ] - }, - "objective": { - "type": "string", - "description": "Replacement objective; valid only with action edit." - }, - "max_goal_rounds": { - "type": "number", - "description": "Replacement cap; valid only with action edit." - }, - "blocked_reason": { - "type": "string", - "description": "Required only with action blocked: the concrete condition that persisted across rounds and blocks progress." - } - }, - "required": [ - "goal_id", - "revision", - "action" - ] - } - }, - { - "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", - "parameters": { - "type": "object", - "properties": { - "timeout_ms": { - "type": "integer", - "description": "Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000." - } - } - } - }, - { - "name": "web_fetch", - "description": "Fetch the content of a specific HTTP(S) URL and return it decoded to text.", - "parameters": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The HTTP(S) URL to fetch." - } - }, - "required": [ - "url" - ] - } - }, - { - "name": "web_search", - "description": "Search the web for current information. Returns an optional summary answer and a list of source URLs.", - "parameters": { - "type": "object", - "properties": { - "queries": { - "type": "array", - "description": "1–4 search queries; their results are merged.", - "items": { - "type": "string" - } - } - }, - "required": [ - "queries" - ] - } - }, - { - "name": "workflow", - "description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides.\n- `pipeline(items, ...stages): Promise` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. A stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) end the whole script instead of producing `null`. The script has no filesystem, network, timer, or Node.js APIs; the agents do the work.", - "parameters": { - "type": "object", - "properties": { - "script": { - "type": "string", - "description": "The plain JavaScript body, not TypeScript and without an `export const meta` statement; top-level await is allowed. End with `return `; the JSON-serializable value is this tool's result." - }, - "meta": { - "type": "object", - "description": "The workflow identity as plain JSON, not code.", - "additionalProperties": true, - "properties": { - "name": { - "type": "string", - "description": "Short kebab-case workflow name." - }, - "description": { - "type": "string", - "description": "One-line description of what the workflow does." - }, - "whenToUse": { - "type": "string", - "description": "Optional guidance on when this workflow applies." - }, - "phases": { - "type": "array", - "description": "Optional phase declarations matched by phase() calls.", - "items": { - "type": "object", - "additionalProperties": true, - "properties": { - "title": { - "type": "string", - "description": "The phase title phase() calls match by exact string." - }, - "detail": { - "type": "string", - "description": "Optional one-line description of the phase." - }, - "provider": { - "type": "string", - "description": "Optional provider override this phase is expected to use." - }, - "model": { - "type": "string", - "description": "Optional model override this phase is expected to use." - } - }, - "required": [ - "title" - ] - } - } - }, - "required": [ - "name", - "description" - ] - }, - "args": { - "type": "object", - "description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]}).", - "additionalProperties": true - }, - "run_in_background": { - "type": "boolean", - "description": "Run as a background job: return a job id immediately instead of waiting; the return value arrives with the completion notice." - } - }, - "required": [ - "script", - "meta" - ] - } - }, - { - "name": "working_directory", - "description": "Read the current working directory, or change it with cd. Relative paths use the current directory. Existing shells and running processes keep their own directories.", - "parameters": { - "type": "object", - "properties": { - "cd": { - "type": "string", - "description": "Existing directory to enter. Omit to read the current directory." - } - } - } - }, - { - "name": "write", - "description": "Create or fully replace a UTF-8 text file.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to write, resolved by the filesystem backend. Provide `file_path` before `content` in the arguments." - }, - "content": { - "type": "string", - "description": "Full UTF-8 text content to write." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact operation the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "file_path", - "content" - ] - } - } - ], - "changes": [ - [ - { - "name": "bash", - "description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell; pass `workdir` instead of using `cd`. Managed `$DSH_*` variables expose current harness environment facts. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Provide `description` before `command` in the arguments. Before any delete or move, verify that the resolved absolute target path is the intended one; never run it against a computed path you have not checked. An unset variable expands to an empty string, so guard variables in such paths with `${VAR:?}`. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under mode]`, a policy denial: do not retry another way.", - "parameters": { - "type": "object", - "properties": { - "description": { - "type": "string", - "description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"." - }, - "command": { - "type": "string", - "description": "The bash command to execute." - }, - "timeoutMs": { - "type": "number", - "description": "Timeout in milliseconds. The executor applies its configured default and cap; on expiry the command moves to the background as a job instead of being killed." - }, - "workdir": { - "type": "string", - "description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it." - }, - "run_in_background": { - "type": "boolean", - "description": "Run in the background and return a job id immediately (collect with job_output, stop with job_kill). No timeout applies." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact command the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "description", - "command" - ] - } - }, - { - "name": "create_goal", - "description": "Create a persisted goal that keeps this session working across automatic continuation rounds. Use it when the direct human request is a long-running objective, even if the user did not say \"goal\"; not for single-turn work.", - "parameters": { - "type": "object", - "properties": { - "objective": { - "type": "string", - "description": "The concrete completion objective inferred from the direct human request." - }, - "max_goal_rounds": { - "type": "number", - "description": "Optional positive safe-integer limit on automatic continuation rounds." - } - }, - "required": [ - "objective" - ] - } - }, - { - "name": "edit", - "description": "Edit an existing UTF-8 text file by replacing literal text.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to edit, resolved by the filesystem backend. Provide `file_path` before `old_string` and `new_string` in the arguments." - }, - "old_string": { - "type": "string", - "description": "Literal text to replace." - }, - "new_string": { - "type": "string", - "description": "Literal replacement text. Use an empty string to delete the match." - }, - "replace_all": { - "type": "boolean", - "description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact operation the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "file_path", - "old_string", - "new_string" - ] - } - }, - { - "name": "exit_plan_mode", - "description": "Use only in plan mode. Present your plan for the user's review and, on approval, leave plan mode. The user may approve (carry out the plan from your next step) or keep planning — their feedback comes back in the tool result; revise and present again.", - "parameters": { - "type": "object", - "properties": { - "plan": { - "type": "string", - "description": "The complete plan, as markdown, starting with a # heading that names it." - } - }, - "required": [ - "plan" - ] - } - }, - { - "name": "get_goal", - "description": "Read the current session goal, including the id and revision that update_goal requires.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "glob", - "description": "Find files, not directories, whose paths match a glob pattern, including hidden and ignored files. Returns up to 100 paths in modification-time order; a larger result keeps the first paths and reports where the complete list was saved.", - "parameters": { - "type": "object", - "properties": { - "pattern": { - "type": "string", - "description": "Glob pattern to match file paths against (e.g. \"**/*.ts\", \"src/**/*.test.js\"). A pattern with no \"/\" matches the basename at any depth, so \"*\" and \"*.ts\" both search the whole tree; include a separator to anchor the depth." - }, - "path": { - "type": "string", - "description": "Directory to search in. Defaults to the session workspace; a relative path resolves against it." - } - }, - "required": [ - "pattern" - ] - } - }, - { - "name": "grep", - "description": "Search file contents with a ripgrep regular expression. Returns matching lines with line numbers, grouped by file. Returns up to 250 matches; a larger result reports where the complete match list was saved.", - "parameters": { - "type": "object", - "properties": { - "pattern": { - "type": "string", - "description": "Regular expression to search for (ripgrep syntax)." - }, - "path": { - "type": "string", - "description": "File or directory to search. Defaults to the session workspace; a relative path resolves against it." - }, - "include": { - "type": "string", - "description": "One glob filter for which files to search (e.g. \"*.ts\", \"*.{js,jsx}\"). Not a list; negation is not supported." - } - }, - "required": [ - "pattern" - ] - } - }, - { - "name": "interrupt_agent", - "description": "Ask a subagent to stop its current work. This call returns without waiting for it to stop. You can continue a local subagent's conversation later with send_message. External executions stop permanently and cannot receive follow-ups. Subagents it started will keep running.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The full path (such as lead/explore/alice) of an agent created under you: your direct child or a deeper descendant." - } - }, - "required": [ - "name" - ] - } - }, - { - "name": "job_kill", - "description": "Request cancellation of a running background job.", - "parameters": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "description": "Job id returned by the tool that started the background work." - }, - "reason": { - "type": "string", - "description": "Optional short reason, recorded in the log and forwarded to the job." - } - }, - "required": [ - "job_id" - ] - } - }, - { - "name": "job_list", - "description": "List your background jobs (running and finished) with their ids, kinds, and statuses.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "job_output", - "description": "Read a background job: output since the previous read for stream jobs, or the result of a finished final-output job.", - "parameters": { - "type": "object", - "properties": { - "job_id": { - "type": "string", - "description": "Job id returned by the tool that started the background work." - }, - "wait": { - "type": "boolean", - "description": "Block until the job finishes or the timeout expires; a timed-out wait leaves the job running. Defaults to false." - }, - "timeout_ms": { - "type": "number", - "description": "Max wait in milliseconds with wait: true. Defaults to and is capped by configuration." - } - }, - "required": [ - "job_id" - ] - } - }, - { - "name": "list_agents", - "description": "List your entire Team: the root agent and all reachable continuable descendants, including yourself. Rows carry full agent paths, descriptions, and status; descendant rows carry parent paths and depth from the root. You can message any other Team member listed as running or inactive. You can interrupt only your own descendants. running means it is working; inactive means it is not currently working. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation.", - "parameters": { - "type": "object", - "properties": {} - } - }, - { - "name": "read", - "description": "Read a UTF-8 text file and return line-numbered content.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to read, resolved by the filesystem backend." - }, - "offset": { - "type": "number", - "description": "1-based first line to return. Defaults to 1." - }, - "limit": { - "type": "number", - "description": "Maximum number of lines to return. Defaults to 2000." - } - }, - "required": [ - "file_path" - ] - } - }, - { - "name": "read_image", - "description": "Read a PNG/JPEG/WebP/GIF file and return the image itself. Large images are downscaled automatically; do not install image libraries or create thumbnails to inspect an image.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to the image file, resolved by the filesystem backend." - } - }, - "required": [ - "file_path" - ] - } - }, - { - "name": "send_message", - "description": "Send a message to an agent. A working agent receives it at its next step; an idle agent starts a new turn with it. Returns delivery confirmation, not the agent's answer.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The full path (such as lead/reviewer) of another member of your Team, as shown by list_agents." - }, - "message": { - "type": "string", - "description": "The message to deliver to the agent." - } - }, - "required": [ - "name", - "message" - ] - } - }, - { - "name": "skill", - "description": "Load the full instructions for a skill. Call it before acting on a task that names or clearly matches a skill in the session skill catalog.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The exact skill name from the available skills list." - } - }, - "required": [ - "name" - ] - } - }, - { - "name": "subagent", - "description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent returns its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its name. If you are still online when it finishes, the runtime automatically delivers its completion notice and any final answer. Use `send_message` to steer it while running or continue its conversation after it finishes.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Optional name unique among your direct children. Omission selects the first unused agent-N, starting at agent-1. Use one segment starting with a lower-case letter, followed by lower-case letters, digits, underscores, or hyphens. Names remain taken after agents finish. The result returns the full path, such as lead/reviewer." - }, - "cwd": { - "type": "string", - "description": "Initial child working directory. Relative paths use your current directory; omitted inherits it. Later directory changes in either agent are independent." - }, - "description": { - "type": "string", - "description": "A short (3-5 word) description of the delegated task, for display." - }, - "prompt": { - "type": "string", - "description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs." - } - }, - "required": [ - "description", - "prompt" - ] - } - }, - { - "name": "subagent_fork", - "description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn). Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive its result, not its intermediate steps. This tool starts an independently managed subagent and immediately returns its name. If you are still online when it finishes, the runtime automatically delivers its completion notice and any final answer. Use `send_message` to steer it while running or continue its conversation after it finishes.", - "parameters": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Optional name unique among your direct children. Omission selects the first unused agent-N, starting at agent-1. Use one segment starting with a lower-case letter, followed by lower-case letters, digits, underscores, or hyphens. Names remain taken after agents finish. The result returns the full path, such as lead/reviewer." - }, - "cwd": { - "type": "string", - "description": "Initial child working directory. Relative paths use your current directory; omitted inherits it. Later directory changes in either agent are independent." - }, - "description": { - "type": "string", - "description": "A short (3-5 word) description of the delegated task, for display." - }, - "prompt": { - "type": "string", - "description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new." - } - }, - "required": [ - "description", - "prompt" - ] - } - }, - { - "name": "todo_write", - "description": "Record and update a task list to plan multi-step work and show progress; skip it for trivial single-step tasks. Add one todo per concrete step before you start. While work remains, keep the todos being worked on `in_progress`, several only when work runs in parallel. Mark each todo `completed` as soon as it is done.", - "parameters": { - "type": "object", - "properties": { - "todos": { - "type": "array", - "description": "The COMPLETE task list, replacing any previous list.", - "items": { - "type": "object", - "additionalProperties": false, - "properties": { - "content": { - "type": "string", - "description": "What the task is — a short imperative line." - }, - "status": { - "type": "string", - "description": "pending (not started) | in_progress (now) | completed (done).", - "enum": [ - "pending", - "in_progress", - "completed" - ] - } - }, - "required": [ - "content", - "status" - ] - } - } - }, - "required": [ - "todos" - ] - } - }, - { - "name": "update_goal", - "description": "Update the current goal.", - "parameters": { - "type": "object", - "properties": { - "goal_id": { - "type": "string", - "description": "Exact id returned by get_goal." - }, - "revision": { - "type": "number", - "description": "Exact positive revision returned by get_goal." - }, - "action": { - "type": "string", - "description": "edit, pause, and resume require a direct top-level human request. complete and blocked are also allowed during an automatic continuation of this goal; blocked is rejected before the configured minimum round count.", - "enum": [ - "edit", - "pause", - "resume", - "complete", - "blocked" - ] - }, - "objective": { - "type": "string", - "description": "Replacement objective; valid only with action edit." - }, - "max_goal_rounds": { - "type": "number", - "description": "Replacement cap; valid only with action edit." - }, - "blocked_reason": { - "type": "string", - "description": "Required only with action blocked: the concrete condition that persisted across rounds and blocks progress." - } - }, - "required": [ - "goal_id", - "revision", - "action" - ] - } - }, - { - "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", - "parameters": { - "type": "object", - "properties": { - "timeout_ms": { - "type": "integer", - "description": "Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000." - } - } - } - }, - { - "name": "web_fetch", - "description": "Fetch the content of a specific HTTP(S) URL and return it decoded to text.", - "parameters": { - "type": "object", - "properties": { - "url": { - "type": "string", - "description": "The HTTP(S) URL to fetch." - } - }, - "required": [ - "url" - ] - } - }, - { - "name": "web_search", - "description": "Search the web for current information. Returns an optional summary answer and a list of source URLs.", - "parameters": { - "type": "object", - "properties": { - "queries": { - "type": "array", - "description": "1–4 search queries; their results are merged.", - "items": { - "type": "string" - } - } - }, - "required": [ - "queries" - ] - } - }, - { - "name": "workflow", - "description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const/oneOf) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides.\n- `pipeline(items, ...stages): Promise` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. A stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) end the whole script instead of producing `null`. The script has no filesystem, network, timer, or Node.js APIs; the agents do the work.", - "parameters": { - "type": "object", - "properties": { - "script": { - "type": "string", - "description": "The plain JavaScript body, not TypeScript and without an `export const meta` statement; top-level await is allowed. End with `return `; the JSON-serializable value is this tool's result." - }, - "meta": { - "type": "object", - "description": "The workflow identity as plain JSON, not code.", - "additionalProperties": true, - "properties": { - "name": { - "type": "string", - "description": "Short kebab-case workflow name." - }, - "description": { - "type": "string", - "description": "One-line description of what the workflow does." - }, - "whenToUse": { - "type": "string", - "description": "Optional guidance on when this workflow applies." - }, - "phases": { - "type": "array", - "description": "Optional phase declarations matched by phase() calls.", - "items": { - "type": "object", - "additionalProperties": true, - "properties": { - "title": { - "type": "string", - "description": "The phase title phase() calls match by exact string." - }, - "detail": { - "type": "string", - "description": "Optional one-line description of the phase." - }, - "provider": { - "type": "string", - "description": "Optional provider override this phase is expected to use." - }, - "model": { - "type": "string", - "description": "Optional model override this phase is expected to use." - } - }, - "required": [ - "title" - ] - } - } - }, - "required": [ - "name", - "description" - ] - }, - "args": { - "type": "object", - "description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]}).", - "additionalProperties": true - }, - "run_in_background": { - "type": "boolean", - "description": "Run as a background job: return a job id immediately instead of waiting; the return value arrives with the completion notice." - } - }, - "required": [ - "script", - "meta" - ] - } - }, - { - "name": "working_directory", - "description": "Read the current working directory, or change it with cd. Relative paths use the current directory. Existing shells and running processes keep their own directories.", - "parameters": { - "type": "object", - "properties": { - "cd": { - "type": "string", - "description": "Existing directory to enter. Omit to read the current directory." - } - } - } - }, - { - "name": "write", - "description": "Create or fully replace a UTF-8 text file.", - "parameters": { - "type": "object", - "properties": { - "file_path": { - "type": "string", - "description": "Path to write, resolved by the filesystem backend. Provide `file_path` before `content` in the arguments." - }, - "content": { - "type": "string", - "description": "Full UTF-8 text content to write." - }, - "sandbox_permissions": { - "type": "string", - "description": "The narrowest wider sandbox mode for a one-shot retry of the exact operation the sandbox just denied; the retry asks the user for approval.", - "enum": [ - "workspace-write", - "danger-full-access" - ] - }, - "justification": { - "type": "string", - "description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. Use the language of the user’s current request." - } - }, - "required": [ - "file_path", - "content" - ] - } - } - ] - ] -} diff --git a/snapshots/sdk/team-session-mode/tool-schemas.expected.json b/snapshots/sdk/team-session-mode/tool-schemas.expected.json new file mode 120000 index 0000000000..425656c12c --- /dev/null +++ b/snapshots/sdk/team-session-mode/tool-schemas.expected.json @@ -0,0 +1 @@ +../../session/team-session-mode/tool-schemas.expected.json \ No newline at end of file diff --git a/snapshots/sdk/text-turn/tool-schemas.expected.json b/snapshots/sdk/text-turn/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/sdk/text-turn/tool-schemas.expected.json +++ b/snapshots/sdk/text-turn/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/sdk/tool-error-details/system-prompt.expected.md b/snapshots/sdk/tool-error-details/system-prompt.expected.md index e0aa569806..a57fa38282 100644 --- a/snapshots/sdk/tool-error-details/system-prompt.expected.md +++ b/snapshots/sdk/tool-error-details/system-prompt.expected.md @@ -243,7 +243,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/sdk/tool-error-details/tool-schemas.expected.json b/snapshots/sdk/tool-error-details/tool-schemas.expected.json index a81dc4b08b..048a577ffb 100644 --- a/snapshots/sdk/tool-error-details/tool-schemas.expected.json +++ b/snapshots/sdk/tool-error-details/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/advanced-toolchain/system-prompt.expected.md b/snapshots/session/advanced-toolchain/system-prompt.expected.md index 1769444b1a..a1b75fa15e 100644 --- a/snapshots/session/advanced-toolchain/system-prompt.expected.md +++ b/snapshots/session/advanced-toolchain/system-prompt.expected.md @@ -256,7 +256,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/advanced-toolchain/tool-schemas.expected.json b/snapshots/session/advanced-toolchain/tool-schemas.expected.json index c10a6b6a33..bbd6ec0784 100644 --- a/snapshots/session/advanced-toolchain/tool-schemas.expected.json +++ b/snapshots/session/advanced-toolchain/tool-schemas.expected.json @@ -513,7 +513,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1238,7 +1238,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/agent-instructions/tool-schemas.expected.json b/snapshots/session/agent-instructions/tool-schemas.expected.json index 0f3e2a5287..5fa1a09109 100644 --- a/snapshots/session/agent-instructions/tool-schemas.expected.json +++ b/snapshots/session/agent-instructions/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1117,7 +1117,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/background-confinement-failure/tool-schemas.expected.json b/snapshots/session/background-confinement-failure/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/background-confinement-failure/tool-schemas.expected.json +++ b/snapshots/session/background-confinement-failure/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/both-mode-turn/system-prompt.expected.md b/snapshots/session/both-mode-turn/system-prompt.expected.md index db6bededc5..6841f67231 100644 --- a/snapshots/session/both-mode-turn/system-prompt.expected.md +++ b/snapshots/session/both-mode-turn/system-prompt.expected.md @@ -210,7 +210,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/browser-use-chrome-devtools-mcp/tool-schemas.expected.json b/snapshots/session/browser-use-chrome-devtools-mcp/tool-schemas.expected.json index 0dd9557462..3be1d4a95d 100644 --- a/snapshots/session/browser-use-chrome-devtools-mcp/tool-schemas.expected.json +++ b/snapshots/session/browser-use-chrome-devtools-mcp/tool-schemas.expected.json @@ -579,7 +579,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json b/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json index e39f45c8c3..27e01a9325 100644 --- a/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json +++ b/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json @@ -581,7 +581,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json b/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json index 89337b6bb2..e73cd16c41 100644 --- a/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json +++ b/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json @@ -685,7 +685,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/claude-code-mods/tool-schemas.expected.json b/snapshots/session/claude-code-mods/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/claude-code-mods/tool-schemas.expected.json +++ b/snapshots/session/claude-code-mods/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/compaction-recovery/tool-schemas.expected.json b/snapshots/session/compaction-recovery/tool-schemas.expected.json index 0f3e2a5287..5fa1a09109 100644 --- a/snapshots/session/compaction-recovery/tool-schemas.expected.json +++ b/snapshots/session/compaction-recovery/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1117,7 +1117,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/computer-use-cua-driver-mcp/tool-schemas.expected.json b/snapshots/session/computer-use-cua-driver-mcp/tool-schemas.expected.json index 37de6e8d2f..5910228ecb 100644 --- a/snapshots/session/computer-use-cua-driver-mcp/tool-schemas.expected.json +++ b/snapshots/session/computer-use-cua-driver-mcp/tool-schemas.expected.json @@ -558,7 +558,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/computer-use-cua-driver-native/tool-schemas.expected.json b/snapshots/session/computer-use-cua-driver-native/tool-schemas.expected.json index 7c0a2aa274..e6f1a045a7 100644 --- a/snapshots/session/computer-use-cua-driver-native/tool-schemas.expected.json +++ b/snapshots/session/computer-use-cua-driver-native/tool-schemas.expected.json @@ -518,7 +518,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json b/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json index 1d346ae87c..c425e34915 100644 --- a/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json +++ b/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json @@ -513,7 +513,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/cua-native/tool-schemas.expected.json b/snapshots/session/cua-native/tool-schemas.expected.json index e143f7dd6e..5506a1040c 100644 --- a/snapshots/session/cua-native/tool-schemas.expected.json +++ b/snapshots/session/cua-native/tool-schemas.expected.json @@ -1629,7 +1629,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/cua-ptc/system-prompt.expected.md b/snapshots/session/cua-ptc/system-prompt.expected.md index 70f58ff185..477e7212a4 100644 --- a/snapshots/session/cua-ptc/system-prompt.expected.md +++ b/snapshots/session/cua-ptc/system-prompt.expected.md @@ -257,7 +257,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json b/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json +++ b/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json b/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json +++ b/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json b/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json index b044ae9b54..0435621db2 100644 --- a/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json +++ b/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1125,7 +1125,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1769,7 +1769,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json b/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json +++ b/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/fs-glob-sampling/tool-schemas.expected.json b/snapshots/session/fs-glob-sampling/tool-schemas.expected.json index 9fac130945..0067522679 100644 --- a/snapshots/session/fs-glob-sampling/tool-schemas.expected.json +++ b/snapshots/session/fs-glob-sampling/tool-schemas.expected.json @@ -331,7 +331,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/headless.snapshot.ts b/snapshots/session/headless.snapshot.ts index cc975899f5..a35dc5ddf3 100644 --- a/snapshots/session/headless.snapshot.ts +++ b/snapshots/session/headless.snapshot.ts @@ -1426,6 +1426,16 @@ describe('headless recorded-session snapshots', () => { expect(log).toContain('# Office task routing') } if (scenario.name === 'advanced-toolchain') verifyAdvancedToolchain(actualLogs) + if (scenario.name === 'subagent-depth-two-rejection') { + expect(actualLogs).toHaveLength(3) + const leaf = actualLogs[2]! + expect(leaf.content).toContain('DEPTH_REJECTED') + for (const ancestor of actualLogs.slice(0, 2)) { + const notices = parseSessionLog(ancestor.content).filter(event => event.type === 'user/message' + && event.data.source.kind === 'subagent-settled' && event.data.source.senderSessionId === leaf.header.id) + expect(notices).toEqual([]) + } + } if (scenario.name === 'team-tree-messages') verifyTeamTreeMessage(actualLogs) if (scenario.name === 'team-task-inbox') verifyTeamTaskInbox(actualLogs) if (scenario.name === 'session-query-spill') { diff --git a/snapshots/session/lsp-definition/tool-schemas.expected.json b/snapshots/session/lsp-definition/tool-schemas.expected.json index 6c11d33fe1..a9828084bb 100644 --- a/snapshots/session/lsp-definition/tool-schemas.expected.json +++ b/snapshots/session/lsp-definition/tool-schemas.expected.json @@ -509,7 +509,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/mcp-resources-ptc/system-prompt.expected.md b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md index 9434b2f851..1f17714ec0 100644 --- a/snapshots/session/mcp-resources-ptc/system-prompt.expected.md +++ b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md @@ -239,7 +239,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/mcp-resources/tool-schemas.expected.json b/snapshots/session/mcp-resources/tool-schemas.expected.json index 00d105e3ca..b9f1865af4 100644 --- a/snapshots/session/mcp-resources/tool-schemas.expected.json +++ b/snapshots/session/mcp-resources/tool-schemas.expected.json @@ -533,7 +533,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json b/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json index 9612480a30..233691eb04 100644 --- a/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json +++ b/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json @@ -491,7 +491,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/office-progressive-native/tool-schemas.expected.json b/snapshots/session/office-progressive-native/tool-schemas.expected.json index 18327e99d4..d1b2497c64 100644 --- a/snapshots/session/office-progressive-native/tool-schemas.expected.json +++ b/snapshots/session/office-progressive-native/tool-schemas.expected.json @@ -427,7 +427,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/office-progressive-ptc/system-prompt.expected.md b/snapshots/session/office-progressive-ptc/system-prompt.expected.md index a5f0d73340..5f272e88eb 100644 --- a/snapshots/session/office-progressive-ptc/system-prompt.expected.md +++ b/snapshots/session/office-progressive-ptc/system-prompt.expected.md @@ -189,7 +189,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/office-skills/tool-schemas.expected.json b/snapshots/session/office-skills/tool-schemas.expected.json index 18327e99d4..d1b2497c64 100644 --- a/snapshots/session/office-skills/tool-schemas.expected.json +++ b/snapshots/session/office-skills/tool-schemas.expected.json @@ -427,7 +427,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json b/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json index 81721a972b..cddde2a826 100644 --- a/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json +++ b/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json @@ -302,7 +302,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json b/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json index 9d9085a674..ac9170580b 100644 --- a/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json +++ b/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json @@ -533,7 +533,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1309,7 +1309,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/plugin-manager/tool-schemas.expected.json b/snapshots/session/plugin-manager/tool-schemas.expected.json index f775996cd9..fb5a5684dd 100644 --- a/snapshots/session/plugin-manager/tool-schemas.expected.json +++ b/snapshots/session/plugin-manager/tool-schemas.expected.json @@ -533,7 +533,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/product-subagent-both/tool-schemas.expected.json b/snapshots/session/product-subagent-both/tool-schemas.expected.json index d5aaa8450d..e662a382a9 100644 --- a/snapshots/session/product-subagent-both/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-both/tool-schemas.expected.json @@ -588,7 +588,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/product-subagent-codex/tool-schemas.expected.json b/snapshots/session/product-subagent-codex/tool-schemas.expected.json index a1807241c3..b98587bcc8 100644 --- a/snapshots/session/product-subagent-codex/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-codex/tool-schemas.expected.json @@ -530,7 +530,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/product-subagent-oar/tool-schemas.expected.json b/snapshots/session/product-subagent-oar/tool-schemas.expected.json index c7eb7512f8..f6e4d24853 100644 --- a/snapshots/session/product-subagent-oar/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-oar/tool-schemas.expected.json @@ -510,7 +510,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/product-subagent-result-diagnostic/tool-schemas.expected.json b/snapshots/session/product-subagent-result-diagnostic/tool-schemas.expected.json index 04b648ddbe..5688e00571 100644 --- a/snapshots/session/product-subagent-result-diagnostic/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-result-diagnostic/tool-schemas.expected.json @@ -501,7 +501,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/provider-cwd/tool-schemas.expected.json b/snapshots/session/provider-cwd/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/provider-cwd/tool-schemas.expected.json +++ b/snapshots/session/provider-cwd/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ptc-node-read-only/system-prompt.expected.md b/snapshots/session/ptc-node-read-only/system-prompt.expected.md index a2b6492900..45ad1597f1 100644 --- a/snapshots/session/ptc-node-read-only/system-prompt.expected.md +++ b/snapshots/session/ptc-node-read-only/system-prompt.expected.md @@ -214,7 +214,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/ptc-node-workspace/system-prompt.expected.md b/snapshots/session/ptc-node-workspace/system-prompt.expected.md index a2b6492900..45ad1597f1 100644 --- a/snapshots/session/ptc-node-workspace/system-prompt.expected.md +++ b/snapshots/session/ptc-node-workspace/system-prompt.expected.md @@ -214,7 +214,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/ptc-persistent/system-prompt.expected.md b/snapshots/session/ptc-persistent/system-prompt.expected.md index b575210b42..eef5331b13 100644 --- a/snapshots/session/ptc-persistent/system-prompt.expected.md +++ b/snapshots/session/ptc-persistent/system-prompt.expected.md @@ -214,7 +214,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/ptc-policy-change/system-prompt.expected.md b/snapshots/session/ptc-policy-change/system-prompt.expected.md index ceaeb4a2d8..896930a1b1 100644 --- a/snapshots/session/ptc-policy-change/system-prompt.expected.md +++ b/snapshots/session/ptc-policy-change/system-prompt.expected.md @@ -210,7 +210,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/ptc-python-turn/system-prompt.expected.md b/snapshots/session/ptc-python-turn/system-prompt.expected.md index 26136146a4..b9ddf81403 100644 --- a/snapshots/session/ptc-python-turn/system-prompt.expected.md +++ b/snapshots/session/ptc-python-turn/system-prompt.expected.md @@ -564,7 +564,7 @@ class Tools(Protocol): async def update_goal(self, args: UpdateGoalArgs) -> UpdateGoalOutput1 | UpdateGoalOutput2: """Update the current goal.""" async def wait_agent(self, args: WaitAgentArgs) -> WaitAgentOutput: - """Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.""" + """Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.""" async def web_fetch(self, args: WebFetchArgs) -> WebFetchOutput: """Fetch the content of a specific HTTP(S) URL and return it decoded to text.""" async def web_search(self, args: WebSearchArgs) -> WebSearchOutput: diff --git a/snapshots/session/ptc-read-image/system-prompt.expected.md b/snapshots/session/ptc-read-image/system-prompt.expected.md index 897d746c39..649c9a5752 100644 --- a/snapshots/session/ptc-read-image/system-prompt.expected.md +++ b/snapshots/session/ptc-read-image/system-prompt.expected.md @@ -210,7 +210,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/ptc-turn/system-prompt.expected.md b/snapshots/session/ptc-turn/system-prompt.expected.md index a2b6492900..45ad1597f1 100644 --- a/snapshots/session/ptc-turn/system-prompt.expected.md +++ b/snapshots/session/ptc-turn/system-prompt.expected.md @@ -214,7 +214,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json b/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json index f3d73d9fff..a1f31e9163 100644 --- a/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json +++ b/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json @@ -601,7 +601,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json b/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json index 87b6536cbd..a45f148b0e 100644 --- a/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json +++ b/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json @@ -383,7 +383,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ralph-loop/tool-schemas.1.expected.json b/snapshots/session/ralph-loop/tool-schemas.1.expected.json index f265ac02ee..a2d1a732dd 100644 --- a/snapshots/session/ralph-loop/tool-schemas.1.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.1.expected.json @@ -535,7 +535,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ralph-loop/tool-schemas.2.expected.json b/snapshots/session/ralph-loop/tool-schemas.2.expected.json index f265ac02ee..a2d1a732dd 100644 --- a/snapshots/session/ralph-loop/tool-schemas.2.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.2.expected.json @@ -535,7 +535,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/ralph-loop/tool-schemas.expected.json b/snapshots/session/ralph-loop/tool-schemas.expected.json index 856b7376da..630f537b46 100644 --- a/snapshots/session/ralph-loop/tool-schemas.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.expected.json @@ -492,7 +492,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/session-query-spill/tool-schemas.expected.json b/snapshots/session/session-query-spill/tool-schemas.expected.json index a2d6b4e24d..0dbbe14a3e 100644 --- a/snapshots/session/session-query-spill/tool-schemas.expected.json +++ b/snapshots/session/session-query-spill/tool-schemas.expected.json @@ -676,7 +676,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-acp-diagnostic/tool-schemas.expected.json b/snapshots/session/subagent-acp-diagnostic/tool-schemas.expected.json index 144c7e88f3..11778fe289 100644 --- a/snapshots/session/subagent-acp-diagnostic/tool-schemas.expected.json +++ b/snapshots/session/subagent-acp-diagnostic/tool-schemas.expected.json @@ -501,7 +501,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json b/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json index 0d6642faf8..4d4c5be135 100644 --- a/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json @@ -508,7 +508,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-agent-preset/tool-schemas.expected.json b/snapshots/session/subagent-agent-preset/tool-schemas.expected.json index a920a914dc..a7b942ac50 100644 --- a/snapshots/session/subagent-agent-preset/tool-schemas.expected.json +++ b/snapshots/session/subagent-agent-preset/tool-schemas.expected.json @@ -479,7 +479,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json b/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json index c278008fd2..81e2843096 100644 --- a/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json +++ b/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json @@ -535,7 +535,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json b/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json index e3bfaa40e2..19f5b2d323 100644 --- a/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json @@ -495,7 +495,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-structured-parent/tool-schemas.expected.json b/snapshots/session/subagent-structured-parent/tool-schemas.expected.json index e1c5d0661c..90ebd452c4 100644 --- a/snapshots/session/subagent-structured-parent/tool-schemas.expected.json +++ b/snapshots/session/subagent-structured-parent/tool-schemas.expected.json @@ -480,7 +480,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json b/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json index 21042932d7..efad901db8 100644 --- a/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json @@ -346,7 +346,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/system-prompt-in-history/tool-schemas.expected.json b/snapshots/session/system-prompt-in-history/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/system-prompt-in-history/tool-schemas.expected.json +++ b/snapshots/session/system-prompt-in-history/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/team-session-mode/tool-schemas.expected.json b/snapshots/session/team-session-mode/tool-schemas.expected.json index 0f2b37b2e9..2fb5c3a55f 100644 --- a/snapshots/session/team-session-mode/tool-schemas.expected.json +++ b/snapshots/session/team-session-mode/tool-schemas.expected.json @@ -619,7 +619,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { @@ -1264,7 +1264,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/team-targets/tool-schemas.expected.json b/snapshots/session/team-targets/tool-schemas.expected.json index dc2e239f7b..2950bf090f 100644 --- a/snapshots/session/team-targets/tool-schemas.expected.json +++ b/snapshots/session/team-targets/tool-schemas.expected.json @@ -619,7 +619,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/team-tree-messages/replay.override.json b/snapshots/session/team-tree-messages/replay.override.json index 622cee40f1..85421dc9b7 100644 --- a/snapshots/session/team-tree-messages/replay.override.json +++ b/snapshots/session/team-tree-messages/replay.override.json @@ -1,104 +1,33 @@ -[ - { - "kind": "chunks", - "chunks": [ - { - "type": "block-start", - "index": 0, - "blockType": "tool-call" - }, - { - "type": "block-end", - "index": 0, - "block": { - "type": "tool-call", - "id": "desc-1-1", - "name": "workflow", - "arguments": "{\"meta\":{\"description\":\"Create the nested reviewer.\",\"name\":\"branch\"},\"script\":\"return await agent(\\\"Use workflow to create a nested reviewer that lists the entire Team, sends the Lead a message, and replies LEAF_READY. Then reply BRANCH_READY.\\\");\"}" - } - }, - { - "type": "finish", - "reason": { - "kind": "tool-calls" - } +{ + "patches": [ + { + "at": 2, + "entry": { + "kind": "chunks", + "chunks": [ + { + "type": "block-start", + "index": 0, + "blockType": "tool-call" + }, + { + "type": "block-end", + "index": 0, + "block": { + "type": "tool-call", + "id": "root-resume-leaf", + "name": "send_message", + "arguments": "{\"name\":\"{{fromRequest:(lead/[^ ]+) .inactive. parent=[^ ]+ depth=2}}\",\"message\":\"Continue once with LEAF_RESUMED.\"}" + } + }, + { + "type": "finish", + "reason": { + "kind": "tool-calls" + } + } + ] } - ] - }, - { - "kind": "chunks", - "chunks": [ - { - "type": "block-start", - "index": 0, - "blockType": "tool-call" - }, - { - "type": "block-end", - "index": 0, - "block": { - "type": "tool-call", - "id": "root-list", - "name": "list_agents", - "arguments": "{}" - } - }, - { - "type": "finish", - "reason": { - "kind": "tool-calls" - } - } - ] - }, - { - "kind": "chunks", - "chunks": [ - { - "type": "block-start", - "index": 0, - "blockType": "tool-call" - }, - { - "type": "block-end", - "index": 0, - "block": { - "type": "tool-call", - "id": "root-resume-leaf", - "name": "send_message", - "arguments": "{\"name\":\"{{fromRequest:(lead/[^ ]+) .inactive. parent=[^ ]+ depth=2}}\",\"message\":\"Continue once with LEAF_RESUMED.\"}" - } - }, - { - "type": "finish", - "reason": { - "kind": "tool-calls" - } - } - ] - }, - { - "kind": "chunks", - "chunks": [ - { - "type": "block-start", - "index": 0, - "blockType": "text" - }, - { - "type": "block-end", - "index": 0, - "block": { - "type": "text", - "text": "TEAM_TREE_REVIEWED" - } - }, - { - "type": "finish", - "reason": { - "kind": "stop" - } - } - ] - } -] + } + ] +} diff --git a/snapshots/session/text-turn/tool-schemas.expected.json b/snapshots/session/text-turn/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/text-turn/tool-schemas.expected.json +++ b/snapshots/session/text-turn/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/web-fetch/tool-schemas.expected.json b/snapshots/session/web-fetch/tool-schemas.expected.json index 122103eb7a..8a8c879577 100644 --- a/snapshots/session/web-fetch/tool-schemas.expected.json +++ b/snapshots/session/web-fetch/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/workflow-confinement/tool-schemas.expected.json b/snapshots/session/workflow-confinement/tool-schemas.expected.json index 164d1873a3..118500c381 100644 --- a/snapshots/session/workflow-confinement/tool-schemas.expected.json +++ b/snapshots/session/workflow-confinement/tool-schemas.expected.json @@ -472,7 +472,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/workspace-dependencies/tool-schemas.expected.json b/snapshots/session/workspace-dependencies/tool-schemas.expected.json index 6904151f26..775e22deae 100644 --- a/snapshots/session/workspace-dependencies/tool-schemas.expected.json +++ b/snapshots/session/workspace-dependencies/tool-schemas.expected.json @@ -435,7 +435,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/session/worktree/tool-schemas.expected.json b/snapshots/session/worktree/tool-schemas.expected.json index 2095f777c1..eaa62aeae4 100644 --- a/snapshots/session/worktree/tool-schemas.expected.json +++ b/snapshots/session/worktree/tool-schemas.expected.json @@ -489,7 +489,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/web/fresh-round-trip/tool-schemas.expected.json b/snapshots/web/fresh-round-trip/tool-schemas.expected.json index bd0820865b..6e796111f6 100644 --- a/snapshots/web/fresh-round-trip/tool-schemas.expected.json +++ b/snapshots/web/fresh-round-trip/tool-schemas.expected.json @@ -864,7 +864,7 @@ }, { "name": "wait_agent", - "description": "Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending.", + "description": "Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending.", "parameters": { "type": "object", "properties": { diff --git a/snapshots/web/official-subagent-bundle/system-prompt.expected.md b/snapshots/web/official-subagent-bundle/system-prompt.expected.md index e767739c70..7d1f0496d4 100644 --- a/snapshots/web/official-subagent-bundle/system-prompt.expected.md +++ b/snapshots/web/official-subagent-bundle/system-prompt.expected.md @@ -349,7 +349,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/web/optional-string-editor/system-prompt.expected.md b/snapshots/web/optional-string-editor/system-prompt.expected.md index 6656dc8f1d..894de19050 100644 --- a/snapshots/web/optional-string-editor/system-prompt.expected.md +++ b/snapshots/web/optional-string-editor/system-prompt.expected.md @@ -355,7 +355,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number; diff --git a/snapshots/web/ptc-round/system-prompt.expected.md b/snapshots/web/ptc-round/system-prompt.expected.md index ac16636e55..500f3515db 100644 --- a/snapshots/web/ptc-round/system-prompt.expected.md +++ b/snapshots/web/ptc-round/system-prompt.expected.md @@ -338,7 +338,7 @@ interface ToolArgsMap { /** Required only with action blocked: the concrete condition that persisted across rounds and blocks progress. */ blocked_reason?: string; } & Record; - /** Wait for new input in your own inbox, including agent messages, completion notifications, and user steering. Returns immediately when input is already pending. */ + /** Wait for input at your next step, including agent messages, completion notifications, and user steering. Returns immediately when that input is already pending. */ wait_agent: { /** Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000. */ timeout_ms?: number;