Преглед на файлове

fix(agent-team): disable direct subagent tools in Team profiles

Dudu-0223 преди 3 седмици
родител
ревизия
41196ae591

+ 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: 8bf0547fb466089a97255cc42712e017694b929e
-2026-08-05-agent-teams.zh.md: 287d3bba0faa0fd81695121cd8cb4679a19d85e5
+2026-08-05-agent-teams.md: bdf646db9b1617ded7f50f4145887f34a52ae83c
+2026-08-05-agent-teams.zh.md: 5386044edb57dad9fabf114d565808c735846464

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

@@ -20,6 +20,10 @@ The implementation is split into `@deepseek-ai/dsh-experimental-agent-team`, whi
 
 The Lead must wait for required work before its final answer. Process teardown remains the final lifecycle owner and drains continuation Activations; a Team task owner is durable state and is not automatically released by idle, interruption, or process exit.
 
+## Profile delegation
+
+The [Team profile](../../../../packages/experimental/agent-team-profile/README.md) disables `subagent` and `subagent_fork` together with the overlapping global controls. Direct model delegation uses `spawn_teammate` with fresh or fork context, keeping those children in the durable roster. Workflow remains available through the base profile’s fresh `spawn` provider for scripted orchestration; it cannot inherit a teammate’s conversation identity through the model tool. The Subagent services and providers remain shared infrastructure. Ordinary Session forks retain their history without identity correction. Provider-owned child tool visibility remains a [documented limitation](../../../../packages/experimental/tool-agent-team/README.md#known-limitations-and-deferred-work).
+
 ## Team identity
 
 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.

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

@@ -20,6 +20,10 @@ Agent Teams 的公开约定仍处于实验阶段,因此需要显式启用的
 
 Lead 必须等待所需工作后才能给出最终答案。进程 teardown 仍是最终生命周期 owner,并会 drain continuation Activation;Team task owner 是持久状态,不会因 idle、interrupt 或进程退出自动释放。
 
+## Profile delegation
+
+[Team profile](../../../../packages/experimental/agent-team-profile/README.zh.md) 禁用 `subagent`、`subagent_fork` 以及名称重叠的全局控件。模型直接委派使用支持 fresh 或 fork 上下文的 `spawn_teammate`,使这些子代理进入持久 roster。Workflow 仍通过 base profile 的 fresh `spawn` 提供方执行脚本编排;经模型工具创建的 workflow 子代理不会继承 teammate 的对话身份。Subagent 服务和提供方仍是共享基础设施。普通 Session fork 保留历史,不纠正身份。提供方所拥有的子代理工具可见性仍是[已记录的限制](../../../../packages/experimental/tool-agent-team/README.zh.md#known-limitations-and-deferred-work)。
+
 ## Team identity
 
 工具 `spawn_teammate` 在初始任务前加上 user-role `<system-reminder>`,声明 `You are teammate "<name>".`。身份和任务进入同一条持久化收件箱消息。共享 system 策略和全部工具 schema 在成员间保持一致;执行时检查角色权限。Team 工具根据调用者确定 Team,并接受成员名字,因此模型不需要 Team id。身份随普通历史经历冷恢复和压缩;插件不检查提醒是否保留,也不添加替代消息。fork 继承已记录文本,不补发 Lead 身份修正。把身份放进 system prompt 会在继承历史之前改变前缀;把它留在初始任务中既保留此前缀,也无需每步维护身份提醒。已有的 system 内嵌身份可能需要一次提示词协调;保留的事件格式代际保持不变。

+ 13 - 2
apps/cli/tests/agent-team-headless.e2e.ts

@@ -54,7 +54,7 @@ describe('dsh run with Agent Teams enabled', () => {
       ].join('\n'))
       const launch = resolveExampleLaunch({
         srcBin: dshBinScript,
-        configArgs: ['--profile', 'headless', '请明确使用 Agent Teams,把调研和实现拆给两个 teammate,等待完成后汇总。'],
+        configArgs: ['--profile', 'headless', '请先运行 workflow 检查,再使用 Agent Teams 把调研和实现拆给两个 teammate,等待完成后汇总。'],
         tsconfigPath,
         env: {
           DSH_HOME: home,
@@ -85,9 +85,17 @@ describe('dsh run with Agent Teams enabled', () => {
 
       const files = (await readdir(sessions, { recursive: true }))
         .filter(file => file.endsWith('.jsonl'))
-      expect(files).toHaveLength(3)
+      expect(files).toHaveLength(4)
       const logs = await Promise.all(files.map(file => readFile(join(sessions, file), 'utf8')))
       const parsed = logs.map(records)
+      const workflowChild = parsed.find(log => log.some(record => record.type === 'subagent/descriptor'
+        && (record.data as { mode: string }).mode === 'one-shot'))
+      expect(workflowChild).toBeDefined()
+      expect(workflowChild!.find(record => record.type === 'subagent/descriptor')?.data)
+        .toMatchObject({ mode: 'one-shot', provider: 'spawn' })
+      expect(workflowChild!.filter(record => record.type === 'user/message'
+        && (record.data as { source: { kind: string } }).source.kind === 'user').map(record => record.data))
+        .toEqual([expect.objectContaining({ content: [{ type: 'text', text: 'TEAM_WORKFLOW_CHILD' }] })])
       const root = parsed.find((log) => {
         const header = log[0]
         return header?.type === 'session' && typeof header.parentSession !== 'string'
@@ -107,6 +115,9 @@ describe('dsh run with Agent Teams enabled', () => {
       expect(toolNames).toContain('wait_agent')
       expect(toolNames).toContain('team_task_list')
       expect(toolNames).toContain('list_agents')
+      expect(toolNames).toContain('workflow')
+      expect(root!.find(record => record.type === 'tool-workflow/run-end')?.data)
+        .toMatchObject({ stopReason: 'completed' })
     } finally {
       await rm(cwd, { recursive: true, force: true })
     }

+ 2 - 11
apps/cli/tests/profiles/headless/team-snapshot.patch.yml

@@ -31,19 +31,10 @@
   disabled: true
 
 - id: tool-subagent
-  config:
-    provider: spawn
-    toolName: subagent
-    backgroundMode: one-shot
-    maxDepth: 1
+  disabled: true
 
 - id: tool-subagent-fork
-  config:
-    provider: fork
-    toolName: subagent_fork
-    backgroundMode: one-shot
-    enableRunInBackground: false
-    maxDepth: 1
+  disabled: true
 
 - insert:
     - id: agent-team

+ 16 - 1
apps/cli/tests/profiles/headless/tests/fixtures/team-llm.mjs

@@ -134,6 +134,12 @@ function implementer(messages) {
 function lead(messages) {
   const names = calls(messages)
   const last = latestAssistantCalls(messages)
+  if (!names.includes('workflow')) {
+    return toolChunks([{ name: 'workflow', args: {
+      meta: { name: 'team-preflight', description: 'Check fresh workflow delegation alongside teammates.' },
+      script: 'return await agent("TEAM_WORKFLOW_CHILD");',
+    } }])
+  }
   const spawned = names.filter(name => name === 'spawn_teammate').length
   if (spawned === 0) {
     return toolChunks([{
@@ -174,9 +180,18 @@ function lead(messages) {
 
 class TeamFixtureAdapter extends LlmAdapter {
   async * stream(options) {
+    const tools = options.tools.map(tool => tool.name)
+    if (tools.includes('subagent') || tools.includes('subagent_fork')) {
+      throw new Error('Team profile exposes a direct subagent tool')
+    }
     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>'
+    if (identity !== 'TEAM_WORKFLOW_CHILD' && (!tools.includes('spawn_teammate') || !tools.includes('workflow'))) {
+      throw new Error('Team profile is missing teammate or workflow tools')
+    }
+    const chunks = identity === 'TEAM_WORKFLOW_CHILD'
+      ? textChunks('Fresh workflow child complete.')
+      : identity === '<system-reminder>\nYou are teammate "researcher".\n</system-reminder>'
       ? researcher(options.messages)
       : identity === '<system-reminder>\nYou are teammate "implementer".\n</system-reminder>'
         ? implementer(options.messages)

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

@@ -599,7 +599,7 @@ describe('headless stream-json snapshots', () => {
       configPath: teamConfigPath,
       binArgs: [
         teamConfigPath,
-        '请明确使用 Agent Teams,把调研和实现拆给两个 teammate,等待完成后汇总。',
+        '请先运行 workflow 检查,再使用 Agent Teams 把调研和实现拆给两个 teammate,等待完成后汇总。',
       ],
       tsconfigPath,
       processTimeoutMs: 60_000,
@@ -612,6 +612,15 @@ describe('headless stream-json snapshots', () => {
         const parent = logs.find(log => typeof log.header.parentSession !== 'string')
         if (parent === undefined) throw new Error('Agent Teams snapshot did not persist its Lead')
         const rows = parseJsonl(parent.content)
+        const workflowChild = logs.find(log => parseJsonl(log.content).some(row => row.type === 'subagent/descriptor'
+          && (row.data as JsonObject).mode === 'one-shot'))
+        if (workflowChild === undefined) throw new Error('Team profile did not persist its workflow child')
+        const workflowRows = parseJsonl(workflowChild.content)
+        expect(workflowRows.find(row => row.type === 'subagent/descriptor')?.data)
+          .toMatchObject({ mode: 'one-shot', provider: 'spawn' })
+        expect(workflowRows.filter(row => row.type === 'user/message'
+          && ((row.data as JsonObject).source as JsonObject).kind === 'user').map(row => row.data))
+          .toEqual([expect.objectContaining({ content: [{ type: 'text', text: 'TEAM_WORKFLOW_CHILD' }] })])
         const members = rows.filter(row => row.type === 'team/member')
           .map(row => ((row.data as JsonObject).member as JsonObject))
         const tasks = rows.filter(row => row.type === 'team/task')
@@ -661,7 +670,7 @@ describe('headless stream-json snapshots', () => {
           return (JSON.parse(data.arguments) as JsonObject).action === 'complete'
         })
         const identityReminders = logs.flatMap((log) => {
-          if (log === parent) return []
+          if (log === parent || log === workflowChild) 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')
@@ -673,6 +682,7 @@ describe('headless stream-json snapshots', () => {
         }).sort()
         projection = {
           sessions: logs.length,
+          workflowStopReason: (rows.find(row => row.type === 'tool-workflow/run-end')?.data as JsonObject)?.stopReason,
           memberEdges: members.length,
           identityReminders,
           activeMembers: members.filter(member => member.phase === 'active').map(member => member.name).sort(),
@@ -719,7 +729,7 @@ describe('headless stream-json snapshots', () => {
         ],
         "memberEdges": 4,
         "queuedMessages": 2,
-        "sessions": 3,
+        "sessions": 4,
         "steerEvidence": {
           "completedAfterMessage": true,
           "enteredOpenTurn": true,
@@ -739,6 +749,7 @@ describe('headless stream-json snapshots', () => {
           },
         ],
         "waited": true,
+        "workflowStopReason": "completed",
       }
     `)
   }, 75_000)

+ 2 - 2
packages/experimental/agent-team-profile/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/agent-team-profile/README.md
-README.md: 511b42269d68e508c797bbd9499c462a269bf552
-README.zh.md: 243fd9145982d05626c04cae758e7cb3c688ebd9
+README.md: 86ac08c5d2278013ecbc371301bbb6a3a01a65d9
+README.zh.md: 973f5f2342b32109d26291d762f4ad7b9b4c3306

+ 6 - 5
packages/experimental/agent-team-profile/README.md

@@ -1,5 +1,5 @@
 ---
-description: "Published experimental Agent Teams profile layer over dsh-base with Team-scoped coordination tools and one-shot delegation."
+description: "Published experimental Agent Teams profile layer over dsh-base with teammate delegation and fresh workflow children."
 kind: "package-bundle"
 ---
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
 
 ## Summary
 
-`dsh-experimental-agent-team-profile` is a published experimental profile layer that enables [Agent Teams](../agent-team/README.md) over `@deepseek-ai/dsh-base`. Its patch inserts the Team domain and Team-scoped tools, disables the overlapping global continuable-child controls, and keeps the ordinary fresh and fork delegation tools as one-shot operations. Add it explicitly to an initialized profile; no shipped profile enables it by default.
+`dsh-experimental-agent-team-profile` is a published experimental profile layer that enables [Agent Teams](../agent-team/README.md) over `@deepseek-ai/dsh-base`. Its patch inserts the Team domain and Team-scoped tools and disables ordinary subagent delegation and the overlapping global continuable-child controls. Workflow remains available with fresh children. Add it explicitly to an initialized profile; no shipped profile enables it by default.
 
 ## Table of Contents
 
@@ -38,7 +38,7 @@ The profile must already contain `@deepseek-ai/dsh-base`, whose Subagent service
 
 ### What you get
 
-The layer adds the Agent Teams domain and its scoped creation, roster, messaging, interruption, waiting, and task-board tools. It disables the global continuable-child control rows whose tool names overlap with Team controls, while leaving `subagent` and `subagent_fork` available as one-shot delegation tools.
+The layer adds the Agent Teams domain and its scoped creation, roster, messaging, interruption, waiting, and task-board tools. Direct delegation uses `spawn_teammate`, which supports fresh and fork context. The `subagent` and `subagent_fork` tools and overlapping global child controls are disabled. Workflow retains the base profile’s `spawn` provider, while the underlying Subagent services and both providers remain available to teammates and workflow.
 
 -----
 
@@ -48,7 +48,7 @@ The layer adds the Agent Teams domain and its scoped creation, roster, messaging
 <details>
 <summary>Implementation internals — click to expand</summary>
 
-The package's runtime content is [`cordis.patch.yml`](cordis.patch.yml). Applied after `dsh-base`, the patch disables `tool-subagent-control` and `tool-subagent-list-agents`; sets the fresh and fork Subagent rows to `one-shot`; and inserts the Team service and tool rows with explicit providers and limits.
+The package's runtime content is [`cordis.patch.yml`](cordis.patch.yml). Applied after `dsh-base`, the patch disables `tool-subagent-control`, `tool-subagent-list-agents`, `tool-subagent`, and `tool-subagent-fork`, and inserts the Team service and tool rows with explicit providers and limits.
 
 | File | Role |
 |---|---|
@@ -77,7 +77,7 @@ The package's runtime content is [`cordis.patch.yml`](cordis.patch.yml). Applied
 
 #### What the model sees
 
-The Team policy and schemas belong to [`@deepseek-ai/dsh-experimental-tool-agent-team`](../tool-agent-team/README.md). This bundle changes composition only: Team-scoped `list_agents`, `send_message`, and `interrupt_agent` replace the disabled global continuable-child controls. `subagent` and `subagent_fork` remain available as one-shot delegation tools, whose children do not receive the continuable-child `report` tool.
+The Team policy and schemas belong to [`@deepseek-ai/dsh-experimental-tool-agent-team`](../tool-agent-team/README.md). This bundle changes composition only: Team-scoped `list_agents`, `send_message`, and `interrupt_agent` replace the disabled global continuable-child controls. `spawn_teammate` is the direct delegation tool. Workflow’s `agent()` calls create fresh one-shot children; their prompts must contain the context needed for their tasks.
 
 #### Token effect
 
@@ -92,6 +92,7 @@ The bundle's composition is prefix-stable while its patch, Team identity, and co
 <a id="known-limitations-and-deferred-work"></a>
 
 - **Opt-in only** — the package is public, but no shipped CLI, Web, SDK, ACP, or Python profile enables it.
+- **Workflow child tools** — the [Team tool visibility limitation](../tool-agent-team/README.md#known-limitations-and-deferred-work) also applies to workflow children.
 - **Shared checkout** — every teammate observes the same working directory; this bundle adds no worktree isolation or filesystem locking.
 - **Base profile required** — the patch depends on row ids and Subagent providers supplied by `dsh-base`; it is not a standalone profile.
 

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

@@ -1,5 +1,5 @@
 ---
-description: "叠加在 dsh-base 上公开发布的实验性 Agent Teams profile 层,提供 Team-scoped 协作工具并保留一次性 delegation。"
+description: "叠加在 dsh-base 上公开发布的实验性 Agent Teams profile 层,提供 teammate 委派与 fresh workflow 子代理。"
 kind: "package-bundle"
 ---
 
@@ -9,7 +9,7 @@ kind: "package-bundle"
 
 ## 概述
 
-`dsh-experimental-agent-team-profile` 是在 `@deepseek-ai/dsh-base` 之上启用 [Agent Teams](../agent-team/README.zh.md) 的公开实验性 profile 层。它的 patch 会插入 Team domain 与 Team-scoped 工具、禁用名称重叠的全局 continuable-child control,并保留普通的一次性 fresh 与 fork delegation 工具。必须将本包显式添加到已初始化的 profile;随附 profile 默认都不会启用它。
+`dsh-experimental-agent-team-profile` 是在 `@deepseek-ai/dsh-base` 之上启用 [Agent Teams](../agent-team/README.zh.md) 的公开实验性 profile 层。它的 patch 会插入 Team domain 与 Team-scoped 工具,并禁用普通 subagent 委派和名称重叠的全局 continuable-child control。Workflow 仍可创建 fresh 子代理。必须将本包显式添加到已初始化的 profile;随附 profile 默认都不会启用它。
 
 ## 目录
 
@@ -38,7 +38,7 @@ profile 必须已经包含 `@deepseek-ai/dsh-base`,本层会使用其中的 Su
 
 ### 获得的功能
 
-本层会添加 Agent Teams domain,以及 Team-scoped 创建、roster、消息、interrupt、等待与任务板工具。它会禁用工具名与 Team control 重叠的全局 continuable-child control 行,同时保留 `subagent` 与 `subagent_fork` 作为一次性 delegation 工具。
+本层会添加 Agent Teams domain,以及 Team-scoped 创建、roster、消息、interrupt、等待与任务板工具。直接委派使用支持 fresh 和 fork 上下文的 `spawn_teammate`。`subagent`、`subagent_fork` 工具和名称重叠的全局 child control 均被禁用。Workflow 保留 base profile 的 `spawn` 提供方,底层 Subagent 服务和两个提供方仍供 teammate 与 workflow 使用。
 
 -----
 
@@ -48,7 +48,7 @@ profile 必须已经包含 `@deepseek-ai/dsh-base`,本层会使用其中的 Su
 <details>
 <summary>实现细节——点击展开</summary>
 
-本包的运行时内容是 [`cordis.patch.yml`](cordis.patch.yml)。在 `dsh-base` 之后应用时,patch 会禁用 `tool-subagent-control` 与 `tool-subagent-list-agents`,把 fresh 与 fork Subagent 行设置为 `one-shot`,并以显式 provider 和限制插入 Team 服务与工具行。
+本包的运行时内容是 [`cordis.patch.yml`](cordis.patch.yml)。在 `dsh-base` 之后应用时,patch 会禁用 `tool-subagent-control`、`tool-subagent-list-agents`、`tool-subagent` 和 `tool-subagent-fork`,并以显式 provider 和限制插入 Team 服务与工具行。
 
 | 文件 | 职责 |
 |---|---|
@@ -77,7 +77,7 @@ profile 必须已经包含 `@deepseek-ai/dsh-base`,本层会使用其中的 Su
 
 #### 模型会看到什么
 
-Team 策略与 schema 由 [`@deepseek-ai/dsh-experimental-tool-agent-team`](../tool-agent-team/README.zh.md) 所有。本 bundle 只改变 composition:Team-scoped `list_agents`、`send_message` 与 `interrupt_agent` 会替代已禁用的全局 continuable-child control。`subagent` 与 `subagent_fork` 仍作为一次性 delegation 工具可用,其子 agent 不会获得 continuable-child `report` 工具。
+Team 策略与 schema 由 [`@deepseek-ai/dsh-experimental-tool-agent-team`](../tool-agent-team/README.zh.md) 所有。本 bundle 只改变 composition:Team-scoped `list_agents`、`send_message` 与 `interrupt_agent` 会替代已禁用的全局 continuable-child control。`spawn_teammate` 是直接委派工具。Workflow 的 `agent()` 调用创建 fresh 一次性子代理;其提示词必须包含任务所需的上下文。
 
 #### Token 影响
 
@@ -92,6 +92,7 @@ Team 策略与 schema 由 [`@deepseek-ai/dsh-experimental-tool-agent-team`](../t
 <a id="known-limitations-and-deferred-work"></a>
 
 - **仅显式启用**——本包公开发布,但随附 CLI、Web、SDK、ACP 与 Python profile 都不会启用它。
+- **Workflow 子代理工具**——[Team 工具可见性限制](../tool-agent-team/README.zh.md#known-limitations-and-deferred-work)也适用于 workflow 子代理。
 - **共享 checkout**——所有 teammate 都观察同一个工作目录;本 bundle 不提供 worktree 隔离或文件系统锁。
 - **需要 base profile**——本 patch 依赖 `dsh-base` 提供的配置行 id 与 Subagent 提供方;它不是独立 profile。
 

+ 3 - 10
packages/experimental/agent-team-profile/cordis.patch.yml

@@ -1,6 +1,5 @@
 # Experimental Agent Teams profile layer. Apply after dsh-base so these replacements
-# remove the global continuable-child controls before the scoped Team tools
-# register the overlapping list_agents, send_message, and interrupt_agent names.
+# keep direct delegation and coordination on the Team tools.
 
 - id: tool-subagent-control
   disabled: true
@@ -9,16 +8,10 @@
   disabled: true
 
 - id: tool-subagent
-  config:
-    provider: spawn
-    toolName: subagent
-    backgroundMode: one-shot
+  disabled: true
 
 - id: tool-subagent-fork
-  config:
-    provider: fork
-    toolName: subagent_fork
-    backgroundMode: one-shot
+  disabled: true
 
 - insert:
     - id: agent-team

+ 2 - 2
packages/experimental/agent-team-profile/tests/profile.spec.ts

@@ -37,8 +37,8 @@ describe('Agent Teams profile bundle', () => {
     }[]
     expect(patches.find(patch => patch.id === 'tool-subagent-control')).toMatchObject({ disabled: true })
     expect(patches.find(patch => patch.id === 'tool-subagent-list-agents')).toMatchObject({ disabled: true })
-    expect(patches.find(patch => patch.id === 'tool-subagent')?.config).toMatchObject({ backgroundMode: 'one-shot' })
-    expect(patches.find(patch => patch.id === 'tool-subagent-fork')?.config).toMatchObject({ backgroundMode: 'one-shot' })
+    expect(patches.find(patch => patch.id === 'tool-subagent')).toMatchObject({ disabled: true })
+    expect(patches.find(patch => patch.id === 'tool-subagent-fork')).toMatchObject({ disabled: true })
     const inserted = patches.flatMap(patch => patch.insert ?? [])
     expect(inserted.find(entry => entry.id === 'agent-team')).toMatchObject({
       name: '@deepseek-ai/dsh-experimental-agent-team',

+ 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: de29238bb057021a2199c053e9689aa67a5e4370
-README.zh.md: c7ef5ab1a92186c225a8f011f4c9d091b60baabf
+README.md: 225b9cb90173803ba7c777c5d643e04e47c6c602
+README.zh.md: 382c60ee0c92c26919d52ae24137913800fb5fa7

+ 3 - 2
packages/experimental/tool-agent-team/README.md

@@ -81,7 +81,7 @@ This section explains the design decisions behind the adapter and points at the
 
 The adapter is built on three commitments:
 
-- **Scoped, not global.** Every registration lives on the member Agent's own `ctx`; nothing is installed for non-Team subagents or the host.
+- **Scoped, not global.** Every registration lives on the member Agent's own `ctx`; installation uses the member identity available when the Agent is published.
 - **Declared results, compact JSON.** Every tool declares its complete result schema and renders that value as compact JSON, so the compiler checks `execute` against what the model is promised and no result spends tokens on indentation.
 - **The domain owns authority.** Tools delegate to `ctx.agentTeams`, which enforces Lead authority and revision checks; the adapter adds no weaker path.
 
@@ -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 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.
+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 are registered in scopes recognized as Team members at publication. Scoped registrations with the same names as the legacy global continuable-subagent controls shadow those globals for team members only.
 
 ### Scoped registration and teardown
 
@@ -139,6 +139,7 @@ With the same provider/model, shared system policy, and tool schemas, a fork ret
 
 <a id="known-limitations-and-deferred-work"></a>
 
+- **One-shot child tool visibility** — in-process one-shot children receive their subagent descriptor after publication. Team installation can therefore mistake them for Leads and expose Team policy and tools. Calls are rejected once the descriptor identifies them as non-members. Correcting installation timing is deferred.
 
 These limits describe what the policy and tools cannot guarantee for a team. They are current package constraints, not a comparison with other collaboration surfaces.
 

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

@@ -81,7 +81,7 @@ kind: "package-reference"
 
 适配器建立在三项承诺之上:
 
-- **按作用域,而非全局。** 每个注册都位于成员 Agent(智能体)自己的 `ctx` 上;非 Team subagent 或宿主不会安装任何内容。
+- **按作用域,而非全局。** 每个注册都位于成员 Agent(智能体)自己的 `ctx` 上;安装依据 Agent 发布时可用的成员身份。
 - **声明式结果,紧凑 JSON。** 每个工具都声明完整结果 schema,并把该值渲染为紧凑 JSON,因此编译器会对照向模型承诺的结果检查 `execute`,任何结果都不会在缩进上消耗 token。
 - **领域掌握裁决权。** 工具委托给 `ctx.agentTeams`,后者强制执行 Lead 权限与 revision 校验;适配器不添加更弱的路径。
 
@@ -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 中。与旧全局 continuable-subagent 控件同名的 scoped 注册只会为团队成员覆盖这些全局控件。
 
 ### 按作用域注册与拆除
 
@@ -139,6 +139,7 @@ provider/model、共享 system 策略和工具 schema 相同时,fork 保留
 
 <a id="known-limitations-and-deferred-work"></a>
 
+- **一次性子代理工具可见性**——进程内一次性子代理在发布后才获得 subagent descriptor。Team 安装因此可能将它们误认作 Lead 并暴露 Team 策略和工具。descriptor 将它们识别为非成员后,调用会被拒绝。安装时序修复留待以后处理。
 
 这些限制说明策略与工具无法为一支团队保证什么。它们是当前包约束,不是与其他协作方式的对比。