diff --git a/apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs b/apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs index bcbff9f11c..4eb464ea6a 100644 --- a/apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs +++ b/apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs @@ -195,7 +195,7 @@ class TeamFixtureAdapter extends LlmAdapter { const workflowChild = initial?.content[0]?.text === 'TEAM_WORKFLOW_CHILD' const context = options.messages.findLast(message => message.source?.kind === 'runtime-context') const section = name => context?.source.sections?.find(candidate => candidate.name === name)?.text - const role = section('team:identity') + const role = section('subagent:identity') const task = initial?.content.filter(block => block.type === 'text').map(block => block.text).join('\n') if (!workflowChild && (!tools.includes('subagent') || !tools.includes('workflow') || !tools.includes('team_task_create'))) { throw new Error('Team profile is missing delegation, workflow, or task tools') @@ -204,7 +204,7 @@ class TeamFixtureAdapter extends LlmAdapter { if (workflowChild) chunks = textChunks('Fresh workflow child complete.') else if (task?.startsWith('RESEARCHER_MARK:')) chunks = researcher(options.messages) else if (task?.startsWith('IMPLEMENTER_MARK:')) chunks = implementer(options.messages) - else if (role?.startsWith('Your name is "lead".')) chunks = lead(options.messages) + else if (role?.startsWith('You are the root agent named "lead".')) chunks = lead(options.messages) else throw new Error('Team runtime context is missing a recognized identity') for (const chunk of chunks) { options.signal?.throwIfAborted() diff --git a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts index 578bbbd460..d73a573242 100644 --- a/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts +++ b/apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts @@ -1191,8 +1191,8 @@ describe('headless stream-json snapshots', () => { if (typeof value !== 'string') throw new Error(`Teammate runtime context has no ${name}`) return value.replaceAll(log.header.id as string, '').replaceAll(leadId, '') } - expect(sections?.some(section => section.name === 'subagent:identity')).toBe(false) - return [text('team:identity')] + expect(sections?.some(section => section.name === 'team:identity')).toBe(false) + return [text('subagent:identity')] }).sort() const allRows = logs.flatMap(log => parseJsonl(log.content)) const messageSources = allRows.flatMap((row) => { @@ -1254,12 +1254,8 @@ describe('headless stream-json snapshots', () => { "checkedAgents": true, "deliveredMessages": 0, "identities": [ - "Your name is "lead/implementer". Your team lead is "lead". - Use list_agents({}) to find your teammates and their full names. - Use send_message({ name: "", message: "..." }) to message a teammate.", - "Your name is "lead/researcher". Your team lead is "lead". - Use list_agents({}) to find your teammates and their full names. - Use send_message({ name: "", message: "..." }) to message a teammate.", + "Your name is "lead/implementer". Your parent agent is "lead". Your root agent is "lead".", + "Your name is "lead/researcher". Your parent agent is "lead". Your root agent is "lead".", ], "queuedMessages": 0, "sessions": 4, diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index 84c0f093c0..a97b28dbc3 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: 4ea5ac9717e2f092 + zh: ca028322a663d5db /deepseek-harness-architecture/where-new-behavior-goes: en: ee73b1861378b099 zh: 9a182be609485f67 diff --git a/docs/architecture.md b/docs/architecture.md index 4a6d503ce7..3b854e5de4 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, a task board, and wait signals. Ordinary subagent controls own identity, messaging, discovery, interruption, and inbox waiting. ## Where new behavior goes diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 830cb2039b..2e8dc75987 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/config-catalog.md b/docs/config-catalog.md index f846de28fe..c97137a2b2 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -3404,7 +3404,7 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' - `inject`: `workingDirectory` · `sessionProjections` - `refs`: `Volatile` (`@deepseek-ai/cordis`) -- `source`: [`packages/subagent/subagent/src/index.ts:181`](../packages/subagent/subagent/src/index.ts) +- `source`: [`packages/subagent/subagent/src/index.ts:189`](../packages/subagent/subagent/src/index.ts) ```ts config-catalog /** Host configuration for continuable subagent capacity. */ diff --git a/docs/config-catalog.zh.md b/docs/config-catalog.zh.md index 5b97518fbc..8e838111cb 100644 --- a/docs/config-catalog.zh.md +++ b/docs/config-catalog.zh.md @@ -3406,7 +3406,7 @@ export type JournalMode = 'wal' | 'delete' | 'truncate' | 'persist' - `inject`: `workingDirectory` · `sessionProjections` - `refs`: `Volatile` (`@deepseek-ai/cordis`) -- `source`: [`packages/subagent/subagent/src/index.ts:181`](../packages/subagent/subagent/src/index.ts) +- `source`: [`packages/subagent/subagent/src/index.ts:189`](../packages/subagent/subagent/src/index.ts) ```ts config-catalog /** Host configuration for continuable subagent capacity. */ diff --git a/docs/event-producer-consumer.md b/docs/event-producer-consumer.md index f0737d02f0..7dd90547a1 100644 --- a/docs/event-producer-consumer.md +++ b/docs/event-producer-consumer.md @@ -70,10 +70,11 @@ This matrix shows which packages dispatch each harness-owned event and which pac | `session/flush` | `parallel` | [`packages/core/session/src/index.ts:84`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-telemetry`](../packages/session/session-telemetry) | | `settings/document-updated` | `emit` | [`packages/settings/settings/src/types.ts:75`](../packages/settings/settings/src/types.ts) | [`settings`](../packages/settings/settings) (`emit`) | `remotes` | | `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:296`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | - | -| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:161`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), `server` | -| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:137`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) | -| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:143`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) | -| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:153`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`tool-agent-team`](../packages/team/tool-agent-team) | +| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:169`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), `server` | +| `subagent/message-name` | `bail` | [`packages/subagent/subagent/src/index.ts:139`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`bail`) | [`subagent-names`](../packages/subagent/subagent-names) | +| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:145`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) | +| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:151`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) | +| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:161`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`tool-agent-team`](../packages/team/tool-agent-team) | | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | `browser-use-runtime`, [`working-directory`](../packages/session/working-directory) | | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - | | `team/mode-changed` | `parallel` | [`packages/team/agent-team/src/index.ts:34`](../packages/team/agent-team/src/index.ts) | [`agent-team`](../packages/team/agent-team) (`parallel`) | [`tool-agent-team`](../packages/team/tool-agent-team) | diff --git a/docs/event-producer-consumer.zh.md b/docs/event-producer-consumer.zh.md index 724a6917b7..18a99f3c5f 100644 --- a/docs/event-producer-consumer.zh.md +++ b/docs/event-producer-consumer.zh.md @@ -72,10 +72,11 @@ | `session/flush` | `parallel` | [`packages/core/session/src/index.ts:84`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`session-persistence-jsonl`](../packages/session/session-persistence-jsonl), [`session-telemetry`](../packages/session/session-telemetry) | | `settings/document-updated` | `emit` | [`packages/settings/settings/src/types.ts:75`](../packages/settings/settings/src/types.ts) | [`settings`](../packages/settings/settings) (`emit`) | `remotes` | | `skills/change` | `emit` | [`packages/skill/skill/src/index.ts:296`](../packages/skill/skill/src/index.ts) | [`skill`](../packages/skill/skill) (`events.dispatch`) | - | -| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:161`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), `server` | -| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:137`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) | -| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:143`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) | -| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:153`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`tool-agent-team`](../packages/team/tool-agent-team) | +| `subagent/end` | `emit` | [`packages/subagent/subagent/src/index.ts:169`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), `server` | +| `subagent/message-name` | `bail` | [`packages/subagent/subagent/src/index.ts:139`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`bail`) | [`subagent-names`](../packages/subagent/subagent-names) | +| `subagent/provider-added` | `emit` | [`packages/subagent/subagent/src/index.ts:145`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`emit`) | [`tool-subagent`](../packages/subagent/tool-subagent) | +| `subagent/provider-removed` | `emit` | [`packages/subagent/subagent/src/index.ts:151`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`tool-subagent`](../packages/subagent/tool-subagent) | +| `subagent/start` | `emit` | [`packages/subagent/subagent/src/index.ts:161`](../packages/subagent/subagent/src/index.ts) | [`subagent`](../packages/subagent/subagent) (`events.dispatch`) | [`hooks-claude-code`](../packages/experimental/hooks-claude-code), [`tool-agent-team`](../packages/team/tool-agent-team) | | `system-prompt/assemble` | `waterfall` | [`packages/core/system-prompt/src/index.ts:31`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`waterfall`) | `browser-use-runtime`, [`working-directory`](../packages/session/working-directory) | | `system-prompt/change` | `emit` | [`packages/core/system-prompt/src/index.ts:37`](../packages/core/system-prompt/src/index.ts) | [`system-prompt`](../packages/core/system-prompt) (`emit`) | - | | `team/mode-changed` | `parallel` | [`packages/team/agent-team/src/index.ts:34`](../packages/team/agent-team/src/index.ts) | [`agent-team`](../packages/team/agent-team) (`parallel`) | [`tool-agent-team`](../packages/team/tool-agent-team) | diff --git a/docs/subsystems/subagent.i18n.yaml b/docs/subsystems/subagent.i18n.yaml index e138f10cda..37817a62d1 100644 --- a/docs/subsystems/subagent.i18n.yaml +++ b/docs/subsystems/subagent.i18n.yaml @@ -3,8 +3,8 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/subsystems/subagent.md /subagents: - en: 6af28b3c7e92cf9f - zh: 210f2c490dc23d3c + en: 3d828004dee084c6 + zh: f71296b677691151 /subagents/capabilities-and-activation-requests: en: f09270fcd7bfa26a zh: 082664920b3c65e2 diff --git a/docs/subsystems/subagent.md b/docs/subsystems/subagent.md index 2a103e572e..014427f629 100644 --- a/docs/subsystems/subagent.md +++ b/docs/subsystems/subagent.md @@ -2,7 +2,7 @@ English | [中文](subagent.zh.md) -The unified activation API manages local conversations and external executions. Creation ancestry, runtime ownership, and communication belong to the subagent tree. [Subagent controls](../../packages/subagent/tool-subagent-control/README.md) own exact-caller inbox waiting; experimental Team tools add shared tasks, task policy, and Team identity. +The unified activation API manages local conversations and external executions. Creation ancestry, runtime ownership, and communication belong to the subagent tree. [Subagent controls](../../packages/subagent/tool-subagent-control/README.md) own agent identity and exact-caller inbox waiting; Team tools add shared tasks and task policy. ## Capabilities and activation requests @@ -637,6 +637,27 @@ Types: [Scoped](scope.md) Source: [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts) + + +#### `subagent/message-name` — bail + +Resolve visible message attribution from the authenticated sender. The naming provider returns its current name; without a provider the Session id is used. Lookup failures reject the send before inbox acceptance. + +```ts cordis-catalog +/** + * Resolve visible message attribution from the authenticated sender. + * The naming provider returns its current name; without a provider the Session id is used. + * Lookup failures reject the send before inbox acceptance. + * @param sender - exact live Agent whose message is being delivered. + * @mode bail + */ +'subagent/message-name'(this: Scoped, sender: Agent): string | undefined +``` + +Types: [Agent](core.md) · [Scoped](scope.md) + +Source: [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts) + #### `subagent/provider-added` — emit diff --git a/docs/subsystems/subagent.zh.md b/docs/subsystems/subagent.zh.md index 04f7961476..a480d799ed 100644 --- a/docs/subsystems/subagent.zh.md +++ b/docs/subsystems/subagent.zh.md @@ -2,7 +2,7 @@ [English](subagent.md) | 中文 -统一的 activation API 管理本地对话和外部执行。创建祖先关系、运行归属和通信由 subagent 树管理。[subagent 控制工具](../../packages/subagent/tool-subagent-control/README.zh.md)负责当前调用者的收件箱等待;实验性 Team 工具添加共享任务、任务策略和 Team 身份。 +统一的 activation API 管理本地对话和外部执行。创建祖先关系、运行归属和通信由 subagent 树管理。[subagent 控制工具](../../packages/subagent/tool-subagent-control/README.zh.md)负责 agent 身份和当前调用者的收件箱等待;Team 工具添加共享任务与任务策略。 ## 能力与 activation 请求 @@ -639,6 +639,27 @@ Types: [Scoped](scope.zh.md) Source: [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts) + + +#### `subagent/message-name` — bail + +Resolve visible message attribution from the authenticated sender. The naming provider returns its current name; without a provider the Session id is used. Lookup failures reject the send before inbox acceptance. + +```ts cordis-catalog +/** + * Resolve visible message attribution from the authenticated sender. + * The naming provider returns its current name; without a provider the Session id is used. + * Lookup failures reject the send before inbox acceptance. + * @param sender - exact live Agent whose message is being delivered. + * @mode bail + */ +'subagent/message-name'(this: Scoped, sender: Agent): string | undefined +``` + +Types: [Agent](core.zh.md) · [Scoped](scope.zh.md) + +Source: [`packages/subagent/subagent/src/index.ts`](../../packages/subagent/subagent/src/index.ts) + #### `subagent/provider-added` — emit diff --git a/docs/tool-catalog.i18n.yaml b/docs/tool-catalog.i18n.yaml index b06a90fc68..7a62c46a77 100644 --- a/docs/tool-catalog.i18n.yaml +++ b/docs/tool-catalog.i18n.yaml @@ -255,11 +255,11 @@ en: a622b15cc2598d51 zh: 48e1b05956d63e26 /tool-schema-catalog/deepseek-ai-dsh-tool-subagent-control/list-agents: - en: 7e5f44383f14d021 - zh: 8037d7b62ddb33b2 + en: 131207f18c7c8940 + zh: 0faac9de2795bf70 /tool-schema-catalog/deepseek-ai-dsh-tool-subagent-control/send-message: - en: aaccc4f194398018 - zh: d918c1346a87a4b1 + en: b53326fedeab850d + zh: ecb75b6ce31da009 /tool-schema-catalog/deepseek-ai-dsh-tool-subagent-control/wait-agent: en: 7426beca6d7f766d zh: 94803f041b819a66 diff --git a/docs/tool-catalog.md b/docs/tool-catalog.md index dc4809cc71..6533c5c1d3 100644 --- a/docs/tool-catalog.md +++ b/docs/tool-catalog.md @@ -2245,7 +2245,7 @@ Source: [`packages/subagent/tool-subagent-control/src/index.ts`](../packages/sub ### `list_agents` -List your entire subagent tree: the root agent and all reachable continuable descendants, including yourself. Rows carry ids, descriptions, and status; descendant rows carry parent ids and depth from the root. Rows also include available names alongside Session ids. You can message any other agent in the subagent tree listed as running or inactive. running means it is working; inactive means it is not currently working. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation. +List your entire subagent tree: the root agent and all reachable continuable descendants, including yourself. Rows carry ids, descriptions, and status; descendant rows carry parent ids and depth from the root. Rows also include available names alongside Session ids. You can message any other agent in the subagent tree listed as running or inactive. running means it is working; inactive means no turn is executing; it does not indicate task completion, success, or failure. While you remain active, you will be notified when a direct child finishes; there is no need to keep checking its status. Use send_message to continue the conversation. ```json { @@ -2258,7 +2258,7 @@ Source: [`packages/subagent/tool-subagent-control/src/list-agents.ts`](../packag ### `send_message` -Send a message to an agent. A working agent receives it at its next step; an idle agent starts a new turn with it. Returns delivery confirmation, not the agent's answer. +Send a message to an agent. A working agent receives it at its next step; an idle agent starts a new turn with it. Returns delivery confirmation. ```json { diff --git a/docs/tool-catalog.zh.md b/docs/tool-catalog.zh.md index 116064af4c..c56e872eff 100644 --- a/docs/tool-catalog.zh.md +++ b/docs/tool-catalog.zh.md @@ -2251,7 +2251,7 @@ subagent_oar 工具在为外部提供方配置的别名之间选择。本目录 ### `list_agents` -列出整棵 subagent 树:根代理和所有可达的可继续后代,包括调用方自身。行包含 id、描述和状态;后代行包含父级 id 和相对根的深度。行也会在 Session id 之外提供可用名称。可以向树中列为 running 或 inactive 的任意其他代理发送消息。running 表示正在工作;inactive 表示当前没有工作。自身仍在线时,直属子代理结束会收到通知,无需反复查询其状态。使用 send_message 继续对话。 +列出整棵 subagent 树:根代理和所有可达的可继续后代,包括调用方自身。行包含 id、描述和状态;后代行包含父级 id 和相对根的深度。行也会在 Session id 之外提供可用名称。可以向树中列为 running 或 inactive 的任意其他代理发送消息。running 表示正在工作;inactive 表示当前没有正在执行的轮次,不代表任务完成、成功或失败。自身仍在线时,直属子代理结束会收到通知,无需反复查询其状态。使用 send_message 继续对话。 ```json { @@ -2264,7 +2264,7 @@ subagent_oar 工具在为外部提供方配置的别名之间选择。本目录 ### `send_message` -向代理发消息。工作中的代理在下一步骤接收消息,空闲代理用消息启动新轮次。返回投递确认,不返回代理答案。 +向代理发消息。工作中的代理在下一步骤接收消息,空闲代理用消息启动新轮次。返回投递确认。 ```json { diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 52bee75a09..ee65a48eef 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -4706,6 +4706,14 @@ export const EVENT_API: readonly EventApiEntry[] = [ description: 'A published child settled. Scope-filtered dispatch uses the same subagent tree-root carrier as `subagent/start`, so the lifecycle pair reaches the same scoped audience.', parameters: [{ name: 'info', description: 'run identity, durable direct creator `parentSessionId`, and terminal outcome.' }], }, + { + name: 'subagent/message-name', + mode: 'bail', + signature: '\'subagent/message-name\'(this: Scoped, sender: Agent): string | undefined', + summary: 'Resolve visible message attribution from the authenticated sender.', + description: 'Resolve visible message attribution from the authenticated sender. The naming provider returns its current name; without a provider the Session id is used. Lookup failures reject the send before inbox acceptance.', + parameters: [{ name: 'sender', description: 'exact live Agent whose message is being delivered.' }], + }, { name: 'subagent/provider-added', mode: 'emit', diff --git a/packages/subagent/subagent-names/README.i18n.yaml b/packages/subagent/subagent-names/README.i18n.yaml index 4a020d64f8..20644b3406 100644 --- a/packages/subagent/subagent-names/README.i18n.yaml +++ b/packages/subagent/subagent-names/README.i18n.yaml @@ -36,8 +36,8 @@ en: 40f9481ebd3e077d zh: 118c16ec3fed9652 /deepseek-ai-dsh-subagent-names/model-experience/kv-cache-effect: - en: f1456be695a842d8 - zh: ba3a1608efd6100b + en: 59f538f4cefe36da + zh: 42c4c06e8c477b91 /deepseek-ai-dsh-subagent-names/known-limitations-and-deferred-work: en: 81988fdd0823cffa zh: 96bacb5621c1e874 diff --git a/packages/subagent/subagent-names/README.md b/packages/subagent/subagent-names/README.md index d9b2226218..b57415a8bf 100644 --- a/packages/subagent/subagent-names/README.md +++ b/packages/subagent/subagent-names/README.md @@ -89,7 +89,7 @@ Indirectly, through the [delegation tool](../tool-subagent/README.md) and [contr #### KV Cache effect -This plugin adds no prompt text or tool schema. Its consumers own any model-visible name text and its cache effect. +This plugin resolves message sender names through `subagent/message-name` and adds no prompt text or tool schema. Its consumers own any model-visible name text and its cache effect. ## Known Limitations and Deferred Work diff --git a/packages/subagent/subagent-names/README.zh.md b/packages/subagent/subagent-names/README.zh.md index 5320559861..7bc8ed2fd5 100644 --- a/packages/subagent/subagent-names/README.zh.md +++ b/packages/subagent/subagent-names/README.zh.md @@ -89,7 +89,7 @@ kind: "package-reference" #### KV Cache 影响 -此插件不添加提示词文本或工具 schema。消费方负责模型可见的名称文本及其缓存影响。 +此插件通过 `subagent/message-name` 自动解析消息发送者名称,不添加提示词文本或工具 schema。消费方负责模型可见的名称文本及其缓存影响。 ## 已知限制与待办工作 diff --git a/packages/subagent/subagent-names/src/index.ts b/packages/subagent/subagent-names/src/index.ts index 39b56f9099..8d2e19e7dd 100644 --- a/packages/subagent/subagent-names/src/index.ts +++ b/packages/subagent/subagent-names/src/index.ts @@ -48,6 +48,7 @@ export class SubagentNames extends Service { protected async [Service.init](): Promise { this.projection = await this.ctx.sessionProjections.register(subagentNamesProjection) + this.ctx.on('subagent/message-name', sender => this.nameOf(sender)) } /** diff --git a/packages/subagent/subagent-names/tests/names.spec.ts b/packages/subagent/subagent-names/tests/names.spec.ts index f2d98d99d0..9880553ee4 100644 --- a/packages/subagent/subagent-names/tests/names.spec.ts +++ b/packages/subagent/subagent-names/tests/names.spec.ts @@ -153,7 +153,31 @@ describe('root Session name mappings', () => { await child.dispose() }) - it('unregisters only its projection and leaves accepted execution with its owner', async () => { + it('resolves message attribution automatically and unregisters it with the naming provider', async () => { + const { ctx, parent, namesFiber, start } = await setup(['hang']) + const child = await start() + const sender = ctx.agents.get(child.childId)! + ctx.subagentNames.bind(parent, child.childId, 'lead/reviewer') + const steer = vi.spyOn(parent, 'steer') + const send = () => ctx.subagents.sendMessage(sender, parent.id, [{ type: 'text', text: 'finding' }], { + signal: new AbortController().signal, + }) + + await send() + expect(steer.mock.lastCall?.[0]).toMatchObject({ + content: [{ type: 'text', text: 'Agent lead/reviewer sent a message: ' }, { type: 'text', text: 'finding' }], + source: { kind: 'agent-message', senderSessionId: child.childId }, + }) + await namesFiber.dispose() + await send() + expect(steer.mock.lastCall?.[0].content[0]).toEqual({ type: 'text', text: `Agent ${child.childId} sent a message: ` }) + await ctx.plugin(SubagentNames) + await send() + expect(steer.mock.lastCall?.[0].content[0]).toEqual({ type: 'text', text: 'Agent lead/reviewer sent a message: ' }) + await child.dispose() + }) + + it('unregisters its projection and leaves accepted execution with its owner', async () => { const { ctx, parent, namesFiber, start } = await setup(['hang']) const names = ctx.subagentNames const reservation = names.reserve(parent, 'reviewer') diff --git a/packages/subagent/subagent/README.i18n.yaml b/packages/subagent/subagent/README.i18n.yaml index 3396441e3d..81b4cfa88b 100644 --- a/packages/subagent/subagent/README.i18n.yaml +++ b/packages/subagent/subagent/README.i18n.yaml @@ -33,8 +33,8 @@ en: a740e5e9bdd661d9 zh: 712a5458fc8b3615 /deepseek-ai-dsh-subagent/use-this-package/messaging-interrupting-and-discovering: - en: bf3175c2218a16dd - zh: 4be9271efe71ddb7 + en: 64184e394aa6faf8 + zh: 202b9cb9fc12d5b6 /deepseek-ai-dsh-subagent/use-this-package/failure-and-recovery: en: 0dc416ded19e304e zh: 4726e5fe5f188709 diff --git a/packages/subagent/subagent/README.md b/packages/subagent/subagent/README.md index 5c42e2444a..e8f7148a9c 100644 --- a/packages/subagent/subagent/README.md +++ b/packages/subagent/subagent/README.md @@ -68,7 +68,7 @@ In-process spawn accepts an independent `agentPreset`; fork and external backend ### Messaging, interrupting, and discovering -`sendMessage()` lets the live root and resident continuable subagents contact any other root or continuable member in the same tree by Session id. The target receives Steer input at its next step or starts an idle turn. Only the cold target resumes, under the subagent tree root; its creation ancestors stay offline and the message retains its actual sender. `rootOf()` resolves the caller’s live root. Agent interruption requires ancestor authority. Browser prompts and user interrupts address only the child Session; the Gateway checks the corresponding action on that Session before any side effect. Parent or global grants do not imply child access. Prompts require a live subagent tree root; the creator may be offline. User interrupts perform no restoration and accept absent targets as no-ops. They select Queue or Steer and admit image batches through the attachment store; incompatible target models reject images. `listDescendants()` discovers members recursively through parent-owned `subagentCatalog` projections. It preserves catalog order, reads each reachable Session once, skips external leaves, and reports unreadable branches without activating Agents. +`sendMessage()` lets the live root and resident continuable subagents contact any other root or continuable member in the same tree by Session id. The target receives Steer input at its next step or starts an idle turn. Only the cold target resumes, under the subagent tree root; its creation ancestors stay offline and the message retains its actual sender. The naming provider resolves visible message attribution automatically from the sender through `subagent/message-name`; without a provider, the Session id is used. Durable attribution always keeps `senderSessionId`. `rootOf()` resolves the caller’s live root. Agent interruption requires ancestor authority. Browser prompts and user interrupts address only the child Session; the Gateway checks the corresponding action on that Session before any side effect. Parent or global grants do not imply child access. Prompts require a live subagent tree root; the creator may be offline. User interrupts perform no restoration and accept absent targets as no-ops. They select Queue or Steer and admit image batches through the attachment store; incompatible target models reject images. `listDescendants()` discovers members recursively through parent-owned `subagentCatalog` projections. It preserves catalog order, reads each reachable Session once, skips external leaves, and reports unreadable branches without activating Agents. ### Failure and recovery diff --git a/packages/subagent/subagent/README.zh.md b/packages/subagent/subagent/README.zh.md index e9c54205c5..5bdf66e5af 100644 --- a/packages/subagent/subagent/README.zh.md +++ b/packages/subagent/subagent/README.zh.md @@ -68,7 +68,7 @@ kind: "package-reference" ### 消息、中断与发现 -`sendMessage()` 允许在线根和驻留的可继续子代理,按 Session id 联系同一棵树中的任意其他根或可继续成员。目标在下一步接收 Steer 输入,或在空闲时开始轮次。仅在 subagent 树根下恢复冷目标,其创建祖先保持离线;消息保留实际发送者。`rootOf()` 解析调用方的在线根。Agent 中断要求祖先权限。浏览器提示词和用户中断仅按子 Session 寻址;Gateway 在任何副作用之前检查该 Session 上对应操作的权限。父级或 global 授权不意味着子级访问权。提示词要求 subagent 树根在线;创建者可以离线。用户中断不恢复目标,目标缺席时接受为无操作。提示词选择 Queue 或 Steer,经附件存储接受图片批次;不兼容的目标模型拒绝图片。`listDescendants()` 通过父级拥有的 `subagentCatalog` 投影递归发现成员。它保留目录顺序,每个可达 Session 读取一次,跳过外部叶子,并报告不可读分支而不激活 Agent。 +`sendMessage()` 允许在线根和驻留的可继续子代理,按 Session id 联系同一棵树中的任意其他根或可继续成员。目标在下一步接收 Steer 输入,或在空闲时开始轮次。仅在 subagent 树根下恢复冷目标,其创建祖先保持离线;消息保留实际发送者。`rootOf()` 解析调用方的在线根。Agent 中断要求祖先权限。浏览器提示词和用户中断仅按子 Session 寻址;Gateway 在任何副作用之前检查该 Session 上对应操作的权限。父级或 global 授权不意味着子级访问权。提示词要求 subagent 树根在线;创建者可以离线。用户中断不恢复目标,目标缺席时接受为无操作。提示词选择 Queue 或 Steer,经附件存储接受图片批次;不兼容的目标模型拒绝图片。`listDescendants()` 通过父级拥有的 `subagentCatalog` 投影递归发现成员。它保留目录顺序,每个可达 Session 读取一次,跳过外部叶子,并报告不可读分支而不激活 Agent。 命名提供方通过 `subagent/message-name` 从发送者自动解析消息中显示的名称;没有提供方时使用 Session id。持久归属始终保留 `senderSessionId`。 ### 失败与恢复 diff --git a/packages/subagent/subagent/src/continuation-messages.ts b/packages/subagent/subagent/src/continuation-messages.ts index a06e5acd08..c688db050a 100644 --- a/packages/subagent/subagent/src/continuation-messages.ts +++ b/packages/subagent/subagent/src/continuation-messages.ts @@ -56,15 +56,17 @@ function agentMessageSource(sender: Agent): AgentMessageSource { * Build the model-visible and durable representation of one Agent message. * @param sender - exact live Agent that authored the message. * @param content - model-visible message blocks supplied by the sender. + * @param senderName - resolved sender name or Session id for the visible attribution. * @returns the durable user-message representation delivered to the recipient. */ export function createAgentMessage( sender: Agent, content: ContentBlock[], + senderName: string, ): ReturnType { return createUserMessage({ content: [ - { type: 'text' as const, text: `Agent ${sender.id} sent a message: ` }, + { type: 'text' as const, text: `Agent ${senderName} sent a message: ` }, ...content, ], source: agentMessageSource(sender), diff --git a/packages/subagent/subagent/src/index.ts b/packages/subagent/subagent/src/index.ts index d79e12bb1d..e9442387cb 100644 --- a/packages/subagent/subagent/src/index.ts +++ b/packages/subagent/subagent/src/index.ts @@ -129,6 +129,14 @@ declare module '@deepseek-ai/cordis' { } interface Events { + /** + * Resolve visible message attribution from the authenticated sender. + * The naming provider returns its current name; without a provider the Session id is used. + * Lookup failures reject the send before inbox acceptance. + * @param sender - exact live Agent whose message is being delivered. + * @mode bail + */ + 'subagent/message-name'(this: Scoped, sender: Agent): string | undefined /** * A provider became resolvable in the registry. * @param provider - the registered provider. @@ -216,6 +224,7 @@ export class SubagentRuntime extends TypertRemoteService { const manager = new SubagentManager(childCtx, { identityOf: session => identityProjection.stateOf(session).identity, emitLifecycle: this.emitLifecycle, + senderNameOf: sender => this.ctx.bail(scopeTarget(this, sender), 'subagent/message-name', sender) ?? sender.id, }, () => this.config.maxActiveSubagents.get()) this.manager = manager childCtx.effect(() => () => { diff --git a/packages/subagent/subagent/src/manager.ts b/packages/subagent/subagent/src/manager.ts index dbd9d3a382..1caea1f53f 100644 --- a/packages/subagent/subagent/src/manager.ts +++ b/packages/subagent/subagent/src/manager.ts @@ -63,6 +63,8 @@ interface SubagentHost { identityOf(session: Session): SubagentIdentityProjection | undefined /** Publish lifecycle events through the owning service's scoped dispatcher. */ readonly emitLifecycle: LifecycleEmitter + /** Resolve display attribution from the live sender through the naming provider. */ + senderNameOf(sender: Agent): string } /** Inputs shared by fresh and resumed Activation materialization. */ @@ -485,7 +487,7 @@ export class SubagentManager { const root = this.rootOf(sender) if (sender.id === targetId) throw new SubagentError('send_message requires another agent in the subagent tree', 'UNAUTHORIZED') this.assertAdmitting(sender) - const message = createAgentMessage(sender, content) + const message = createAgentMessage(sender, content, this.host.senderNameOf(sender)) const activation = this.resident.get(sender.id) return this.deliverToMember(root, targetId, [...message.content], { signal: options.signal, delivery: 'steer', source: message.source, diff --git a/packages/subagent/subagent/tests/continuation.spec.ts b/packages/subagent/subagent/tests/continuation.spec.ts index 8a9698210e..762a631293 100644 --- a/packages/subagent/subagent/tests/continuation.spec.ts +++ b/packages/subagent/subagent/tests/continuation.spec.ts @@ -3068,10 +3068,10 @@ describe('continuable settlement delivery', () => { let childTurns = 0 const respond = (options: GenerateOptions): StreamChunk[] => { const history = JSON.stringify(options.messages) - if (!history.includes('Your parent agent id is')) return textResponse('parent ack') + if (!history.includes('Your parent agent is')) return textResponse('parent ack') childTurns += 1 if (childTurns > 1) return textResponse('the answer') - const parentId = /Your parent agent id is \\"([^\\]+)\\"/.exec(history)![1]! + const parentId = /Your parent agent is \\"([^\\]+)\\"/.exec(history)![1]! const calls = order.map(name => name === 'noop' ? { id: ToolCallId('n1'), name, arguments: '{}' } : { id: ToolCallId('s1'), name, arguments: JSON.stringify({ name: parentId, message: 'interim' }) }) diff --git a/packages/subagent/tool-subagent-control/README.i18n.yaml b/packages/subagent/tool-subagent-control/README.i18n.yaml index 92aef13e94..bfcb9160a5 100644 --- a/packages/subagent/tool-subagent-control/README.i18n.yaml +++ b/packages/subagent/tool-subagent-control/README.i18n.yaml @@ -39,8 +39,8 @@ en: b3cadf07711bf1ba zh: 3cd7c3147e64c1c1 /deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/delivery-and-signal-ownership: - en: 65137f5fe14ebb7f - zh: ce00e4f32709246a + en: 186b5c0fad1a176b + zh: 6c3a104e7f4ba0fe /deepseek-ai-dsh-tool-subagent-control/understand-the-implementation/listing-projection: en: ee3136b09f0c9ef2 zh: 5cbe189bb857fdc7 @@ -65,18 +65,18 @@ /deepseek-ai-dsh-tool-subagent-control/model-experience/tool-schema/kv-cache-effect: en: 53a81243f3acd543 zh: 472cf727e5180501 -/deepseek-ai-dsh-tool-subagent-control/model-experience/communication-context: - en: 95d76d1aba983086 - zh: b645a3837ec17ef4 -/deepseek-ai-dsh-tool-subagent-control/model-experience/communication-context/what-the-model-sees: - en: adb5b38af8f1cfb1 - zh: 2a7f2779ed5e8a9f -/deepseek-ai-dsh-tool-subagent-control/model-experience/communication-context/token-effect: - en: 24d9c15986cb4d3d - zh: 6fb66cf84f8bc812 -/deepseek-ai-dsh-tool-subagent-control/model-experience/communication-context/kv-cache-effect: - en: be04fa8fa539cad0 - zh: 797fd37af7d32f90 +/deepseek-ai-dsh-tool-subagent-control/model-experience/identity-and-communication-context: + en: fc88c8ffb8ebc083 + zh: 0baf2e5df37d813e +/deepseek-ai-dsh-tool-subagent-control/model-experience/identity-and-communication-context/what-the-model-sees: + en: 5329c2fc2adafcde + zh: 74a20f232bfc31ce +/deepseek-ai-dsh-tool-subagent-control/model-experience/identity-and-communication-context/token-effect: + en: a09538db53078cc6 + zh: 1bc3c3f585acd312 +/deepseek-ai-dsh-tool-subagent-control/model-experience/identity-and-communication-context/kv-cache-effect: + en: 8126fbb896622f24 + zh: 9e057beb0a1f9cee /deepseek-ai-dsh-tool-subagent-control/model-experience/interrupt-result: en: d64e936e83e89a2b zh: b9905c33af6cca45 @@ -105,8 +105,8 @@ en: 094e55d258f2ec2e zh: f364bc9046530bff /deepseek-ai-dsh-tool-subagent-control/model-experience/listing-result/what-the-model-sees: - en: aeef4da057b78b1f - zh: f500ca4ceab1f87a + en: 158de3d51543f110 + zh: 374e68c0afb8aecb /deepseek-ai-dsh-tool-subagent-control/model-experience/listing-result/token-effect: en: 027aaeb808238388 zh: 5b974abc429d9e23 diff --git a/packages/subagent/tool-subagent-control/README.md b/packages/subagent/tool-subagent-control/README.md index 6cd953a881..28afd11dd4 100644 --- a/packages/subagent/tool-subagent-control/README.md +++ b/packages/subagent/tool-subagent-control/README.md @@ -76,7 +76,7 @@ This section explains what the tools delegate to the subagent service; the obser ### Delivery and signal ownership -The execution signal owns work only until inbox acceptance. Accepted messages cannot be cancelled through this tool. Every message is framed as `Agent sent a message:` and recorded with `{ kind: 'agent-message', form: 'relay', senderSessionId: sender.id }`. Tree membership authorizes communication; the subagent tree root owns runtime instances and capacity. +The execution signal owns work only until inbox acceptance. Accepted messages cannot be cancelled through this tool. Every message is framed as `Agent sent a message:` and recorded with `{ kind: 'agent-message', form: 'relay', senderSessionId: sender.id }`. Tree membership authorizes communication; the subagent tree root owns runtime instances and capacity. ### Listing projection @@ -122,19 +122,19 @@ Fixed schema cost per request, independent of the number or descriptions of chil The schema prefix stays stable while these tools are available. A parent and its continuable children, including fork children, inherit the same definitions. -### Communication context +### Identity and communication context #### What the model sees -The logged `subagent:communication` runtime context tells delegated children their direct creator’s JSON-encoded Session id and how to send findings that change the parent’s next action. Named controls also accept this id. Sending a message does not end the turn. Root Agents and ordinary Session forks receive no communication context; delegated children receive it only when the standard messaging definition is admitted in the current scope and step. Automatic result delivery remains owned by the subagent service. +Required `subagent:identity` runtime context identifies the root as `lead`, and each delegated child by its full name, direct parent, and root. It is independent of Team mode and tool filtering. Before a name is bound, it explicitly uses the Session id as the agent address; a missing live root is reported without preventing independent work. Required `subagent:communication` context explains discovery and messaging only when their standard tools are admitted. Delegated children also receive guidance to message their parent when a finding changes its next action. Automatic result delivery remains owned by the subagent service. #### Token effect -One short context entry per delegated child, independent of tree size. +Two short context entries per agent, independent of tree size. #### KV Cache effect -Communication guidance is retained as runtime context after inherited history, leaving the shared system prompt and tool definitions unchanged between parent and child. The parent id comes from the child Session header; prompt assembly does not wait for a name binding or read the name map. +Identity and communication guidance follow inherited history as logged runtime context, leaving the shared system prompt and tool definitions unchanged. Names come from the root mapping; a later binding updates the next request. Required context survives optional-context suppression and is restored after compaction. Team mode changes neither entry. ### Interrupt result @@ -168,7 +168,7 @@ Append-only; newly visible content follows the reusable request prefix and does #### What the model sees -The first row is ` [] — lead: root agent`. Each continuable descendant follows as ` [] parent= depth= —