diff --git a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.i18n.yaml b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.i18n.yaml
index b6af3b5b68..12fb19df97 100644
--- a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.i18n.yaml
+++ b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.i18n.yaml
@@ -9,11 +9,11 @@
en: 6fdc766a348d8659
zh: 8a84748e147deed5
/agent-note-team-task-boards-share-ordinary-subagent-waits/decision:
- en: e7efedaeddecb73d
- zh: 9d192de8f6ee653e
+ en: 09fa039ac5663c3b
+ zh: 97ef7c7c98902ba8
/agent-note-team-task-boards-share-ordinary-subagent-waits/alternatives-considered:
en: eed6f6d4fa0ccd7b
zh: 61271057d6e5d019
/agent-note-team-task-boards-share-ordinary-subagent-waits/consequences:
- en: c0c0c646042f4e48
- zh: 342b743f65b8c9dc
+ en: cc250eb21dbb45a1
+ zh: 9d03d1eb53075fb6
diff --git a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.md b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.md
index 1c05b9c9e0..339b62c1ae 100644
--- a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.md
+++ b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.md
@@ -12,11 +12,9 @@ The subagent tree already supplies identity, discovery, routing, and lifecycle.
The subagent tree is the Team. Ordinary controls own communication, discovery, interruption, and `wait_agent`, independently of the optional task board. Team tools own task policy and required Team identity, without adding either to ordinary subagent compositions. Team owns task storage, dependencies, and compare-and-set commands. Assignment accepts the root or any discovered continuable descendant. Historical roster records grant no authority; the Team panel shows tasks and ordinary navigation owns members.
-`wait_agent` subscribes before checking the exact caller’s next-step inbox. Pending input returns immediately; later input ends the wait. An `agent/wait-notified` event for that same Agent also ends an active wait. Other inboxes, other Agents’ signals, queued next-turn input, and status changes do not end it. Waiting neither consumes input nor starts Agents. Settlement removes listeners and the timer, including on timeout, cancellation, disposal, or plugin unload.
+The wait-signal decision is superseded by [inbox-only Agent waits](../simplification/2026-10-29-inbox-only-agent-wait.md). Tree ownership, task durability, and historical non-authority remain unchanged.
-After task append and flush succeed, Team emits that generic Agent-scoped signal for all resident Agents in the same tree, including the caller and root. Signals only end current waits: they leave no unread state, add no inbox entry, and cannot make a later wait return immediately. They never restore cold members or start idle turns. After waiting, the caller reads current tasks before acting.
-
-Task state remains in the existing journal. Signals are transient runtime events, not Session events or messages; ordinary tool results record what the waiting model observes. Historical roster, mailbox, and task generations remain readable and unchanged. This partially supersedes the roster and wait decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and [subagent teammates](2026-10-15-agent-teams-as-subagents.md). Their task durability and single-identity rationale remain active; [required runtime identity](2026-10-13-team-identity-runtime-context.md) owns retention.
+Task state remains in the existing journal. Ordinary tool results record what the waiting model observes. Historical roster, mailbox, and task generations remain readable and unchanged. This partially supersedes the roster and wait decisions in [Agent Teams](../feature/2026-08-05-agent-teams.md) and [subagent teammates](2026-10-15-agent-teams-as-subagents.md). Their task durability and single-identity rationale remain active; [required runtime identity](2026-10-13-team-identity-runtime-context.md) owns retention.
## Alternatives considered
@@ -28,6 +26,6 @@ Task state remains in the existing journal. Signals are transient runtime events
## Consequences
-Task completion, inbox availability, current waiting, and Agent lifetime remain separate. Changes outside a wait are deliberately not remembered; callers inspect current tasks when deciding what to do. The task journal retains committed revisions without storing redundant notifications in recipient Sessions.
+Task completion, inbox availability, current waiting, and Agent lifetime remain separate. Task updates do not end waits; callers inspect current tasks when deciding what to do. The task journal retains committed revisions without storing redundant notifications in recipient Sessions.
-Verification distinguishes pending inbox input from transient signals, isolates callers and trees, excludes next-turn input and status changes, and covers wait cleanup and post-flush signaling. Assignment coverage retains arbitrary depth and historical non-authority.
+Verification distinguishes pending inbox input from task commits, isolates callers, excludes next-turn input and status changes, and covers wait cleanup and task persistence. Assignment coverage retains arbitrary depth and historical non-authority.
diff --git a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.zh.md b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.zh.md
index 7c1d55e25b..8d8c8408ba 100644
--- a/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.zh.md
+++ b/.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.zh.md
@@ -12,11 +12,9 @@ subagent 树已经提供身份、发现、路由和生命周期。第二份 Team
subagent 树就是 Team。普通控制工具独立于可选任务板,负责通信、发现、中断和 `wait_agent`。Team 工具负责任务策略和必需 Team 身份,不向普通 subagent 组合添加二者。Team 负责任务存储、依赖和比较并交换命令。分配接受根或任意已发现的可继续执行后代。历史名册不授予权限;Team 面板显示任务,普通导航负责成员。
-`wait_agent` 先订阅,再检查当前调用者的下一步收件箱。已有待处理输入时立即返回;后续输入结束等待。面向同一 Agent 的 `agent/wait-notified` 事件也结束当前等待。其他收件箱、其他 Agent 的信号、排队的下一轮输入和状态变化不会结束等待。等待既不消费输入,也不启动 Agent。等待结束会移除监听器和定时器,包括超时、取消、释放或插件卸载。
+等待信号决策已由[仅等待收件箱的 Agent 等待](../simplification/2026-10-29-inbox-only-agent-wait.zh.md)取代。树归属、任务持久性和历史记录不授予权限的决策保持不变。
-任务追加并刷盘成功后,Team 为同树所有驻留 Agent(包括调用者和根)发出该通用 Agent 作用域信号。信号仅结束当前等待:不保留未读状态,不增加收件箱条目,也不能使后续等待立即返回。信号绝不恢复冷成员或启动空闲轮次。等待结束后,调用者先读取当前任务再行动。
-
-任务状态保留在现有日志中。信号是临时运行时事件,不是 Session 事件或消息;普通工具结果记录等待中的模型所观察到的结果。历史名册、邮箱和任务代次保持可读且不变。这部分取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 和 [subagent 队友](2026-10-15-agent-teams-as-subagents.zh.md) 中的名册与等待决策。其任务持久性和单一身份理由继续生效;[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责保留规则。
+任务状态保留在现有日志中。普通工具结果记录等待中的模型所观察到的结果。历史名册、邮箱和任务代次保持可读且不变。这部分取代 [Agent Teams](../feature/2026-08-05-agent-teams.zh.md) 和 [subagent 队友](2026-10-15-agent-teams-as-subagents.zh.md) 中的名册与等待决策。其任务持久性和单一身份理由继续生效;[必需运行时身份](2026-10-13-team-identity-runtime-context.zh.md)负责保留规则。
## 考虑过的替代方案
@@ -28,6 +26,6 @@ subagent 树就是 Team。普通控制工具独立于可选任务板,负责通
## 后果
-任务完成、收件箱可用性、当前等待和 Agent 生命周期保持独立。等待之外的变更有意不被记住;调用者在决定下一步工作时查询当前任务。任务日志保留已提交修订,不在接收者 Session 中存储重复通知。
+任务完成、收件箱可用性、当前等待和 Agent 生命周期保持独立。任务更新不会结束等待;调用者在决定下一步工作时查询当前任务。任务日志保留已提交修订,不在接收者 Session 中存储重复通知。
-验证区分已有收件箱输入和临时信号,隔离调用者与树,排除下一轮输入及状态变化,并覆盖等待清理和刷盘后发信号。分配验证保留任意深度和历史记录无权限场景。
+验证区分已有收件箱输入和任务提交,隔离调用者,排除下一轮输入及状态变化,并覆盖等待清理和任务持久性。分配验证保留任意深度和历史记录无权限场景。
diff --git a/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.i18n.yaml b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.i18n.yaml
new file mode 100644
index 0000000000..3c03e11100
--- /dev/null
+++ b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.i18n.yaml
@@ -0,0 +1,19 @@
+# Bilingual-pair consistency record for 2026-10-29-inbox-only-agent-wait.md (docs/i18n/README.md): per heading
+# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
+# After editing either side, bring the other along and re-record with:
+# pnpm run verify-translation-pairing --write .agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.md
+/agent-note-inbox-only-agent-waits:
+ en: 8a451f9706af8dea
+ zh: 9818b5abdee12a9c
+/agent-note-inbox-only-agent-waits/problem:
+ en: 7a6e28745bda3305
+ zh: e862166572cb9a9f
+/agent-note-inbox-only-agent-waits/decision:
+ en: 553a1e166dd7e7e9
+ zh: c05f16577fa6753c
+/agent-note-inbox-only-agent-waits/alternatives-considered:
+ en: 64a44ad3884a90c3
+ zh: 1fffe0c6fd78acbe
+/agent-note-inbox-only-agent-waits/consequences:
+ en: 45b6d456c3ca226c
+ zh: e9ebacd4b6bcaeca
diff --git a/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.md b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.md
new file mode 100644
index 0000000000..80e46f63e6
--- /dev/null
+++ b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.md
@@ -0,0 +1,31 @@
+# Agent Note: Inbox-only Agent waits
+
+Status: implemented
+
+English | [中文](2026-10-29-inbox-only-agent-wait.zh.md)
+
+## Problem
+
+A task-board commit changes shared state but does not deliver input to a member. Ending every member's wait on each edit couples ordinary subagent controls to Team coordination and makes callers resume without a message explaining what needs attention.
+
+## Decision
+
+`wait_agent` observes only its exact caller's next-step inbox through the existing insertion event. It subscribes before checking pending input, consumes nothing, and removes listeners and timers on input, timeout, cancellation, caller disposal, or tool unload. Queued next-turn input, other Agents' activity, and task-board changes do not end the wait.
+
+Task commands append and flush their revisions without notifying waits or writing inbox messages. A caller sends an explicit message when a member needs to act on an update. No Agent method, shared waiting service, or task-specific waiting event is introduced.
+
+This supersedes only the wait-signal decision in [Team task boards](../architecture/2026-10-19-team-task-board-inbox.md). Its tree authority, durable task storage, historical non-authority, and separation from ordinary controls remain applicable. No Session event generation changes.
+
+## Alternatives considered
+
+**Notify all current waits after every task commit.** This avoids duplicate inbox state but still makes unrelated task edits interrupt every waiting member. Moving the notification behind a service method retains that behavior and coupling.
+
+**Persist each task change as a message.** The task journal already preserves revisions; per-member notices add unread state and delivery recovery. Explicit messages communicate which updates require action.
+
+**Add waiting to Agent or its inbox.** Existing input events and tool-owned cancellation already implement the required wait without extending the core API.
+
+## Consequences
+
+Task updates alone do not resume a waiting or idle member. Callers must communicate when work should continue. The tool returns no task contents; members query the board when needed. Task changes before or during a wait have the same effect: none.
+
+Unit tests cover pending input, caller isolation, concurrent waits, cancellation, disposal, and task commits that leave waits pending. The recorded task-board scenario checks past and concurrent commits against timed-out waits, while completion notification remains ordinary inbox input.
diff --git a/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.zh.md b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.zh.md
new file mode 100644
index 0000000000..6bf2e39562
--- /dev/null
+++ b/.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.zh.md
@@ -0,0 +1,31 @@
+# Agent Note: 仅等待收件箱的 Agent 等待
+
+Status: implemented
+
+[English](2026-10-29-inbox-only-agent-wait.md) | 中文
+
+## 问题
+
+任务板提交改变共享状态,但不向成员投递输入。每次编辑都结束所有成员的等待,会使普通 subagent 控制依赖 Team 协调,并让调用者在没有消息说明需要关注什么时恢复执行。
+
+## 决策
+
+`wait_agent` 仅通过现有插入事件观察当前调用者自己的下一步收件箱。它先订阅再检查待处理输入,不消费输入,并在输入到达、超时、取消、调用者释放或工具卸载时移除监听器和定时器。排队的下一轮输入、其他 Agent 的活动和任务板变化不会结束等待。
+
+任务命令追加并刷盘修订,不通知等待者,也不写入收件箱消息。需要成员处理更新时,调用者发送明确消息。不新增 Agent 方法、共享等待服务或任务专用等待事件。
+
+这仅取代[Team 任务板](../architecture/2026-10-19-team-task-board-inbox.zh.md)中的等待信号决策。树权限、持久任务存储、历史记录不授予权限及与普通控制工具分离的决策仍然适用。不改变 Session 事件代次。
+
+## 考虑过的替代方案
+
+**每次任务提交后通知所有当前等待。** 这避免了重复收件箱状态,但仍让无关任务编辑打断每个等待成员。将通知移到服务方法后仍保留该行为和耦合。
+
+**将每次任务变化持久化为消息。** 任务日志已经保留修订;逐成员通知增加未读状态和投递恢复。明确消息可以表达哪些更新需要行动。
+
+**在 Agent 或收件箱中增加等待。** 现有输入事件和工具拥有的取消机制已能实现所需等待,无须扩展核心 API。
+
+## 后果
+
+仅更新任务不会恢复等待中或空闲的成员。需要继续工作时,调用者必须通信。工具不返回任务内容;成员按需查询任务板。等待前和等待期间发生的任务变化效果相同:都不影响等待。
+
+单元测试覆盖已有输入、调用者隔离、并行等待、取消、释放,以及不结束等待的任务提交。任务板录制场景检查过去和并发提交下的等待超时,完成通知仍作为普通收件箱输入。
diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml
index 84c0f093c0..dd25485f7b 100644
--- a/docs/architecture.i18n.yaml
+++ b/docs/architecture.i18n.yaml
@@ -30,8 +30,8 @@
en: 8a7f19b95d426c99
zh: d5bb0538404e20ea
/deepseek-harness-architecture/capability-seams:
- en: a419cc2a37a80f7f
- zh: 70654d3c77ea2cc0
+ en: 4252d945276b5b50
+ zh: 710bdfd965cc7cee
/deepseek-harness-architecture/where-new-behavior-goes:
en: ee73b1861378b099
zh: 9a182be609485f67
diff --git a/docs/architecture.md b/docs/architecture.md
index 4a6d503ce7..0863184c19 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -139,7 +139,7 @@ A **seam** is a swappable capability with three roles: a **Service Definition**
Seams are why one provider swap changes the whole product. Filesystem and subprocess providers share one execution world, so pointing them at a remote sandbox moves Bash, PTY, and LSP with them, with no provider forks. [Subagent providers](subsystems/subagent.md) vary just as widely behind one interface, from a fresh child agent to a delegated turn in another product.
-[Experimental Agent Teams](subsystems/agent-team.md) adds opt-in task policy, identity, a task board, and wait signals. Ordinary subagent controls own messaging, discovery, interruption, and inbox waiting.
+[Experimental Agent Teams](subsystems/agent-team.md) adds opt-in task policy, identity, a task board. Ordinary subagent controls own messaging, discovery, interruption, and inbox waiting.
## Where new behavior goes
diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md
index 830cb2039b..47f2a11746 100644
--- a/docs/architecture.zh.md
+++ b/docs/architecture.zh.md
@@ -143,7 +143,7 @@ Session 消费方只了解当前逻辑格式。仅 header 的 `stat` 与 `list`
seam 正是替换一个提供方就能改变整个产品的原因。文件系统与进程提供方共享同一个执行世界,因此把它们指向远程沙箱,也就把 Bash、PTY 和 LSP 一并搬了过去,无需提供方专用 fork。[subagent 提供方](subsystems/subagent.zh.md)在同一个接口之后同样千差万别,从新建一个子 agent,到把一个轮次委派给另一个产品。
-[实验性 Agent Teams](subsystems/agent-team.zh.md)提供可选的任务策略、身份、任务板和等待信号。普通 subagent 控制工具负责通信、发现、中断和收件箱等待。
+[实验性 Agent Teams](subsystems/agent-team.zh.md)提供可选的任务策略、身份、任务板。普通 subagent 控制工具负责通信、发现、中断和收件箱等待。
## 新行为的归属位置
diff --git a/docs/capability-seams.i18n.yaml b/docs/capability-seams.i18n.yaml
index 9a5797451c..d2086a78d5 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: 558e7d9eb88b21e8
- zh: 4e1f632d1cfc68fb
+ en: 4a1758b6e2b14749
+ zh: fb8c2370d479a2ce
diff --git a/docs/capability-seams.md b/docs/capability-seams.md
index f315c8a196..fdcd645472 100644
--- a/docs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -719,7 +719,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 shared task DAG and wait signals for a subagent tree; tool-agent-team contributes task 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 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 b69f543248..bf16d6ea7a 100644
--- a/docs/capability-seams.zh.md
+++ b/docs/capability-seams.zh.md
@@ -721,7 +721,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) | - | 负责子代理树共享的任务 DAG 和等待信号;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/event-producer-consumer.md b/docs/event-producer-consumer.md
index f340e97ddf..2fbbef7243 100644
--- a/docs/event-producer-consumer.md
+++ b/docs/event-producer-consumer.md
@@ -10,18 +10,17 @@ This matrix shows which packages dispatch each harness-owned event and which pac
| --- | --- | --- | --- | --- |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:198`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` |
-| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:407`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
+| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `browser-use-runtime`, `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
-| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:437`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
-| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
-| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:344`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
-| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:325`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
-| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:358`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
-| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:397`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
+| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
+| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
+| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
+| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
+| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
+| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
-| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:425`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
-| `agent/wait-notified` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | `agent-team` (`emitAgentEvent`) | [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
+| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:600`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:580`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:607`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
diff --git a/docs/event-producer-consumer.zh.md b/docs/event-producer-consumer.zh.md
index dd177d0c3f..9d7f7c1ac0 100644
--- a/docs/event-producer-consumer.zh.md
+++ b/docs/event-producer-consumer.zh.md
@@ -12,18 +12,17 @@
| --- | --- | --- | --- | --- |
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:198`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | - |
| `agent-preset/selected` | `emit` | [`packages/preset/agent-preset-registry/src/types.ts:66`](../packages/preset/agent-preset-registry/src/types.ts) | [`agent-preset-registry`](../packages/preset/agent-preset-registry) (`emit`) | [`cua`](../packages/experimental/cua), `remotes` |
-| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:407`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
+| `agent/assistant-stream` | `emit` | [`packages/core/agent/src/runtime-types.ts:399`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`headless`](../packages/bundle/headless), `session-controller` |
| `agent/created` | `serial` | [`packages/core/agent/src/runtime-types.ts:290`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`serial`) | `browser-use-runtime`, `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`cua`](../packages/experimental/cua), [`file-reference-local`](../packages/context/file-reference-local), [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`loader-smoke`](../packages/test-support/loader-smoke), [`permission-presets`](../packages/interaction/permission-presets), `session-controller`, `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent) |
| `agent/disposed` | `emit` | [`packages/core/agent/src/runtime-types.ts:299`](../packages/core/agent/src/runtime-types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`action-cards`](../packages/experimental/action-cards), [`agent-loop`](../packages/core/agent-loop), `claude-code-mods`, [`creation-recommendations`](../packages/experimental/creation-recommendations), [`file-reference-local`](../packages/context/file-reference-local), [`goal-round-driver`](../packages/goal/goal-round-driver), `session-controller`, [`subagent`](../packages/subagent/subagent), `tool-agent-team`, [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
-| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:437`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
-| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
-| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:344`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
-| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:325`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
-| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:358`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
-| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:397`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
+| `agent/error` | `emit` | [`packages/core/agent/src/runtime-types.ts:429`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`deepseek-account`](../packages/credentials/deepseek-account), [`goal-round-driver`](../packages/goal/goal-round-driver), [`headless`](../packages/bundle/headless), `server`, `session-controller`, [`session-telemetry`](../packages/session/session-telemetry), [`subagent`](../packages/subagent/subagent) |
+| `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:328`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`user-questions`](../packages/interaction/user-questions) |
+| `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:336`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`user-questions`](../packages/interaction/user-questions) |
+| `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver), [`subagent`](../packages/subagent/subagent), [`tool-jobs`](../packages/jobs/tool-jobs), [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
+| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:350`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`action-cards`](../packages/experimental/action-cards), [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), `claude-code-mods`, [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`plan-mode`](../packages/plan/plan-mode), [`repeat-tool-reminder`](../packages/guard/repeat-tool-reminder), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), `session-controller`, [`session-reference`](../packages/context/session-reference), [`subagent`](../packages/subagent/subagent), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-skill`](../packages/skill/tool-skill) |
+| `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:389`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`compaction-image-offload`](../packages/compaction/compaction-image-offload), [`llm-retry`](../packages/llm/llm-retry) |
| `agent/status` | `emit` | [`packages/core/agent/src/runtime-types.ts:309`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`acp`](../packages/acp/acp), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), `server`, `session-controller` |
-| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:425`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
-| `agent/wait-notified` | `emit` | [`packages/core/agent/src/runtime-types.ts:317`](../packages/core/agent/src/runtime-types.ts) | `agent-team` (`emitAgentEvent`) | [`tool-subagent-control`](../packages/subagent/tool-subagent-control) |
+| `agent/turn-stopping` | `serial` | [`packages/core/agent/src/runtime-types.ts:417`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`hooks-codex`](../packages/experimental/hooks-codex), [`workspace-changes`](../packages/deliverables/workspace-changes) |
| `api-session/activity` | `emit` | [`packages/api/session-controller/src/types.ts:600`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/added` | `emit` | [`packages/api/session-controller/src/types.ts:580`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
| `api-session/error` | `emit` | [`packages/api/session-controller/src/types.ts:607`](../packages/api/session-controller/src/types.ts) | `session-controller` (`emit`) | `remotes` |
diff --git a/docs/subsystems/README.i18n.yaml b/docs/subsystems/README.i18n.yaml
index fd13bb4d00..c315422ffb 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: 857da1eede1f5523
- zh: 93fc3928f4eb43b5
+ en: ef54c3c24cd7d961
+ zh: 72a46c77b9dfc6ef
diff --git a/docs/subsystems/README.md b/docs/subsystems/README.md
index 92ac9554ce..97d75dfd24 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: shared task DAG, task ownership, and wait signals for a subagent tree |
+| [agent-team.md](agent-team.md) | Agent Teams: shared task DAG and task ownership 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 6e9fa95917..4caaa33784 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:子代理树共享的任务 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 a0e3e503bf..b39898a2de 100644
--- a/docs/subsystems/agent-team.i18n.yaml
+++ b/docs/subsystems/agent-team.i18n.yaml
@@ -18,5 +18,5 @@
en: 3fce926f1d135d5a
zh: a835885262792d89
/agent-teams/replay:
- en: 81d0de978aa92363
- zh: a5bacfd4051afe51
+ en: 9a3cc5b173014a3b
+ zh: 531632d2a7eefe94
diff --git a/docs/subsystems/agent-team.md b/docs/subsystems/agent-team.md
index 54605fbd60..1bd3155216 100644
--- a/docs/subsystems/agent-team.md
+++ b/docs/subsystems/agent-team.md
@@ -107,7 +107,7 @@ interface TeamProjection {
## Replay
-The `agentTeam` projection replays historical roster, task, and mailbox records selected by `TeamId`. Subagent discovery owns current tree membership and ignores inherited catalog authority. Session event `seq` and `time` own ordering and timing. Committed durable generations are preserved. The [package README](../../packages/experimental/agent-team/README.md) defines task operations, wait signals, and limits.
+The `agentTeam` projection replays historical roster, task, and mailbox records selected by `TeamId`. Subagent discovery owns current tree membership and ignores inherited catalog authority. Session event `seq` and `time` own ordering and timing. Committed durable generations are preserved. The [package README](../../packages/experimental/agent-team/README.md) defines task operations and limits.
diff --git a/docs/subsystems/agent-team.zh.md b/docs/subsystems/agent-team.zh.md
index 69abb36006..0d11c9617c 100644
--- a/docs/subsystems/agent-team.zh.md
+++ b/docs/subsystems/agent-team.zh.md
@@ -107,7 +107,7 @@ interface TeamProjection {
## 回放
-`agentTeam` 投影按 `TeamId` 重放历史名册、任务和邮箱记录。subagent 发现负责当前树成员关系,不将继承目录作为权限来源。Session 事件的 `seq` 和 `time` 负责排序和时间。已提交的持久化代次保持不变。[包 README](../../packages/experimental/agent-team/README.zh.md)定义任务操作、等待信号和限制。
+`agentTeam` 投影按 `TeamId` 重放历史名册、任务和邮箱记录。subagent 发现负责当前树成员关系,不将继承目录作为权限来源。Session 事件的 `seq` 和 `time` 负责排序和时间。已提交的持久化代次保持不变。[包 README](../../packages/experimental/agent-team/README.zh.md)定义任务操作和限制。
diff --git a/docs/subsystems/core.md b/docs/subsystems/core.md
index de964e340a..a7c70bc0f7 100644
--- a/docs/subsystems/core.md
+++ b/docs/subsystems/core.md
@@ -1116,27 +1116,6 @@ Types: [Scoped](scope.md)
Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
-
-
-#### `agent/wait-notified` — emit
-
-End current input waits without adding inbox content or starting the agent. This process-local signal is not retained for later waits.
-
-```ts cordis-catalog
-/**
- * End current input waits without adding inbox content or starting the agent.
- * This process-local signal is not retained for later waits.
- * @param payload.agent - the exact agent whose current waits should end.
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
- * @mode emit
- */
-'agent/wait-notified'(this: Scoped, payload: { agent: Agent }): void
-```
-
-Types: [Scoped](scope.md)
-
-Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
-
### `agent-loop/*` events
diff --git a/docs/subsystems/core.zh.md b/docs/subsystems/core.zh.md
index 17cfb2d701..3016a5c7e8 100644
--- a/docs/subsystems/core.zh.md
+++ b/docs/subsystems/core.zh.md
@@ -1128,27 +1128,6 @@ Types: [Scoped](scope.zh.md)
Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
-
-
-#### `agent/wait-notified` — emit
-
-End current input waits without adding inbox content or starting the agent. This process-local signal is not retained for later waits.
-
-```ts cordis-catalog
-/**
- * End current input waits without adding inbox content or starting the agent.
- * This process-local signal is not retained for later waits.
- * @param payload.agent - the exact agent whose current waits should end.
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
- * @mode emit
- */
-'agent/wait-notified'(this: Scoped, payload: { agent: Agent }): void
-```
-
-Types: [Scoped](scope.zh.md)
-
-Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
-
### `agent-loop/*` events
diff --git a/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.i18n.yaml b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.i18n.yaml
new file mode 100644
index 0000000000..dadc616c76
--- /dev/null
+++ b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.i18n.yaml
@@ -0,0 +1,16 @@
+# Bilingual-pair consistency record for guide.md (docs/i18n/README.md): per heading
+# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
+# After editing either side, bring the other along and re-record with:
+# pnpm run verify-translation-pairing --write docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.md
+/:
+ en: f09a5fde2cedb48c
+ zh: beec903a10ed62ee
+/agent-waits-require-inbox-input:
+ en: 96c9f5fc6514557a
+ zh: aac52f37d543ed9b
+/agent-waits-require-inbox-input/change:
+ en: 568583cb15f85337
+ zh: 6c7e3028f23d1f3e
+/agent-waits-require-inbox-input/migration:
+ en: 156a027d11ad3769
+ zh: 301029cecd3ae640
diff --git a/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.md b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.md
new file mode 100644
index 0000000000..9252add26b
--- /dev/null
+++ b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.md
@@ -0,0 +1,19 @@
+---
+kind: upgrade-guide
+description: "Task-board changes no longer end wait_agent calls; send a message to request further work."
+---
+# Agent waits require inbox input
+
+English | [中文](guide.zh.md)
+
+## Change
+
+`wait_agent` waits only for the calling Agent's next-step inbox. Task creation, assignment, completion, and other task updates no longer end an active wait. They do not add inbox messages or start idle Agents. Queued next-turn input and other Agents' status changes also leave the wait pending.
+
+Existing next-step input returns immediately; new messages, completion notifications, and user steering end the wait without consuming that input. Timeout and cancellation behavior remain unchanged.
+
+## Migration
+
+1. After changing a task, use `send_message` when its owner or another member needs to act on the update. Do not rely on a task mutation to release `wait_agent`.
+2. Read current task state before acting; a task update is not a message or an instruction to begin a new turn.
+3. Verify that a task update leaves an empty-inbox wait pending, then send a message and confirm that the wait returns with `timedOut: false`.
diff --git a/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.zh.md b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.zh.md
new file mode 100644
index 0000000000..27bd015147
--- /dev/null
+++ b/docs/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.zh.md
@@ -0,0 +1,19 @@
+---
+kind: upgrade-guide
+description: "任务板变更不再结束 wait_agent 调用;需要继续工作时发送消息。"
+---
+# Agent 等待需要收件箱输入
+
+[English](guide.md) | 中文
+
+## 变更
+
+`wait_agent` 仅等待调用 Agent 的下一步收件箱。任务创建、分配、完成和其他任务更新不再结束正在进行的等待,也不添加收件箱消息或启动空闲 Agent。排队的下一轮输入和其他 Agent 的状态变化同样不会结束等待。
+
+已有下一步输入时立即返回;新消息、完成通知和用户引导会结束等待,但不消费输入。超时和取消行为保持不变。
+
+## 迁移
+
+1. 修改任务后,需要任务所有者或其他成员处理更新时使用 `send_message`。不要依赖任务修改来结束 `wait_agent`。
+2. 行动前读取当前任务状态;任务更新不是消息,也不是开始新轮次的指令。
+3. 验证任务更新不会结束空收件箱上的等待,然后发送消息,确认等待返回 `timedOut: false`。
diff --git a/packages/core/agent/src/runtime-types.ts b/packages/core/agent/src/runtime-types.ts
index 41ba74d47d..2b1698e39c 100644
--- a/packages/core/agent/src/runtime-types.ts
+++ b/packages/core/agent/src/runtime-types.ts
@@ -307,14 +307,6 @@ declare module '@deepseek-ai/cordis' {
* @mode emit
*/
'agent/status'(this: Scoped, payload: { agent: Agent; status: AgentStatus }): void
- /**
- * End current input waits without adding inbox content or starting the agent.
- * This process-local signal is not retained for later waits.
- * @param payload.agent - the exact agent whose current waits should end.
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
- * @mode emit
- */
- 'agent/wait-notified'(this: Scoped, payload: { agent: Agent }): void
/**
* One message entered the live inbox.
* @param payload.agent - the agent whose inbox changed.
diff --git a/packages/experimental/agent-team/README.i18n.yaml b/packages/experimental/agent-team/README.i18n.yaml
index ef2ec50693..83a8fb45b8 100644
--- a/packages/experimental/agent-team/README.i18n.yaml
+++ b/packages/experimental/agent-team/README.i18n.yaml
@@ -3,14 +3,14 @@
# After editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/experimental/agent-team/README.md
/:
- en: d26e678a9efd33b0
- zh: b20b1c565005d152
+ en: d13ab6d936f3017e
+ zh: c210c25fd9b6cc92
/deepseek-ai-dsh-experimental-agent-team:
en: b28084855adcdabd
zh: 59e3d49c3d63baf9
/deepseek-ai-dsh-experimental-agent-team/summary:
- en: ef6ddcbb3c5c6d7b
- zh: 81caab2bd6c12a7e
+ en: ab936d59a3993ad5
+ zh: 22bb510326adc136
/deepseek-ai-dsh-experimental-agent-team/table-of-contents:
en: d152484eb41ac6b4
zh: 09388d293f9be9cb
@@ -27,14 +27,14 @@
en: 501812f9e9174d1d
zh: 55303d71b67cc0be
/deepseek-ai-dsh-experimental-agent-team/use-this-package/messages-between-teammates:
- en: 07dce4ca3b85c8e4
- zh: 6c872836fa785aa9
+ en: 5ebaa8681a92ee97
+ zh: a9be3e8e5f9bbaf1
/deepseek-ai-dsh-experimental-agent-team/use-this-package/shared-task-board:
en: dfecbb3adbc360da
zh: 4855fd99f03675f4
/deepseek-ai-dsh-experimental-agent-team/use-this-package/waiting-and-interruption:
- en: 2a8172e07ec20808
- zh: 8aa93e8556517b92
+ en: cdca16d846db4a71
+ zh: e67f1eb26707ac5f
/deepseek-ai-dsh-experimental-agent-team/use-this-package/what-success-and-failure-look-like:
en: 36814f2b06dc2359
zh: c46af795bc89942f
@@ -42,11 +42,11 @@
en: 6a18034f1ccc247a
zh: e08b0eb0ccb41ea8
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/design-philosophy:
- en: 8b9ed8b31facd102
- zh: fe6678ab9e2b2c0f
+ en: 864117c368113c14
+ zh: 6f8b66333e517ca2
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/source-map:
en: fdb608bd196db6c3
- zh: e699bbb116af6b4c
+ zh: 242977c941da5f77
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/team-identity-and-membership:
en: 6edb88f9d3fde16a
zh: 7918022f3e09ef46
@@ -54,17 +54,14 @@
en: fb370a3606dfe1bf
zh: 67e3de3c5ebe0758
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/messages-and-historical-mailbox:
- en: 813078d6f281ebc0
- zh: 106887aaf0760e95
+ en: 762313234714981a
+ zh: 39d69ad1d7542fd2
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/shared-task-board:
en: a3eaccd15b02cc8f
zh: c2829d437a1dfe57
-/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/task-board-wait-signals:
- en: adc035cc569df172
- zh: c7fa7aec7e9ed2bf
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/durability-model:
- en: f85bb7e9dd357ae6
- zh: 63638f8ebcf18a3d
+ en: bf23d19c30bee66e
+ zh: 617dec77817158d3
/deepseek-ai-dsh-experimental-agent-team/understand-the-implementation/disposal:
en: a8baf51f2fcd6d49
zh: 357c8db36823fe0e
@@ -81,17 +78,17 @@
en: 8b29cb821f80db40
zh: 43e402736a553ce5
/deepseek-ai-dsh-experimental-agent-team/model-experience/task-views-and-errors/what-the-model-sees:
- en: 42e3a8e77cd1f549
- zh: 52460903771fa74b
+ en: 3d9d67aa37a19de5
+ zh: f4e4a8764cbe022f
/deepseek-ai-dsh-experimental-agent-team/model-experience/task-views-and-errors/token-effect:
- en: f8857619242bb16e
- zh: aca99dc785e83d56
+ en: 9e87d5f5aec04775
+ zh: 2c3f4b2e3f199252
/deepseek-ai-dsh-experimental-agent-team/model-experience/task-views-and-errors/kv-cache-effect:
- en: 28354a0f44ef61aa
- zh: e3007733c50e5514
+ en: 2b6842dc044d70ce
+ zh: 9f120fb4eb656f5f
/deepseek-ai-dsh-experimental-agent-team/known-limitations-and-deferred-work:
en: 208fdd33e8a1be9a
- zh: bc67492ad17d218f
+ zh: 0e4f6c25f7a7854d
/deepseek-ai-dsh-experimental-agent-team/known-limitations-and-deferred-work/dev-note:
en: 498e4cf9fe5741a7
zh: 9d2bab630e655c99
diff --git a/packages/experimental/agent-team/README.md b/packages/experimental/agent-team/README.md
index 3e3da80486..42c65984bd 100644
--- a/packages/experimental/agent-team/README.md
+++ b/packages/experimental/agent-team/README.md
@@ -1,5 +1,5 @@
---
-description: "Coordinate subagents through a durable shared task board and in-memory wait signals in experimental Team compositions."
+description: "Coordinate subagents through a durable shared task board in experimental Team compositions."
kind: "package-reference"
---
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
## Summary
-`dsh-experimental-agent-team` adds a durable shared task board to an existing subagent tree. Members use standard subagent tools to discover and contact one another. Team adds compare-and-set task ownership and signals all resident Agents in the tree after each task commit is flushed. Mount `dsh-experimental-tool-agent-team` for task tools. The package is experimental, and members share one checkout.
+`dsh-experimental-agent-team` adds a durable shared task board to an existing subagent tree. Members use standard subagent tools to discover and contact one another. Team adds compare-and-set task ownership. Mount `dsh-experimental-tool-agent-team` for task tools. The package is experimental, and members share one checkout.
## Table of Contents
@@ -59,7 +59,7 @@ Every root agent and its descendants form one Team. Task assignment accepts the
### Messages between teammates
-Members use standard `list_agents` to discover the tree and `send_message` to contact another member by Session id. The subagent runtime owns routing, sender attribution, and cold restoration. Task-board signals address all resident Agents in the same tree and never restore a cold member.
+Members use standard `list_agents` to discover the tree and `send_message` to contact another member by Session id. The subagent runtime owns routing, sender attribution, and cold restoration.
### Shared task board
@@ -71,7 +71,7 @@ File hints produce warnings when two in-progress tasks plan to touch overlapping
### Waiting and interruption
-The standard `wait_agent` tool waits for the exact caller’s next-step inbox and works without Team mode; see [subagent controls](../../subagent/tool-subagent-control/README.md#wait_agent). Team mode also ends an active wait when a task-board commit finishes flushing. Re-read the board after waiting. A change outside an active wait is not retained and cannot make a later wait return immediately.
+The standard `wait_agent` tool waits for the exact caller’s next-step inbox and works without Team mode; see [subagent controls](../../subagent/tool-subagent-control/README.md#wait_agent). Task changes do not end waits, add inbox input, or start idle Agents. Use `send_message` when an Agent needs to act on an update.
An ancestor stops a descendant’s current turn with the standard `interrupt_agent` tool. Interruption keeps unclaimed queued messages and leaves task ownership unchanged.
@@ -98,7 +98,7 @@ The service is built on four commitments:
- **Explicit authority.** Every service method takes the exact live calling `Agent`; a task owner or the Lead mutates a task, and only the Lead reassigns one.
- **Bounds that fail loud.** `maxTasks` is a validated deployment value, and exhaustion reports a typed error instead of reusing an id.
-The [task-board wait decision](../../../.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.md) owns task-board wait signals and the separation from ordinary subagent controls.
+The [inbox-only wait decision](../../../.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.md) separates task commits from Agent input waiting.
### Source map
@@ -120,21 +120,17 @@ Reassignment resolves the root or a continuable descendant through the same disc
### Messages and historical mailbox
-Peer messaging, Steer delivery, cold resume, and sender framing belong to the subagent runtime. Task-board changes use in-memory wait signals. Historical `team/message/queued` and `team/message/delivered` records and `team-message` sources remain decodable but are never delivered or acknowledged.
+Peer messaging, Steer delivery, cold resume, and sender framing belong to the subagent runtime. Task-board changes do not deliver input. Historical `team/message/queued` and `team/message/delivered` records and `team-message` sources remain decodable but are never delivered or acknowledged.
### Shared task board
Tasks are complete versioned snapshots. Mutations require `expectedRevision`; stale updates report `TEAM_TASK_STALE_REVISION`. Numeric `task-` ids stay within the safe-integer range. Deleted tasks remain replay tombstones but do not consume `maxTasks` or appear in `listTasks()`. Normalized workspace-relative `writeScopes` warn about overlapping in-progress work without blocking claims or authorizing writes.
-### Task-board wait signals
-
-After a successful append and flush, Team emits an Agent-scoped in-memory signal for resident Agents whose `subagents.rootOf()` returns that exact root instance. Agents without an authorized live root are skipped; other lookup failures reject the operation after the task is committed. The signal ends only an active wait. It adds no inbox entry or unread state, never starts an idle turn, and never restores a cold member. Changes between waits are not replayed to the next wait.
-
### Durability model
Team requires `sessionPersistence` before activation. `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 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 failed flush or one with no participating listener raises `TEAM_TASK_PERSISTENCE_UNCONFIRMED` with the task id and revision: the task is already visible, but durability is unknown, and no wait signal is emitted. 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.
+Task mutations honor cancellation before appending `team/task` to the exact live root Session, then await the flush. Cancellation cannot retract an appended record. A failed flush or one with no participating listener 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. 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.
@@ -173,15 +169,15 @@ Read these pages when the package-level contract is not enough. They move from t
#### What the model sees
-The model reads task views through task-tool results. Views identify owners by `ownerId`; stale updates report the current revision. A task-board signal ends an active `wait_agent` call without supplying task contents. The model re-reads current tasks before acting.
+The model reads task views through task-tool results. Views identify owners by `ownerId`; stale updates report the current revision. Task updates do not end `wait_agent`; send an explicit message when a member needs to act on an update.
#### Token effect
-Each task result adds a compact task view. Wait signals add no message text.
+Each task result adds a compact task view. Task updates add no inbox messages.
#### KV Cache effect
-Task records stay outside model history. Task and wait results append after the reusable request prefix.
+Task records stay outside model history. Task results append after the reusable request prefix.
## Known Limitations and Deferred Work
diff --git a/packages/experimental/agent-team/README.zh.md b/packages/experimental/agent-team/README.zh.md
index 645defb110..d4da2236e8 100644
--- a/packages/experimental/agent-team/README.zh.md
+++ b/packages/experimental/agent-team/README.zh.md
@@ -1,5 +1,5 @@
---
-description: "在实验性 Team 组合中通过持久化共享任务板和内存等待信号协调 subagent。"
+description: "在实验性 Team 组合中通过持久化共享任务板协调 subagent。"
kind: "package-reference"
---
@@ -9,7 +9,7 @@ kind: "package-reference"
## 概述
-`dsh-experimental-agent-team` 为现有 subagent(子智能体)树添加持久化共享任务板。成员使用标准 subagent 工具发现和联系彼此。Team 提供比较并交换的任务归属更新,并在任务提交刷盘后向树中所有驻留 Agent 发出信号。挂载 `dsh-experimental-tool-agent-team` 可提供任务工具。此包为实验性包,成员共享同一检出目录。
+`dsh-experimental-agent-team` 为现有 subagent(子智能体)树添加持久化共享任务板。成员使用标准 subagent 工具发现和联系彼此。Team 提供比较并交换的任务归属更新。挂载 `dsh-experimental-tool-agent-team` 可提供任务工具。此包为实验性包,成员共享同一检出目录。
## 目录
@@ -59,7 +59,7 @@ kind: "package-reference"
### teammate 之间的消息
-成员通过标准 `list_agents` 发现整棵树,并通过 `send_message` 按 Session id 联系另一成员。subagent 运行时负责路由、发送方归属和冷恢复。任务板信号面向同树所有驻留 Agent,绝不恢复冷成员。
+成员通过标准 `list_agents` 发现整棵树,并通过 `send_message` 按 Session id 联系另一成员。subagent 运行时负责路由、发送方归属和冷恢复。
### 共享任务板
@@ -71,7 +71,7 @@ kind: "package-reference"
### 等待与中断
-标准 `wait_agent` 工具等待当前调用者的下一步收件箱,无需 Team 模式即可使用;参见 [subagent 控制工具](../../subagent/tool-subagent-control/README.zh.md#wait_agent)。Team 模式还会在任务板提交完成刷盘后结束正在进行的等待。等待结束后重新读取任务板。不在等待期间发生的变更不会保留,也不能使后续等待立即返回。
+标准 `wait_agent` 工具等待当前调用者的下一步收件箱,无需 Team 模式即可使用;参见 [subagent 控制工具](../../subagent/tool-subagent-control/README.zh.md#wait_agent)。任务变化不会结束等待、添加收件箱输入或启动空闲 Agent。需要 Agent 处理更新时使用 `send_message`。
祖先通过标准 `interrupt_agent` 工具停止后代的当前轮次。中断保留尚未认领的排队消息,也不改变任务归属。
@@ -98,7 +98,7 @@ kind: "package-reference"
- **显式权限。** 每个服务方法都接收确切的 live 调用方 `Agent`;任务 owner 或 Lead 可以变更任务,只有 Lead 可以 reassign。
- **超出上限时明确失败。** `maxTasks` 是经过校验的部署值,耗尽时报告类型化错误,而不是复用 id。
-[任务板等待决策](../../../.agents/notes/implemented/architecture/2026-10-19-team-task-board-inbox.zh.md)负责说明任务板等待信号及其与普通 subagent 控制工具的职责划分。
+[仅等待收件箱的决策](../../../.agents/notes/implemented/simplification/2026-10-29-inbox-only-agent-wait.zh.md)区分任务提交与 Agent 输入等待。
### 源码地图
@@ -106,7 +106,7 @@ kind: "package-reference"
|---|---|
| [`src/index.ts`](src/index.ts) | 插件入口、配置校验和任务服务注册 |
| [`src/task-board.ts`](src/task-board.ts) | 任务 CAS 命令、DAG 校验与派生视图 |
-| [`src/journal.ts`](src/journal.ts) | 串行化的 Lead 日志事务与提交通知 |
+| [`src/journal.ts`](src/journal.ts) | 串行化的 Lead 日志事务 |
| [`src/projection.ts`](src/projection.ts) | 严格回放历史成员、任务和邮箱记录,发布 `agentTeam` 客户端视图 |
| [`src/task-view.ts`](src/task-view.ts) | 任务板与客户端视图共用的纯任务就绪性和写入重叠推导 |
@@ -120,21 +120,17 @@ subagent 服务通过 `subagents.rootOf()` 解析当前在线调用者的根。
### 消息与历史 mailbox
-成员通信、Steer 投递、冷恢复和发送方包装属于 subagent 运行时。任务板变更使用内存等待信号。历史 `team/message/queued`、`team/message/delivered` 记录和 `team-message` 来源仍可解码,但绝不再投递或确认。
+成员通信、Steer 投递、冷恢复和发送方包装属于 subagent 运行时。任务板变更不投递输入。历史 `team/message/queued`、`team/message/delivered` 记录和 `team-message` 来源仍可解码,但绝不再投递或确认。
### 共享任务板
任务是完整的版本化快照。修改必须携带 `expectedRevision`;过期更新报告 `TEAM_TASK_STALE_REVISION`。数字形式的 `task-` id 保持在安全整数范围内。删除的任务保留为重放墓碑,但不占用 `maxTasks`,也不出现在 `listTasks()` 中。规范化的工作区相对 `writeScopes` 会警告进行中工作的重叠,而不阻止认领或授权写入。
-### 任务板等待信号
-
-成功追加并刷盘后,Team 向 `subagents.rootOf()` 返回该根精确实例的驻留 Agent 发出以 Agent 为作用域的内存信号。没有获授权在线根的 Agent 会被跳过;其他查询失败会在任务提交后拒绝操作。信号仅结束正在进行的等待,不增加收件箱条目或未读状态,绝不启动空闲轮次,也不恢复冷成员。两次等待之间的变更不会向下一次等待重放。
-
### 持久性模型
Team 在激活前要求 `sessionPersistence` 可用。`Service.init` 等待 `agentTeam` 投影注册完成后才发布服务就绪状态。日志持有返回的 handle,通过 `stateOf(session)` 读取任务状态;调用者使用 Team 服务,不另行注册 reader。
-任务修改在向当前在线根 Session 追加 `team/task` 前响应取消,随后刷盘并发出等待信号。取消不能撤回已追加的记录。刷盘失败或没有监听器参与时,以 `TEAM_TASK_PERSISTENCE_UNCONFIRMED` 报告任务 id 和修订号:任务已可见,但持久性未知,且不发出等待信号。重试前先读取该任务。任务记录仅保留在日志中;信号不是消息,也不是持久化事件。等待工具通过普通工具流水线记录结果。不重写已提交任务或历史载荷代次。
+任务修改在向当前在线根 Session 追加 `team/task` 前响应取消,随后等待刷盘完成。取消不能撤回已追加的记录。刷盘失败或没有监听器参与时,以 `TEAM_TASK_PERSISTENCE_UNCONFIRMED` 报告任务 id 和修订号:任务已可见,但持久性未知。重试前先读取该任务。任务记录仅保留在日志中。不重写已提交任务或历史载荷代次。
邮箱投影和检查点接纳保留本地声明校验器之外的已解码 JSON 字段,包括自身的 `__proto__` 键。历史载荷代次保持不变;投影缓存版本变化时从 Session 日志重建。
@@ -173,15 +169,15 @@ Team 在激活前要求 `sessionPersistence` 可用。`Service.init` 等待 `age
#### 模型看到什么
-模型通过任务工具结果读取任务视图。视图以 `ownerId` 标识所有者;过期更新报告当前修订。任务板信号结束正在进行的 `wait_agent` 调用,不提供任务内容。模型在行动前重新读取当前任务。
+模型通过任务工具结果读取任务视图。视图以 `ownerId` 标识所有者;过期更新报告当前修订。任务更新不会结束 `wait_agent`;需要成员处理更新时发送明确消息。
#### Token 影响
-每个任务结果增加一个紧凑任务视图。等待信号不增加消息文本。
+每个任务结果增加一个紧凑任务视图。任务更新不增加收件箱消息。
#### KV Cache 影响
-任务记录保持在模型历史之外。任务和等待结果追加在可复用请求前缀之后。
+任务记录保持在模型历史之外。任务结果追加在可复用请求前缀之后。
## 已知限制与延期工作
@@ -194,7 +190,7 @@ Team 在激活前要求 `sessionPersistence` 可用。`Service.init` 等待 `age
- **实验原型,无稳定性承诺**——本包公开发布,但孵化期间约定仍可自由变更。
- **单进程** — 成员可观察共享文件系统编辑;此包不提供 worktree、远程成员、合并或文件系统锁。
- **write scope 仅作提示**——Bash、formatter、代码生成器与直接外部写入可以绕过文件版本检查;Lead 必须协调 owner 并检查最终 diff。
-- **通知仅到达本地在线成员** — 不恢复冷成员,空闲成员在被明确恢复前仍保持空闲。
+- **任务更新不会恢复成员** — 需要成员继续工作时发送明确消息。
- **通信限定在一棵树内**——成员不能向无关 Team 发消息;只有祖先能中断后代。
- **成员关系需要在线 Team 根**——中间创建者完成或释放后,成员保留 Team 身份;根缺失时不再属于活动 Team。
diff --git a/packages/experimental/agent-team/src/index.ts b/packages/experimental/agent-team/src/index.ts
index 2b92523655..91fdadbbfd 100644
--- a/packages/experimental/agent-team/src/index.ts
+++ b/packages/experimental/agent-team/src/index.ts
@@ -2,10 +2,9 @@
import { Context, Service } from '@deepseek-ai/cordis'
import z from '@deepseek-ai/schemastery'
-import { emitAgentEvent } from '@deepseek-ai/dsh-agent'
import type { Agent } from '@deepseek-ai/dsh-agent'
import type {} from '@deepseek-ai/dsh-session-persistence'
-import { SubagentError } from '@deepseek-ai/dsh-subagent'
+import type {} from '@deepseek-ai/dsh-subagent'
import { TeamError } from './error.ts'
import { TeamJournal } from './journal.ts'
import { teamProjectionDefinition } from './projection.ts'
@@ -69,20 +68,7 @@ export class TeamService extends Service {
protected async [Service.init](): Promise {
const ctx = this.ctx
const teamProjectionHandle = await ctx.sessionProjections.register(teamProjectionDefinition)
- this.journal = new TeamJournal(ctx, teamProjectionHandle, (root) => {
- for (const agent of ctx.agents.list()) {
- let agentRoot: Agent
- try {
- agentRoot = ctx.subagents.rootOf(agent)
- } catch (error: unknown) {
- if (error instanceof SubagentError && error.code === 'UNAUTHORIZED') continue
- throw error
- }
- if (agentRoot === root) {
- emitAgentEvent(ctx, agent, 'agent/wait-notified', {})
- }
- }
- })
+ this.journal = new TeamJournal(ctx, teamProjectionHandle)
this.tasks = new TeamTaskBoard(ctx, this.journal, this.maxTasks)
}
diff --git a/packages/experimental/agent-team/src/journal.ts b/packages/experimental/agent-team/src/journal.ts
index c8f18f7938..fd7c7fcfad 100644
--- a/packages/experimental/agent-team/src/journal.ts
+++ b/packages/experimental/agent-team/src/journal.ts
@@ -17,12 +17,10 @@ export class TeamJournal {
/**
* @param ctx - Team service context with the injected Session service.
* @param teamProjectionHandle - Team state registration owned by the Team service.
- * @param onCommit - synchronous notification after the Team event flush succeeds.
*/
constructor(
private readonly ctx: Context,
private readonly teamProjectionHandle: ProjectionHandle,
- private readonly onCommit: (root: Agent) => void,
) {}
/**
@@ -56,7 +54,7 @@ export class TeamJournal {
}
/**
- * Commit a task before notifying current waits in its tree.
+ * Append and flush one task revision.
* @param root - live tree root whose Session stores the task.
* @param task - complete committed task revision.
* @throws TEAM_TASK_PERSISTENCE_UNCONFIRMED after append when flush fails or has no listener; read the task before retrying.
@@ -73,6 +71,5 @@ export class TeamJournal {
{ cause: error },
)
}
- this.onCommit(root)
}
}
diff --git a/packages/experimental/agent-team/tests/team.spec.ts b/packages/experimental/agent-team/tests/team.spec.ts
index 3b45c396c8..686e6e42ed 100644
--- a/packages/experimental/agent-team/tests/team.spec.ts
+++ b/packages/experimental/agent-team/tests/team.spec.ts
@@ -9,7 +9,7 @@ import { mountAgentLoopTestDependencies } from '@deepseek-ai/dsh-agent-loop-test
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { SessionLogOffset, SessionId } from '@deepseek-ai/dsh-session'
import * as JsonlSessionPersistence from '@deepseek-ai/dsh-session-persistence-jsonl'
-import SubagentService, { SubagentError, type SubagentActivation } from '@deepseek-ai/dsh-subagent'
+import SubagentService, { type SubagentActivation } from '@deepseek-ai/dsh-subagent'
import * as SubagentFork from '@deepseek-ai/dsh-subagent-fork-in-process'
import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
import { MockAdapter, textResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
@@ -638,8 +638,8 @@ describe('Team shared task DAG', () => {
})
})
-describe('Task-board wait notifications', () => {
- it('notifies the root and every resident descendant without writing inboxes or starting idle agents', async () => {
+describe('Task-board persistence', () => {
+ it('commits tasks without writing inboxes or starting idle agents', async () => {
const { ctx, lead, adapter } = await setup([])
const alpha = await resident(ctx, lead, 'notify-alpha')
const beta = (await ctx.agents.create({
@@ -653,19 +653,10 @@ describe('Task-board wait notifications', () => {
meta: { origin: 'subagent', parentSession: lead.id },
agentOptions: { provider: 'mock', model: 'mock' },
})).agent
- const notified: Agent[] = []
- const rootNotifications: Agent[] = []
- const betaNotifications: Agent[] = []
- ctx.on('agent/wait-notified', ({ agent }) => { notified.push(agent) })
- lead.ctx.on('agent/wait-notified', ({ agent }) => { rootNotifications.push(agent) })
- beta.ctx.on('agent/wait-notified', ({ agent }) => { betaNotifications.push(agent) })
const first = await ctx.agentTeams.createTask(lead, { subject: 'one', description: 'first change' })
await ctx.agentTeams.updateTask(beta, {
taskId: first.id, expectedRevision: first.revision, action: 'claim',
})
- expect(notified).toEqual([lead, alpha, beta, lead, alpha, beta])
- expect(rootNotifications).toEqual([lead, lead])
- expect(betaNotifications).toEqual([beta, beta])
for (const agent of [lead, alpha, beta, unrelated, unbound]) {
expect(agent.inbox.nextStep).toHaveLength(0)
expect(agent.inbox.nextTurn).toHaveLength(0)
@@ -675,34 +666,8 @@ describe('Task-board wait notifications', () => {
expect(adapter.requests).toHaveLength(0)
})
- it.each([
- new Error('unexpected root lookup failure'),
- new SubagentError('runtime unavailable', 'RUNTIME_UNAVAILABLE'),
- ])('propagates unexpected root lookup failures after committing the task: %s', async (failure) => {
+ it('resolves task commits only after persistence succeeds', async () => {
const { ctx, lead } = await setup([])
- const child = await resident(ctx, lead, 'failed-root-lookup')
- const rootOf = ctx.subagents.rootOf.bind(ctx.subagents)
- vi.spyOn(ctx.subagents, 'rootOf').mockImplementation((agent) => {
- if (agent === child) throw failure
- return rootOf(agent)
- })
- const notified: Agent[] = []
- ctx.on('agent/wait-notified', ({ agent }) => { notified.push(agent) })
-
- await expect(ctx.agentTeams.createTask(lead, { subject: 'committed', description: 'notification interrupted' }))
- .rejects.toBe(failure)
-
- expect(notified).toEqual([lead])
- expect(ctx.agentTeams.getTask(lead, TeamTaskId('task-1'))).toMatchObject({ subject: 'committed', revision: 1 })
- await using stored = await ctx.sessionPersistence.open(lead.id, 'read')
- expect((await stored.read()).events.filter(event => event.type === 'team/task')).toHaveLength(1)
- })
-
- it('publishes notifications only after persistence succeeds', async () => {
- const { ctx, lead } = await setup([])
- const child = await resident(ctx, lead, 'flush-recipient')
- const notified: Agent[] = []
- ctx.on('agent/wait-notified', ({ agent }) => { notified.push(agent) })
const flush = ctx.sessions.flush.bind(ctx.sessions)
const entered = Promise.withResolvers()
const release = Promise.withResolvers()
@@ -711,16 +676,19 @@ describe('Task-board wait notifications', () => {
await release.promise
return await flush(session)
})
- const creating = ctx.agentTeams.createTask(lead, { subject: 'one', description: 'wait for flush' })
+ let committed = false
+ const creating = ctx.agentTeams.createTask(lead, { subject: 'one', description: 'wait for flush' }).then((task) => {
+ committed = true
+ return task
+ })
try {
await entered.promise
- expect(notified).toEqual([])
+ expect(committed).toBe(false)
} finally {
release.resolve(undefined)
await creating
}
- expect(notified).toEqual([lead, child])
- notified.length = 0
+ expect(committed).toBe(true)
vi.spyOn(ctx.sessions, 'flush').mockRejectedValueOnce(new Error('storage unavailable'))
await expect(ctx.agentTeams.createTask(lead, { subject: 'two', description: 'failed flush' })).rejects.toMatchObject({
code: 'TEAM_TASK_PERSISTENCE_UNCONFIRMED',
@@ -729,50 +697,23 @@ describe('Task-board wait notifications', () => {
})
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',
})).rejects.toMatchObject({ code: 'TEAM_TASK_STALE_REVISION' })
- expect(notified).toEqual([])
})
- it('reports unconfirmed task revisions without notifications when no flush listener participates', async () => {
+ it('reports unconfirmed task revisions when no flush listener participates', async () => {
const { ctx, lead } = await setup([])
- const notified: Agent[] = []
- ctx.on('agent/wait-notified', ({ agent }) => { notified.push(agent) })
vi.spyOn(ctx.sessions, 'flush').mockResolvedValue(false)
await expect(ctx.agentTeams.createTask(lead, { subject: 'unconfirmed', description: 'read before retrying' }))
.rejects.toMatchObject({ code: 'TEAM_TASK_PERSISTENCE_UNCONFIRMED' })
const taskId = TeamTaskId('task-1')
expect(ctx.agentTeams.getTask(lead, taskId)).toMatchObject({ subject: 'unconfirmed', revision: 1 })
- expect(notified).toEqual([])
await expect(ctx.agentTeams.updateTask(lead, { taskId, expectedRevision: 1, action: 'claim' }))
.rejects.toMatchObject({ code: 'TEAM_TASK_PERSISTENCE_UNCONFIRMED' })
expect(ctx.agentTeams.getTask(lead, taskId)).toMatchObject({ revision: 2, ownerId: lead.id })
- expect(notified).toEqual([])
})
- it('contains listener failures and continues notifying the same agent and later recipients', async () => {
- const { ctx, lead } = await setup([])
- const child = await resident(ctx, lead, 'healthy-recipient')
- const warnings: string[] = []
- ctx.logger.warn = ((message: unknown) => { warnings.push(String(message)) }) as typeof ctx.logger.warn
- lead.ctx.on('agent/wait-notified', () => { throw new Error('sync listener') })
- lead.ctx.on('agent/wait-notified', () => Promise.reject(new Error('async listener')) as never)
- const notified: Agent[] = []
- ctx.on('agent/wait-notified', ({ agent }) => { notified.push(agent) })
- const task = await ctx.agentTeams.createTask(lead, { subject: 'committed', description: 'listener failure' })
- expect(task).toMatchObject({ id: 'task-1', revision: 1 })
- expect(notified).toEqual([lead, child])
- expect(warnings).toEqual([
- 'agent event "agent/wait-notified" listener threw: Error: sync listener',
- 'agent event "agent/wait-notified" listener rejected: Error: async listener',
- ])
- await using stored = await ctx.sessionPersistence.open(lead.id, 'read')
- const events = (await stored.read()).events.filter(event => event.type === 'team/task')
- expect(events).toHaveLength(1)
- expect(events[0]!.data).toMatchObject({ task: { id: task.id, revision: 1, subject: 'committed' } })
- })
})
diff --git a/packages/experimental/tool-agent-team/README.md b/packages/experimental/tool-agent-team/README.md
index 4df2e49bf9..55760fff00 100644
--- a/packages/experimental/tool-agent-team/README.md
+++ b/packages/experimental/tool-agent-team/README.md
@@ -126,7 +126,7 @@ A shared task board is available to every agent in your subagent tree. Task owne
Read tasks with team_task_list or team_task_get, claim with the current revision, perform the work, then complete the task. Dependencies determine readiness; write scopes describe expected edits and overlapping scopes are advisory, not locks. Task readiness does not start an agent.
-Task changes end current wait_agent calls in your subagent tree. Changes outside a wait are not retained as notifications, and no task-board messages are added to an inbox. After wait_agent returns, read the latest tasks before acting. Task changes do not start idle agents; use send_message when an agent needs to continue working.
+Task changes do not end wait_agent calls, add inbox messages, or start idle agents. Use send_message when an agent needs to act on a task update.
```
#### Token effect
diff --git a/packages/experimental/tool-agent-team/README.zh.md b/packages/experimental/tool-agent-team/README.zh.md
index 2d9739b006..4eb5f3a67e 100644
--- a/packages/experimental/tool-agent-team/README.zh.md
+++ b/packages/experimental/tool-agent-team/README.zh.md
@@ -126,7 +126,7 @@ A shared task board is available to every agent in your subagent tree. Task owne
Read tasks with team_task_list or team_task_get, claim with the current revision, perform the work, then complete the task. Dependencies determine readiness; write scopes describe expected edits and overlapping scopes are advisory, not locks. Task readiness does not start an agent.
-Task changes end current wait_agent calls in your subagent tree. Changes outside a wait are not retained as notifications, and no task-board messages are added to an inbox. After wait_agent returns, read the latest tasks before acting. Task changes do not start idle agents; use send_message when an agent needs to continue working.
+Task changes do not end wait_agent calls, add inbox messages, or start idle agents. Use send_message when an agent needs to act on a task update.
```
#### Token 影响
diff --git a/packages/experimental/tool-agent-team/src/index.ts b/packages/experimental/tool-agent-team/src/index.ts
index c9968b99c7..116cde7d51 100644
--- a/packages/experimental/tool-agent-team/src/index.ts
+++ b/packages/experimental/tool-agent-team/src/index.ts
@@ -28,7 +28,7 @@ const TEAM_POLICY = `A shared task board is available to every agent in your sub
Read tasks with team_task_list or team_task_get, claim with the current revision, perform the work, then complete the task. Dependencies determine readiness; write scopes describe expected edits and overlapping scopes are advisory, not locks. Task readiness does not start an agent.
-Task changes end current wait_agent calls in your subagent tree. Changes outside a wait are not retained as notifications, and no task-board messages are added to an inbox. After wait_agent returns, read the latest tasks before acting. Task changes do not start idle agents; use send_message when an agent needs to continue working.`
+Task changes do not end wait_agent calls, add inbox messages, or start idle agents. Use send_message when an agent needs to act on a task update.`
/** One shared task, matching the public `TeamTaskView`. */
const TASK_VIEW_SCHEMA = {
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 229e2a68c6..94b587b77b 100644
--- a/packages/experimental/tool-agent-team/tests/tool-team.spec.ts
+++ b/packages/experimental/tool-agent-team/tests/tool-team.spec.ts
@@ -207,7 +207,7 @@ describe('dsh-tool-team', () => {
const leadPrompt = renderPrompt(leadAssembly)
expect(leadPrompt).toContain('A shared task board is available to every agent in your subagent tree')
expect(leadPrompt).toContain('Task readiness does not start an agent')
- expect(leadPrompt).toContain('Task changes end current wait_agent calls')
+ expect(leadPrompt).toContain('Task changes do not end wait_agent calls')
expect(renderContextSnapshot(leadAssembly)).toContain(leadIdentity(lead))
expect((await ctx.systemPrompt.assemble()).contexts.some(context => context.name === 'team:identity')).toBe(false)
expect(leadAssembly.tools.map(schema => schema.name)).not.toContain('spawn_teammate')
@@ -420,10 +420,15 @@ describe('dsh-tool-team', () => {
lead.inbox.clear()
const { result: wait } = await startWait(ctx, lead)
+ let returned = false
+ void wait.then(() => { returned = true })
const completed = await execute(ctx, child, 'team_task_update', {
task_id: task.id, expected_revision: 2, action: 'complete',
})
expect(completed.isError).toBe(false)
+ expect(returned).toBe(false)
+ expect(lead.inbox.nextStep).toHaveLength(0)
+ lead.inject(createUserMessage({ content: [{ type: 'text', text: 'task is ready' }], source: { kind: 'user' } }))
expect(JSON.parse(text(await wait))).toEqual({ timedOut: false })
await childActivation.dispose()
@@ -491,8 +496,13 @@ describe('dsh-tool-team', () => {
const child = ctx.agents.get(childId)!
child.inbox.clear()
const { result: wait } = await startWait(ctx, child)
- const wake = await execute(ctx, lead, 'team_task_create', { subject: 'wake', description: 'notify waiting child' })
- expect(wake.isError).toBe(false)
+ let returned = false
+ void wait.then(() => { returned = true })
+ const created = await execute(ctx, lead, 'team_task_create', { subject: 'queued work', description: 'await explicit assignment' })
+ expect(created.isError).toBe(false)
+ expect(returned).toBe(false)
+ expect(child.inbox.nextStep).toHaveLength(0)
+ child.inject(createUserMessage({ content: [{ type: 'text', text: 'read the task board' }], source: { kind: 'user' } }))
expect(JSON.parse(text(await wait))).toEqual({ timedOut: false })
await childActivation.dispose()
@@ -551,7 +561,7 @@ describe('dsh-tool-team', () => {
}
})
- it('ends every current wait in the committing tree without waking an unrelated tree', async () => {
+ it('leaves all current waits pending when tasks change', async () => {
const { ctx, lead, adapter } = await setup([])
const child = await ctx.agents.create({
sessionId: SessionId('waiting-child'), agentOptions: { provider: 'mock', model: 'mock' },
@@ -566,12 +576,9 @@ describe('dsh-tool-team', () => {
const second = await startWait(ctx, lead)
const descendant = await startWait(ctx, child.agent)
const separate = await startWait(ctx, unrelated.agent)
- const committed = await execute(ctx, lead, 'team_task_create', { subject: 'committed', description: 'wake current waits' })
+ const committed = await execute(ctx, lead, 'team_task_create', { subject: 'committed', description: 'persist shared work' })
expect(committed.isError).toBe(false)
- for (const result of await Promise.all([first.result, second.result, descendant.result])) {
- expect(result).toMatchObject({ isError: false, value: { timedOut: false } })
- }
- expect(vi.getTimerCount()).toBe(1)
+ expect(vi.getTimerCount()).toBe(4)
for (const agent of [lead, child.agent, unrelated.agent]) {
expect(agent.inbox.nextStep).toHaveLength(0)
expect(agent.inbox.nextTurn).toHaveLength(0)
@@ -580,13 +587,13 @@ describe('dsh-tool-team', () => {
expect(adapter.requests).toHaveLength(0)
const later = await startWait(ctx, lead)
await vi.advanceTimersByTimeAsync(10_000)
- for (const result of await Promise.all([separate.result, later.result])) {
+ for (const result of await Promise.all([first.result, second.result, descendant.result, separate.result, later.result])) {
expect(result).toMatchObject({ isError: false, value: { timedOut: true } })
}
expect(vi.getTimerCount()).toBe(0)
})
- it('keeps waiting until a task flush succeeds and ignores a failed flush', async () => {
+ it('keeps waiting across successful and failed task flushes', async () => {
const { ctx, lead } = await setup([])
vi.useFakeTimers()
const flush = ctx.sessions.flush.bind(ctx.sessions)
@@ -609,7 +616,8 @@ describe('dsh-tool-team', () => {
release.resolve(undefined)
expect((await creating).isError).toBe(false)
}
- expect(await waiting.result).toMatchObject({ isError: false, value: { timedOut: false } })
+ await vi.advanceTimersByTimeAsync(1)
+ expect(await waiting.result).toMatchObject({ isError: false, value: { timedOut: true } })
const afterCommit = await startWait(ctx, lead)
vi.spyOn(ctx.sessions, 'flush').mockRejectedValueOnce(new Error('storage unavailable'))
const failed = await execute(ctx, lead, 'team_task_create', { subject: 'failed', description: 'flush fails' })
diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts
index 2a62b52a5d..7b49a18ff6 100644
--- a/packages/extensions/tool-cordis/src/api-catalog.ts
+++ b/packages/extensions/tool-cordis/src/api-catalog.ts
@@ -4263,14 +4263,6 @@ export const EVENT_API: readonly EventApiEntry[] = [
description: 'The turn is about to close: the model owes no response (no live tool calls, no fresh steering). Awaited before the boundary commits — a listener that objects steers (`agent.steer(...)`) and the machine re-reads its inbox: fresh steering runs another step, none closes the turn. Data decides, so listener order cannot change the outcome. The inverse control (stop a tool loop early) is data too: a tool result carrying `concludesTurn` ends the turn at its step. The conclusion never short-circuits already-submitted next-step work: same-step `additionalContexts` or racing steering still runs, and the turn closes only when that inbox drains.',
parameters: [{ name: 'payload', description: '.signal - the current turn\'s explicit abort signal. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.' }],
},
- {
- name: 'agent/wait-notified',
- mode: 'emit',
- signature: '\'agent/wait-notified\'(this: Scoped, payload: { agent: Agent }): void',
- summary: 'End current input waits without adding inbox content or starting the agent.',
- description: 'End current input waits without adding inbox content or starting the agent. This process-local signal is not retained for later waits.',
- parameters: [{ name: 'payload', description: '.agent - the exact agent whose current waits should end. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.' }],
- },
{
name: 'api-session/activity',
mode: 'emit',
diff --git a/packages/subagent/tool-subagent-control/README.i18n.yaml b/packages/subagent/tool-subagent-control/README.i18n.yaml
index 0e7efb8cc8..83515c71aa 100644
--- a/packages/subagent/tool-subagent-control/README.i18n.yaml
+++ b/packages/subagent/tool-subagent-control/README.i18n.yaml
@@ -27,8 +27,8 @@
en: de45d5a4b98d3c91
zh: 666aa187170b9c0d
/deepseek-ai-dsh-tool-subagent-control/use-this-package/wait-agent:
- en: 41ed5ec6e82e0282
- zh: 0545e0719d5565d8
+ en: 29a3466bcc8e8e9d
+ zh: ea38770df450a43e
/deepseek-ai-dsh-tool-subagent-control/use-this-package/list-agents:
en: 912206fb3da120b5
zh: 3b247046a52d5f2a
@@ -36,8 +36,8 @@
en: c5cdf01a78c990f2
zh: 53c180d726ce4502
/deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/design-concept:
- en: 397401e9305cf11e
- zh: b3b342fa3f05cf32
+ en: 3b6f1956d232964d
+ zh: 9949b906020c9a79
/deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/delivery-and-signal-ownership:
en: 65137f5fe14ebb7f
zh: ce00e4f32709246a
@@ -57,8 +57,8 @@
en: e4c7376d4f29dbd5
zh: 6b7a9eaf69c9ea53
/deepseek-ai-dsh-tool-subagent-control/model-experience/tool-schema/what-the-model-sees:
- en: 1ea7f529eb698c45
- zh: c8af269730f179c1
+ en: db2e45684a4d0117
+ zh: 5cd3b646c010b530
/deepseek-ai-dsh-tool-subagent-control/model-experience/tool-schema/token-effect:
en: 5eb1d46a94b7ec1b
zh: 5670760ca695d4ad
diff --git a/packages/subagent/tool-subagent-control/README.md b/packages/subagent/tool-subagent-control/README.md
index cd36d81831..88675a1062 100644
--- a/packages/subagent/tool-subagent-control/README.md
+++ b/packages/subagent/tool-subagent-control/README.md
@@ -53,7 +53,7 @@ Sends a message to another agent in the same subagent tree by `agent_id`. The ca
### wait_agent
-Waits for the exact caller’s next-step inbox. Pending input returns immediately; later input, including messages, completion notifications, and user steering, ends the wait. Queued next-turn messages, unrelated inboxes, and Agent status changes do not end it. An Agent-scoped in-memory wait signal also ends an active wait; signals are not retained for future waits. The tool neither consumes input nor starts an Agent. `timeout_ms` defaults to 30000 and must be an integer from 10000 through 3600000. Results contain only `timedOut`; cancellation, caller disposal, or plugin unload rejects the wait.
+Waits for the exact caller’s next-step inbox. Pending input returns immediately; later input, including messages, completion notifications, and user steering, ends the wait. Queued next-turn messages, unrelated inboxes, and Agent status changes do not end it. Task changes do not end the wait. The tool neither consumes input nor starts an Agent. `timeout_ms` defaults to 30000 and must be an integer from 10000 through 3600000. Results contain only `timedOut`; cancellation, caller disposal, or plugin unload rejects the wait.
### list_agents
@@ -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 starting the timer and 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 events before starting the timer and checking pending next-step input, and removes its listeners and timer on every settlement.
### Delivery and signal ownership
@@ -111,7 +111,7 @@ Read these pages when the package-level contract is not enough; they move from t
#### What the model sees
-The generated [schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent-control) define id-addressed messaging and interruption, whole-tree discovery, and inbox waiting with `timeout_ms`. `wait_agent` returns `{ "timedOut": false }` for available input or a wait signal, and `{ "timedOut": true }` on timeout. It returns no message body or peer-activity diagnosis.
+The generated [schemas](../../../docs/tool-catalog.md#deepseek-aidsh-tool-subagent-control) define id-addressed messaging and interruption, whole-tree discovery, and inbox waiting with `timeout_ms`. `wait_agent` returns `{ "timedOut": false }` for available input, and `{ "timedOut": true }` on timeout. It returns no message body or peer-activity diagnosis.
#### Token effect
diff --git a/packages/subagent/tool-subagent-control/README.zh.md b/packages/subagent/tool-subagent-control/README.zh.md
index bdd12a236f..1e22ff7397 100644
--- a/packages/subagent/tool-subagent-control/README.zh.md
+++ b/packages/subagent/tool-subagent-control/README.zh.md
@@ -53,7 +53,7 @@ kind: "package-reference"
### wait_agent
-等待当前调用者的下一步收件箱。已有待处理输入时立即返回;之后到达的输入,包括消息、完成通知和用户引导,会结束等待。排队的下一轮消息、其他收件箱和 Agent 状态变化不会结束等待。以 Agent 为作用域的内存等待信号也会结束正在进行的等待;信号不会保留到后续等待。工具既不消费输入,也不启动 Agent。`timeout_ms` 默认为 30000,必须是 10000 至 3600000 的整数。结果仅含 `timedOut`;取消、调用者释放或插件卸载会拒绝等待。
+等待当前调用者的下一步收件箱。已有待处理输入时立即返回;之后到达的输入,包括消息、完成通知和用户引导,会结束等待。排队的下一轮消息、其他收件箱和 Agent 状态变化不会结束等待。任务变化不会结束等待。工具既不消费输入,也不启动 Agent。`timeout_ms` 默认为 30000,必须是 10000 至 3600000 的整数。结果仅含 `timedOut`;取消、调用者释放或插件卸载会拒绝等待。
### list_agents
@@ -71,7 +71,7 @@ kind: "package-reference"
### 设计理念
-通信、中断和发现委派给 `ctx.subagents`;驻留、冷恢复和授权属于该服务。等待工具在启动定时器并检查待处理下一步输入前订阅当前调用者的收件箱和等待信号,并在每次结束时移除监听器和定时器。
+通信、中断和发现委派给 `ctx.subagents`;驻留、冷恢复和授权属于该服务。等待工具在启动定时器并检查待处理下一步输入前订阅当前调用者的收件箱事件,并在每次结束时移除监听器和定时器。
### 投递与信号所有权
@@ -111,7 +111,7 @@ kind: "package-reference"
#### 模型看到什么
-生成的 [schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-subagent-control) 定义按 id 寻址的通信与中断、整树发现,以及带 `timeout_ms` 的收件箱等待。`wait_agent` 在输入可用或收到等待信号时返回 `{ "timedOut": false }`,超时时返回 `{ "timedOut": true }`。它不返回消息正文或其他成员活动诊断。
+生成的 [schema](../../../docs/tool-catalog.zh.md#deepseek-aidsh-tool-subagent-control) 定义按 id 寻址的通信与中断、整树发现,以及带 `timeout_ms` 的收件箱等待。`wait_agent` 在输入可用时返回 `{ "timedOut": false }`,超时时返回 `{ "timedOut": true }`。它不返回消息正文或其他成员活动诊断。
#### Token 影响
diff --git a/packages/subagent/tool-subagent-control/src/wait-agent.ts b/packages/subagent/tool-subagent-control/src/wait-agent.ts
index 823204fefe..10635b579b 100644
--- a/packages/subagent/tool-subagent-control/src/wait-agent.ts
+++ b/packages/subagent/tool-subagent-control/src/wait-agent.ts
@@ -29,7 +29,6 @@ function waitForInput(
const finish = (settle: () => void): void => {
clearTimeout(timer)
offInbox()
- offNotified()
offDisposed()
signal.removeEventListener('abort', onAbort)
lifetime.removeEventListener('abort', onUnload)
@@ -45,9 +44,6 @@ function waitForInput(
const offInbox = ctx.on('agent/inbox/inserted', ({ agent: recipient }) => {
if (recipient === agent) onInput()
})
- const offNotified = ctx.on('agent/wait-notified', ({ agent: recipient }) => {
- if (recipient === agent) finish(() => { resolve({ timedOut: false }) })
- })
const offDisposed = ctx.on('agent/disposed', ({ agent: disposed }) => {
if (disposed === agent) {
finish(() => {
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 aecca5f46f..d9c6337fd6 100644
--- a/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts
+++ b/packages/subagent/tool-subagent-control/tests/wait-agent.spec.ts
@@ -85,7 +85,6 @@ describe('wait_agent', () => {
agent.inbox.splice('next-turn', 0, 0, [input('queued')])
other.inject(input('someone else'))
emitAgentEvent(ctx, other, 'agent/status', { status: 'running' })
- emitAgentEvent(ctx, other, 'agent/wait-notified', {})
await vi.advanceTimersByTimeAsync(9_999)
expect(returned).toBe(false)
agent.inject(input('steering'))
@@ -95,22 +94,21 @@ describe('wait_agent', () => {
expect(vi.getTimerCount()).toBe(0)
})
- it('ends all current waits for an exact agent without retaining notifications', async () => {
- const { ctx, agent, other } = await setup()
+ it('ends concurrent waits on input without consuming it', async () => {
+ const { ctx, agent } = await setup()
vi.useFakeTimers()
- emitAgentEvent(ctx, agent, 'agent/wait-notified', {})
const first = await startWait(ctx, agent, { timeout_ms: 10_000 })
const second = await startWait(ctx, agent, { timeout_ms: 10_000 })
let returned = false
void first.result.then(() => { returned = true })
- emitAgentEvent(ctx, other, 'agent/wait-notified', {})
await vi.advanceTimersByTimeAsync(9_999)
expect(returned).toBe(false)
- emitAgentEvent(ctx, agent, 'agent/wait-notified', {})
+ agent.inject(input())
for (const result of await Promise.all([first.result, second.result])) {
expect(result).toMatchObject({ isError: false, value: { timedOut: false } })
}
- expect(agent.inbox.nextStep).toHaveLength(0)
+ expect(agent.inbox.nextStep).toHaveLength(1)
+ agent.inbox.clear()
expect(agent.inbox.nextTurn).toHaveLength(0)
expect(vi.getTimerCount()).toBe(0)
const later = await startWait(ctx, agent, { timeout_ms: 10_000 })
@@ -118,12 +116,12 @@ describe('wait_agent', () => {
expect(await later.result).toMatchObject({ isError: false, value: { timedOut: true } })
})
- it.each(['input', 'notification', 'timeout', 'cancel', 'dispose', 'unload'] as const)(
- 'removes its notification subscription after %s',
+ it.each(['input', 'timeout', 'cancel', 'dispose', 'unload'] as const)(
+ 'removes its subscriptions after %s',
async (settlement) => {
const { ctx, agent, handle, fiber } = await setup()
vi.useFakeTimers()
- const listeners = () => ['agent/inbox/inserted', 'agent/wait-notified', 'agent/disposed'].map(name =>
+ const listeners = () => ['agent/inbox/inserted', 'agent/disposed'].map(name =>
ctx.events.dispatch('emit', [agentCarrier(agent), name, { agent }]).length)
const baseline = listeners()
const controller = new AbortController()
@@ -131,7 +129,6 @@ describe('wait_agent', () => {
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
case 'timeout': await vi.advanceTimersByTimeAsync(10_000); break
case 'cancel': controller.abort(new Error('stop waiting')); break
case 'dispose': await handle.dispose(); break
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index a229036b00..8997f577b5 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -817,7 +817,7 @@ const SERVICE_ROLES: ServiceRole[] = [
title: 'Agent Teams coordination domain',
mode: 'core',
consumers: ['experimental-tool-agent-team'],
- note: 'Owns the shared task DAG and wait signals for a subagent tree; tool-agent-team contributes task controls.',
+ note: 'Owns the shared task DAG for a subagent tree; tool-agent-team contributes task controls.',
},
{
key: 'claudeCodeMods',
diff --git a/snapshots/session/headless.snapshot.ts b/snapshots/session/headless.snapshot.ts
index 61e59bb1d5..6b27e1d0e6 100644
--- a/snapshots/session/headless.snapshot.ts
+++ b/snapshots/session/headless.snapshot.ts
@@ -694,7 +694,7 @@ function verifyTeamTreeMessage(logs: readonly SessionLog[]): void {
}])
}
-/** Task commits wake an active wait after persistence without leaving inbox input or a retained signal. */
+/** Task commits leave inbox waits pending until timeout without inserting input. */
function verifyTeamTaskInbox(logs: readonly SessionLog[]): void {
expect(logs).toHaveLength(2)
const root = parseSessionLog(logs[0]!.content)
@@ -723,7 +723,7 @@ function verifyTeamTaskInbox(logs: readonly SessionLog[]): void {
expect(results.find(result => result.toolCallId === 'wait-past-change')?.content)
.toEqual([{ type: 'text', text: '{"timedOut":true}' }])
expect(results.find(result => result.toolCallId === 'wait-current-change')?.content)
- .toEqual([{ type: 'text', text: '{"timedOut":false}' }])
+ .toEqual([{ type: 'text', text: '{"timedOut":true}' }])
}
/** Preserve successful native, program, direct-child, and workflow execution in one flow. */
diff --git a/snapshots/session/team-targets/system-prompt.expected.md b/snapshots/session/team-targets/system-prompt.expected.md
index 167d7d5a59..786af22fad 100644
--- a/snapshots/session/team-targets/system-prompt.expected.md
+++ b/snapshots/session/team-targets/system-prompt.expected.md
@@ -9,7 +9,7 @@ A shared task board is available to every agent in your subagent tree. Task owne
Read tasks with team_task_list or team_task_get, claim with the current revision, perform the work, then complete the task. Dependencies determine readiness; write scopes describe expected edits and overlapping scopes are advisory, not locks. Task readiness does not start an agent.
-Task changes end current wait_agent calls in your subagent tree. Changes outside a wait are not retained as notifications, and no task-board messages are added to an inbox. After wait_agent returns, read the latest tasks before acting. Task changes do not start idle agents; use send_message when an agent needs to continue working.
+Task changes do not end wait_agent calls, add inbox messages, or start idle agents. Use send_message when an agent needs to act on a task update.
Check the [exit code: N] marker on every bash result; investigate failures before moving on.
diff --git a/snapshots/session/team-task-inbox/session.v4.jsonl b/snapshots/session/team-task-inbox/session.v4.jsonl
index def6f09a23..9c37af9b38 100644
--- a/snapshots/session/team-task-inbox/session.v4.jsonl
+++ b/snapshots/session/team-task-inbox/session.v4.jsonl
@@ -28,7 +28,7 @@
{"type":"assistant/message","data":{"turn":1,"step":3,"message":{"role":"assistant","content":[{"type":"tool-call","id":"wait-current-change","name":"wait_agent","arguments":"{\"timeout_ms\":10000}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:8}}"},"stream":[{"type":"chunk","time":1792389098084,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792389098084,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"wait-current-change","name":"wait_agent","arguments":"{\"timeout_ms\":10000}"}}},{"type":"chunk","time":1792389098084,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}
{"type":"tool/call","data":{"turn":1,"step":3,"callId":"wait-current-change","name":"wait_agent","arguments":"{\"timeout_ms\":10000}"}}
{"type":"team/task","data":{"version":2,"teamId":"{{session:1}}","task":{"id":"task-1","revision":3,"subject":"Check memory wake","description":"Complete this task while the root is waiting.","status":"completed","ownerId":"{{session:2}}","blockedBy":[],"writeScopes":[]}}}
-{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"wait-current-change"},"toolCallId":"wait-current-change","content":[{"type":"text","text":"{\"timedOut\":false}"}],"isError":false,"id":"{{message:9}}"}},"sourceEventSeqs":[27],"surfaceOp":"append"}
+{"type":"tool/result","data":{"turn":1,"step":3,"message":{"role":"tool","source":{"kind":"tool","callId":"wait-current-change"},"toolCallId":"wait-current-change","content":[{"type":"text","text":"{\"timedOut\":true}"}],"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":"read-board","name":"team_task_list","arguments":"{}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:10}}"},"stream":[{"type":"chunk","time":1792389098119,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}},{"type":"chunk","time":1792389098119,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"read-board","name":"team_task_list","arguments":"{}"}}},{"type":"chunk","time":1792389098119,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}]},"surfaceOp":"append"}