From 8c8b3027a6424797ca862908a5254f422856c24e Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Mon, 19 Oct 2026 19:58:37 +0800 Subject: [PATCH] fix: address subagent and task-board review findings --- ...7-adjacent-agent-steer-messaging.i18n.yaml | 24 ++-- ...26-08-27-adjacent-agent-steer-messaging.md | 26 ++-- ...08-27-adjacent-agent-steer-messaging.zh.md | 26 ++-- .../headless/tests/headless.expected.e2e.ts | 2 - apps/web/tests/agent-team-panel.e2e.ts | 2 +- apps/web/tests/assembled-remote.ts | 1 - .../agent-team-panel/resize.expected.md | 2 +- .../agent-team-panel/task.expected.md | 2 +- docs/capability-seams.i18n.yaml | 4 +- docs/capability-seams.md | 2 +- docs/capability-seams.zh.md | 2 +- docs/config-catalog.md | 2 +- docs/config-catalog.zh.md | 2 +- docs/persistence-catalog.i18n.yaml | 40 +++--- docs/persistence-catalog.md | 20 +-- docs/persistence-catalog.zh.md | 20 +-- docs/persistence-schema.json | 12 +- docs/subsystems/README.i18n.yaml | 4 +- docs/subsystems/README.md | 2 +- docs/subsystems/README.zh.md | 2 +- docs/subsystems/agent-team.i18n.yaml | 4 +- docs/subsystems/agent-team.md | 29 ++-- docs/subsystems/agent-team.zh.md | 29 ++-- docs/subsystems/subagent.i18n.yaml | 4 +- docs/subsystems/subagent.md | 4 +- docs/subsystems/subagent.zh.md | 4 +- docs/tool-catalog.i18n.yaml | 4 +- docs/tool-catalog.md | 2 +- docs/tool-catalog.zh.md | 2 +- .../agent-team-subagents/guide.i18n.yaml | 8 +- .../v0.2.1-rc.2/agent-team-subagents/guide.md | 11 +- .../agent-team-subagents/guide.zh.md | 11 +- .../client/ui-subagent/src/client/index.ts | 3 +- .../experimental/agent-team/README.i18n.yaml | 8 +- packages/experimental/agent-team/README.md | 4 +- packages/experimental/agent-team/README.zh.md | 4 +- .../experimental/agent-team/locale/en.json | 2 +- .../experimental/agent-team/locale/zh.json | 2 +- packages/experimental/agent-team/package.json | 3 +- .../experimental/agent-team/src/client.ts | 3 +- packages/experimental/agent-team/src/index.ts | 12 +- .../experimental/agent-team/src/journal.ts | 13 +- .../experimental/agent-team/src/projection.ts | 43 ++---- .../experimental/agent-team/src/task-board.ts | 16 ++- packages/experimental/agent-team/src/types.ts | 17 +-- .../agent-team/tests/persistence.spec.ts | 10 +- .../tests/projection-events.spec.ts | 48 +------ .../agent-team/tests/team.spec.ts | 22 ++- .../client-ui-agent-team/README.i18n.yaml | 4 +- .../client-ui-agent-team/README.md | 2 +- .../client-ui-agent-team/README.zh.md | 2 +- .../client-ui-agent-team/package.json | 1 - .../src/client/TeamAction.tsx | 3 +- .../src/client/locales.ts | 4 +- .../tests/team-action.client.spec.tsx | 30 ++-- .../client-ui-agent-team/tsconfig.json | 1 - .../experimental/tool-agent-team/src/index.ts | 4 +- .../tool-agent-team/tests/tool-team.spec.ts | 34 ++++- .../extensions/tool-cordis/src/api-catalog.ts | 10 +- .../references/packages.md | 2 +- packages/sdk/server/README.i18n.yaml | 4 +- packages/sdk/server/README.md | 2 +- packages/sdk/server/README.zh.md | 2 +- packages/sdk/server/src/server.ts | 5 +- packages/sdk/server/tests/resume.spec.ts | 19 +++ packages/subagent/subagent/README.i18n.yaml | 4 +- packages/subagent/subagent/README.md | 2 +- packages/subagent/subagent/README.zh.md | 2 +- .../subagent/src/continuation-messages.ts | 6 +- packages/subagent/subagent/src/types.ts | 2 +- .../tool-subagent-control/README.i18n.yaml | 4 +- .../subagent/tool-subagent-control/README.md | 2 +- .../tool-subagent-control/README.zh.md | 2 +- .../tool-subagent-control/src/wait-agent.ts | 4 +- .../tests/wait-agent.spec.ts | 24 +++- pnpm-lock.yaml | 6 - python/sdk/tests/test_client.py | 4 +- scripts/gen-cordis-catalog.ts | 1 - scripts/gen-doc-graphs.ts | 2 +- scripts/type-equiv.manifest.json | 5 - .../sdk/bash-tool/tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../sdk/text-turn/tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../tool-schemas.expected.json | 4 +- .../tool-schemas.expected.json | 4 +- .../tool-schemas.expected.json | 2 +- .../both-mode-turn/system-prompt.expected.md | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 4 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../cua-native/tool-schemas.expected.json | 2 +- .../session/cua-ptc/system-prompt.expected.md | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 6 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- snapshots/session/headless.snapshot.ts | 10 ++ .../lsp-definition/tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../mcp-resources/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../office-skills/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 4 +- .../plugin-manager/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../provider-cwd/tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../system-prompt.expected.md | 2 +- .../ptc-persistent/system-prompt.expected.md | 2 +- .../system-prompt.expected.md | 2 +- .../ptc-python-turn/system-prompt.expected.md | 2 +- .../ptc-read-image/system-prompt.expected.md | 2 +- .../ptc-turn/system-prompt.expected.md | 2 +- .../tool-schemas.expected.json | 2 +- .../pwsh-tool-turn/tool-schemas.expected.json | 2 +- .../ralph-loop/tool-schemas.1.expected.json | 2 +- .../ralph-loop/tool-schemas.2.expected.json | 2 +- .../ralph-loop/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.1.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../team-direct-inbox/session.v4.jsonl | 6 +- .../team-targets/tool-schemas.expected.json | 2 +- .../team-tree-messages/replay.override.json | 135 +++++------------- .../text-turn/tool-schemas.expected.json | 2 +- .../web-fetch/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../worktree/tool-schemas.expected.json | 2 +- .../tool-schemas.expected.json | 2 +- .../system-prompt.expected.md | 2 +- .../system-prompt.expected.md | 2 +- .../web/ptc-round/system-prompt.expected.md | 2 +- 161 files changed, 474 insertions(+), 563 deletions(-) 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 a4a1666b4d..2199cab608 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 ebc011eb5a..0583ac9c3f 100644 --- a/apps/web/tests/agent-team-panel.e2e.ts +++ b/apps/web/tests/agent-team-panel.e2e.ts @@ -105,7 +105,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 b987fab3c6..1683f2b8f2 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:146`](../packages/experimental/agent-team/src/types.ts) +- `source`: [`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts) ```ts config-catalog /** Team-service deployment limits. */ diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 3b6f805bb8..dec153a767 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:146`](../packages/experimental/agent-team/src/types.ts) +- `source`: [`packages/experimental/agent-team/src/types.ts:131`](../packages/experimental/agent-team/src/types.ts) ```ts config-catalog /** Team-service deployment limits. */ diff --git a/docs/persistence-catalog.i18n.yaml b/docs/persistence-catalog.i18n.yaml index bbcd884d12..379ee71554 100644 --- a/docs/persistence-catalog.i18n.yaml +++ b/docs/persistence-catalog.i18n.yaml @@ -225,17 +225,17 @@ en: a640800a38b3c874 zh: a640800a38b3c874 /session-persistence-event-catalog/events/team/team-member-log-only: - en: 62ca3cc66c256e54 - zh: a5d709533f745f24 + en: 8710c60e94d6115c + zh: ca1c923e2c43ea3c /session-persistence-event-catalog/events/team/team-message-delivered-log-only: - en: 46c19f088d5a94f8 - zh: 04d17ef4413b042a + en: 79ecdb0f03a0f310 + zh: d0db8dc2b0414fa1 /session-persistence-event-catalog/events/team/team-message-queued-log-only: - en: 88fc05449dbe5b99 - zh: bc8ff3dc06b27e63 + en: 29baf1c57a73e0e6 + zh: 0275630a61028e59 /session-persistence-event-catalog/events/team/team-task-log-only: - en: f3fa855b82108fca - zh: 5ba676e24b30eb2f + en: 2c0bab8d5420ed5e + zh: 79b203f7a341a43f /session-persistence-event-catalog/events/todo: en: 308d4bb4b6b96e35 zh: 308d4bb4b6b96e35 @@ -1251,11 +1251,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 @@ -1869,17 +1869,17 @@ en: 3216b23c131d0070 zh: 73ca171c6014f16e /session-persistence-event-catalog/resolved-persistence-types/member-teamid-version: - en: de1721a0cf5e4004 - zh: b05569904df6273b + en: fa8d55aef7cbc4ee + zh: aff1a32316984f09 /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: c50a3721d90a4140 - zh: 5a7ab76242f90871 + en: a563c1f1cd1a5091 + zh: f1dc81b4b46fe088 /session-persistence-event-catalog/resolved-persistence-types/messageid-targetid-teamid-version: - en: 5fcc35d2cc72ea06 - zh: d5715c3731bbe68a + en: e3de1d2c12631337 + zh: 50f98d422c7f9de0 /session-persistence-event-catalog/resolved-persistence-types/mode-source: en: 3028b222670ce5ad zh: cae4c9437e560d61 @@ -1911,8 +1911,8 @@ en: 5360af614fe753e3 zh: 96427c489cc71de6 /session-persistence-event-catalog/resolved-persistence-types/task-teamid-version: - en: 6f8a715481a90d4f - zh: 90abd71d3328af5e + en: 03858dd78e884161 + zh: 73e7da84b85be71c /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 fcebc0a3eb..01246680d9 100644 --- a/docs/persistence-catalog.md +++ b/docs/persistence-catalog.md @@ -1012,7 +1012,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:185`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:170`](../packages/experimental/agent-team/src/types.ts) @@ -1030,7 +1030,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:185`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamMessageId](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:191`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:176`](../packages/experimental/agent-team/src/types.ts) @@ -1043,7 +1043,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:191`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamMessageSnapshot](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:189`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:174`](../packages/experimental/agent-team/src/types.ts) @@ -1056,7 +1056,7 @@ Source: [`packages/experimental/agent-team/src/types.ts:189`](../packages/experi Types: [TeamId](subsystems/agent-team.md) · [TeamTaskSnapshot](subsystems/agent-team.md) -Source: [`packages/experimental/agent-team/src/types.ts:187`](../packages/experimental/agent-team/src/types.ts) +Source: [`packages/experimental/agent-team/src/types.ts:172`](../packages/experimental/agent-team/src/types.ts) ### `todo/*` @@ -4976,7 +4976,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 | |---|---|---| @@ -4996,7 +4996,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 | |---|---|---| @@ -7706,7 +7706,7 @@ Sources: [`packages/web/web-search-deepseek/src/provider.ts:61`](../packages/web SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1` -Sources: [`packages/experimental/agent-team/src/types.ts:185`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:170`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7734,7 +7734,7 @@ Sources: [`packages/core/session/src/types.ts:331`](../packages/core/session/src SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` -Sources: [`packages/experimental/agent-team/src/types.ts:189`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:174`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7748,7 +7748,7 @@ Sources: [`packages/experimental/agent-team/src/types.ts:189`](../packages/exper SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1` -Sources: [`packages/experimental/agent-team/src/types.ts:191`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:176`](../packages/experimental/agent-team/src/types.ts) | Property | Presence | Type | |---|---|---| @@ -7894,7 +7894,7 @@ Sources: [`packages/compaction/compaction-image-offload/src/projection.ts:25`](. SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c` -Sources: [`packages/experimental/agent-team/src/types.ts:187`](../packages/experimental/agent-team/src/types.ts) +Sources: [`packages/experimental/agent-team/src/types.ts:172`](../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 1306974dc3..9c0f4da64d 100644 --- a/docs/persistence-catalog.zh.md +++ b/docs/persistence-catalog.zh.md @@ -1014,7 +1014,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMemberSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:185`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:170`](../packages/experimental/agent-team/src/types.ts) @@ -1032,7 +1032,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageId](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:191`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:176`](../packages/experimental/agent-team/src/types.ts) @@ -1045,7 +1045,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamMessageSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:189`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:174`](../packages/experimental/agent-team/src/types.ts) @@ -1058,7 +1058,7 @@ export type SessionEvent = { 类型:[TeamId](subsystems/agent-team.zh.md) · [TeamTaskSnapshot](subsystems/agent-team.zh.md) -来源:[`packages/experimental/agent-team/src/types.ts:187`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:172`](../packages/experimental/agent-team/src/types.ts) ### `todo/*` @@ -4978,7 +4978,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) | 属性 | 存在性 | 类型 | |---|---|---| @@ -4998,7 +4998,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) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7708,7 +7708,7 @@ SHA-256: `9c001ecb5e1d3c3894410db2a1e383842e5f8454fb52182338686158abc1b3ee` SHA-256: `b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1` -来源:[`packages/experimental/agent-team/src/types.ts:185`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:170`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7736,7 +7736,7 @@ SHA-256: `6bc8e51124d0874bc89e1d0ceff381a9c3cd0fc240667d69f841ce4f60f65fe5` SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` -来源:[`packages/experimental/agent-team/src/types.ts:189`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:174`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7750,7 +7750,7 @@ SHA-256: `ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403` SHA-256: `0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1` -来源:[`packages/experimental/agent-team/src/types.ts:191`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:176`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| @@ -7896,7 +7896,7 @@ SHA-256: `477614bca595a9e7adb5a1870be446b9a8684f3ba8950259d6289f2d3b74f028` SHA-256: `a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c` -来源:[`packages/experimental/agent-team/src/types.ts:187`](../packages/experimental/agent-team/src/types.ts) +来源:[`packages/experimental/agent-team/src/types.ts:172`](../packages/experimental/agent-team/src/types.ts) | 属性 | 存在性 | 类型 | |---|---|---| diff --git a/docs/persistence-schema.json b/docs/persistence-schema.json index 433e2613a3..9533fcfd81 100644 --- a/docs/persistence-schema.json +++ b/docs/persistence-schema.json @@ -22562,7 +22562,7 @@ "digest": "0bcfaf5566d271028429ac7b562cb59053c9d78b8ae14708c35fac835108d1b1", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:191" + "packages/experimental/agent-team/src/types.ts:176" ] }, { @@ -22947,7 +22947,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" ] }, { @@ -23127,7 +23127,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" ] }, { @@ -24879,7 +24879,7 @@ "digest": "a40d12070f6f4a124f32fb3cc86e7857554702e32f0eda3080a25c4b9ac9b18c", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:187" + "packages/experimental/agent-team/src/types.ts:172" ] }, { @@ -25126,7 +25126,7 @@ "digest": "b1ebd5cda47d90d980bb05bffc3f970c51537b9aff15b3bc3d639811727741b1", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:185" + "packages/experimental/agent-team/src/types.ts:170" ] }, { @@ -25831,7 +25831,7 @@ "digest": "ea08ec1df0c33108fd03345b9af199cf04dea450aa045d3c683f216311403403", "names": [], "sources": [ - "packages/experimental/agent-team/src/types.ts:189" + "packages/experimental/agent-team/src/types.ts:174" ] }, { 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 9f7e447932..a0e3e503bf 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: 9ff89599ee1a891d - zh: c8a1b9e93c6b6cc5 + en: 3fce926f1d135d5a + zh: a835885262792d89 /agent-teams/replay: en: 81d0de978aa92363 zh: a5bacfd4051afe51 diff --git a/docs/subsystems/agent-team.md b/docs/subsystems/agent-team.md index 5c0ef582e6..54605fbd60 100644 --- a/docs/subsystems/agent-team.md +++ b/docs/subsystems/agent-team.md @@ -75,23 +75,7 @@ interface TeamTaskSnapshot { ## Web projection -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. */ @@ -113,10 +97,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 } @@ -145,9 +128,11 @@ Agent Teams service backed by the exact live Lead Session log. * 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. @@ -168,9 +153,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 458f4e3bb7..69abb36006 100644 --- a/docs/subsystems/agent-team.zh.md +++ b/docs/subsystems/agent-team.zh.md @@ -75,23 +75,7 @@ interface TeamTaskSnapshot { ## Web 投影 -根 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. */ @@ -113,10 +97,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 } @@ -145,9 +128,11 @@ Agent Teams service backed by the exact live Lead Session log. * 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. @@ -168,9 +153,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 cb4ecf1336..850d32c013 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: f1921c4d9f85890f - zh: c16d41aaa3697171 + en: 6ff748d66293c367 + zh: b9f8a7183a27e7a1 /subagents/durable-enumeration: en: 30c3607a969696ce zh: e6359cdf22b1e235 diff --git a/docs/subsystems/subagent.md b/docs/subsystems/subagent.md index 21e71c2267..f19af0bb96 100644 --- a/docs/subsystems/subagent.md +++ b/docs/subsystems/subagent.md @@ -173,7 +173,7 @@ interface ContinuableCreateSpec { ## Messages and interruption -sendMessage requires the exact live sender and a target Session id. Every sender can reach a direct continuable child; a resident continuable child can also reach its direct parent and continuable siblings. Sibling delivery uses the shared live parent’s authority, preserving the actual sender’s id. Running targets receive Steer; absent children and siblings cold-resume. External executions reject follow-ups. 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. */ @@ -206,7 +206,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 7f2a6ea7f1..b569b72401 100644 --- a/docs/subsystems/subagent.zh.md +++ b/docs/subsystems/subagent.zh.md @@ -173,7 +173,7 @@ interface ContinuableCreateSpec { ## 消息与中断 -sendMessage 要求确切在线发送方和目标 Session id。每个发送方可联系直接可继续子级;驻留的可继续子级还可联系直接父级和可继续同级。同级投递使用共同在线父级的权限,保留实际发送方 id。运行中目标接收 Steer;不在线的子级和同级冷恢复。外部执行拒绝后续输入。本地和外部执行通过父级结算通知报告最终文本、结构化输出、诊断和状态;caller 投递抑制通知。drainDescendants 和 drainChildren 等待所属资源释放。 +`sendMessage` 要求确切在线发送方和同一根树中的目标 Session id。根和可继续成员可跨分支发消息;接纳时重新校验发送方和根。运行中目标接收 Steer;只冷恢复不在线的目标,其创建祖先可以保持离线。外部执行拒绝后续输入。本地和外部执行仅向仍在线的直接创建者报告最终文本、结构化输出、诊断和状态;caller 投递抑制这些通知。`drainDescendants` 和 `drainChildren` 等待所属资源释放。 ```ts type-equiv /** Durable attribution for one model-authored message between Agents. */ @@ -206,7 +206,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 d63c3b90d0..434d847908 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 2c8633804e..5fb1883d34 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -2232,7 +2232,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 016a6f9b82..27d3a3347b 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -2238,7 +2238,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 2c27ed403c..1751391ebc 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: 703c7766dd80e1d1 zh: 157c67164c1cbc7d /agent-teams-uses-subagents-and-session-ids/change: - en: 16641bbd65e005af - zh: ffe57e39b9c33843 + en: 14f3cdc89ad4296d + zh: eeb602127ada5b18 /agent-teams-uses-subagents-and-session-ids/migration: - en: 51e3139edc8042dd - zh: f022c84d731168fc + en: ce0a34aafa251401 + zh: ca43dc841000826c 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 cba2f28a11..caa06a9f3e 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 and Session ids. Descriptions label responsibilities; only ids identify recipients. +Descriptions label responsibilities; only Session ids identify recipients. - `spawn_teammate` and the Team-scoped `send_message`, `list_agents`, and `interrupt_agent` are removed. The standard control tools use `agent_id`, not `target`. Members of one root tree can message each other across branches by id; 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 ids 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`, `kind: child`, or `kind: diagnostic`; descendants include parent ids and root-relative depth. - Team adds four task tools, task 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. Task ownership uses `ownerId`, including the root’s Session id. +- `ctx.agentTeams` removes creation, messaging, interruption, roster, membership, and wait APIs. `TeamMembership`, `TeamMemberView`, spawn/send request/result types, and `TeamWaitResult` are removed. Task ownership uses `ownerId`, including the root’s Session id. - Runtime instances share root ownership without changing creation ancestry. Member settlement does not dispose descendants. Messages restore only their target. Hosts close the tree when its root closes; members obtain required results explicitly. - Cold resume uses saved presets and permissions, not the creator’s current generation. Missing presets fail restoration; historical targets without one use host composition plus descriptor overrides. Lifecycle dispatch uses the root scope while retaining the direct creator in `parentSessionId`. Browser continuation requires the root online; failure uses `subagent/delivery-unavailable`. - Historical Team roster and mailbox records remain readable. Historical names do not resolve message recipients or task owners. Subagent catalog payloads are unchanged. -- The Web Team panel shows tasks only; ordinary subagent navigation owns member browsing. Historical `agentTeam.members` remains decodable but grants no authority. After flush, task commits end all resident Agents’ active waits in the tree. Signals retain no unread state and start no agents; changes outside a wait cannot wake later waits. +- The Web Team panel shows tasks only; ordinary subagent navigation owns member browsing. The wire `agentTeam.members` field and `TeamMemberProjection` are removed; persisted roster state still decodes. After flush, task commits end all resident Agents’ active waits in the tree. Signals retain no unread state and start no agents; changes outside a wait cannot wake later waits. ## Migration @@ -27,4 +27,5 @@ Agent Teams uses subagent tools and Session ids. Descriptions label responsibili 2. Replace `spawn_teammate({ name, description, prompt, context })` with `subagent({ description, prompt })`, or `subagent_fork` for inherited conversation history. Save the returned agent id. 3. Use `send_message({ agent_id, message })` and `interrupt_agent({ agent_id })`. Discover all Team member ids with `list_agents`. 4. Pass member Session ids to task `owner` fields and read `ownerId` in results. Use the Lead's Session id instead of the alias `lead`. -5. Replace removed service calls with `ctx.subagents` operations. Verify member navigation, assignment to a nested descendant, and task-board wakeups during an active wait. Re-read tasks after waiting. +5. Replace `spawnTeammate`, `sendMessage`, and `interrupt` with `ctx.subagents.startActivation`, `sendMessage`, and `interrupt`. Replace `membership`/`tryMembership` with `rootOf`, and `listMembers` with `listDescendants`. Replace `waitForChange` with `wait_agent`, then re-read tasks. +6. SDK `session/resume` rejects subagent sessions. Resume the root Session instead. Verify navigation, nested task assignment, and active-wait wakeups. 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 1be7765643..83f94643fd 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 工具和 Session id;移除 Te ## 变更 -Agent Teams 使用子代理工具和 Session id。描述标注职责;仅 id 标识接收者。 +描述标注职责;仅 Session id 标识接收者。 - 移除 `spawn_teammate` 以及 Team 作用域的 `send_message`、`list_agents` 和 `interrupt_agent`。标准控制工具使用 `agent_id` 而非 `target`。同一根下的成员可以通过 id 跨分支互发消息;中断仍只允许祖先执行。 -- `list_agents` 不接受参数,列出整棵树。移除脚本中的 `scope` 参数。行使用 `kind: root` 或 `kind: child`;后代包含父 id 和相对根的深度。根可将任务分配给自身或任意已发现的可继续执行后代。 +- `list_agents` 不接受参数,列出整棵树。移除脚本中的 `scope` 参数。行使用 `kind: root`、`kind: child` 或 `kind: diagnostic`;后代包含父 id 和相对根的深度。 - Team 添加四个任务工具、任务策略和必需身份。`wait_agent` 移至普通 subagent 控制工具,等待调用者的下一步收件箱,包括待处理输入。状态变化和其他收件箱不会唤醒它;移除 `noProgress`。超时范围和默认值不变。 - `agent-team` 拒绝 `maxMembers`、`maxMessageBytes` 和 `disposalTimeoutMs`;`tool-agent-team` 拒绝 `interrupt`、`freshProvider` 和 `forkProvider`。 -- `ctx.agentTeams` 移除创建、通信、中断、名册、成员关系和等待 API。移除 `TeamMembership` 和 `TeamMembers`;改用 subagent 根解析和发现。任务归属使用 `ownerId`,包括根的 Session id。 +- `ctx.agentTeams` 移除创建、通信、中断、名册、成员关系和等待 API。`TeamMembership`、`TeamMemberView`、spawn/send 请求和结果类型以及 `TeamWaitResult` 被移除。任务归属使用 `ownerId`,包括根的 Session id。 - 运行时实例共享根所有权,不改变创建祖先关系。成员收尾不释放后代。消息仅恢复目标。根关闭时 host 关闭整棵树;成员显式获取所需结果。 - 冷恢复使用已保存的 preset 和权限,而非创建者当前代次。缺失 preset 会导致恢复失败;历史目标没有保存 preset 时,使用 host 组合及描述符覆盖。生命周期分派使用根作用域,同时在 `parentSessionId` 中保留直接创建者。浏览器继续执行需要根在线;失败使用 `subagent/delivery-unavailable`。 - 历史 Team 名册和邮箱记录仍可读取。其中的名字仅作为历史数据,不用于解析消息收件人或任务所有者。Subagent catalog 载荷不变。 -- Web Team 面板仅显示任务;普通 subagent 导航负责浏览成员。历史 `agentTeam.members` 仍可解码,但不授予权限。任务提交刷盘后结束同树所有驻留 Agent 的当前等待。信号不保留未读状态,也不启动 Agent;等待之外的变更不能唤醒后续等待。 +- Web Team 面板仅显示任务;普通 subagent 导航负责浏览成员。线上 `agentTeam.members` 字段与 `TeamMemberProjection` 被移除;持久化成员状态仍可解码。任务提交刷盘后结束同树所有驻留 Agent 的当前等待。信号不保留未读状态,也不启动 Agent;等待之外的变更不能唤醒后续等待。 ## 迁移 @@ -27,4 +27,5 @@ Agent Teams 使用子代理工具和 Session id。描述标注职责;仅 id 2. 将 `spawn_teammate({ name, description, prompt, context })` 替换为 `subagent({ description, prompt })`;继承对话历史时使用 `subagent_fork`。保存返回的 agent id。 3. 使用 `send_message({ agent_id, message })` 和 `interrupt_agent({ agent_id })`。通过 `list_agents` 查询所有 Team 成员的 id。 4. 任务的 `owner` 字段传入成员 Session id,结果读取 `ownerId`。以 Lead 的 Session id 取代 `lead` 别名。 -5. 将已移除服务调用替换为 `ctx.subagents` 操作。验证成员导航、向嵌套后代分配任务,以及当前等待期间的任务板唤醒。等待结束后重新读取任务。 +5. 将 `spawnTeammate`、`sendMessage` 和 `interrupt` 替换为 `ctx.subagents.startActivation`、`sendMessage` 和 `interrupt`。将 `membership`/`tryMembership` 替换为 `rootOf`,将 `listMembers` 替换为 `listDescendants`。将 `waitForChange` 替换为 `wait_agent`,随后重新读取任务。 +6. 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 9b53942f81..d1a62ff222 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 d3ecef5a7f..deb8a5ffd4 100644 --- a/packages/experimental/agent-team/README.md +++ b/packages/experimental/agent-team/README.md @@ -134,7 +134,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. @@ -144,7 +144,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 6515e5f1e0..96fcbe1041 100644 --- a/packages/experimental/agent-team/README.zh.md +++ b/packages/experimental/agent-team/README.zh.md @@ -134,7 +134,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 日志重建。 @@ -144,7 +144,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 54205020dc..24ce1e1f1a 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" @@ -57,7 +57,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 60475d318b..5dec60535f 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, TeamTaskId, TeamTaskStatus, diff --git a/packages/experimental/agent-team/src/index.ts b/packages/experimental/agent-team/src/index.ts index 8d7131667d..82c6f5fe2e 100644 --- a/packages/experimental/agent-team/src/index.ts +++ b/packages/experimental/agent-team/src/index.ts @@ -81,10 +81,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) } /** @@ -110,10 +112,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 df475dcd35..40d0b47a26 100644 --- a/packages/experimental/agent-team/src/task-board.ts +++ b/packages/experimental/agent-team/src/task-board.ts @@ -43,10 +43,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.journal.state(root) const active = state.tasks.filter(task => task.status !== 'deleted').length if (active >= this.maxTasks) { @@ -66,6 +68,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) @@ -102,14 +105,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.journal.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') @@ -183,7 +189,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': { @@ -205,6 +211,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) @@ -212,9 +220,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 eadd62cdd1..7bb7c19688 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 da9a2e41bf..b9daf65464 100644 --- a/packages/experimental/agent-team/tests/persistence.spec.ts +++ b/packages/experimental/agent-team/tests/persistence.spec.ts @@ -136,9 +136,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([ @@ -190,12 +187,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 9a12b9b31c..e2ae6510bb 100644 --- a/packages/experimental/agent-team/tests/team.spec.ts +++ b/packages/experimental/agent-team/tests/team.spec.ts @@ -215,7 +215,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' }, @@ -232,11 +234,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 @@ -668,7 +672,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 5c208056a9..2597f7de15 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: ba021754d3ae6d9e zh: 18017b40f13e6ffd /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: 181b9b035b2cd4a1 zh: a22b301e97f5ee3d diff --git a/packages/experimental/client-ui-agent-team/README.md b/packages/experimental/client-ui-agent-team/README.md index b006fc63a4..9eceff8197 100644 --- a/packages/experimental/client-ui-agent-team/README.md +++ b/packages/experimental/client-ui-agent-team/README.md @@ -31,7 +31,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 b7f6f87b53..00d7ab4b0c 100644 --- a/packages/experimental/client-ui-agent-team/README.zh.md +++ b/packages/experimental/client-ui-agent-team/README.zh.md @@ -31,7 +31,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 88d6fc8478..6d96dff05b 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 13356d41e6..ca3d1be338 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 7298d2b7cb..ce311d1e04 100644 --- a/packages/experimental/client-ui-agent-team/src/client/locales.ts +++ b/packages/experimental/client-ui-agent-team/src/client/locales.ts @@ -11,7 +11,8 @@ export const zh = { failure: '任务记录无效:{message}', empty: '暂无共享任务,可以通过对话创建', tasks: '共享任务', - owner: 'Owner', + owner: '负责人', + lead: '主代理', unowned: '未分配', blockedBy: '依赖', writeScopes: '写入范围', @@ -36,6 +37,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 745cd68c0d..181553188d 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'] @@ -111,13 +111,11 @@ function setProjection(sessions: ReturnType['sessions'], sessionId describe('TeamAction', () => { it('shows the task board without a roster or a subagent catalog', () => { - const b = bench({ projections: { [SESSION]: { state: 'ready', error: null, values: { 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() @@ -125,7 +123,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), @@ -143,13 +141,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', () => { @@ -159,9 +159,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', () => { @@ -193,7 +193,7 @@ describe('TeamAction', () => { expect(screen.queryByRole('status')).toBeNull() setProjectionSnapshot(b.sessions, SESSION, { state: 'idle', error: null, values: {} }) 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() }) @@ -243,7 +243,7 @@ describe('TeamAction', () => { byId: { ...current.byId, ['unrelated' as SessionId]: summary('unrelated' as SessionId) }, projectionsBySession: { ...current.projectionsBySession, - ['unrelated' as SessionId]: { state: 'ready', error: null, values: { agentTeam: { members: [], tasks: [] } } }, + ['unrelated' as SessionId]: { state: 'ready', error: null, values: { agentTeam: { tasks: [] } } }, }, }) }) @@ -276,8 +276,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 7a2549ca21..a69f963447 100644 --- a/packages/experimental/client-ui-agent-team/tsconfig.json +++ b/packages/experimental/client-ui-agent-team/tsconfig.json @@ -14,7 +14,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 303cf57659..eaec171598 100644 --- a/packages/experimental/tool-agent-team/src/index.ts +++ b/packages/experimental/tool-agent-team/src/index.ts @@ -131,7 +131,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) }, }))) @@ -211,7 +211,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 }, ...args.owner === undefined ? {} : { owner: SessionId(args.owner) }, - }) + }, exec.signal) }, }))) } catch (error: unknown) { 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 0909034817..06ecaafa8b 100644 --- a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts +++ b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts @@ -171,6 +171,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: 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) @@ -570,7 +600,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 } }) }) diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 03c3daa591..824bb574ad 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -335,10 +335,11 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ description: 'Agent Teams service backed by the exact live Lead Session log.', methods: [ { - 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', @@ -353,10 +354,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 73f8196ff8..ff6c49baaf 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 bdd9bce617..ea59e99ef8 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -70,7 +70,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 2ab0b4a033..cf99d532c4 100644 --- a/packages/subagent/subagent/README.zh.md +++ b/packages/subagent/subagent/README.zh.md @@ -70,7 +70,7 @@ kind: "package-reference" ### 失败与恢复 -缺少 `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 a5f89b15c8..352c00e912 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. * @returns the durable user-message representation delivered to the recipient. @@ -72,7 +72,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 parentId - durable parent session id named in the guidance. * @param prompt - initial model-visible task blocks. * @returns task blocks followed by the continuable return guidance. diff --git a/packages/subagent/subagent/src/types.ts b/packages/subagent/subagent/src/types.ts index 129dab3ebb..38f7f141b3 100644 --- a/packages/subagent/subagent/src/types.ts +++ b/packages/subagent/subagent/src/types.ts @@ -67,7 +67,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 aba1e85c3b..77c1013a27 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: 0f56ed14a185d4c9 zh: 676bb9484e3f45d4 diff --git a/packages/subagent/tool-subagent-control/README.md b/packages/subagent/tool-subagent-control/README.md index 348def37db..2bdb20d02f 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 d1f7024532..1316ce422a 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 bc09a4a77b..e0f1504cf6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7631,9 +7631,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 @@ -8153,9 +8150,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 578980f053..88d2d093d9 100644 --- a/scripts/gen-cordis-catalog.ts +++ b/scripts/gen-cordis-catalog.ts @@ -706,7 +706,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/bash-tool/tool-schemas.expected.json +++ b/snapshots/sdk/bash-tool/tool-schemas.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-activation-limit/tool-schemas.1.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-continuable-inheritance/tool-schemas.1.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-continuable/tool-schemas.1.expected.json @@ -464,7 +464,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 799fe816de..90016ebc56 100644 --- a/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json +++ b/snapshots/sdk/subagent-dsh-sdk-diagnostic/tool-schemas.expected.json @@ -514,7 +514,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 d40cb4b6c8..fc6d80566c 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 @@ -464,7 +464,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 e473797dd1..d86883691f 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 @@ -493,7 +493,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-list-agents/tool-schemas.1.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json +++ b/snapshots/sdk/subagent-send-message/tool-schemas.1.expected.json @@ -464,7 +464,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/text-turn/tool-schemas.expected.json b/snapshots/sdk/text-turn/tool-schemas.expected.json index d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/sdk/text-turn/tool-schemas.expected.json +++ b/snapshots/sdk/text-turn/tool-schemas.expected.json @@ -464,7 +464,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 7fc4e99899..9859296312 100644 --- a/snapshots/sdk/tool-error-details/system-prompt.expected.md +++ b/snapshots/sdk/tool-error-details/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/sdk/tool-error-details/tool-schemas.expected.json b/snapshots/sdk/tool-error-details/tool-schemas.expected.json index 8d6e2812c4..619b7bd41e 100644 --- a/snapshots/sdk/tool-error-details/tool-schemas.expected.json +++ b/snapshots/sdk/tool-error-details/tool-schemas.expected.json @@ -464,7 +464,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 5a0bbdb9cb..5e4ac96081 100644 --- a/snapshots/session/advanced-toolchain/system-prompt.expected.md +++ b/snapshots/session/advanced-toolchain/system-prompt.expected.md @@ -252,7 +252,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 2744dc4e44..d274b96e7f 100644 --- a/snapshots/session/advanced-toolchain/tool-schemas.expected.json +++ b/snapshots/session/advanced-toolchain/tool-schemas.expected.json @@ -505,7 +505,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": { @@ -1222,7 +1222,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 fb53b8c55e..1e2f800623 100644 --- a/snapshots/session/agent-instructions/tool-schemas.expected.json +++ b/snapshots/session/agent-instructions/tool-schemas.expected.json @@ -464,7 +464,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": { @@ -1101,7 +1101,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/background-confinement-failure/tool-schemas.expected.json +++ b/snapshots/session/background-confinement-failure/tool-schemas.expected.json @@ -464,7 +464,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 f1a5f18921..109b88b9dc 100644 --- a/snapshots/session/both-mode-turn/system-prompt.expected.md +++ b/snapshots/session/both-mode-turn/system-prompt.expected.md @@ -206,7 +206,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 8a1b46faf8..f3a2b74e3c 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 @@ -571,7 +571,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 4be9725e7a..bf10046a42 100644 --- a/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json +++ b/snapshots/session/browser-use-playwright-mcp/tool-schemas.expected.json @@ -573,7 +573,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 74ca86cbe7..4b43d15087 100644 --- a/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json +++ b/snapshots/session/browser-use-stagehand-native/tool-schemas.expected.json @@ -677,7 +677,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/claude-code-mods/tool-schemas.expected.json +++ b/snapshots/session/claude-code-mods/tool-schemas.expected.json @@ -464,7 +464,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 fb53b8c55e..1e2f800623 100644 --- a/snapshots/session/compaction-recovery/tool-schemas.expected.json +++ b/snapshots/session/compaction-recovery/tool-schemas.expected.json @@ -464,7 +464,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": { @@ -1101,7 +1101,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 be4a49e8ca..8cebca0ea8 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 @@ -550,7 +550,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 c68d168e64..d774af0334 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 @@ -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/cordis-inspect-jsdoc/tool-schemas.expected.json b/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json index deee604dfb..d643c50e55 100644 --- a/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json +++ b/snapshots/session/cordis-inspect-jsdoc/tool-schemas.expected.json @@ -505,7 +505,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 5dcaa74f43..260e32c5ee 100644 --- a/snapshots/session/cua-native/tool-schemas.expected.json +++ b/snapshots/session/cua-native/tool-schemas.expected.json @@ -1621,7 +1621,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 79d21ec993..0fc89b9a0e 100644 --- a/snapshots/session/cua-ptc/system-prompt.expected.md +++ b/snapshots/session/cua-ptc/system-prompt.expected.md @@ -253,7 +253,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json +++ b/snapshots/session/deepseek-messages-system-prompt/tool-schemas.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json +++ b/snapshots/session/deepseek-protocol-system-prompt/tool-schemas.expected.json @@ -464,7 +464,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 8ce97b65af..60e85dd125 100644 --- a/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json +++ b/snapshots/session/dynamic-tool-updates/tool-schemas.expected.json @@ -464,7 +464,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": { @@ -1109,7 +1109,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": { @@ -1745,7 +1745,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json +++ b/snapshots/session/foreground-confinement-timeout/tool-schemas.expected.json @@ -464,7 +464,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 6638ce8085..c6e21316bd 100644 --- a/snapshots/session/fs-glob-sampling/tool-schemas.expected.json +++ b/snapshots/session/fs-glob-sampling/tool-schemas.expected.json @@ -323,7 +323,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 d10bb925ea..6596a81bec 100644 --- a/snapshots/session/headless.snapshot.ts +++ b/snapshots/session/headless.snapshot.ts @@ -1421,6 +1421,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 0ba66e8103..9704b5b63d 100644 --- a/snapshots/session/lsp-definition/tool-schemas.expected.json +++ b/snapshots/session/lsp-definition/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/mcp-resources-ptc/system-prompt.expected.md b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md index 9054b52b0f..cb23530c8e 100644 --- a/snapshots/session/mcp-resources-ptc/system-prompt.expected.md +++ b/snapshots/session/mcp-resources-ptc/system-prompt.expected.md @@ -235,7 +235,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 b9e0106a95..77bebedd8d 100644 --- a/snapshots/session/mcp-resources/tool-schemas.expected.json +++ b/snapshots/session/mcp-resources/tool-schemas.expected.json @@ -525,7 +525,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 e7fc1edc2e..a7d2a65d46 100644 --- a/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json +++ b/snapshots/session/multimodal-spill-ends/tool-schemas.expected.json @@ -483,7 +483,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 768d7eaf6f..d93a7c91b3 100644 --- a/snapshots/session/office-progressive-native/tool-schemas.expected.json +++ b/snapshots/session/office-progressive-native/tool-schemas.expected.json @@ -419,7 +419,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 dc41fbaa17..f620a77714 100644 --- a/snapshots/session/office-progressive-ptc/system-prompt.expected.md +++ b/snapshots/session/office-progressive-ptc/system-prompt.expected.md @@ -185,7 +185,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 768d7eaf6f..d93a7c91b3 100644 --- a/snapshots/session/office-skills/tool-schemas.expected.json +++ b/snapshots/session/office-skills/tool-schemas.expected.json @@ -419,7 +419,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 e0ae7d22c5..1af7acf9ff 100644 --- a/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json +++ b/snapshots/session/persistent-pwsh-tool-turn/tool-schemas.expected.json @@ -294,7 +294,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 278b2f0d94..b59c2fa677 100644 --- a/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json +++ b/snapshots/session/plugin-manager-mcp/tool-schemas.expected.json @@ -525,7 +525,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": { @@ -1293,7 +1293,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 3095eba9cb..be9043e7be 100644 --- a/snapshots/session/plugin-manager/tool-schemas.expected.json +++ b/snapshots/session/plugin-manager/tool-schemas.expected.json @@ -525,7 +525,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 c95f504cd2..6483e8c5c6 100644 --- a/snapshots/session/product-subagent-both/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-both/tool-schemas.expected.json @@ -564,7 +564,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 c76768836c..cfe0cca76f 100644 --- a/snapshots/session/product-subagent-codex/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-codex/tool-schemas.expected.json @@ -514,7 +514,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 a9dfc55982..73d4cd4f6d 100644 --- a/snapshots/session/product-subagent-oar/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-oar/tool-schemas.expected.json @@ -498,7 +498,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 d22595979b..bacaba1c84 100644 --- a/snapshots/session/product-subagent-result-diagnostic/tool-schemas.expected.json +++ b/snapshots/session/product-subagent-result-diagnostic/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/session/provider-cwd/tool-schemas.expected.json b/snapshots/session/provider-cwd/tool-schemas.expected.json index d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/provider-cwd/tool-schemas.expected.json +++ b/snapshots/session/provider-cwd/tool-schemas.expected.json @@ -464,7 +464,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 fd937ff7e9..56d61ca7e3 100644 --- a/snapshots/session/ptc-node-read-only/system-prompt.expected.md +++ b/snapshots/session/ptc-node-read-only/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-node-workspace/system-prompt.expected.md b/snapshots/session/ptc-node-workspace/system-prompt.expected.md index fd937ff7e9..56d61ca7e3 100644 --- a/snapshots/session/ptc-node-workspace/system-prompt.expected.md +++ b/snapshots/session/ptc-node-workspace/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-persistent/system-prompt.expected.md b/snapshots/session/ptc-persistent/system-prompt.expected.md index 9cdc7254b1..2924be3ef0 100644 --- a/snapshots/session/ptc-persistent/system-prompt.expected.md +++ b/snapshots/session/ptc-persistent/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-policy-change/system-prompt.expected.md b/snapshots/session/ptc-policy-change/system-prompt.expected.md index 0221335e19..838ef7f87e 100644 --- a/snapshots/session/ptc-policy-change/system-prompt.expected.md +++ b/snapshots/session/ptc-policy-change/system-prompt.expected.md @@ -206,7 +206,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 ae17127df9..c8bc48c668 100644 --- a/snapshots/session/ptc-python-turn/system-prompt.expected.md +++ b/snapshots/session/ptc-python-turn/system-prompt.expected.md @@ -560,7 +560,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 9fa8d1ea8b..8d7b1997f4 100644 --- a/snapshots/session/ptc-read-image/system-prompt.expected.md +++ b/snapshots/session/ptc-read-image/system-prompt.expected.md @@ -206,7 +206,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 fd937ff7e9..56d61ca7e3 100644 --- a/snapshots/session/ptc-turn/system-prompt.expected.md +++ b/snapshots/session/ptc-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/pty-tools-sandbox-backend/tool-schemas.expected.json b/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json index 6843215a8d..1144d87179 100644 --- a/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json +++ b/snapshots/session/pty-tools-sandbox-backend/tool-schemas.expected.json @@ -593,7 +593,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 502f8f2bc7..b478f366c0 100644 --- a/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json +++ b/snapshots/session/pwsh-tool-turn/tool-schemas.expected.json @@ -375,7 +375,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 40c0d5e718..46f239961c 100644 --- a/snapshots/session/ralph-loop/tool-schemas.1.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.1.expected.json @@ -527,7 +527,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 40c0d5e718..46f239961c 100644 --- a/snapshots/session/ralph-loop/tool-schemas.2.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.2.expected.json @@ -527,7 +527,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 b8736f3aab..076d5e4339 100644 --- a/snapshots/session/ralph-loop/tool-schemas.expected.json +++ b/snapshots/session/ralph-loop/tool-schemas.expected.json @@ -484,7 +484,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 6e32753eed..e98fcd4f86 100644 --- a/snapshots/session/session-query-spill/tool-schemas.expected.json +++ b/snapshots/session/session-query-spill/tool-schemas.expected.json @@ -668,7 +668,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 0dbf34a471..f1cf3f49e2 100644 --- a/snapshots/session/subagent-acp-diagnostic/tool-schemas.expected.json +++ b/snapshots/session/subagent-acp-diagnostic/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/session/subagent-agent-preset/tool-schemas.1.expected.json b/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json index 58932edd98..c3902ff689 100644 --- a/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-agent-preset/tool-schemas.1.expected.json @@ -496,7 +496,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 1bb18aeca1..3a9bf853dd 100644 --- a/snapshots/session/subagent-agent-preset/tool-schemas.expected.json +++ b/snapshots/session/subagent-agent-preset/tool-schemas.expected.json @@ -471,7 +471,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 596294807a..a9453a96ca 100644 --- a/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json +++ b/snapshots/session/subagent-child-question-rejection/tool-schemas.expected.json @@ -527,7 +527,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 b830b44891..bad6df50e4 100644 --- a/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-structured-parent/tool-schemas.1.expected.json @@ -487,7 +487,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 12e0483d03..ccaa863459 100644 --- a/snapshots/session/subagent-structured-parent/tool-schemas.expected.json +++ b/snapshots/session/subagent-structured-parent/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/subagent-tool-filter/tool-schemas.1.expected.json b/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json index 305b4bcc4e..e48583f286 100644 --- a/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json +++ b/snapshots/session/subagent-tool-filter/tool-schemas.1.expected.json @@ -338,7 +338,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/system-prompt-in-history/tool-schemas.expected.json +++ b/snapshots/session/system-prompt-in-history/tool-schemas.expected.json @@ -464,7 +464,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-direct-inbox/session.v4.jsonl b/snapshots/session/team-direct-inbox/session.v4.jsonl index 06d170e4cc..b9ca7256ac 100644 --- a/snapshots/session/team-direct-inbox/session.v4.jsonl +++ b/snapshots/session/team-direct-inbox/session.v4.jsonl @@ -12,7 +12,7 @@ {"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"tools":"{{tools}}"},"reason":"initial"}} {"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}} {"type":"session/title","data":{"title":"Spawn teammate reviewer, wait for","messageSeqs":[8],"source":{"kind":"fallback"}}} -{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-1","name":"subagent","arguments":"{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720691,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720691,"index":0,"dt":[],"id":"direct-1","name":"subagent","args":["{\"name\":\"reviewer\",\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"]},{"type":"chunk","time":1792050720691,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-1","name":"subagent","arguments":"{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"}}},{"type":"chunk","time":1792050720691,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720691,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-1","name":"subagent","arguments":"{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720691,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720691,"index":0,"dt":[],"id":"direct-1","name":"subagent","args":["{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"]},{"type":"chunk","time":1792050720691,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-1","name":"subagent","arguments":"{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"}}},{"type":"chunk","time":1792050720691,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720691,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":1,"callId":"direct-1","name":"subagent","arguments":"{\"description\":\"Check direct inbox delivery\",\"prompt\":\"Create the empty file team-ready, then wait in Bash until interrupted.\"}"}} {"type":"subagent/catalog","data":{"version":0,"childId":"{{session:2}}","childCreatedAt":1792050720697,"mode":"continuable","label":"Check direct inbox delivery"}} {"type":"tool/result","data":{"turn":1,"step":1,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-1"},"toolCallId":"direct-1","content":[{"type":"text","text":"started subagent {{session:2}}"}],"isError":false,"id":"{{message:5}}"}},"sourceEventSeqs":[14],"surfaceOp":"append"} @@ -25,12 +25,12 @@ {"type":"tool/result","data":{"turn":1,"step":2,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-2"},"toolCallId":"direct-2","content":[{"type":"text","text":"(no output)"}],"isError":false,"id":"{{message:7}}"},"meta":{"cwd":"{{cwd}}"}},"sourceEventSeqs":[20],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":2}} {"type":"step/start","data":{"turn":1,"step":3}} -{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720776,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720776,"index":0,"dt":[],"id":"direct-3","name":"send_message","args":["{\"agent_id\":\"reviewer\",\"message\":\"Review the direct inbox delivery.\"}"]},{"type":"chunk","time":1792050720776,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"}}},{"type":"chunk","time":1792050720776,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720776,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720776,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720776,"index":0,"dt":[],"id":"direct-3","name":"send_message","args":["{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"]},{"type":"chunk","time":1792050720776,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-3","name":"send_message","arguments":"{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"}}},{"type":"chunk","time":1792050720776,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720776,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":3,"callId":"direct-3","name":"send_message","arguments":"{\"agent_id\":\"{{session:2}}\",\"message\":\"Review the direct inbox delivery.\"}"}} {"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-3"},"toolCallId":"direct-3","content":[{"type":"text","text":"message delivered to agent {{session:2}}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[27],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":3}} {"type":"step/start","data":{"turn":1,"step":4}} -{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"agent_id\":\"{{session:2}}\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720793,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720793,"index":0,"dt":[],"id":"direct-4","name":"interrupt_agent","args":["{\"agent_id\":\"reviewer\"}"]},{"type":"chunk","time":1792050720793,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"agent_id\":\"{{session:2}}\"}"}}},{"type":"chunk","time":1792050720793,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720793,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} +{"type":"assistant/message","data":{"turn":1,"step":4,"message":{"role":"assistant","content":[{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"agent_id\":\"{{session:2}}\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"usage":{"inputTokens":3,"outputTokens":3},"stream":[{"type":"chunk","time":1792050720793,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"tool-call-chunks","time0":1792050720793,"index":0,"dt":[],"id":"direct-4","name":"interrupt_agent","args":["{\"agent_id\":\"{{session:2}}\"}"]},{"type":"chunk","time":1792050720793,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"direct-4","name":"interrupt_agent","arguments":"{\"agent_id\":\"{{session:2}}\"}"}}},{"type":"chunk","time":1792050720793,"chunk":{"type":"usage","usage":{"inputTokens":3,"outputTokens":3}}},{"type":"chunk","time":1792050720793,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"} {"type":"tool/call","data":{"turn":1,"step":4,"callId":"direct-4","name":"interrupt_agent","arguments":"{\"agent_id\":\"{{session:2}}\"}"}} {"type":"tool/result","data":{"turn":1,"step":4,"message":{"role":"tool","source":{"kind":"tool","callId":"direct-4"},"toolCallId":"direct-4","content":[{"type":"text","text":"interrupt requested for agent {{session:2}}"}],"isError":false,"id":"{{message:11}}"}},"sourceEventSeqs":[32],"surfaceOp":"append"} {"type":"step/end","data":{"turn":1,"step":4}} diff --git a/snapshots/session/team-targets/tool-schemas.expected.json b/snapshots/session/team-targets/tool-schemas.expected.json index 281a57fd31..2c9ef08dae 100644 --- a/snapshots/session/team-targets/tool-schemas.expected.json +++ b/snapshots/session/team-targets/tool-schemas.expected.json @@ -611,7 +611,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 1d5c5682de..5098d13e85 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": "{\"agent_id\":\"{{fromRequest:([a-f0-9-]+) .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": "{\"agent_id\":\"{{fromRequest:([a-f0-9-]+) .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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/text-turn/tool-schemas.expected.json +++ b/snapshots/session/text-turn/tool-schemas.expected.json @@ -464,7 +464,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 707fd16557..0fc6c8744c 100644 --- a/snapshots/session/web-fetch/tool-schemas.expected.json +++ b/snapshots/session/web-fetch/tool-schemas.expected.json @@ -464,7 +464,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 d40cb4b6c8..fc6d80566c 100644 --- a/snapshots/session/workflow-confinement/tool-schemas.expected.json +++ b/snapshots/session/workflow-confinement/tool-schemas.expected.json @@ -464,7 +464,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 43b4e307ea..63371a8df2 100644 --- a/snapshots/session/workspace-dependencies/tool-schemas.expected.json +++ b/snapshots/session/workspace-dependencies/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/worktree/tool-schemas.expected.json b/snapshots/session/worktree/tool-schemas.expected.json index a5c23e4387..3356d3dce4 100644 --- a/snapshots/session/worktree/tool-schemas.expected.json +++ b/snapshots/session/worktree/tool-schemas.expected.json @@ -481,7 +481,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 6f05974502..93b1b7301e 100644 --- a/snapshots/web/fresh-round-trip/tool-schemas.expected.json +++ b/snapshots/web/fresh-round-trip/tool-schemas.expected.json @@ -856,7 +856,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 303965d9a9..add680b8fa 100644 --- a/snapshots/web/official-subagent-bundle/system-prompt.expected.md +++ b/snapshots/web/official-subagent-bundle/system-prompt.expected.md @@ -343,7 +343,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 7f48bb5815..6d01bee9b1 100644 --- a/snapshots/web/optional-string-editor/system-prompt.expected.md +++ b/snapshots/web/optional-string-editor/system-prompt.expected.md @@ -351,7 +351,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 6d60a992d4..3670751d5d 100644 --- a/snapshots/web/ptc-round/system-prompt.expected.md +++ b/snapshots/web/ptc-round/system-prompt.expected.md @@ -334,7 +334,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;