@nanmicoder/dsh-agent-teams 0.1.16-rc.1 → 0.1.16-rc.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  `dsh-agent-teams` turns the current DeepSeek Harness session into a captain that can assemble durable sub-agents, split a goal into dependency-aware tasks, and coordinate work through direct messages.
24
24
 
25
- Ask in natural language. The plugin provides the team protocol, eleven coordination tools, persistent state, an automatic shared-task scheduler, and a live Web UI—without requiring a separate workflow engine.
25
+ Ask in natural language. The plugin provides the team protocol, 13 coordination tools, persistent state, an automatic shared-task scheduler, and a live Web UI—without requiring a separate workflow engine.
26
26
 
27
27
  <p align="center">
28
28
  <img src="./assets/ui.png" width="100%" alt="DeepSeek Harness conversation with the AgentTeams live activity panel, members, tasks, dependencies, and reports">
@@ -30,7 +30,7 @@ Ask in natural language. The plugin provides the team protocol, eleven coordinat
30
30
 
31
31
  ## Releases
32
32
 
33
- [v0.1.16-rc.1](./release-notes/v0.1.16-rc.1.md) is the release candidate for the npm `next` channel: a shared host adapter, exact compatibility matrix, real-host acceptance and release gates. Check [GitHub Releases](https://github.com/NanmiCoder/dsh-agent-teams/releases) for release availability and history.
33
+ [v0.1.16-rc.3](./release-notes/v0.1.16-rc.3.md) targets npm `next`, with a concise fixed team protocol, existing-team reuse guidance, Web approval wakeups, and team-lock cleanup. Choose a version pair below.
34
34
 
35
35
  ## Why AgentTeams?
36
36
 
@@ -49,77 +49,38 @@ The conversation card and activity panel use Harness's official locale service.
49
49
 
50
50
  ## Install and choose versions
51
51
 
52
- > [!IMPORTANT]
53
- > **0.1.16-rc.1 is a prerelease on the `next` track.** Use the exact version below for the supported 0.1.2 hosts. Plugin 0.1.15 targets Alpha.2; do not use a mutable `latest` tag as a compatibility guarantee. Check the actual running host and profile before updating either side.
52
+ **Recommended pair: DeepSeek Harness `0.1.2-rc.1` + AgentTeams `0.1.16-rc.3`. Both are still prereleases.**
54
53
 
55
- | Harness host | Candidate track | Installation rule |
54
+ | Use case | DeepSeek Harness | AgentTeams plugin |
56
55
  | --- | --- | --- |
57
- | **0.1.2-rc.1** | Default acceptance target | There is currently no GA host. RC is still a prerelease; ordinary users should not have to follow Alpha. |
58
- | **0.1.2-alpha.5** | Opt-in preview | Select the exact version and lock the entire host dependency cohort. |
59
- | **0.1.2-alpha.2** | Retained legacy preview | Pinning the CLI alone can still resolve rc.1 transitive dependencies. |
60
- | Other versions, source HEAD, embedded Desktop cores | Outside the current matrix | Keep a known working exact pair or complete acceptance before adding a target. |
56
+ | **Recommended installation** | **`0.1.2-rc.1`** | **`0.1.16-rc.3`** |
57
+ | Developer Alpha testing | `0.1.2-alpha.5` | `0.1.16-rc.3` |
58
+ | Retaining an older Alpha | `0.1.2-alpha.2` | `0.1.16-rc.3` |
61
59
 
62
- [compatibility.json](./compatibility.json) is the single source for development, PR checks and release gates. Plugin prereleases use `next`. Unsuffixed versions may use `latest` only after the recommended host and full supported matrix pass. Harness dist-tags belong to upstream; this project cannot change them, so installation instructions use exact versions.
63
-
64
- ### Ordinary users: keep a matching host and plugin
65
-
66
- The recommended host installation target is:
60
+ ### 1. Install DeepSeek Harness
67
61
 
68
62
  ```sh
69
63
  npm install --global @deepseek-ai/dsh@0.1.2-rc.1
70
64
  dsh --version
71
65
  ```
72
66
 
73
- Once the release is available, install the exact plugin version into the profile you actually use (`web` below):
74
-
75
- ```sh
76
- dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.1
77
- ```
78
-
79
- Restart the actual Harness process after changing either side and refresh the browser. Updating a global CLI does not replace an embedded Desktop core or another source checkout. For an unpublished checkout, use the source build below.
80
-
81
- An exact npm CLI version can still contain broad transitive dependencies. Preserve a verified lockfile and inspect the actual installation. Do not delete credentials or `.agent-teams` data to address a version mismatch.
67
+ Skip this if you already run this version. Alpha is opt-in: select an exact Alpha version from the table and lock all host dependencies as described in the [maintenance guide](./docs/maintenance-workflow.md).
82
68
 
83
- ### Build and verify this candidate
69
+ ### 2. Install the AgentTeams plugin
84
70
 
85
- In a checkout containing this change:
71
+ This installs into the `web` profile. Replace `web` with your actual profile name if different:
86
72
 
87
73
  ```sh
88
- pnpm install --frozen-lockfile
89
- pnpm typecheck
90
- pnpm build
91
- pnpm verify
92
- pnpm pack --out ./agent-teams-candidate.tgz
74
+ dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.3
93
75
  ```
94
76
 
95
- Inspect the actual host, then install the artifact into a separate test profile:
77
+ **After installation, stop and restart Harness for that profile, then refresh the browser.**
96
78
 
97
- ```sh
98
- node scripts/doctor.mjs --host-root "/actual/host/package/directory" --json
99
- dsh plugin --profile agent-teams-preview add --save-exact /absolute/path/agent-teams-candidate.tgz
100
- node scripts/doctor.mjs --host-root "/actual/host/package/directory" --profile-root "/actual/test/profile/directory" --json
101
- dsh --profile agent-teams-preview --dump-config
102
- dsh web --profile agent-teams-preview
103
- ```
104
-
105
- The doctor checks the DSH dependency cohort, duplicate runtime identities and the profile's plugin version. It reads package metadata without reading credentials or changing configuration. Passing it does not replace team, task and UI acceptance. Rebuild, repack and restart after source changes.
106
-
107
- ### Developers: explicitly test Alpha
79
+ Plugin `0.1.16-rc.3` uses the `next` channel; `latest` still points to `0.1.15`, which targets Alpha.2. Use the exact-version command above. Future plugin prereleases use `next`; only stable plugin releases that pass the full verification matrix may use `latest`.
108
80
 
109
- Choose an exact Alpha version; `^0.1.2-alpha.2` does not mean “Alpha.2 only.” Development uses exact rc.1 packages, cohort-wide `pnpm.overrides` and a frozen lockfile. The runtime runner creates separate dependency cohorts, profiles and workspaces for Alpha.2, Alpha.5 and rc.1, installs the same candidate tgz, and checks actual resolution.
81
+ > Desktop users must check the app's embedded Harness core; upgrading the global CLI does not upgrade it. For older `0.1.0-*` / `0.1.1-*` or unlisted hosts, keep a working pair and follow the [older-version and diagnostic guide](./docs/maintenance-workflow.md).
110
82
 
111
- ```sh
112
- node scripts/harness-runtime-verify.mjs \
113
- --host-version 0.1.2-alpha.2 \
114
- --artifact ./agent-teams-candidate.tgz \
115
- --report-dir /tmp/agent-teams-alpha2-check
116
- ```
117
-
118
- This developer command downloads the host into temporary directories without modifying existing user profiles. It uses deterministic model responses with the real CLI, plugin, sessions, tools and subagents. See the [maintenance workflow](./docs/maintenance-workflow.md) for coverage and release requirements.
119
-
120
- ### Retaining an older host / rollback
121
-
122
- For the historical Harness `0.1.0-rc.8` + plugin `0.1.14` pair, pin both sides. This is not a compatibility claim for every older RC. Keep another already verified older pair until a complete migration is ready. Historical [Alpha.2 compatibility](./docs/alpha2-compatibility.md) and [acceptance](./docs/alpha2-release-acceptance.md) reports describe those specific releases, not the current matrix.
83
+ See the full [compatibility matrix](./compatibility.json), [source installation and Alpha testing guide](./docs/maintenance-workflow.md), and [verification coverage and platform limits](./docs/maintenance-2026-09-06/release/README.md).
123
84
 
124
85
  Then ask for a team directly:
125
86
 
@@ -127,7 +88,7 @@ Then ask for a team directly:
127
88
 
128
89
  ## How it works
129
90
 
130
- 1. The current session creates a team and becomes its captain.
91
+ 1. For a request to use AgentTeams, the captain follows the core protocol already in its system instructions. It continues an existing team and uses `agent_teams_status` when current state needs checking. When no team exists, the goal becomes a staged plan for review.
131
92
  2. The captain adds role-specific members backed by continuable sub-agents.
132
93
  3. The goal becomes tasks with owners and explicit dependencies.
133
94
  4. The shared scheduler uses real `running / idle / ready` state to atomically claim one ready task per idle member and wake it. An interrupted resident attempt stays parked and can resume through a direct message without losing its capability; after a cold process restart, the scheduler retries stranded open work with a fresh attempt.
@@ -138,6 +99,8 @@ Team state is stored under `<workspace>/.agent-teams/`; the Web panel reads that
138
99
 
139
100
  Member creation is zero-interaction by default: a member on the captain's current LLM route snapshots that provider, model, and reasoning effort, while a member on a requested alternative route snapshots the target model's default effort; later continuations restore the resolved snapshot. Only an explicit heterogeneous-team request (for example, “backend on provider A/model X, frontend on provider B/model Y”) supplies a member-specific `provider` + `model`; there is no per-member model or reasoning prompt.
140
101
 
102
+ Captain sessions keep the concise core protocol and the original 13 native team tools from their first request. All business tools are directly available; no loading tool or extra activation call is needed. Configured profiles retain their bounded directory in the fixed system prompt. Creating, approving, continuing or ending a team does not rewrite the system prompt or tool schemas. Core rules remain available after history compaction or discarded code-mode tool results. Members receive four team tools, fixed member instructions, and their ordinary coding/research tools. Web approval wakes the captain with a control message; later member reports wake it again. See the [fixed protocol and benchmark contract](./docs/progressive-loading.md).
103
+
141
104
  ## Slash command
142
105
 
143
106
  No “use AgentTeams” phrasing required. The plugin registers the
@@ -152,7 +115,7 @@ command), describe the goal, and press Enter.
152
115
  The command pipeline claims the line, then preserves that exact input as an
153
116
  ordinary user follow-up so it remains visible in the main chat. The gesture
154
117
  boundary adds the deterministic activation directive at pre-step, so the
155
- captain protocol still starts immediately. The invocation is also durably
118
+ first model request follows the staged planning protocol without a mandatory helper call. The invocation is also durably
156
119
  logged (`command/run` / `command/done`).
157
120
 
158
121
  Surfaces without command adjudication (for example the headless CLI) get the
package/README_ZH.md CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  `dsh-agent-teams` 让当前 DeepSeek Harness 会话成为队长:创建可续聊的子 Agent、把目标拆成有依赖的任务,并通过直达消息协调成员工作。
24
24
 
25
- 你只需用自然语言提出目标。插件会提供团队协议、11 个协作工具、持久化状态、自动共享任务调度和实时 Web UI,不需要额外的 Workflow 引擎。
25
+ 你只需用自然语言提出目标。插件会提供精简的固定团队协议、13 个业务工具、持久化状态、自动共享任务调度和实时 Web UI,不需要额外的 Workflow 引擎。
26
26
 
27
27
  <p align="center">
28
28
  <img src="./assets/ui.png" width="100%" alt="DeepSeek Harness 对话与 AgentTeams 实时活动面板,展示成员、任务依赖和回报">
@@ -30,7 +30,7 @@
30
30
 
31
31
  ## 版本更新
32
32
 
33
- [v0.1.16-rc.1](./release-notes/v0.1.16-rc.1.md) 是 npm `next` 渠道的发布候选:统一宿主兼容层、精确版本矩阵、真实宿主验收和发布门禁。发布状态与历史见 [GitHub Releases](https://github.com/NanmiCoder/dsh-agent-teams/releases)。
33
+ [v0.1.16-rc.3](./release-notes/v0.1.16-rc.3.md) 面向 npm `next` 渠道,精简固定团队协议、引导复用已有团队,并修复 Web 批准通知和团队锁清理。安装版本见下方配对表。
34
34
 
35
35
  ## 为什么需要 AgentTeams?
36
36
 
@@ -48,82 +48,45 @@
48
48
 
49
49
  ## 安装与版本选择
50
50
 
51
- > [!IMPORTANT]
52
- > **0.1.16-rc.1 是 `next` 渠道的预发布版本。** 对当前支持的 0.1.2 宿主,请使用下方精确插件版本。0.1.15 面向 Alpha.2,不能把可变的 `latest` 标签当作兼容保证。请先核对实际运行的宿主和 profile,再更新两端。
51
+ **推荐组合:DeepSeek Harness `0.1.2-rc.1` + AgentTeams `0.1.16-rc.3`。两者都仍是预发布版本。**
53
52
 
54
- | Harness 宿主 | 本次候选的定位 | 使用原则 |
53
+ | 使用场景 | DeepSeek Harness | AgentTeams 插件 |
55
54
  | --- | --- | --- |
56
- | **0.1.2-rc.1** | 普通用户默认验收目标 | 当前没有 GA;RC 仍是预发布,但不再要求普通用户跟随 Alpha。 |
57
- | **0.1.2-alpha.5** | 主动选择的预览版本 | 明确指定版本,并锁定整组宿主依赖。 |
58
- | **0.1.2-alpha.2** | 保留兼容的旧预览版本 | 单锁 CLI 不够,间接依赖可能被解析成 rc.1。 |
59
- | 其他版本、源码 HEAD、Desktop 内置核心 | 不在当前支持清单中 | 保留已工作的精确组合,或单独完成验收后加入清单。 |
55
+ | **推荐安装** | **`0.1.2-rc.1`** | **`0.1.16-rc.3`** |
56
+ | 开发者测试 Alpha | `0.1.2-alpha.5` | `0.1.16-rc.3` |
57
+ | 保留旧 Alpha | `0.1.2-alpha.2` | `0.1.16-rc.3` |
60
58
 
61
- 宿主矩阵只有一个来源:[compatibility.json](./compatibility.json)。开发、PR 验证和发布门禁都读取它。插件预发布版本统一发到 `next`;无后缀版本只有在推荐宿主与完整矩阵验收通过后才可以进入 `latest`。上游 Harness 的 npm 标签由上游维护,本项目不能修改它;我们的安装说明使用精确版本。
62
-
63
- ### 普通用户:固定匹配的宿主与插件
64
-
65
- 推荐宿主安装目标为:
59
+ ### 1. 安装 DeepSeek Harness
66
60
 
67
61
  ```sh
68
62
  npm install --global @deepseek-ai/dsh@0.1.2-rc.1
69
63
  dsh --version
70
64
  ```
71
65
 
72
- 确认发行版本可用后,在实际使用的 profile 中安装精确插件版本(以下以 `web` 为例):
73
-
74
- ```sh
75
- dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.1
76
- ```
77
-
78
- 宿主或插件更新后,停止并重启实际运行的 Harness,再刷新浏览器。全局 CLI 升级不会更换 Desktop 内置核心,也不会更新另一个源码工作区。尚未发布的 checkout 可使用下方源码构建方式。
79
-
80
- npm 的精确 CLI 版本仍可能包含宽范围间接依赖。升级时保留已验证的锁文件,对实际安装路径运行诊断;不要通过删除凭据或 `.agent-teams` 来处理版本错配。
66
+ 已有该版本可跳过。Alpha 仅供主动测试:手动指定表中的 Alpha 版本,并按[维护指南](./docs/maintenance-workflow.md)锁定整组宿主依赖。
81
67
 
82
- ### 从源码验证本次候选
68
+ ### 2. 安装 AgentTeams 插件
83
69
 
84
- 在包含本次改动的插件 checkout 中:
70
+ 以下安装到 `web` profile;使用其他 profile 时,将 `web` 换成实际名称:
85
71
 
86
72
  ```sh
87
- pnpm install --frozen-lockfile
88
- pnpm typecheck
89
- pnpm build
90
- pnpm verify
91
- pnpm pack --out ./agent-teams-candidate.tgz
73
+ dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.3
92
74
  ```
93
75
 
94
- 先检查实际宿主的依赖,再在独立测试 profile 安装产物:
76
+ **安装后,停止并重新启动该 profile 的 Harness 进程,再刷新浏览器。**
95
77
 
96
- ```sh
97
- node scripts/doctor.mjs --host-root "/实际运行的宿主包目录" --json
98
- dsh plugin --profile agent-teams-preview add --save-exact /完整路径/agent-teams-candidate.tgz
99
- node scripts/doctor.mjs --host-root "/实际运行的宿主包目录" --profile-root "/实际测试profile目录" --json
100
- dsh --profile agent-teams-preview --dump-config
101
- dsh web --profile agent-teams-preview
102
- ```
103
-
104
- `doctor` 检查整组 DSH 包版本、重复运行时身份以及 profile 中的插件版本。它只读文件,不读取凭据、不修改配置;检查通过不替代实际建队、任务执行和 UI 验收。源码更改后需要重新构建、打包和重启。
105
-
106
- ### 开发者:显式测试 Alpha
78
+ 插件 `0.1.16-rc.3` 使用 `next` 渠道;当前 `latest` 仍是面向 Alpha.2 的 `0.1.15`。请使用上面的精确版本命令。后续插件预发布版本继续进入 `next`,通过完整验证的正式版本才进入 `latest`。
107
79
 
108
- Alpha 需要明确指定版本,不能用一个 `^0.1.2-alpha.2` 表达“只接受 Alpha.2”。本仓库开发依赖通过精确版本、整组 `pnpm.overrides` 和 frozen lockfile 固定到 rc.1;自动验收会为 Alpha.2、Alpha.5 和 rc.1 分别创建独立安装闭包、profile 与工作区,安装同一份 tgz,并核对真实解析结果。
80
+ > Desktop 用户需核对应用内置的 Harness 核心;全局 CLI 升级不会升级桌面内核。旧 `0.1.0-*` / `0.1.1-*` 或其他未列出的宿主,请先保留已工作的组合,参考[旧版本与诊断指引](./docs/maintenance-workflow.md)。
109
81
 
110
- ```sh
111
- node scripts/harness-runtime-verify.mjs \
112
- --host-version 0.1.2-alpha.2 \
113
- --artifact ./agent-teams-candidate.tgz \
114
- --report-dir /tmp/agent-teams-alpha2-check
115
- ```
116
-
117
- 这是会创建临时目录并下载宿主的开发验收命令,不会修改已有用户 profile。模型适配器使用固定测试响应;CLI、插件、会话、工具与子代理使用真实宿主实现。完整范围和发布流程见[维护流程](./docs/maintenance-workflow.md)。
118
-
119
- ### 保留旧宿主 / 回退
120
-
121
- 对于历史组合 Harness `0.1.0-rc.8` + 插件 `0.1.14`,应固定两端版本;不能据此推断所有旧 RC 都兼容。已使用其他已验证旧组合的用户先保留该组合。历史 [Alpha.2 兼容记录](./docs/alpha2-compatibility.md)和[验收报告](./docs/alpha2-release-acceptance.md)只描述当时版本,不覆盖当前清单。
82
+ 完整[兼容清单](./compatibility.json)、[源码安装与 Alpha 验证](./docs/maintenance-workflow.md)、[已验证范围与平台限制](./docs/maintenance-2026-09-06/release/README.md)。
122
83
 
123
84
  接着直接用自然语言拉团队:
124
85
 
125
86
  > 使用 AgentTeams 审查 v0.5.3 之后的提交,分别从性能、安全和产品角度分工,最后输出一份汇总报告。
126
87
 
88
+ 队长会话从第一轮起保留精简的固定核心协议和原有 13 个业务工具,直接调用,无需额外的加载工具或激活调用。已配置模板的精简目录固定保留。创建、批准、继续或结束团队都不会改写系统提示词和工具 schema,会话压缩或代码模式丢弃工具结果也不会丢失核心规则。成员保留四个团队工具、固定成员说明及普通编程/研究工具。Web 批准后会通知并唤醒队长,后续成员报告会再次唤醒它,无需忙轮询。详见[固定协议与 benchmark 标准](./docs/progressive-loading.md)。
89
+
127
90
  ## 工作方式
128
91
 
129
92
  1. 当前会话创建团队并成为队长。
@@ -207,7 +170,7 @@ pnpm verify
207
170
 
208
171
  在 `cordis.patch.yml` 的 `profiles` 中配置完整团队模板。每个 profile 都提供成员阵容,可独立指定 provider、model、role、reasoning_effort。`taskPlanning: captain` 表示只提供阵容和约束,由 Captain 根据用户目标设计 DAG;省略该字段或设为 `seed` 时,展开模板中的固定任务图。使用 `/agent-teams --profile <名称> <目标>` 显式激活;不会把首个普通 token 隐式识别为 profile。
209
172
 
210
- 普通 `/agent-teams` 流程会调用 `agent_teams_create({ profile, approval: "required" })`:只落盘可编辑的成员占位和 DAG,不创建子会话、不领取任务。成员模型和推理等级直接读取 Harness 的模型目录。「返回对话修改」会终止仍在运行的规划轮次,让队长先追问修改方向,再用一次原子操作更新同一份草案;「放弃本次计划」经二次确认后会归档草案、中止轮次,并向模型注入不得自动重建团队的控制上下文。只有点击「确认并启动团队」才会按最终配置原子创建成员并启动就绪任务。运行中团队的停止入口位于该团队的面板标题,点击后需要二次确认,不再占用输入区域。直接工具调用方可显式传 `approval: "automatic"` 保留旧的立即执行路径。审查或测试失败不会解锁下游;自动 repair/review 不依赖 failed review。
173
+ 普通 `/agent-teams` 流程继续已有团队,按需调用 `agent_teams_status` 确认状态;仅在没有当前团队时调用 `agent_teams_create({ profile, approval: "required" })`:只落盘可编辑的成员占位和 DAG,不创建子会话、不领取任务。成员模型和推理等级直接读取 Harness 的模型目录。「返回对话修改」会终止仍在运行的规划轮次,让队长先追问修改方向,再用一次原子操作更新同一份草案;「放弃本次计划」经二次确认后会归档草案、中止轮次,并向模型注入不得自动重建团队的控制上下文。只有点击「确认并启动团队」才会按最终配置原子创建成员并启动就绪任务。运行中团队的停止入口位于该团队的面板标题,点击后需要二次确认,不再占用输入区域。直接工具调用方可显式传 `approval: "automatic"` 保留旧的立即执行路径。审查或测试失败不会解锁下游;自动 repair/review 不依赖 failed review。
211
174
 
212
175
  ## 许可证
213
176
 
@@ -0,0 +1,106 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { readTeamSync, readRetiredMemberIdsSync } from "./state.js";
4
+ import { MEMBER_TOOL_NAMES, TEAM_TOOL_NAMES } from "./tool-names.js";
5
+ export const TEAM_ACTIVATION_PROMPT = 'AgentTeams (Agent Teams) provides multi-agent team collaboration. Apply these rules when the user requests it (including /agent-teams) or when continuing an existing team. Mentioning, quoting, discussing, or declining AgentTeams alone is not a request to start work.';
6
+ export const TEAM_MEMBER_PROMPT = 'You are an AgentTeams member. Follow your assigned member persona and task contract. Use agent_teams_claim_task, agent_teams_update_task, agent_teams_send_message and agent_teams_status for your own work. Include the current attempt_id in updates; report completion or failure to the captain. Do not create, approve, edit or resume a team. If your durable membership is unavailable, report that to the parent instead of creating a replacement.';
7
+ function stateRoot(agent, config) {
8
+ return join(agent.session.header.cwd ?? process.cwd(), config.stateDir);
9
+ }
10
+ /** Synchronous startup/HMR hydration must finish before the first assembly. */
11
+ function currentTeam(agent, config) {
12
+ const root = stateRoot(agent, config);
13
+ let entries;
14
+ try {
15
+ entries = readdirSync(root, { withFileTypes: true });
16
+ }
17
+ catch (error) {
18
+ if (error.code === 'ENOENT')
19
+ return undefined;
20
+ throw error;
21
+ }
22
+ let found;
23
+ for (const entry of entries) {
24
+ if (!entry.isDirectory() || entry.name === 'archive')
25
+ continue;
26
+ const team = readTeamSync(root, entry.name);
27
+ if (team === undefined || (team.captainSessionId !== agent.id
28
+ && !team.members.some(member => member.id === agent.id)))
29
+ continue;
30
+ if (found !== undefined)
31
+ throw new Error('ambiguous AgentTeams membership');
32
+ found = team;
33
+ }
34
+ return found;
35
+ }
36
+ /** Call once, after all business definitions have registered. Never per member. */
37
+ export function installTeamCapabilities(ctx, config) {
38
+ const states = new WeakMap();
39
+ const active = new Set();
40
+ let mounted = true;
41
+ // Snapshot policy once: profiles, team state, and tool results must never
42
+ // rewrite this prefix or control whether core instructions are available.
43
+ const captainPrompt = `${TEAM_ACTIVATION_PROMPT}\n\n${config.captainPrompt()}`;
44
+ function attach(agent) {
45
+ const prior = states.get(agent);
46
+ if (prior !== undefined)
47
+ return prior;
48
+ if (!mounted)
49
+ throw new Error('AgentTeams capability provider is disposed');
50
+ // Determine a member's role before its first request and retain it for the
51
+ // lifetime of this scope. Team creation/archive must never rewrite the
52
+ // captain's system/tools prefix, even after a long ordinary conversation.
53
+ let member = config.isPendingMember(agent);
54
+ try {
55
+ member ||= readRetiredMemberIdsSync(stateRoot(agent, config)).has(agent.id);
56
+ const team = currentTeam(agent, config);
57
+ member ||= team !== undefined && team.captainSessionId !== agent.id;
58
+ }
59
+ catch (error) {
60
+ // Unrelated damaged state must not disable ordinary conversation.
61
+ // Business tools still validate durable team state before acting.
62
+ ctx.logger.warn(`agent-teams: capability hydration failed: ${String(error)}`);
63
+ }
64
+ const state = { member, dispose: () => undefined };
65
+ let revoke;
66
+ let disposed = false;
67
+ let releaseLifetime;
68
+ state.dispose = () => {
69
+ if (disposed)
70
+ return;
71
+ disposed = true;
72
+ revoke?.();
73
+ releaseLifetime?.();
74
+ states.delete(agent);
75
+ active.delete(state);
76
+ };
77
+ states.set(agent, state);
78
+ active.add(state);
79
+ try {
80
+ if (member)
81
+ revoke = agent.ctx.tools.restrict({
82
+ deny: TEAM_TOOL_NAMES.filter(name => !MEMBER_TOOL_NAMES.includes(name)),
83
+ });
84
+ releaseLifetime = agent.ctx.effect(() => state.dispose, 'agent-teams: capability lifetime');
85
+ return state;
86
+ }
87
+ catch (error) {
88
+ state.dispose();
89
+ throw error;
90
+ }
91
+ }
92
+ ctx.systemPrompt.section({
93
+ name: 'agent-teams:usage', order: config.order ?? 117,
94
+ text: ({ agent }) => {
95
+ return agent !== undefined && states.get(agent)?.member ? TEAM_MEMBER_PROMPT : captainPrompt;
96
+ },
97
+ });
98
+ ctx.on('agent/session-start', ({ agent }) => { attach(agent); });
99
+ ctx.effect(() => () => {
100
+ mounted = false;
101
+ for (const state of [...active])
102
+ state.dispose();
103
+ }, 'agent-teams: capability scopes');
104
+ for (const agent of ctx.agents.list())
105
+ attach(agent);
106
+ }
package/lib/command.js CHANGED
@@ -54,11 +54,11 @@ export function invokedAgentTeamsGoal(messages) {
54
54
  }
55
55
  export function buildActivationDirective(goal, profile, taskPlanning = 'seed') {
56
56
  const lines = [
57
- 'The user invoked an AgentTeams slash command. Activate the AgentTeams protocol from your instructions now: you are the captain of a multi-agent team.',
58
- 'Call agent_teams_create with approval="required". Build the complete staged roster and DAG, then stop and ask the user to review the Web plan. Do not approve or start it in this same turn.',
57
+ 'The user invoked an AgentTeams slash command. Follow the AgentTeams protocol already in your system instructions. Inspect existing team state with agent_teams_status when needed.',
58
+ 'Respect the current team state. Continue an existing plan or team without recreating it. Only when no current team exists, call agent_teams_create with approval="required". Build the complete staged roster and DAG, then stop and ask the user to review the Web plan. Do not approve or start it in this same turn.',
59
59
  ];
60
60
  if (profile !== undefined) {
61
- lines.push(`Use configured AgentTeams profile "${profile}" when calling agent_teams_create.`);
61
+ lines.push(`Use profile="${profile}" when creating a new team.`);
62
62
  if (taskPlanning === 'captain') {
63
63
  lines.push('This profile supplies the roster and guardrails. After create, do not recreate members.', 'Derive the smallest useful task graph from the goal while the team is staged; do not ask the user whether to split, merge, serialize, or parallelize.', 'Independent supplemental work must become separate ready tasks so idle members can run in parallel. Add dependencies only for genuine prerequisites and later synthesis.');
64
64
  }
package/lib/index.js CHANGED
@@ -2,7 +2,9 @@
2
2
  * AgentTeams for DeepSeek Harness.
3
3
  *
4
4
  * A host-plane plugin that registers the `agent_teams_*` tools and one usage
5
- * section into the global system prompt. After installation any session can
5
+ * agent-scoped usage section. Each session keeps a stable tool set and core
6
+ * instructions. Existing business tools cover the complete team lifecycle.
7
+ * After installation any session can
6
8
  * run multi-agent teamwork through natural language (e.g. "use AgentTeams to research X"):
7
9
  * the model creates a team (it becomes the captain), spawns members as
8
10
  * durable continuable subagents, breaks the goal into tasks with
@@ -17,7 +19,8 @@
17
19
  * @module dsh-agent-teams
18
20
  */
19
21
  import z from '@deepseek-ai/schemastery';
20
- import { haltTeamWork, registerAgentTeamsTools, } from "./tools.js";
22
+ import { createUserMessage } from '@deepseek-ai/dsh-llm';
23
+ import { haltTeamWork, stagedPlanApprovedContext, registerAgentTeamsTools, } from "./tools.js";
21
24
  import { installAgentTeamsGestureBoundary, registerAgentTeamsCommand } from "./command.js";
22
25
  import { readFile } from 'node:fs/promises';
23
26
  import { join } from 'node:path';
@@ -25,7 +28,8 @@ import { fileURLToPath } from 'node:url';
25
28
  import { collectArchivedTeamsActivity, collectTeamsActivity } from "./snapshot.js";
26
29
  import { findTeamByCaptain } from "./state.js";
27
30
  import { formatProfilesForPrompt } from "./profiles.js";
28
- import { qualityPlanningPrompt } from "./quality-gates.js";
31
+ import { installTeamCapabilities } from "./capabilities.js";
32
+ import { TEAM_TOOL_NAMES } from "./tool-names.js";
29
33
  import { authenticatedWebRoutes, readJsonRequest, RequestBodyError } from "./web-routes.js";
30
34
  /** Web-server service key candidates, newest first. */
31
35
  const WEB_SERVER_KEYS = ['webServer', 'httpServer'];
@@ -83,18 +87,17 @@ export const Config = z.object({
83
87
  });
84
88
  /** The model-facing usage policy: when and how to drive AgentTeams. */
85
89
  export function usageSectionText(toolNames, profilesText = '') {
86
- return `When the user asks to run something with AgentTeams (e.g. "use AgentTeams to do X"), or an activation message from the /agent-teams slash command arrives, you are the captain of a multi-agent team. Follow this protocol:
87
- 1. Call agent_teams_create with a team name, the goal as description, and approval="required". This creates a staged plan and must not spawn members or schedule work. Use approval="automatic" only when the user explicitly asks to skip review and run immediately.
88
- 2. Call agent_teams_add_member once per role the goal needs (researcher, engineer, reviewer, ...). In staging these are editable roster entries, not running subagents. By default a member snapshots your current provider/model/reasoning route; use a different route only when the goal or user requires it.
89
- 3. Analyze the goal and create the smallest useful task DAG while staged. Every agent_teams_create_task call must include a non-empty subject, including verification and review tasks. Independent work should be parallel; dependencies are only genuine prerequisites. Finish the complete roster and DAG, tell the user the Web plan is ready, then end this turn. Never call agent_teams_approve during the planning turn. The user may click Approve & Run, explicitly approve in a later user turn, return to chat to request changes, or discard the plan. The review UI injects an authoritative control message for return/discard actions: follow it exactly and never infer that a missing or paused team should be recreated. When the user returns to chat, first ask one concise clarification question without editing or recreating; after their answer, call agent_teams_edit_plan once with an ordered atomic batch, update downstream dependencies/assignees before removals, summarize the revision, and wait for review again. Never inspect or edit .agent-teams state files or plugin source code to revise a plan. Only explicit approval may call agent_teams_approve.
90
- 4. After approval, the final member configuration is spawned atomically and the scheduler starts ready work. Lead by delegation: monitor with agent_teams_status, send guidance with agent_teams_send_message, and let idle teammates execute ready work. Do not duplicate a teammate's work merely because its turn is slow. If the user requires every member to contribute or report, create one task per required contribution (or message each member directly); never wait for an unassigned member to produce work it was never given.
91
- 5. If the user explicitly asks to pause a running member, its open attempt remains parked after interruption; after answering the user, send that same member guidance with agent_teams_send_message so it continues the same attempt. Do not interrupt members for an ordinary user question that did not request a pause. If work must change owner, restart from scratch, or be taken over, call agent_teams_reassign_task first. Prefer another idle member or a retry with the same member. Use assignee=captain only for one ready task that you will personally drive to a terminal status in this same turn; never start a second captain takeover while one is unfinished, and never end your turn with captain-owned work open. Reassignment revokes the old attempt and waits for that member to quiesce, preventing late results from overwriting the new attempt.
92
- 6. Tasks carry attempt_id capabilities. Members must use the current attempt_id for updates; stale-attempt errors mean ownership changed. After dispatch, end your turn while members work: mailbox progress deliveries will wake you. Read status after a delivery or an explicit user request until every required task is terminal and every member is idle/ready; do not busy-poll or require reports from members with no assigned work.
93
- 7. If the user names a configured profile / template / fixed roster, pass that name as profile= to agent_teams_create. After a successful profile create, do not recreate the same members. Seed profiles provide their template tasks; captain-planning profiles provide only the roster and guardrails, so you must design their DAG while staged. Add repair or retry tasks when review/test fails, but never make a new task depend on a failed task. Do not send_message to start the next stage; the scheduler assigns ready work after approval. Watch every required task until it is terminal before deleting the team. Never perform a real deployment without explicit user confirmation.
94
- 8. Quality kinds (requirements, implementation, verification, review, repair, integration) need a contract: non-empty objective and acceptance; implementation/repair also need inScope and verify. Review/requirements can complete only with verdict=pass; needs_revision/reject must fail with findings. The system then opens repair + next review that depend on the successful source, never the failed review. Do not approve your own implementation. create_task no longer silently resumes a halted team — call agent_teams_resume with a reason, or create_task({resume:true, resumeReason}).
95
- 9. ${qualityPlanningPrompt()}
96
- 10. Present the team's results to the user, then agent_teams_delete the team unless the user wants to keep working with it. Stopping a team aborts the Captain's current turn as well as member work; only a later explicit user turn may resume it.
97
-
90
+ return `AgentTeams captain protocol:
91
+ 1. Inspect current team state when needed, using agent_teams_status. Continue existing work without duplicating its roster/tasks. Create only when no current team exists, with the user's goal as description and approval="required"; automatic approval requires an explicit request to run immediately. Staged plans never spawn or schedule work.
92
+ 2. Add each needed role once; members inherit your model route unless another is requested/needed. A requested profile goes to create({profile}); it supplies its roster. Seed profiles also supply tasks; captain-planning profiles require your DAG. Do not duplicate either.
93
+ 3. Build the complete smallest useful DAG while staged. Every task needs a subject; dependencies represent prerequisites. Give every required contributor a task or explicit message. Present the plan and end your turn for review; never approve in that planning turn. Approve only after a later explicit user approval or the Web action.
94
+ 4. Respect Web approve/return/discard control messages. On return, ask what to change before editing; after the answer, use one atomic agent_teams_edit_plan batch (edit downstream references before removals), summarize and await review again. Never inspect or edit .agent-teams state files or plugin source code to revise plans. Discard does not authorize a replacement.
95
+ 5. The scheduler dispatches ready tasks after approval. Delegate; do not duplicate slow work or send messages merely to start a stage. Handle reports/user work, then yield when waiting is all that remains: reports wake you automatically. Use status after a delivery or user request, never busy-poll or wait for unassigned members.
96
+ 6. Tasks carry attempt_id capabilities. Use the current attempt_id; stale means ownership changed. Pause members only on explicit request; later guidance via send_message continues that same attempt. Retry, transfer or take over through reassign_task first; it revokes the old attempt and waits for quiescence. Prefer a member. Captain implementation/review takeover requires a user request. Every takeover is one ready task at a time, finished in this turn; never yield with captain-owned work open.
97
+ 7. Quality kinds (requirements, implementation, verification, review, repair, integration) require objective + acceptance; implementation/repair also require inScope + verify. Derive paths/commands from the workspace/profile, never assume src/ or pnpm test. Review/requirements complete only with verdict=pass; needs_revision/reject fail with findings. Never approve your own implementation or ask for a deliberate failure.
98
+ 8. When full quality mode is requested: requirements → implementation → verification → review → integration. Plan the entire DAG while staged, including implementation before requirements finishes and integration depending on review round 1. Failed review automatically adds repair + next review and rewires pending downstream gates. Do not recreate this loop, omit integration or depend on a failed task. Review acceptance judges the latest implementation. Do not put smoke-test scripts into task instructions.
99
+ 9. Halted means the user stopped work (including the captain turn). Resume only on a later explicit user request with a reason, via agent_teams_resume or create_task({resume:true,resumeReason}); creating tasks alone never resumes. Escalated means the review loop hit its limit, not a halt. Deployment requires explicit user confirmation.
100
+ 10. Wait for all required tasks to be terminal and members idle/ready, present results, then delete/archive unless the user wants to continue. Never discard unfinished work without authorization.
98
101
  Tools: ${toolNames}${profilesText === '' ? '' : `\n\n${profilesText}`}`;
99
102
  }
100
103
  export function apply(ctx, config) {
@@ -113,28 +116,15 @@ export function apply(ctx, config) {
113
116
  // concurrent activation — so capability validation happens at the first
114
117
  // member spawn (`spawnMember`), the earliest point the provider list is
115
118
  // settled, rather than here.
116
- const toolNames = [
117
- 'agent_teams_create',
118
- 'agent_teams_approve',
119
- 'agent_teams_edit_plan',
120
- 'agent_teams_add_member',
121
- 'agent_teams_remove_member',
122
- 'agent_teams_create_task',
123
- 'agent_teams_reassign_task',
124
- 'agent_teams_claim_task',
125
- 'agent_teams_update_task',
126
- 'agent_teams_send_message',
127
- 'agent_teams_status',
128
- 'agent_teams_resume',
129
- 'agent_teams_delete',
130
- ].join(', ');
131
- ctx.systemPrompt.section({
132
- name: 'agent-teams:usage',
133
- order: config.promptSectionOrder ?? 117,
134
- text: () => usageSectionText(toolNames, formatProfilesForPrompt(config.profiles ?? {})),
135
- });
136
- // Exported for TDD / docs checks. Not a public runtime API.
137
119
  const agentTeamsRuntime = registerAgentTeamsTools(ctx, resolved);
120
+ installTeamCapabilities(ctx, {
121
+ stateDir: resolved.stateDir,
122
+ isPendingMember: agentTeamsRuntime.isPendingMember,
123
+ order: config.promptSectionOrder,
124
+ // Keep the bounded profile directory available without extra tool calls.
125
+ // installTeamCapabilities snapshots this once; no business state rewrites it.
126
+ captainPrompt: () => usageSectionText(TEAM_TOOL_NAMES.join(', '), formatProfilesForPrompt(config.profiles)),
127
+ });
138
128
  // Deterministic activation surfaces: the closed-namespace `/agent-teams`
139
129
  // host command (surfaces in the Web GUI slash menu via the Harness
140
130
  // ui-commands client) and the plain-text gesture boundary for surfaces
@@ -289,6 +279,20 @@ export function apply(ctx, config) {
289
279
  try {
290
280
  if (action === 'approve') {
291
281
  const approved = await agentTeamsRuntime.approveStagedTeam(captain, teamId);
282
+ // The browser receives the HTTP result, so the model needs its own
283
+ // control message. steer wakes an idle captain or joins its next
284
+ // step; the tool approve path already returns to the model itself.
285
+ try {
286
+ captain.steer(createUserMessage({
287
+ content: [{ type: 'text', text: stagedPlanApprovedContext(team.name) }],
288
+ source: { kind: 'plugin', plugin: 'dsh-agent-teams' },
289
+ }));
290
+ }
291
+ catch (error) {
292
+ // Approval is already committed. Do not report a failed approval
293
+ // and invite a retry that could duplicate the user's action.
294
+ ctx.logger.warn(`agent-teams: approval notification failed for ${teamId}: ${String(error)}`);
295
+ }
292
296
  res.writeHead(200, { 'content-type': 'application/json; charset=utf-8', 'cache-control': 'no-store' });
293
297
  res.end(JSON.stringify({ ok: true, phase: 'running', ...approved }));
294
298
  return;
package/lib/members.js CHANGED
@@ -20,18 +20,11 @@ import { guardSubagentDelivery, installContinuableMemberSetup, queueMemberPrompt
20
20
  import { acknowledgeMailbox, appendMailbox, CAPTAIN_KEY, createMessage, readRetiredMemberIds, readTeamSync, readTeam, releaseMailboxDelivery, withTeamLock, writeTeam } from "./state.js";
21
21
  import { appendTeamEvent, captainSessionOf } from "./events.js";
22
22
  import { TERMINAL_TASK_STATUSES } from "./types.js";
23
+ import { CAPTAIN_TOOL_NAMES } from "./tool-names.js";
23
24
  /** Persona snapshot of a profile protocol; the full text lives on team.json. */
24
25
  export const PERSONA_PROTOCOL_MAX_CHARS = 400;
25
26
  /** Captain-only AgentTeams tools hidden from newly spawned members. */
26
- const MEMBER_DENIED_TOOLS = [
27
- 'agent_teams_create',
28
- 'agent_teams_add_member',
29
- 'agent_teams_remove_member',
30
- 'agent_teams_reassign_task',
31
- 'agent_teams_create_task',
32
- 'agent_teams_resume',
33
- 'agent_teams_delete',
34
- ];
27
+ const MEMBER_DENIED_TOOLS = CAPTAIN_TOOL_NAMES;
35
28
  /**
36
29
  * Restore the SessionId brand on a value that round-tripped through the
37
30
  * durable team file. The brand is erased by JSON serialization; the value
@@ -384,6 +377,12 @@ export function installMemberSelectionRuntime(ctx, stateDir, onFailureSettled) {
384
377
  };
385
378
  });
386
379
  return {
380
+ isPendingMember(agent) {
381
+ const parent = agent.session.header.parentSession;
382
+ const descriptor = foldSubagentDescriptor(sessionOwnEvents(agent.session));
383
+ return parent !== undefined && descriptor?.mode === 'continuable'
384
+ && pending.has(pendingSelectionKey(parent, descriptor.label));
385
+ },
387
386
  async withPending(parentSessionId, label, selection, operation) {
388
387
  const key = pendingSelectionKey(parentSessionId, label);
389
388
  if (pending.has(key)) {
package/lib/profiles.js CHANGED
@@ -116,7 +116,7 @@ function formatProfileListingLine(entry) {
116
116
  ? 'captain planning'
117
117
  : countLabel(Array.isArray(entry.config.tasks) ? entry.config.tasks.length : 0, 'task');
118
118
  const counts = `(${countLabel(memberCount, 'member')}, ${graph})`;
119
- const summary = protocolSummary(entry.config.protocol);
119
+ const summary = protocolSummary(entry.config.protocol) ?? protocolSummary(entry.config.description);
120
120
  return summary === undefined
121
121
  ? `- ${entry.name} ${counts}`
122
122
  : `- ${entry.name} ${counts}: ${summary}`;
package/lib/state.js CHANGED
@@ -37,15 +37,29 @@ export async function withTeamLock(key, fn) {
37
37
  const previous = locks.get(key) ?? Promise.resolve();
38
38
  let release;
39
39
  const gate = new Promise((resolve) => { release = resolve; });
40
- locks.set(key, previous.then(() => gate));
40
+ const tail = previous.then(() => gate);
41
+ locks.set(key, tail);
41
42
  await previous;
42
43
  try {
43
44
  return await fn();
44
45
  }
45
46
  finally {
46
47
  release();
48
+ // Drop this key's queue entry once we are still its tail, so settled
49
+ // teams do not leave one resolved promise chained forever (the same
50
+ // cleanup discipline as the scheduler's serializeMember). A successor
51
+ // that already appended itself owns the map slot; keep its entry.
52
+ if (locks.get(key) === tail)
53
+ locks.delete(key);
47
54
  }
48
55
  }
56
+ /**
57
+ * Keys with an in-process lock queue (held or waiting), snapshot for
58
+ * diagnostics and leak checks. The queue promises themselves stay private.
59
+ */
60
+ export function teamLockQueueKeys() {
61
+ return [...locks.keys()];
62
+ }
49
63
  /** Longest key emitted before truncating and appending a digest. */
50
64
  const MAX_KEY_LENGTH = 48;
51
65
  /** Short stable digest, used to keep otherwise-colliding keys distinct. */
@@ -228,13 +242,27 @@ export async function writeTeam(stateRoot, state) {
228
242
  await atomicWriteText(join(stateRoot, state.id, 'team.json'), JSON.stringify(state, null, 2));
229
243
  }
230
244
  /** Read the durable set of member session ids retired by remove/delete. */
245
+ function parseRetiredMemberIds(raw) {
246
+ const parsed = JSON.parse(stripLeadingBom(raw));
247
+ if (!Array.isArray(parsed) || parsed.some(value => typeof value !== 'string' || value === '')) {
248
+ throw new Error('invalid AgentTeams retired member index');
249
+ }
250
+ return new Set(parsed);
251
+ }
252
+ /** Synchronous role hydration before the host's first prompt assembly. */
253
+ export function readRetiredMemberIdsSync(stateRoot) {
254
+ try {
255
+ return parseRetiredMemberIds(readFileSync(join(stateRoot, RETIRED_MEMBERS_FILE), 'utf8'));
256
+ }
257
+ catch (error) {
258
+ if (error.code === 'ENOENT')
259
+ return new Set();
260
+ throw error;
261
+ }
262
+ }
231
263
  export async function readRetiredMemberIds(stateRoot) {
232
264
  try {
233
- const parsed = JSON.parse(stripLeadingBom(await readFile(join(stateRoot, RETIRED_MEMBERS_FILE), 'utf8')));
234
- if (!Array.isArray(parsed) || parsed.some(value => typeof value !== 'string' || value === '')) {
235
- throw new Error('invalid AgentTeams retired member index');
236
- }
237
- return new Set(parsed);
265
+ return parseRetiredMemberIds(await readFile(join(stateRoot, RETIRED_MEMBERS_FILE), 'utf8'));
238
266
  }
239
267
  catch (error) {
240
268
  if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
@@ -0,0 +1,11 @@
1
+ /** Stable business API names; exposure changes never rename these operations. */
2
+ export const TEAM_TOOL_NAMES = [
3
+ 'agent_teams_create', 'agent_teams_approve', 'agent_teams_edit_plan',
4
+ 'agent_teams_add_member', 'agent_teams_remove_member', 'agent_teams_create_task',
5
+ 'agent_teams_reassign_task', 'agent_teams_claim_task', 'agent_teams_update_task',
6
+ 'agent_teams_send_message', 'agent_teams_status', 'agent_teams_resume', 'agent_teams_delete',
7
+ ];
8
+ export const MEMBER_TOOL_NAMES = [
9
+ 'agent_teams_claim_task', 'agent_teams_update_task', 'agent_teams_send_message', 'agent_teams_status',
10
+ ];
11
+ export const CAPTAIN_TOOL_NAMES = TEAM_TOOL_NAMES.filter(name => !MEMBER_TOOL_NAMES.includes(name));
package/lib/tools.js CHANGED
@@ -276,6 +276,15 @@ export async function haltTeamWork(input) {
276
276
  alreadyHalted: halted.alreadyHalted,
277
277
  };
278
278
  }
279
+ /** Web approval has no tool result in the captain's conversation. */
280
+ export function stagedPlanApprovedContext(teamName) {
281
+ return [
282
+ `The user approved the staged AgentTeams plan "${teamName}" from the pre-run review UI.`,
283
+ 'Approval has committed; the scheduler owns dispatch of the approved team. Do not approve again, recreate the roster, or send messages merely to start assigned tasks.',
284
+ 'Acknowledge the approval and handle any reports or user work already pending. Yield only when waiting for members is the remaining action. Their reports will wake you automatically; do not busy-poll status or keep a turn running just to wait.',
285
+ 'On a report, inspect the result and coordinate the next necessary action. If work has since been halted, respect that state and resume only on an explicit user request.',
286
+ ].join('\n');
287
+ }
279
288
  /** Context queued after the human rejects a staged plan. */
280
289
  export function stagedPlanDiscardContext(teamName) {
281
290
  return [
@@ -524,6 +533,7 @@ export function registerAgentTeamsTools(ctx, config) {
524
533
  return { teamId: discarded.teamId };
525
534
  };
526
535
  const runtime = {
536
+ isPendingMember: memberSelections.isPendingMember,
527
537
  updateStagedPlan,
528
538
  updateStagedPlanBatch,
529
539
  approveStagedTeam,
@@ -584,7 +594,10 @@ export function registerAgentTeamsTools(ctx, config) {
584
594
  const current = await findTeamByParticipant(stateRoot, captain.id);
585
595
  if (current !== undefined) {
586
596
  const relationship = current.captainSessionId === captain.id ? 'lead' : 'belong to';
587
- throw new Error(`you already ${relationship} team "${current.name}" — end or leave it before creating another`);
597
+ const guidance = current.captainSessionId === captain.id
598
+ ? 'Use agent_teams_status and continue the existing team. Do not delete and recreate it merely to continue work. End it only when the user explicitly wants a separate new team.'
599
+ : 'Continue your assigned member work and report to your captain; do not create a separate team.';
600
+ throw new Error(`you already ${relationship} team "${current.name}" (id ${current.id}). ${guidance}`);
588
601
  }
589
602
  return withTeamLock(teamLockKey(stateRoot, teamId), async () => {
590
603
  const existing = await readTeam(stateRoot, teamId);
@@ -1873,7 +1886,7 @@ export function registerAgentTeamsTools(ctx, config) {
1873
1886
  }));
1874
1887
  ctx.tools.register(defineTool({
1875
1888
  name: 'agent_teams_delete',
1876
- description: 'End your team: interrupts all members (best effort) and deletes the team\'s state directory (team file, tasks, mailboxes). Use when the team\'s work is done or abandoned.',
1889
+ description: 'End and archive your team: interrupts members and moves the current tasks and mailboxes out of active state for later inspection. Use when the work is done or explicitly abandoned. A same-name archive replaces its previous generation.',
1877
1890
  parameters: {},
1878
1891
  output: {
1879
1892
  schema: {
@@ -1886,7 +1899,7 @@ export function registerAgentTeamsTools(ctx, config) {
1886
1899
  },
1887
1900
  render: (args, value) => [{
1888
1901
  type: 'text',
1889
- text: `Team "${value.team_name}" deleted.`,
1902
+ text: `Team "${value.team_name}" ended and archived.`,
1890
1903
  }],
1891
1904
  },
1892
1905
  async execute(_args, exec) {
@@ -0,0 +1,14 @@
1
+ /** Stable, agent-scoped presentation. Business authority stays in the tools. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import type { Agent } from '@deepseek-ai/dsh-agent';
4
+ export declare const TEAM_ACTIVATION_PROMPT = "AgentTeams (Agent Teams) provides multi-agent team collaboration. Apply these rules when the user requests it (including /agent-teams) or when continuing an existing team. Mentioning, quoting, discussing, or declining AgentTeams alone is not a request to start work.";
5
+ export declare const TEAM_MEMBER_PROMPT = "You are an AgentTeams member. Follow your assigned member persona and task contract. Use agent_teams_claim_task, agent_teams_update_task, agent_teams_send_message and agent_teams_status for your own work. Include the current attempt_id in updates; report completion or failure to the captain. Do not create, approve, edit or resume a team. If your durable membership is unavailable, report that to the parent instead of creating a replacement.";
6
+ interface CapabilityConfig {
7
+ stateDir: string;
8
+ isPendingMember: (agent: Agent) => boolean;
9
+ captainPrompt: () => string;
10
+ order?: number;
11
+ }
12
+ /** Call once, after all business definitions have registered. Never per member. */
13
+ export declare function installTeamCapabilities(ctx: Context, config: CapabilityConfig): void;
14
+ export {};
@@ -2,7 +2,9 @@
2
2
  * AgentTeams for DeepSeek Harness.
3
3
  *
4
4
  * A host-plane plugin that registers the `agent_teams_*` tools and one usage
5
- * section into the global system prompt. After installation any session can
5
+ * agent-scoped usage section. Each session keeps a stable tool set and core
6
+ * instructions. Existing business tools cover the complete team lifecycle.
7
+ * After installation any session can
6
8
  * run multi-agent teamwork through natural language (e.g. "use AgentTeams to research X"):
7
9
  * the model creates a team (it becomes the captain), spawns members as
8
10
  * durable continuable subagents, breaks the goal into tasks with
@@ -63,6 +63,8 @@ export interface MemberLlmSelectionRequest {
63
63
  }
64
64
  /** Process-local bridge between spawn admission and synchronous child setup. */
65
65
  export interface MemberSelectionRuntime {
66
+ /** Trusted fresh-child admission, before the durable member id is written. */
67
+ isPendingMember(agent: Agent): boolean;
66
68
  /** Make one selection visible while Harness materializes the fresh child. */
67
69
  withPending<T>(parentSessionId: string, label: string, selection: MemberLlmSelection, operation: () => Promise<T>): Promise<T>;
68
70
  }
@@ -23,6 +23,11 @@ export declare const CAPTAIN_KEY = "captain";
23
23
  * @returns the mutation's result.
24
24
  */
25
25
  export declare function withTeamLock<T>(key: string, fn: () => Promise<T>): Promise<T>;
26
+ /**
27
+ * Keys with an in-process lock queue (held or waiting), snapshot for
28
+ * diagnostics and leak checks. The queue promises themselves stay private.
29
+ */
30
+ export declare function teamLockQueueKeys(): readonly string[];
26
31
  /**
27
32
  * Fold a free-form name into a safe path/key segment.
28
33
  *
@@ -101,7 +106,8 @@ export declare function readTeamSync(stateRoot: string, teamId: string): TeamSta
101
106
  * @param state - the record to persist.
102
107
  */
103
108
  export declare function writeTeam(stateRoot: string, state: TeamState): Promise<void>;
104
- /** Read the durable set of member session ids retired by remove/delete. */
109
+ /** Synchronous role hydration before the host's first prompt assembly. */
110
+ export declare function readRetiredMemberIdsSync(stateRoot: string): Set<string>;
105
111
  export declare function readRetiredMemberIds(stateRoot: string): Promise<Set<string>>;
106
112
  /** Atomically add session ids to the durable retired-member deny-list. */
107
113
  export declare function recordRetiredMemberIds(stateRoot: string, memberIds: readonly string[]): Promise<void>;
@@ -0,0 +1,4 @@
1
+ /** Stable business API names; exposure changes never rename these operations. */
2
+ export declare const TEAM_TOOL_NAMES: readonly ["agent_teams_create", "agent_teams_approve", "agent_teams_edit_plan", "agent_teams_add_member", "agent_teams_remove_member", "agent_teams_create_task", "agent_teams_reassign_task", "agent_teams_claim_task", "agent_teams_update_task", "agent_teams_send_message", "agent_teams_status", "agent_teams_resume", "agent_teams_delete"];
3
+ export declare const MEMBER_TOOL_NAMES: readonly string[];
4
+ export declare const CAPTAIN_TOOL_NAMES: ("agent_teams_create" | "agent_teams_approve" | "agent_teams_edit_plan" | "agent_teams_add_member" | "agent_teams_remove_member" | "agent_teams_create_task" | "agent_teams_reassign_task" | "agent_teams_claim_task" | "agent_teams_update_task" | "agent_teams_send_message" | "agent_teams_status" | "agent_teams_resume" | "agent_teams_delete")[];
@@ -62,6 +62,7 @@ export type StagedPlanMutation = {
62
62
  };
63
63
  /** Runtime bridge shared by model-facing tools and the Web staging surface. */
64
64
  export interface AgentTeamsRuntime {
65
+ isPendingMember(agent: Agent): boolean;
65
66
  updateStagedPlan(captain: Agent, teamId: string, mutation: StagedPlanMutation, signal?: AbortSignal): Promise<TeamState>;
66
67
  updateStagedPlanBatch(captain: Agent, teamId: string, mutations: readonly StagedPlanMutation[], signal?: AbortSignal): Promise<TeamState>;
67
68
  approveStagedTeam(captain: Agent, teamId: string, signal?: AbortSignal): Promise<{
@@ -88,6 +89,8 @@ export declare function haltTeamWork(input: {
88
89
  cancelledTasks: number;
89
90
  alreadyHalted: boolean;
90
91
  }>;
92
+ /** Web approval has no tool result in the captain's conversation. */
93
+ export declare function stagedPlanApprovedContext(teamName: string): string;
91
94
  /** Context queued after the human rejects a staged plan. */
92
95
  export declare function stagedPlanDiscardContext(teamName: string): string;
93
96
  /** Model-facing continuation that turns the review UI back into a conversation. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanmicoder/dsh-agent-teams",
3
- "version": "0.1.16-rc.1",
3
+ "version": "0.1.16-rc.3",
4
4
  "description": "AgentTeams for DeepSeek Harness: multi-agent team collaboration (captain, members, tasks with dependencies, messaging) driven by natural language, with a tree monitor in the web GUI",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -223,12 +223,13 @@
223
223
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
224
224
  "sync:skill": "node scripts/sync-skill.mjs",
225
225
  "verify:skill": "node scripts/sync-skill.mjs --check",
226
- "verify": "node scripts/verify.mjs && node scripts/fallback-tdd.mjs && node scripts/member-failure-tdd.mjs && node scripts/quality-gates-tdd.mjs && node scripts/lifecycle-verify.mjs && node scripts/stress-verify.mjs && pnpm verify:web-routes && pnpm verify:release && pnpm verify:skill && pnpm verify:harness-contract && pnpm verify:http-body && pnpm verify:compatibility",
226
+ "verify": "node scripts/verify.mjs && node scripts/fallback-tdd.mjs && node scripts/member-failure-tdd.mjs && node scripts/quality-gates-tdd.mjs && node scripts/lifecycle-verify.mjs && node scripts/stress-verify.mjs && pnpm verify:web-routes && pnpm verify:release && pnpm verify:skill && pnpm verify:harness-contract && pnpm verify:http-body && pnpm verify:compatibility && pnpm verify:capabilities",
227
227
  "verify:web-routes": "node scripts/web-routes-verify.mjs",
228
228
  "verify:release": "node --test scripts/release-metadata.test.mjs",
229
229
  "verify:harness-contract": "node scripts/harness-compat-tdd.mjs && node scripts/lifecycle-verify.mjs --modern-harness && node scripts/member-failure-tdd.mjs --modern-harness",
230
230
  "verify:compatibility": "node scripts/compatibility.mjs && node --test scripts/compatibility.test.mjs && node scripts/doctor.mjs",
231
231
  "verify:http-body": "node --test scripts/http-body.test.mjs",
232
- "doctor": "node scripts/doctor.mjs"
232
+ "doctor": "node scripts/doctor.mjs",
233
+ "verify:capabilities": "node --test scripts/capabilities.test.mjs"
233
234
  }
234
235
  }
@@ -0,0 +1,33 @@
1
+ # AgentTeams v0.1.16-rc.2 — unpublished candidate
2
+
3
+ This tag did not publish: the Windows verification job rejected an absolute filesystem path used as an ESM import in a test. The release gate correctly skipped npm and GitHub publication. See [v0.1.16-rc.3](./v0.1.16-rc.3.md) for the replacement candidate.
4
+
5
+ This prerelease keeps the original 13 business tools directly available, shortens the fixed team instructions, guides captains to continue existing work, and notifies the captain when a Web plan is approved. It also includes the team-lock cleanup from [#143](https://github.com/NanmiCoder/dsh-agent-teams/pull/143).
6
+
7
+ The npm channel remains `next`. Supported Harness versions remain `0.1.2-rc.1` (recommended), `0.1.2-alpha.5`, and `0.1.2-alpha.2`. This release does not change `latest` or extend support to older embedded Desktop cores.
8
+
9
+ ## Changes
10
+
11
+ - **Existing-team reuse ([#146](https://github.com/NanmiCoder/dsh-agent-teams/issues/146)).** Continue the current team and inspect state only when needed. A duplicate create error directs the captain back to the existing team instead of instructing it to end or leave first. Existing work, approval requirements and creation guards remain intact.
12
+ - **Fixed instruction overhead ([#138](https://github.com/NanmiCoder/dsh-agent-teams/issues/138)).** Keep a concise core protocol and configured profile directory in the fixed system prompt. Captains use the original 13 business tools; members see four team tools plus their ordinary coding/research tools. Team activation, approval and archive do not rewrite the plugin's system/tools prefix. No `agent_teams_open` tool or progressive-loading step is shipped.
13
+ - **Rules survive history changes.** Slash commands, natural-language requests, original tool allowlists, PTC code execution, result pruning, history compaction and cold restoration can use the business tools directly. Core rules do not depend on retaining a helper's result.
14
+ - **Web approval wakes the captain.** Approve & Run sends a control message after committing approval and starting scheduling. The captain can yield while waiting and be woken again by member reports. Repeated or failed approvals do not send a success notification.
15
+ - **Settled team locks are released.** Drop a settled queue entry only after its last waiter finishes, including rejection paths, while preserving serialization. Thanks to [spacexun2](https://github.com/spacexun2) for #143. This focused cleanup does not resolve the broader OOM investigation.
16
+
17
+ ## Verification
18
+
19
+ The release workflow requires Ubuntu and Windows typecheck/build/repository verification, then installs one packed candidate through the published CLI/Loader on all three exact Harness cohorts. Nine scenarios per host cover lifecycle, two cold restores, fallback, failure, idle wakeup, six entry paths after ordinary conversation, authenticated Web approval, and protocol/compaction compatibility. The publish job verifies and publishes that same candidate file.
20
+
21
+ Pre-release source acceptance also used the real `deepseek-official / deepseek-v4.1-flash-expires-on-0910` model with `max` reasoning. A three-member review and a follow-up from an older session containing `open` history both passed external report/task/source checks; neither called the removed tool. The follow-up retained the same team and all three member identities. See the [source acceptance record](https://github.com/NanmiCoder/dsh-agent-teams/blob/v0.1.16-rc.2/docs/no-open-verification.json) for exact artifacts and limitations.
22
+
23
+ This is not a claim of lower total model cost or identical success rates across all work. The fresh real-model trial made some unnecessary dispatch/status calls. Stable plugin prefixes avoid this plugin's lifecycle-driven prefix changes; provider cache behavior, other host context, deployment changes and cache expiry remain separate.
24
+
25
+ ## Install
26
+
27
+ Confirm that the actual running Harness belongs to the supported version matrix, then install into the profile you use:
28
+
29
+ ```sh
30
+ dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.2
31
+ ```
32
+
33
+ Restart the actual Harness process and refresh Web after upgrading. Updating a global CLI does not replace a Desktop application's embedded core.
@@ -0,0 +1,33 @@
1
+ # AgentTeams v0.1.16-rc.3
2
+
3
+ This prerelease keeps the original 13 business tools directly available, shortens the fixed team instructions, guides captains to continue existing work, and notifies the captain when a Web plan is approved. It also includes the team-lock cleanup from [#143](https://github.com/NanmiCoder/dsh-agent-teams/pull/143).
4
+
5
+ The npm channel remains `next`. Supported Harness versions remain `0.1.2-rc.1` (recommended), `0.1.2-alpha.5`, and `0.1.2-alpha.2`. This release does not change `latest` or extend support to older embedded Desktop cores.
6
+
7
+ ## Changes
8
+
9
+ - **Existing-team reuse ([#146](https://github.com/NanmiCoder/dsh-agent-teams/issues/146)).** Continue the current team and inspect state only when needed. A duplicate create error directs the captain back to the existing team instead of instructing it to end or leave first. Existing work, approval requirements and creation guards remain intact.
10
+ - **Fixed instruction overhead ([#138](https://github.com/NanmiCoder/dsh-agent-teams/issues/138)).** Keep a concise core protocol and configured profile directory in the fixed system prompt. Captains use the original 13 business tools; members see four team tools plus their ordinary coding/research tools. Team activation, approval and archive do not rewrite the plugin's system/tools prefix. No `agent_teams_open` tool or progressive-loading step is shipped.
11
+ - **Rules survive history changes.** Slash commands, natural-language requests, original tool allowlists, PTC code execution, result pruning, history compaction and cold restoration can use the business tools directly. Core rules do not depend on retaining a helper's result.
12
+ - **Web approval wakes the captain.** Approve & Run sends a control message after committing approval and starting scheduling. The captain can yield while waiting and be woken again by member reports. Repeated or failed approvals do not send a success notification.
13
+ - **Settled team locks are released.** Drop a settled queue entry only after its last waiter finishes, including rejection paths, while preserving serialization. Thanks to [spacexun2](https://github.com/spacexun2) for #143. This focused cleanup does not resolve the broader OOM investigation.
14
+
15
+ ## Verification
16
+
17
+ The rc.2 tag was blocked by a Windows-only ESM path error in the new tests; no rc.2 package was published. This release uses file URLs for dynamic imports in the capability tests and real-model benchmark runner. The lifecycle test also waits for actual downstream delivery instead of assuming scheduling finishes within 20 ms, with an intentionally delayed host delivery as regression coverage. Runtime code and assets are unchanged from the locally accepted candidate.
18
+
19
+ The release workflow requires Ubuntu and Windows typecheck/build/repository verification, then installs one packed candidate through the published CLI/Loader on all three exact Harness cohorts. Nine scenarios per host cover lifecycle, two cold restores, fallback, failure, idle wakeup, six entry paths after ordinary conversation, authenticated Web approval, and protocol/compaction compatibility. The publish job verifies and publishes that same candidate file.
20
+
21
+ Pre-release source acceptance also used the real `deepseek-official / deepseek-v4.1-flash-expires-on-0910` model with `max` reasoning. A three-member review and a follow-up from an older session containing `open` history both passed external report/task/source checks; neither called the removed tool. The follow-up retained the same team and all three member identities. See the [source acceptance record](https://github.com/NanmiCoder/dsh-agent-teams/blob/v0.1.16-rc.3/docs/no-open-verification.json) for exact artifacts and limitations.
22
+
23
+ This is not a claim of lower total model cost or identical success rates across all work. The fresh real-model trial made some unnecessary dispatch/status calls. Stable plugin prefixes avoid this plugin's lifecycle-driven prefix changes; provider cache behavior, other host context, deployment changes and cache expiry remain separate.
24
+
25
+ ## Install
26
+
27
+ Confirm that the actual running Harness belongs to the supported version matrix, then install into the profile you use:
28
+
29
+ ```sh
30
+ dsh plugin --profile web add --save-exact @nanmicoder/dsh-agent-teams@0.1.16-rc.3
31
+ ```
32
+
33
+ Restart the actual Harness process and refresh Web after upgrading. Updating a global CLI does not replace a Desktop application's embedded core.