Sfoglia il codice sorgente

fix(agent-team): include identity only in the initial task

Dudu-0223 2 settimane fa
parent
commit
4491da1374

+ 2 - 2
.agents/notes/implemented/feature/2026-08-05-agent-teams.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-08-05-agent-teams.md
-2026-08-05-agent-teams.md: 3aefd08609474bdcdc6113f0383ee20e7e38d354
-2026-08-05-agent-teams.zh.md: 70eb7d58737778ced2cd08a8d163a1f011c1dfa1
+2026-08-05-agent-teams.md: 8bf0547fb466089a97255cc42712e017694b929e
+2026-08-05-agent-teams.zh.md: 287d3bba0faa0fd81695121cd8cb4679a19d85e5

+ 1 - 1
.agents/notes/implemented/feature/2026-08-05-agent-teams.md

@@ -22,7 +22,7 @@ The Lead must wait for required work before its final answer. Process teardown r
 
 ## Team identity
 
-The tool plugin appends a durable user-role `<system-reminder>` before a teammate's first task, stating only `You are teammate "<name>".`. Shared system policy and all tool schemas stay uniform across members; execution owns role restrictions. Team tools resolve the caller's Team and accept member names, so the model needs no Team id. Ordinary Leads receive no identity message. An ordinary fork with an inherited teammate reminder receives `You are the Team Lead.`; the inherited log identifies this case even when a summary shadows the reminder. The pre-step listener reuses the latest retained reminder and restores it after compaction removes it, including after cold recovery. It preserves rejected or emptied steps and does not depend on optional runtime-context assembly. Putting identity in the system prompt changes the prefix before inherited history; trusting a one-time reminder alone leaves its survival to summarization. Existing system-embedded identities may require a one-time prompt reconciliation; retained event generations are unchanged.
+The `spawn_teammate` tool prefixes the initial task with a user-role `<system-reminder>` stating `You are teammate "<name>".`. Identity and task enter the same durable inbox message. Shared system policy and all tool schemas stay uniform across members; execution owns role restrictions. Team tools resolve the caller’s Team and accept member names, so the model needs no Team id. Identity follows ordinary history through cold recovery and compaction; the plugin does not inspect reminder retention or add replacement messages. Forks inherit the recorded text without a Lead identity correction. Putting identity in the system prompt changes the prefix before inherited history; keeping it in the initial task preserves that prefix without per-step identity bookkeeping. Existing system-embedded identities may require a one-time prompt reconciliation; retained event generations are unchanged.
 
 ## Provisioning and recovery
 

+ 1 - 1
.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md

@@ -22,7 +22,7 @@ Lead 必须等待所需工作后才能给出最终答案。进程 teardown 仍
 
 ## Team identity
 
-工具插件在 teammate 的首次任务前追加持久化 user-role `<system-reminder>`,只声明 `You are teammate "<name>".`。共享 system 策略和全部工具 schema 在成员间保持一致;执行时检查角色权限。Team 工具根据调用者确定 Team,并接受成员名字,因此模型不需要 Team id。普通 Lead 不接收身份消息。继承 teammate 提醒的普通 fork 会收到 `You are the Team Lead.`;即使摘要遮蔽了提醒,继承日志仍可识别这种情况。pre-step 监听器复用最新保留的提醒,并在压缩移除提醒后恢复它,冷恢复后也同样处理。它保留被拒绝或清空的步骤,不依赖可选的运行时上下文组装。把身份放进 system prompt 会在继承历史之前改变前缀;只信赖一次性提醒则让摘要决定身份是否保留。已有的 system 内嵌身份可能需要一次提示词协调;保留的事件格式代际保持不变。
+工具 `spawn_teammate` 在初始任务前加上 user-role `<system-reminder>`,声明 `You are teammate "<name>".`。身份和任务进入同一条持久化收件箱消息。共享 system 策略和全部工具 schema 在成员间保持一致;执行时检查角色权限。Team 工具根据调用者确定 Team,并接受成员名字,因此模型不需要 Team id。身份随普通历史经历冷恢复和压缩;插件不检查提醒是否保留,也不添加替代消息。fork 继承已记录文本,不补发 Lead 身份修正。把身份放进 system prompt 会在继承历史之前改变前缀;把它留在初始任务中既保留此前缀,也无需每步维护身份提醒。已有的 system 内嵌身份可能需要一次提示词协调;保留的事件格式代际保持不变。
 
 ## Provisioning and recovery
 

+ 2 - 4
apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs

@@ -174,10 +174,8 @@ function lead(messages) {
 
 class TeamFixtureAdapter extends LlmAdapter {
   async * stream(options) {
-    const snapshot = options.messages.findLast(message => message.role === 'user'
-      && message.source.kind === 'plugin' && message.source.form === 'snapshot'
-      && message.source.sections.some(section => section.name === 'team:identity'))
-    const identity = snapshot?.source.sections.find(section => section.name === 'team:identity')?.text
+    const initial = options.messages.findLast(message => message.role === 'user' && message.source.kind === 'user')
+    const identity = initial?.content[0]?.text?.trimEnd()
     const chunks = identity === '<system-reminder>\nYou are teammate "researcher".\n</system-reminder>'
       ? researcher(options.messages)
       : identity === '<system-reminder>\nYou are teammate "implementer".\n</system-reminder>'

+ 9 - 14
apps/cli/tests/profiles/headless/tests/headless.expected.e2e.ts

@@ -661,20 +661,15 @@ describe('headless stream-json snapshots', () => {
           return (JSON.parse(data.arguments) as JsonObject).action === 'complete'
         })
         const identityReminders = logs.flatMap((log) => {
-          const identities = parseJsonl(log.content).flatMap((row) => {
-            if (row.type !== 'user/message') return []
-            const source = (row.data as JsonObject).source as JsonObject
-            if (source.kind !== 'plugin' || source.form !== 'snapshot' || !Array.isArray(source.sections)) return []
-            const sections: unknown[] = source.sections
-            return sections.filter(section => (section as JsonObject).name === 'team:identity')
-          })
-          const reminder = (identities.at(-1) as JsonObject | undefined)?.text
-          if (log === parent) {
-            expect(identities).toEqual([])
-            return []
-          }
-          if (typeof reminder !== 'string') throw new Error('Teammate Session has no identity reminder')
-          return [reminder]
+          if (log === parent) return []
+          const initial = parseJsonl(log.content).find(row => row.type === 'user/message'
+            && ((row.data as JsonObject).source as JsonObject).kind === 'user')
+          if (initial === undefined) throw new Error('Teammate Session has no initial task')
+          const content = (initial.data as JsonObject).content as JsonObject[]
+          expect(content).toHaveLength(2)
+          const identity = content[0]!.text
+          if (typeof identity !== 'string') throw new Error('Teammate initial task has no identity text')
+          return [identity.trimEnd()]
         }).sort()
         projection = {
           sessions: logs.length,

+ 2 - 2
docs/config-catalog.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/config-catalog.md
-config-catalog.md: 61785137a139d354039a9e639097b9c064776c01
-config-catalog.zh.md: 87f2de17d27c2e365de9bdb4fbdc22251b94718a
+config-catalog.md: 79878fc8daaa5ba3e64d4ffb27d279ff172b6749
+config-catalog.zh.md: f254241618d280acaf738cc011ba1f7c5a2b6b21

+ 1 - 1
docs/config-catalog.md

@@ -733,7 +733,7 @@ export interface Config {
 }
 ```
 
-Source: [`packages/experimental/tool-agent-team/src/index.ts:19`](../packages/experimental/tool-agent-team/src/index.ts)
+Source: [`packages/experimental/tool-agent-team/src/index.ts:17`](../packages/experimental/tool-agent-team/src/index.ts)
 
 <a id="deepseek-aidsh-file-reference-local"></a>
 

+ 1 - 1
docs/config-catalog.zh.md

@@ -735,7 +735,7 @@ export interface Config {
 }
 ```
 
-来源:[`packages/experimental/tool-agent-team/src/index.ts:19`](../packages/experimental/tool-agent-team/src/index.ts)
+来源:[`packages/experimental/tool-agent-team/src/index.ts:17`](../packages/experimental/tool-agent-team/src/index.ts)
 
 <a id="deepseek-aidsh-file-reference-local"></a>
 

+ 2 - 2
docs/event-producer-consumer.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/event-producer-consumer.md
-event-producer-consumer.md: 6ccebebdab01048d08b7e1f8e73e2c685448d028
-event-producer-consumer.zh.md: 72e0ffd3b72705d971265fe8062f73ec8f54563b
+event-producer-consumer.md: 323e9f3eea8703c46f3be082db6ab67e9339084b
+event-producer-consumer.zh.md: 1f34a6f3d53bfd6ddf6c19adb0a3c7cb7043c6ab

+ 1 - 1
docs/event-producer-consumer.md

@@ -16,7 +16,7 @@ This matrix shows which packages dispatch each harness-owned event and which pac
 | `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:296`](../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) |
 | `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:304`](../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) |
 | `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:285`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) |
-| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:330`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/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-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), `tool-agent-team`, [`tool-cordis`](../packages/extensions/tool-cordis), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent) |
+| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:330`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/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-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-cordis`](../packages/extensions/tool-cordis), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent) |
 | `agent/request` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:347`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`webhook`](../packages/webhook/webhook) |
 | `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`llm-retry`](../packages/llm/llm-retry) |
 | `agent/session-start` | `emit` | [`packages/core/agent/src/runtime-types.ts:316`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | `agent-team`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |

+ 1 - 1
docs/event-producer-consumer.zh.md

@@ -18,7 +18,7 @@
 | `agent/inbox/claimed` | `emit` | [`packages/core/agent/src/runtime-types.ts:296`](../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) |
 | `agent/inbox/discarded` | `emit` | [`packages/core/agent/src/runtime-types.ts:304`](../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) |
 | `agent/inbox/inserted` | `emit` | [`packages/core/agent/src/runtime-types.ts:285`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`goal-round-driver`](../packages/goal/goal-round-driver) |
-| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:330`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/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-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), `tool-agent-team`, [`tool-cordis`](../packages/extensions/tool-cordis), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent) |
+| `agent/pre-step` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:330`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`agent-instructions`](../packages/context/agent-instructions), [`compaction-basic`](../packages/compaction/compaction-basic), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/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-reference`](../packages/context/session-reference), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver), [`time-context`](../packages/context/time-context), [`tmux-context`](../packages/context/tmux-context), [`tool-cordis`](../packages/extensions/tool-cordis), [`tool-skill`](../packages/skill/tool-skill), [`tool-subagent`](../packages/subagent/tool-subagent) |
 | `agent/request` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:347`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`agent`](../packages/core/agent), [`webhook`](../packages/webhook/webhook) |
 | `agent/request-error` | `waterfall` | [`packages/core/agent/src/runtime-types.ts:363`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compaction-basic`](../packages/compaction/compaction-basic), [`llm-retry`](../packages/llm/llm-retry) |
 | `agent/session-start` | `emit` | [`packages/core/agent/src/runtime-types.ts:316`](../packages/core/agent/src/runtime-types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emitAgentEvent`) | `agent-team`, [`goal`](../packages/goal/goal), [`goal-round-driver`](../packages/goal/goal-round-driver), [`hooks-claude-code`](../packages/hooks/hooks-claude-code), [`hooks-codex`](../packages/hooks/hooks-codex) |

+ 2 - 2
docs/module-graph.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write docs/module-graph.md
-module-graph.md: 18d1635c3f62ae676ca750078e52f2f078a959c0
-module-graph.zh.md: ef62897d97f9e4f74588a362b0917da790ffcfe5
+module-graph.md: cbaa8cb04a7ec2406b1f6e7f2c1dbf0a8dff0527
+module-graph.zh.md: 148b2730fccc1740d1c2f24d766f2aa7213093cf

+ 1 - 2
docs/module-graph.md

@@ -1142,7 +1142,6 @@ flowchart TD
   pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
   pkg_experimental_tool_agent_team --> pkg_agent
   pkg_experimental_tool_agent_team --> pkg_experimental_agent_team
-  pkg_experimental_tool_agent_team --> pkg_llm
   pkg_experimental_tool_agent_team --> pkg_session
   pkg_experimental_tool_agent_team --> pkg_system_prompt
   pkg_experimental_tool_agent_team --> pkg_tools
@@ -1443,7 +1442,7 @@ flowchart TD
 | [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
 | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
 | [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
-| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
+| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
 | [`sdk-client`](../packages/sdk/client) | `sdk` | [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) |
 | [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
 | [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |

+ 1 - 2
docs/module-graph.zh.md

@@ -1144,7 +1144,6 @@ flowchart TD
   pkg_experimental_client_ui_agent_team --> pkg_typert_protocol
   pkg_experimental_tool_agent_team --> pkg_agent
   pkg_experimental_tool_agent_team --> pkg_experimental_agent_team
-  pkg_experimental_tool_agent_team --> pkg_llm
   pkg_experimental_tool_agent_team --> pkg_session
   pkg_experimental_tool_agent_team --> pkg_system_prompt
   pkg_experimental_tool_agent_team --> pkg_tools
@@ -1445,7 +1444,7 @@ flowchart TD
 | [`subagent-fork-in-process`](../packages/subagent/subagent-fork-in-process) | `subagent` | [`agent`](../packages/core/agent), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
 | [`subagent-spawn-in-process`](../packages/subagent/subagent-spawn-in-process) | `subagent` | [`subagent`](../packages/subagent/subagent), [`subagent-in-process-driver`](../packages/subagent/subagent-in-process-driver) |
 | [`experimental-client-ui-agent-team`](../packages/experimental/client-ui-agent-team) | `experimental` | [`api-remotes`](../packages/api/remotes), [`api-session-controller`](../packages/api/session-controller), [`client-locale`](../packages/client/locale), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-slots`](../packages/client/ui-slots), [`experimental-agent-team`](../packages/experimental/agent-team), [`session`](../packages/core/session), [`typert-protocol`](../packages/typert/protocol) |
-| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
+| [`experimental-tool-agent-team`](../packages/experimental/tool-agent-team) | `experimental` | [`agent`](../packages/core/agent), [`experimental-agent-team`](../packages/experimental/agent-team), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
 | [`sdk-client`](../packages/sdk/client) | `sdk` | [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session) |
 | [`sdk-jsonrpc-server`](../packages/sdk/server) | `sdk` | [`agent`](../packages/core/agent), [`attachment`](../packages/attachment/attachment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
 | [`client-test-runtime`](../packages/test-support/client-runtime) | `test-support` | [`api-session-controller`](../packages/api/session-controller), [`api-workspace-controller`](../packages/api/workspace-controller), [`attachment`](../packages/attachment/attachment), [`client-connection`](../packages/client/connection), [`client-store`](../packages/client/store), [`client-ui-chat`](../packages/client/ui-chat), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-renderer`](../packages/client/ui-renderer), [`client-ui-session`](../packages/client/ui-session), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`typert-protocol`](../packages/typert/protocol) |

+ 2 - 2
packages/experimental/tool-agent-team/README.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write packages/experimental/tool-agent-team/README.md
-README.md: 80023e35d37b1efc2127486556819b9783adecde
-README.zh.md: 6650baf8c28998ab219faf217cee2b11b40a79c3
+README.md: de29238bb057021a2199c053e9689aa67a5e4370
+README.zh.md: c7ef5ab1a92186c225a8f011f4c9d091b60baabf

+ 5 - 5
packages/experimental/tool-agent-team/README.md

@@ -25,7 +25,7 @@ This package lets the model create named teammates, send them messages, inspect
 <a id="use-this-package"></a>
 ## Use this package
 
-Add this package on top of `@deepseek-ai/dsh-experimental-agent-team` when the model should run a team through tools. Once mounted, every team member — the Lead and each teammate — gets the same nine tools plus a policy paragraph that states its own role and name.
+Add this package on top of `@deepseek-ai/dsh-experimental-agent-team` when the model should run a team through tools. Once mounted, every team member — the Lead and each teammate — gets the same nine tools plus the same coordination policy. `spawn_teammate` prefixes the initial task with the teammate’s role and name.
 
 ### When to choose it
 
@@ -96,7 +96,7 @@ The [Agent Teams Agent Note](../../../.agents/notes/implemented/feature/2026-08-
 
 ### Policy and tools
 
-One `team:policy` section on the member scope teaches each member its role and the coordination rules; the fixed text and the nine tool registrations are declared in [`src/index.ts`](src/index.ts). The nine tool schemas appear only in Team member scopes, so non-Team subagents keep the default catalog. Scoped registrations with the same names as the legacy global continuable-subagent controls shadow those globals for team members only.
+One `team:policy` section on the member scope states the shared coordination rules; the fixed text and the nine tool registrations are declared in [`src/index.ts`](src/index.ts). The nine tool schemas appear only in Team member scopes, so non-Team subagents keep the default catalog. Scoped registrations with the same names as the legacy global continuable-subagent controls shadow those globals for team members only.
 
 ### Scoped registration and teardown
 
@@ -125,15 +125,15 @@ Read these pages when the package-level contract is not enough. They move from t
 
 #### What the model sees
 
-One shared system policy states the explicit-delegation requirement, shared-cwd behavior, filesystem stale-version recovery, Bash/formatter/codegen risk, task and write-scope coordination, Steer delivery, the no-retry mailbox rule, and the Lead's duty to wait before answering. All nine Team schemas are identical for Leads and teammates; execution enforces Lead-only operations. Before a teammate's first task, a durable user message states `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`. Leads receive no identity reminder unless an ordinary fork inherits one from a teammate; that fork receives `You are the Team Lead.` instead. Identity reminders contain no Team id and work when runtime context is disabled.
+One shared system policy states the explicit-delegation requirement, shared-cwd behavior, filesystem stale-version recovery, Bash/formatter/codegen risk, task and write-scope coordination, Steer delivery, the no-retry mailbox rule, and the Lead's duty to wait before answering. All nine Team schemas are identical for Leads and teammates; execution enforces Lead-only operations. `spawn_teammate` prefixes its initial user message with `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`, followed by a blank line and the task. The prefix contains no Team id and works when runtime context is disabled. Forks inherit history without an additional Lead identity message.
 
 #### Token effect
 
-Fixed policy and schema cost on every Team member request. A retained identity reminder is reused across steps and cold recovery; if compaction removes it, the plugin appends it before the next admitted step. Tool calls add compact JSON roster, task, wait, or receipt results. Peer content is retained by the Team domain in the target's history.
+Fixed policy and schema cost on every Team member request. The initial identity text follows ordinary history through later steps, cold recovery, and compaction; the plugin neither scans for it nor reinserts it. Tool calls add compact JSON roster, task, wait, or receipt results. Peer content is retained by the Team domain in the target's history.
 
 #### KV Cache effect
 
-With the same provider/model, shared system policy, and tool schemas, a fork retains the parent request prefix and appends its reminder and task. Tool results and peer messages append after the reusable request prefix. Sessions recorded with identity inside the system prompt can change that prefix on their first request under this layout; actual provider cache hits remain best-effort.
+With the same provider/model, shared system policy, and tool schemas, a fork retains the parent request prefix and appends the initial task with its identity prefix. Tool results and peer messages append after the reusable request prefix. Sessions recorded with identity inside the system prompt can change that prefix on their first request under this layout; actual provider cache hits remain best-effort.
 
 ## Known Limitations and Deferred Work
 

+ 5 - 5
packages/experimental/tool-agent-team/README.zh.md

@@ -25,7 +25,7 @@ kind: "package-reference"
 <a id="use-this-package"></a>
 ## 使用本包
 
-当模型应该通过工具运行一支团队时,在 `@deepseek-ai/dsh-experimental-agent-team` 之上挂载本包。挂载后,每个团队成员——Lead 与每个 teammate——都会获得相同的九个工具,外加一段说明自身角色与名字的策略段落。
+当模型应该通过工具运行一支团队时,在 `@deepseek-ai/dsh-experimental-agent-team` 之上挂载本包。挂载后,每个团队成员——Lead 与每个 teammate——都会获得相同的九个工具,以及相同的协作策略。`spawn_teammate` 在初始任务前加上 teammate 的角色和名字。
 
 ### 何时选择
 
@@ -96,7 +96,7 @@ kind: "package-reference"
 
 ### 策略与工具
 
-member scope 上的一个 `team:policy` 段落教每个成员自己的角色与协作规则;固定文本与九个工具注册都声明在 [`src/index.ts`](src/index.ts)。九个工具 schema 只出现在 Team member scope 中,因此非 Team subagent 保持默认目录。与旧全局 continuable-subagent 控件同名的 scoped 注册只会为团队成员覆盖这些全局控件。
+member scope 上的一个 `team:policy` 段落说明共享的协作规则;固定文本与九个工具注册都声明在 [`src/index.ts`](src/index.ts)。九个工具 schema 只出现在 Team member scope 中,因此非 Team subagent 保持默认目录。与旧全局 continuable-subagent 控件同名的 scoped 注册只会为团队成员覆盖这些全局控件。
 
 ### 按作用域注册与拆除
 
@@ -125,15 +125,15 @@ member scope 上的一个 `team:policy` 段落教每个成员自己的角色与
 
 #### 模型看到什么
 
-一段共享 system 策略会说明显式 delegation 要求、共享 cwd 行为、文件陈旧版本恢复、Bash/formatter/codegen 风险、task/write-scope 协调、Steer 投递、mailbox 不重试规则,以及 Lead 必须在回答前等待。Lead 与 teammate 的全部九个 Team schema 相同;执行时检查仅限 Lead 的操作权限。teammate 的首次任务前,一条持久化 user 消息声明 `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`。Lead 不接收身份提醒,除非普通 fork 继承了 teammate 的提醒;该 fork 会收到 `You are the Team Lead.`。身份提醒不含 Team id,禁用运行时上下文时也能生效。
+一段共享 system 策略会说明显式 delegation 要求、共享 cwd 行为、文件陈旧版本恢复、Bash/formatter/codegen 风险、task/write-scope 协调、Steer 投递、mailbox 不重试规则,以及 Lead 必须在回答前等待。Lead 与 teammate 的全部九个 Team schema 相同;执行时检查仅限 Lead 的操作权限。`spawn_teammate` 在初始 user 消息前加上 `<system-reminder>\nYou are teammate "<name>".\n</system-reminder>`,接着是一个空行和任务。该前缀不含 Team id,禁用运行时上下文时也能生效。fork 继承历史,不额外添加 Lead 身份消息。
 
 #### Token 影响
 
-每次 Team member 请求都有固定策略与 schema 成本。保留的身份提醒在后续步骤与冷恢复中复用;若压缩移除了提醒,插件会在下一步获准执行前追加它。工具调用会增加紧凑 JSON roster、task、wait 或 receipt 结果。Peer 内容由 Team 领域保留在 target 历史中。
+每次 Team member 请求都有固定策略与 schema 成本。初始身份文本随普通历史经历后续步骤、冷恢复和压缩;插件不扫描或重新插入它。工具调用会增加紧凑 JSON roster、task、wait 或 receipt 结果。Peer 内容由 Team 领域保留在 target 历史中。
 
 #### KV Cache 影响
 
-provider/model、共享 system 策略和工具 schema 相同时,fork 保留父请求前缀并追加提醒与任务。工具结果与 peer 消息追加在可复用请求前缀之后。原先在 system prompt 中记录身份的 Session,首次使用此布局请求时可能改变该前缀;提供方实际缓存命中仍为尽力而为。
+provider/model、共享 system 策略和工具 schema 相同时,fork 保留父请求前缀并追加带身份前缀的初始任务。工具结果与 peer 消息追加在可复用请求前缀之后。原先在 system prompt 中记录身份的 Session,首次使用此布局请求时可能改变该前缀;提供方实际缓存命中仍为尽力而为。
 
 ## 已知限制与延期工作
 

+ 3 - 3
packages/experimental/tool-agent-team/package.json

@@ -35,8 +35,7 @@
     "@deepseek-ai/dsh-system-prompt": "workspace:^",
     "@deepseek-ai/dsh-experimental-agent-team": "workspace:^",
     "@deepseek-ai/dsh-tools": "workspace:^",
-    "@deepseek-ai/cordis": "workspace:^",
-    "@deepseek-ai/dsh-llm": "workspace:^"
+    "@deepseek-ai/cordis": "workspace:^"
   },
   "devDependencies": {
     "@deepseek-ai/dsh-agent": "workspace:^",
@@ -55,6 +54,7 @@
     "@deepseek-ai/dsh-experimental-agent-team": "workspace:^",
     "@deepseek-ai/dsh-tools": "workspace:^",
     "@deepseek-ai/cordis": "workspace:^",
-    "@deepseek-ai/dsh-llm": "workspace:^"
+    "@deepseek-ai/dsh-llm": "workspace:^",
+    "@deepseek-ai/dsh-llm-deepseek": "workspace:^"
   }
 }

+ 4 - 36
packages/experimental/tool-agent-team/src/index.ts

@@ -3,8 +3,6 @@
 import type { Context } from '@deepseek-ai/cordis'
 import z from '@deepseek-ai/schemastery'
 import type { Agent } from '@deepseek-ai/dsh-agent'
-import { createUserMessage } from '@deepseek-ai/dsh-llm'
-import type { SessionEvent } from '@deepseek-ai/dsh-session'
 import { TeamTaskId } from '@deepseek-ai/dsh-experimental-agent-team'
 import type { TeamMemberView } from '@deepseek-ai/dsh-experimental-agent-team'
 import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -157,38 +155,6 @@ function callingAgent(agent: Agent | undefined, toolName: string): Agent {
   return agent
 }
 
-/** Identify the plugin-owned reminder without interpreting user-authored text. */
-function isIdentity(event: SessionEvent): event is SessionEvent<'user/message'> {
-  return event.type === 'user/message'
-    && event.data.source.kind === 'plugin' && event.data.source.plugin === name
-}
-
-/** Keep member identity after inherited history and restore it if compaction removes it. */
-function installIdentity(agent: Agent, ctx: Context): () => void {
-  const membership = ctx.agentTeams.membership(agent)
-  // Ordinary forks of teammates become Leads even when the inherited reminder
-  // is shadowed by a summary. Their full inherited log still identifies it.
-  const inheritedIdentity = agent.session.snapshotEvents().slice(0, agent.session.inheritedEventCount).some(isIdentity)
-  if (membership.role === 'lead' && !inheritedIdentity) return () => {}
-  const text = membership.role === 'teammate'
-    ? `<system-reminder>\nYou are teammate "${membership.name}".\n</system-reminder>`
-    : '<system-reminder>\nYou are the Team Lead.\n</system-reminder>'
-  return agent.ctx.on('agent/pre-step', async ({ messages, step, signal }, next) => {
-    const decision = await next()
-    if (decision.kind === 'reject' || signal.aborted) return decision
-    if (decision.messages.length === 0 && (step === 1 || messages.length > 0)) return decision
-    const retained = agent.session.surface.nodes.map(seq => agent.session.eventAt(seq)).findLast(event =>
-      event !== undefined && isIdentity(event))
-    if (retained !== undefined && isIdentity(retained)
-      && retained.data.content.some(block => block.type === 'text' && block.text === text)) return decision
-    const reminder = createUserMessage({
-      content: [{ type: 'text', text }],
-      source: { kind: 'plugin', plugin: name, form: 'snapshot', sections: [{ name: 'team:identity', text }] },
-    })
-    return { ...decision, messages: [reminder, ...decision.messages] }
-  }, { prepend: true })
-}
-
 /** Register the complete Team tool set in one exact Agent scope. */
 function install(agent: Agent, ctx: Context, config: Required<Config>): () => void {
   const scoped = agent.ctx
@@ -200,7 +166,6 @@ function install(agent: Agent, ctx: Context, config: Required<Config>): () => vo
       order: scoped.systemPrompt.getSectionOrder('TEAM_POLICY'),
       text: POLICY,
     }))
-    register(installIdentity(agent, ctx))
 
     register(scoped.tools.register(defineTool({
       name: 'spawn_teammate',
@@ -222,7 +187,10 @@ function install(agent: Agent, ctx: Context, config: Required<Config>): () => vo
         return await ctx.agentTeams.spawnTeammate(agent, {
           name: args.name,
           description: args.description,
-          prompt: [{ type: 'text', text: args.prompt }],
+          prompt: [
+            { type: 'text', text: `<system-reminder>\nYou are teammate "${args.name.trim()}".\n</system-reminder>\n\n` },
+            { type: 'text', text: args.prompt },
+          ],
           context,
           provider: context === 'fork' ? config.forkProvider : config.freshProvider,
           signal: exec.signal,

+ 57 - 65
packages/experimental/tool-agent-team/tests/tool-team.spec.ts

@@ -17,7 +17,7 @@ import * as SubagentSpawn from '@deepseek-ai/dsh-subagent-spawn-in-process'
 import { renderPrompt, renderContextSnapshot } from '@deepseek-ai/dsh-system-prompt'
 import * as ToolSubagentControl from '@deepseek-ai/dsh-tool-subagent-control'
 import { defineContentToolFixture } from '@deepseek-ai/dsh-tools'
-import { serializeRequest } from '../../../llm/llm-deepseek/src/serialize.ts'
+import { serializeRequest } from '@deepseek-ai/dsh-llm-deepseek/src/serialize.ts'
 import { MockAdapter, textResponse, toolCallResponse } from '../../../core/agent-loop/tests/mock-adapter.ts'
 import TeamService from '../../agent-team/src/index.ts'
 import * as toolTeam from '../src/index.ts'
@@ -161,12 +161,12 @@ describe('dsh-tool-team', () => {
     expect(renderPrompt(childAssembly)).toBe(leadPrompt)
     expect(renderContextSnapshot(childAssembly)).not.toContain('team:identity')
     expect(child.session.deriveMessages().some(message => message.content.some(block =>
-      block.type === 'text' && block.text === '<system-reminder>\nYou are teammate "tool-worker".\n</system-reminder>'))).toBe(true)
+      block.type === 'text' && block.text === '<system-reminder>\nYou are teammate "tool-worker".\n</system-reminder>\n\n'))).toBe(true)
     const initialPrompt = child.session.snapshotEvents().find(event => event.type === 'user/message'
       && event.data.source.kind === 'user')
     expect(initialPrompt?.type === 'user/message'
       ? initialPrompt.data.content.flatMap(block => block.type === 'text' ? [block.text] : [])
-      : []).toEqual(['stay available'])
+      : []).toEqual(['<system-reminder>\nYou are teammate "tool-worker".\n</system-reminder>\n\n', 'stay available'])
 
     const denied = await execute(ctx, child, 'spawn_teammate', {
       name: 'nested', description: 'not allowed', prompt: 'no',
@@ -201,17 +201,17 @@ describe('dsh-tool-team', () => {
     } else {
       expect(JSON.stringify(childRequest.messages)).not.toContain('Parent task')
     }
-    const taskIndex = childRequest.messages.findIndex(message => message.content === 'Review the work')
-    const latest = childRequest.messages[taskIndex - 1]
-    expect(taskIndex).toBeGreaterThan(0)
-    expect(latest?.role).toBe('user')
-    expect(latest?.content).toContain('<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>')
+    expect(childRequest.messages).toContainEqual({
+      role: 'user',
+      content: '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\nReview the work',
+    })
     await using persisted = await ctx.sessionPersistence.open(childId, 'read')
     const { events } = await persisted.read()
-    expect(events.some(event => event.type === 'user/message'
-      && event.data.source.kind === 'plugin'
-      && event.data.source.form === 'snapshot'
-      && event.data.source.sections.some(section => section.name === 'team:identity'))).toBe(true)
+    const initial = events.findLast(event => event.type === 'user/message' && event.data.source.kind === 'user')
+    expect(initial?.type === 'user/message' ? initial.data.content : []).toEqual([
+      { type: 'text', text: '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n' },
+      { type: 'text', text: 'Review the work' },
+    ])
   })
 
   it('keeps an ordinary Lead fork free of identity reminders without replacing the inherited prefix', async () => {
@@ -239,7 +239,42 @@ describe('dsh-tool-team', () => {
     await handle.dispose()
   })
 
-  it('reissues teammate identity after compaction removes its retained reminder', async () => {
+  it.each([undefined, 'in-history'] as const)('keeps a teammate fork prefix with update mode %s without a Lead correction', async (systemPromptUpdate) => {
+    const { ctx, lead, adapter } = await setup([textResponse('review done'), textResponse('lead notified'), textResponse('fork answer')])
+    if (systemPromptUpdate !== undefined) adapter.systemPromptUpdate = systemPromptUpdate
+    const spawned = await execute(ctx, lead, 'spawn_teammate', {
+      name: 'reviewer', description: 'review', prompt: 'Review the work',
+    })
+    const teammateId = spawnedChildId(spawned)
+    await waitNoAgent(ctx, teammateId)
+    await using persisted = await ctx.sessionPersistence.open(teammateId, 'read')
+    const { events: seed } = await persisted.read()
+    const handle = await ctx.agents.create({
+      sessionId: SessionId('fork-from-teammate'),
+      seed,
+      inheritedEventCount: SessionLogOffset(seed.length),
+      meta: { parentSession: teammateId, isSeeded: true },
+      agentOptions: { provider: 'mock', model: 'mock' },
+      signal: SIGNAL,
+    })
+    try {
+      const inheritedHistory = structuredClone(handle.agent.session.deriveMessages())
+      await runTurn(handle.agent, 'Continue independently')
+      const original = serializeRequest(adapter.requests.find(request => request.sessionId === teammateId)!)
+      const forkRequest = adapter.requests.find(request => request.sessionId === handle.agent.id)!
+      const fork = serializeRequest(forkRequest)
+      expect(fork.tools).toEqual(original.tools)
+      expect(fork.messages.slice(0, original.messages.length)).toEqual(original.messages)
+      expect(forkRequest.messages.slice(0, inheritedHistory.length)).toEqual(inheritedHistory)
+      expect(fork.messages).toContainEqual({ role: 'user', content: 'Continue independently' })
+      expect(JSON.stringify(fork.messages)).not.toContain('You are the Team Lead')
+      expect(handle.agent.session.snapshotEvents().slice(0, seed.length)).toEqual(seed)
+    } finally {
+      await handle.dispose()
+    }
+  })
+
+  it('leaves initial identity to ordinary history compaction without reinserting it', async () => {
     const { ctx, lead, adapter } = await setup([
       toolCallResponse('first-list', 'list_agents', {}),
       toolCallResponse('second-list', 'list_agents', {}),
@@ -248,7 +283,7 @@ describe('dsh-tool-team', () => {
     ctx.on('agent/pre-step', async ({ agent, step }, next) => {
       if (step === 3) {
         const identity = agent.session.snapshotEvents().find(event => event.type === 'user/message'
-          && event.data.source.kind === 'plugin' && event.data.source.plugin === toolTeam.name)
+          && event.data.source.kind === 'user')
         if (identity === undefined) throw new Error('expected initial teammate reminder')
         agent.session.append('user/message', createUserMessage({
           content: [{ type: 'text', text: 'Compacted earlier context.' }],
@@ -265,58 +300,16 @@ describe('dsh-tool-team', () => {
     })
     const childId = spawnedChildId(spawned)
     await waitNoAgent(ctx, childId)
-    const reminder = '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>'
+    const reminder = '<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\n'
     const first = serializeRequest(adapter.requests[0]!).messages
-    expect(first[first.findIndex(message => message.content === 'Review the work') - 1]?.content).toBe(reminder)
+    expect(first).toContainEqual({ role: 'user', content: `${reminder}Review the work` })
     expect(adapter.requests[1]!.messages.filter(message => message.content.some(block =>
       block.type === 'text' && block.text === reminder))).toHaveLength(1)
-    expect(serializeRequest(adapter.requests[2]!).messages.at(-1)?.content).toBe(reminder)
+    expect(JSON.stringify(serializeRequest(adapter.requests[2]!).messages)).not.toContain('You are teammate')
     await using persisted = await ctx.sessionPersistence.open(childId, 'read')
     const { events } = await persisted.read()
     expect(events.filter(event => event.type === 'user/message'
-      && event.data.source.kind === 'plugin' && event.data.source.plugin === toolTeam.name)).toHaveLength(2)
-  })
-
-  it.each([false, true])('corrects inherited teammate identity on an ordinary fork, compacted=%s', async (compacted) => {
-    const { ctx, lead, adapter } = await setup([textResponse('review done'), textResponse('independent work')])
-    const spawned = await execute(ctx, lead, 'spawn_teammate', {
-      name: 'reviewer', description: 'review', prompt: 'Review the work',
-    })
-    const teammateId = spawnedChildId(spawned)
-    await waitNoAgent(ctx, teammateId)
-    await using persisted = await ctx.sessionPersistence.open(teammateId, 'read')
-    const { events: seed } = await persisted.read()
-    const handle = await ctx.agents.create({
-      sessionId: SessionId('fork-from-teammate'),
-      seed,
-      inheritedEventCount: SessionLogOffset(seed.length),
-      meta: { parentSession: teammateId, isSeeded: true },
-      agentOptions: { provider: 'mock', model: 'mock' },
-      signal: SIGNAL,
-    })
-    try {
-      if (compacted) {
-        const identity = seed.find(event => event.type === 'user/message'
-          && event.data.source.kind === 'plugin' && event.data.source.plugin === toolTeam.name)
-        if (identity === undefined) throw new Error('expected inherited identity')
-        handle.agent.session.append('user/message', createUserMessage({
-          content: [{ type: 'text', text: 'The reviewer completed its task.' }],
-          source: { kind: 'plugin', plugin: 'test-compaction' },
-        }), {
-          surfaceOp: { op: 'replace', startSeq: identity.seq, endSeq: identity.seq },
-          sourceEventSeqs: [identity.seq],
-        })
-      }
-      await runTurn(handle.agent, 'Continue independently')
-      const request = serializeRequest(adapter.requests.find(request => request.sessionId === handle.agent.id)!)
-      const taskIndex = request.messages.findIndex(message => message.content === 'Continue independently')
-      expect(request.messages[taskIndex - 1]?.content).toBe('<system-reminder>\nYou are the Team Lead.\n</system-reminder>')
-      expect(taskIndex).toBeGreaterThan(0)
-      expect(ctx.agentTeams.membership(handle.agent).role).toBe('lead')
-      expect(handle.agent.session.snapshotEvents().slice(0, seed.length)).toEqual(seed)
-    } finally {
-      await handle.dispose()
-    }
+      && event.data.source.kind === 'plugin' && event.data.source.plugin === toolTeam.name)).toHaveLength(0)
   })
 
   it.each(['reject', 'empty', 'abort'] as const)('does not revive a teammate step after %s', async (mode) => {
@@ -337,7 +330,7 @@ describe('dsh-tool-team', () => {
     await using persisted = await ctx.sessionPersistence.open(childId, 'read')
     const { events } = await persisted.read()
     expect(events.filter(event => event.type === 'user/message'
-      && event.data.source.kind === 'plugin' && event.data.source.plugin === toolTeam.name)).toEqual([])
+      && event.data.source.kind === 'user')).toEqual([])
   })
 
   it('keeps teammate reminders when runtime context is suppressed', async () => {
@@ -348,8 +341,8 @@ describe('dsh-tool-team', () => {
     })
     expect(spawned.isError, text(spawned)).toBe(false)
     await waitNoAgent(ctx, spawnedChildId(spawned))
-    expect(serializeRequest(adapter.requests[0]!).messages.at(-2)?.content)
-      .toBe('<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>')
+    expect(serializeRequest(adapter.requests[0]!).messages.at(-1)?.content)
+      .toBe('<system-reminder>\nYou are teammate "reviewer".\n</system-reminder>\n\nReview the work')
   })
 
   it('returns actionable no-progress output and renders structured wait cancellation', async () => {
@@ -623,8 +616,7 @@ describe('dsh-tool-team', () => {
     const firstMessages = childRequests()[0]!.messages
     expect(childRequests()[1]!.messages.slice(0, firstMessages.length)).toEqual(firstMessages)
     expect(resumed.session.snapshotEvents().filter(event => event.type === 'user/message'
-      && event.data.source.kind === 'plugin' && event.data.source.form === 'snapshot'
-      && event.data.source.sections.some(section => section.name === 'team:identity'))).toHaveLength(1)
+      && event.data.source.kind === 'user')).toHaveLength(1)
     await execute(ctx, lead, 'interrupt_agent', { target: 'cold-worker' })
     await vi.waitFor(() => { expect(ctx.agents.get(childId)).toBeUndefined() }, { timeout: 5_000 })
   })

+ 0 - 1
packages/experimental/tool-agent-team/tsconfig.json

@@ -14,7 +14,6 @@
     { "path": "../../core/session" },
     { "path": "../../core/system-prompt" },
     { "path": "../../core/tools" },
-    { "path": "../../llm/llm" },
     { "path": "../agent-team" }
   ]
 }

+ 3 - 0
pnpm-lock.yaml

@@ -5589,6 +5589,9 @@ importers:
       '@deepseek-ai/dsh-llm':
         specifier: workspace:^
         version: link:../../llm/llm
+      '@deepseek-ai/dsh-llm-deepseek':
+        specifier: workspace:^
+        version: link:../../llm/llm-deepseek
       '@deepseek-ai/dsh-scope':
         specifier: workspace:^
         version: link:../../core/scope