@deepseek-ai/dsh-experimental-tool-agent-team 0.1.5-alpha.2
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +158 -0
- package/README.zh.md +158 -0
- package/lib/index.js +550 -0
- package/lib/types/index.d.ts +19 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeepSeek
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/experimental/tool-agent-team/README.md
|
|
5
|
+
README.md: 58e2853a863710ec094f61b0af04a0fe9b6bd4ce
|
|
6
|
+
README.zh.md: 63b24c3772ea277a11b2bc4c456d56bbfaa0a772
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Nine tools that let the model create, message, and coordinate teammates, for compositions mounting the experimental Team plugins."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-experimental-tool-agent-team
|
|
7
|
+
|
|
8
|
+
English | [中文](README.zh.md)
|
|
9
|
+
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
This package lets the model create named teammates, send them messages, inspect availability, wait for progress, interrupt stuck work, and coordinate through a shared task board. Every team member receives the same nine tools and guidance for coordinating in a shared workspace. Choose it when the model should operate a team only after you explicitly request one. It replaces legacy subagent controls with the same tool names, so compositions that need both must disable the legacy definitions. The package is published under its experimental name and provides no stability guarantee.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
Add this package on top of `@deepseek-ai/dsh-experimental-agent-team` when the model should run a team through tools. Once mounted, every team member — the Lead and each teammate — gets the same nine tools plus a policy paragraph that states its own role and name.
|
|
29
|
+
|
|
30
|
+
### When to choose it
|
|
31
|
+
|
|
32
|
+
Choose it when the model should create and coordinate teammates by itself rather than a human driving subagent controls. Avoid it when the legacy global subagent tools with the same names must stay available: the team tools replace them for team members, so a composition that wants both must disable the legacy definitions. The fixed policy creates teammates only when you explicitly ask for a team or teammates, so ordinary tasks never trigger delegation on their own.
|
|
33
|
+
|
|
34
|
+
### Smallest working example
|
|
35
|
+
|
|
36
|
+
The smallest addition to an existing composition is the two-package fragment from the [agent-team README](../agent-team/README.md#smallest-working-setup): durable session storage, the team domain package, and this package. The plugin itself takes two optional settings:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- id: tool-agent-team
|
|
40
|
+
name: '@deepseek-ai/dsh-experimental-tool-agent-team'
|
|
41
|
+
config:
|
|
42
|
+
freshProvider: spawn
|
|
43
|
+
forkProvider: fork
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| Field | Default | Meaning |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `freshProvider` | `spawn` | Provider that starts fresh teammates |
|
|
49
|
+
| `forkProvider` | `fork` | Provider that starts fork teammates |
|
|
50
|
+
|
|
51
|
+
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-experimental-tool-agent-team) is the exhaustive source for every accepted field and its JSDoc.
|
|
52
|
+
|
|
53
|
+
Try it by asking the Lead model: "create a teammate named reviewer to check the diff, then send reviewer the change summary". The model calls the creation tool and then the messaging tool.
|
|
54
|
+
|
|
55
|
+
### What the model can do
|
|
56
|
+
|
|
57
|
+
The nine tools group into four capabilities:
|
|
58
|
+
|
|
59
|
+
- **Create a teammate** — `spawn_teammate` takes a name, a description, and the initial task; only the Lead can call it.
|
|
60
|
+
- **Send messages** — `send_message` steers a running member at its nearest step boundary, starts an idle member, and cold-resumes an inactive teammate.
|
|
61
|
+
- **See and wait** — `list_agents` shows the roster with live status; `wait_agent` waits for the next team change; `interrupt_agent` stops a teammate's current turn (Lead only).
|
|
62
|
+
- **Manage the task board** — `team_task_create`, `team_task_list`, `team_task_get`, and `team_task_update` add, browse, read, and update shared tasks.
|
|
63
|
+
|
|
64
|
+
Any member can message any other member and use the task board; only the Lead creates and interrupts teammates. Task updates keep the domain's owner and revision checks, so an outdated edit is rejected instead of overwriting newer work.
|
|
65
|
+
|
|
66
|
+
### What success and failure look like
|
|
67
|
+
|
|
68
|
+
Sending a message succeeds as soon as it is safely stored: the result is `accepted` (delivered now) or `queued` (waiting), and a queued message must not be resent. `wait_agent` returns `noProgress` right away when no other member is running or provisioning, telling the caller to wake a teammate first; otherwise it waits for the next change and the caller re-reads state afterward. Task edits based on an outdated revision are rejected rather than overwriting newer work.
|
|
69
|
+
|
|
70
|
+
-----
|
|
71
|
+
|
|
72
|
+
<a id="understand-the-implementation"></a>
|
|
73
|
+
## Understand the implementation
|
|
74
|
+
|
|
75
|
+
<details>
|
|
76
|
+
<summary>Implementation internals — click to expand</summary>
|
|
77
|
+
|
|
78
|
+
This section explains the design decisions behind the adapter and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
79
|
+
|
|
80
|
+
### Design philosophy
|
|
81
|
+
|
|
82
|
+
The adapter is built on three commitments:
|
|
83
|
+
|
|
84
|
+
- **Scoped, not global.** Every registration lives on the member Agent's own `ctx`; nothing is installed for non-Team subagents or the host.
|
|
85
|
+
- **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.
|
|
86
|
+
- **The domain owns authority.** Tools delegate to `ctx.agentTeams`, which enforces Lead authority and revision checks; the adapter adds no weaker path.
|
|
87
|
+
|
|
88
|
+
The [Agent Teams Agent Note](../../../.agents/notes/implemented/feature/2026-08-05-agent-teams.md) owns the model-facing and scoping decisions.
|
|
89
|
+
|
|
90
|
+
### Source map
|
|
91
|
+
|
|
92
|
+
| File | Role |
|
|
93
|
+
|---|---|
|
|
94
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: config, the fixed policy text, and the nine scoped tool registrations |
|
|
95
|
+
| — | No runtime invariant companion is published; the Team service owns durable and authorization relations. |
|
|
96
|
+
|
|
97
|
+
### Policy and tools
|
|
98
|
+
|
|
99
|
+
One `team:policy` section on the member scope teaches each member its role and the coordination rules; the fixed text and the nine tool registrations are declared in [`src/index.ts`](src/index.ts). The nine tool schemas appear only in Team member scopes, so non-Team subagents keep the default catalog. Scoped registrations with the same names as the legacy global continuable-subagent controls shadow those globals for team members only.
|
|
100
|
+
|
|
101
|
+
### Scoped registration and teardown
|
|
102
|
+
|
|
103
|
+
`maybeInstall` runs for every live Agent and subscribes to `agent/created`; it skips Agents without Team membership. Disposal of an Agent runs the installed disposer, and plugin HMR disposes every installed scope before reinstall. Each disposer unwinds registrations in reverse order, so a failed install cannot leave a partial scope.
|
|
104
|
+
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
-----
|
|
108
|
+
|
|
109
|
+
<a id="further-exploration"></a>
|
|
110
|
+
## Further Exploration
|
|
111
|
+
|
|
112
|
+
Read these pages when the package-level contract is not enough. They move from the domain service to the exact schemas and the decisions behind the design.
|
|
113
|
+
|
|
114
|
+
- [agent-team package](../agent-team/README.md) — the `ctx.agentTeams` domain service behind these tools.
|
|
115
|
+
- [Agent Teams subsystem](../../../docs/subsystems/agent-team.md) — durable Team types and service API.
|
|
116
|
+
- [Generated tool catalog](../../../docs/tool-catalog.md#deepseek-aidsh-experimental-tool-agent-team) — every tool schema the model receives.
|
|
117
|
+
- [Agent Teams Agent Note](../../../.agents/notes/implemented/feature/2026-08-05-agent-teams.md) — model-facing, scoping, and isolation decisions.
|
|
118
|
+
|
|
119
|
+
-----
|
|
120
|
+
|
|
121
|
+
<a id="model-experience"></a>
|
|
122
|
+
## Model Experience
|
|
123
|
+
|
|
124
|
+
### Team policy and tools
|
|
125
|
+
|
|
126
|
+
#### What the model sees
|
|
127
|
+
|
|
128
|
+
One stable policy section states the exact Team role/name/id, the explicit-delegation requirement, shared-cwd behavior, filesystem stale-version recovery, Bash/formatter/codegen risk, task and write-scope coordination, Steer delivery, the no-retry mailbox rule, and the Lead's duty to wait before answering. The nine Team schemas from `spawn_teammate` through `team_task_update` appear only in Team member scopes.
|
|
129
|
+
|
|
130
|
+
#### Token effect
|
|
131
|
+
|
|
132
|
+
Fixed policy and schema cost on every Team member request. Tool calls add compact JSON roster, task, wait, or receipt results. Peer content is retained by the Team domain in the target's history.
|
|
133
|
+
|
|
134
|
+
#### KV Cache effect
|
|
135
|
+
|
|
136
|
+
Prefix-stable while the Team plugin generation, configuration, member role/name, and schemas remain unchanged. The per-member identity line differs across Agents. Tool results and peer messages append after the reusable request prefix.
|
|
137
|
+
|
|
138
|
+
## Known Limitations and Deferred Work
|
|
139
|
+
|
|
140
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
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.
|
|
144
|
+
|
|
145
|
+
- **Prompt policy is coordination, not confinement** — it cannot stop Bash or external processes from writing overlapping files.
|
|
146
|
+
- **No autonomous team creation** — ordinary tasks do not trigger delegation unless the user explicitly requests it.
|
|
147
|
+
- **No Web controls** — browser roster and task-board presentation is outside this runtime package.
|
|
148
|
+
- **Experimental prototype with no stability promise** — the package is public, but its schemas can change freely while it incubates.
|
|
149
|
+
|
|
150
|
+
<a id="dev-note"></a>
|
|
151
|
+
### Dev Note
|
|
152
|
+
|
|
153
|
+
<details>
|
|
154
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
155
|
+
|
|
156
|
+
None.
|
|
157
|
+
|
|
158
|
+
</details>
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "九个让模型创建、发消息与协调 teammate 的工具,供组合实验性 Team 插件的部署方阅读。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @deepseek-ai/dsh-experimental-tool-agent-team
|
|
7
|
+
|
|
8
|
+
[English](README.md) | 中文
|
|
9
|
+
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
本包让模型创建具名 teammate、向它们发送消息、查看可用状态、等待进展、中断卡住的工作,并通过共享任务板协调。每个团队成员都会获得相同的九个工具,以及在共享工作区协调的指引。当模型只应在你明确要求后运行团队时,选择本包。它会取代同名的旧版 subagent 控件,因此同时需要两者的组合必须禁用旧定义。本包以实验性名称公开发布,但不提供稳定性保证。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
当模型应该通过工具运行一支团队时,在 `@deepseek-ai/dsh-experimental-agent-team` 之上挂载本包。挂载后,每个团队成员——Lead 与每个 teammate——都会获得相同的九个工具,外加一段说明自身角色与名字的策略段落。
|
|
29
|
+
|
|
30
|
+
### 何时选择
|
|
31
|
+
|
|
32
|
+
当模型应该自行创建与协调 teammate、而不是由人来操作 subagent 控件时,选择它。当同名的旧全局 subagent 工具必须继续可用时,请不要选择:团队工具会为团队成员取代它们,因此想同时使用两者的组合必须禁用旧定义。固定策略只在明确要求团队或 teammate 时创建成员,因此普通任务永远不会自行触发委派。
|
|
33
|
+
|
|
34
|
+
### 最小工作示例
|
|
35
|
+
|
|
36
|
+
对现有组合的最小增量是 [agent-team README](../agent-team/README.zh.md#smallest-working-setup) 中的两包片段:持久会话存储、团队领域包与本包。插件本身只有两个可选设置:
|
|
37
|
+
|
|
38
|
+
```yaml
|
|
39
|
+
- id: tool-agent-team
|
|
40
|
+
name: '@deepseek-ai/dsh-experimental-tool-agent-team'
|
|
41
|
+
config:
|
|
42
|
+
freshProvider: spawn
|
|
43
|
+
forkProvider: fork
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
| 字段 | 默认值 | 含义 |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| `freshProvider` | `spawn` | 启动 fresh teammate 的 provider |
|
|
49
|
+
| `forkProvider` | `fork` | 启动 fork teammate 的 provider |
|
|
50
|
+
|
|
51
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-experimental-tool-agent-team)是每个受支持字段及其 JSDoc 的穷尽式真源。
|
|
52
|
+
|
|
53
|
+
试试这样要求 Lead 模型:「创建一个名为 reviewer 的 teammate 检查 diff,再把变更摘要发给 reviewer」。模型会调用创建工具,然后调用消息工具。
|
|
54
|
+
|
|
55
|
+
### 模型能做什么
|
|
56
|
+
|
|
57
|
+
九个工具分为四类能力:
|
|
58
|
+
|
|
59
|
+
- **创建 teammate**——`spawn_teammate` 接收名字、描述与初始任务;只有 Lead 可以调用它。
|
|
60
|
+
- **发送消息**——`send_message` 在最近的步骤边界 Steer running member、启动 idle member,并冷恢复 inactive teammate。
|
|
61
|
+
- **查看与等待**——`list_agents` 显示带实时状态的 roster;`wait_agent` 等待下一次团队变化;`interrupt_agent` 停止 teammate 的当前轮次(仅限 Lead)。
|
|
62
|
+
- **管理任务板**——`team_task_create`、`team_task_list`、`team_task_get` 与 `team_task_update` 添加、浏览、读取与更新共享任务。
|
|
63
|
+
|
|
64
|
+
任何成员都可以给任何其他成员发消息并使用任务板;只有 Lead 可以创建与中断 teammate。任务更新保留领域的 owner 与 revision 校验,因此过期的编辑会被拒绝,而不是覆盖更新的成果。
|
|
65
|
+
|
|
66
|
+
### 成功与失败的表现
|
|
67
|
+
|
|
68
|
+
发送消息在安全存储后即成功:结果为 `accepted`(已送达)或 `queued`(等待中),排队的消息绝不能重发。当没有其他成员 running 或 provisioning 时,`wait_agent` 会立即返回 `noProgress`,提示调用方先唤醒 teammate;否则它会等待下一次变化,调用方随后重新读取状态。基于过期 revision 的任务编辑会被拒绝,而不是覆盖更新的成果。
|
|
69
|
+
|
|
70
|
+
-----
|
|
71
|
+
|
|
72
|
+
<a id="understand-the-implementation"></a>
|
|
73
|
+
## 理解实现
|
|
74
|
+
|
|
75
|
+
<details>
|
|
76
|
+
<summary>实现细节——点击展开</summary>
|
|
77
|
+
|
|
78
|
+
本节解释适配器背后的设计决策并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
79
|
+
|
|
80
|
+
### 设计理念
|
|
81
|
+
|
|
82
|
+
适配器建立在三项承诺之上:
|
|
83
|
+
|
|
84
|
+
- **按作用域,而非全局。** 每个注册都位于成员 Agent 自己的 `ctx` 上;非 Team subagent 或宿主不会安装任何内容。
|
|
85
|
+
- **声明式结果,紧凑 JSON。** 每个工具都声明完整结果 schema,并把该值渲染为紧凑 JSON,因此编译器会对照模型被承诺的值检查 `execute`,任何结果都不会在缩进上消耗 token。
|
|
86
|
+
- **领域拥有权限。** 工具委托给 `ctx.agentTeams`,后者强制执行 Lead 权限与 revision 校验;适配器不添加更弱的路径。
|
|
87
|
+
|
|
88
|
+
[Agent Teams Agent Note](../../../.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md)负责模型侧与 scoping 决策。
|
|
89
|
+
|
|
90
|
+
### 源码地图
|
|
91
|
+
|
|
92
|
+
| 文件 | 职责 |
|
|
93
|
+
|---|---|
|
|
94
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:配置、固定策略文本与九个 scoped 工具注册 |
|
|
95
|
+
| — | 不发布运行时不变式伴生入口;Team 服务拥有持久化与授权关系。 |
|
|
96
|
+
|
|
97
|
+
### 策略与工具
|
|
98
|
+
|
|
99
|
+
member scope 上的一个 `team:policy` 段落教每个成员自己的角色与协作规则;固定文本与九个工具注册都声明在 [`src/index.ts`](src/index.ts)。九个工具 schema 只出现在 Team member scope 中,因此非 Team subagent 保持默认目录。与旧全局 continuable-subagent 控件同名的 scoped 注册只会为团队成员覆盖这些全局控件。
|
|
100
|
+
|
|
101
|
+
### 按作用域注册与拆除
|
|
102
|
+
|
|
103
|
+
`maybeInstall` 对每个 live Agent 运行,并订阅 `agent/created`;它跳过没有 Team 成员关系的 Agent。Agent dispose 会运行已安装的 disposer,插件 HMR 会在重新安装前处置每个已安装的 scope。每个 disposer 按逆序展开注册,因此失败的安装不会留下残缺 scope。
|
|
104
|
+
|
|
105
|
+
</details>
|
|
106
|
+
|
|
107
|
+
-----
|
|
108
|
+
|
|
109
|
+
<a id="further-exploration"></a>
|
|
110
|
+
## 进一步探索
|
|
111
|
+
|
|
112
|
+
当包级约定不够用时阅读以下页面。它们从领域服务逐步进入精确 schema 与设计背后的决策。
|
|
113
|
+
|
|
114
|
+
- [agent-team 包](../agent-team/README.zh.md)——这些工具背后的 `ctx.agentTeams` 领域服务。
|
|
115
|
+
- [Agent Teams 子系统](../../../docs/subsystems/agent-team.zh.md)——持久 Team 类型与服务 API。
|
|
116
|
+
- [生成的工具目录](../../../docs/tool-catalog.zh.md#deepseek-aidsh-experimental-tool-agent-team)——模型接收的每个工具 schema。
|
|
117
|
+
- [Agent Teams Agent Note](../../../.agents/notes/implemented/feature/2026-08-05-agent-teams.zh.md)——模型侧、scoping 与隔离决策。
|
|
118
|
+
|
|
119
|
+
-----
|
|
120
|
+
|
|
121
|
+
<a id="model-experience"></a>
|
|
122
|
+
## 模型体验
|
|
123
|
+
|
|
124
|
+
### Team 策略与工具
|
|
125
|
+
|
|
126
|
+
#### 模型看到什么
|
|
127
|
+
|
|
128
|
+
一段稳定策略会说明确切 Team role/name/id、显式 delegation 要求、共享 cwd 行为、文件 stale-version 恢复、Bash/formatter/codegen 风险、task/write-scope 协调、Steer 投递、mailbox 不重试规则,以及 Lead 必须在回答前等待。`spawn_teammate` 到 `team_task_update` 的九个 Team schema 只出现在 Team member scope。
|
|
129
|
+
|
|
130
|
+
#### Token 影响
|
|
131
|
+
|
|
132
|
+
每次 Team member 请求都有固定策略与 schema 成本。工具调用会增加紧凑 JSON roster、task、wait 或 receipt 结果。Peer 内容由 Team 领域保留在 target 历史中。
|
|
133
|
+
|
|
134
|
+
#### KV Cache 影响
|
|
135
|
+
|
|
136
|
+
Team 插件 generation、配置、member role/name 与 schema 不变时,前缀保持稳定。每个成员的身份行不同。工具结果与 peer 消息追加在可复用请求前缀之后。
|
|
137
|
+
|
|
138
|
+
## 已知限制与延期工作
|
|
139
|
+
|
|
140
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
这些限制说明策略与工具无法为一支团队保证什么。它们是当前包约束,不是与其他协作表面的对比。
|
|
144
|
+
|
|
145
|
+
- **提示词策略只负责协调,不负责 confinement**——它无法阻止 Bash 或外部进程写入重叠文件。
|
|
146
|
+
- **不会自主创建 Team**——除非用户明确要求,普通任务不会触发 delegation。
|
|
147
|
+
- **没有 Web 控制功能**——浏览器 roster 与任务板呈现不属于该运行时包。
|
|
148
|
+
- **实验原型,无稳定性承诺**——本包公开发布,但孵化期间 schema 仍可自由变更。
|
|
149
|
+
|
|
150
|
+
<a id="dev-note"></a>
|
|
151
|
+
### 开发备注
|
|
152
|
+
|
|
153
|
+
<details>
|
|
154
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
155
|
+
|
|
156
|
+
无。
|
|
157
|
+
|
|
158
|
+
</details>
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { TeamTaskId } from "@deepseek-ai/dsh-experimental-agent-team";
|
|
3
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
4
|
+
//#region lib/types/index.js
|
|
5
|
+
/** Scoped model-facing tools for the opt-in Agent Teams runtime. */
|
|
6
|
+
/** Cordis plugin name. */
|
|
7
|
+
const name = "tool-agent-team";
|
|
8
|
+
/** Services required by the Team tool plugin. */
|
|
9
|
+
const inject = [
|
|
10
|
+
"agents",
|
|
11
|
+
"agentTeams",
|
|
12
|
+
"tools",
|
|
13
|
+
"systemPrompt"
|
|
14
|
+
];
|
|
15
|
+
/** Loader schema for the opt-in Team tool plugin. */
|
|
16
|
+
const Config = z.object({
|
|
17
|
+
freshProvider: z.string().default("spawn"),
|
|
18
|
+
forkProvider: z.string().default("fork")
|
|
19
|
+
});
|
|
20
|
+
/** Model-facing collaboration guidance shared by Lead and teammates. */
|
|
21
|
+
const POLICY = `Agent Teams is available in this session, but create teammates only when the user explicitly asks to use Agent Teams or teammates.
|
|
22
|
+
|
|
23
|
+
The Team Lead and all teammates share the same working directory and filesystem. Edits are immediately visible to every member. Split write work into disjoint scopes, record expected write scopes on shared tasks, and use task dependencies when work must be ordered. Write-scope overlap is advisory, not a lock.
|
|
24
|
+
|
|
25
|
+
Prefer read/edit/write for file changes. If a file operation returns FS_STALE_VERSION, read the current file, rebase your intended change onto the new content, and retry. Bash, formatters, code generators, and scripts are not fully protected by the filesystem version guard; coordinate them explicitly and have the Lead review the final diff and run tests.
|
|
26
|
+
|
|
27
|
+
send_message steers a running target at its nearest step boundary, starts an idle target, and cold-resumes an inactive teammate. A delivered peer item starts with its stable message id and sender name. A successful send is already durable even when its result says queued; do not resend it. Shared-task workflow is list, get, claim with the current revision, perform the work, then complete. Task readiness never starts an owner. Before wait_agent, use list_agents and make sure another required member is running or provisioning; use send_message first when the required member is inactive. wait_agent observes only changes after that call starts, never wakes a member, and returns noProgress immediately when no other member can produce a change. Re-list after wakeup or timeout. The Lead must wait for required teammates before giving the final answer.`;
|
|
28
|
+
const ACTIVE_WAIT_STATUSES = new Set(["running", "provisioning"]);
|
|
29
|
+
const NO_ACTIVE_PEER_MESSAGE = "No other Team member is running or provisioning. wait_agent cannot make progress or wake inactive teammates. Re-list with list_agents and team_task_list, then use send_message to wake each required inactive teammate before waiting again.";
|
|
30
|
+
/**
|
|
31
|
+
* One roster row, matching `TeamMemberView`. The Lead pseudo-row omits the
|
|
32
|
+
* teammate-only provisioning fields, so only identity, role, status, and
|
|
33
|
+
* diagnostics are required.
|
|
34
|
+
*/
|
|
35
|
+
const MEMBER_VIEW_SCHEMA = {
|
|
36
|
+
type: "object",
|
|
37
|
+
additionalProperties: false,
|
|
38
|
+
properties: {
|
|
39
|
+
id: {
|
|
40
|
+
type: "string",
|
|
41
|
+
required: true
|
|
42
|
+
},
|
|
43
|
+
name: {
|
|
44
|
+
type: "string",
|
|
45
|
+
required: true
|
|
46
|
+
},
|
|
47
|
+
role: {
|
|
48
|
+
type: "string",
|
|
49
|
+
required: true,
|
|
50
|
+
enum: ["lead", "teammate"]
|
|
51
|
+
},
|
|
52
|
+
status: {
|
|
53
|
+
type: "string",
|
|
54
|
+
required: true,
|
|
55
|
+
enum: [
|
|
56
|
+
"running",
|
|
57
|
+
"idle",
|
|
58
|
+
"inactive",
|
|
59
|
+
"provisioning",
|
|
60
|
+
"failed"
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
description: { type: "string" },
|
|
64
|
+
provider: { type: "string" },
|
|
65
|
+
context: {
|
|
66
|
+
type: "string",
|
|
67
|
+
enum: ["fresh", "fork"]
|
|
68
|
+
},
|
|
69
|
+
model: { type: "string" },
|
|
70
|
+
diagnostics: {
|
|
71
|
+
type: "array",
|
|
72
|
+
required: true,
|
|
73
|
+
items: { type: "string" }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
/** One shared task, matching the public `TeamTaskView`. */
|
|
78
|
+
const TASK_VIEW_SCHEMA = {
|
|
79
|
+
type: "object",
|
|
80
|
+
additionalProperties: false,
|
|
81
|
+
properties: {
|
|
82
|
+
id: {
|
|
83
|
+
type: "string",
|
|
84
|
+
required: true
|
|
85
|
+
},
|
|
86
|
+
revision: {
|
|
87
|
+
type: "integer",
|
|
88
|
+
required: true
|
|
89
|
+
},
|
|
90
|
+
subject: {
|
|
91
|
+
type: "string",
|
|
92
|
+
required: true
|
|
93
|
+
},
|
|
94
|
+
description: {
|
|
95
|
+
type: "string",
|
|
96
|
+
required: true
|
|
97
|
+
},
|
|
98
|
+
status: {
|
|
99
|
+
type: "string",
|
|
100
|
+
required: true,
|
|
101
|
+
enum: [
|
|
102
|
+
"pending",
|
|
103
|
+
"in_progress",
|
|
104
|
+
"completed",
|
|
105
|
+
"deleted"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
ownerName: { type: "string" },
|
|
109
|
+
blockedBy: {
|
|
110
|
+
type: "array",
|
|
111
|
+
required: true,
|
|
112
|
+
items: { type: "string" }
|
|
113
|
+
},
|
|
114
|
+
writeScopes: {
|
|
115
|
+
type: "array",
|
|
116
|
+
required: true,
|
|
117
|
+
items: { type: "string" }
|
|
118
|
+
},
|
|
119
|
+
ready: {
|
|
120
|
+
type: "boolean",
|
|
121
|
+
required: true
|
|
122
|
+
},
|
|
123
|
+
writeScopeWarnings: {
|
|
124
|
+
type: "array",
|
|
125
|
+
required: true,
|
|
126
|
+
items: { type: "string" }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const SPAWN_VALUE_SCHEMA = {
|
|
131
|
+
type: "object",
|
|
132
|
+
additionalProperties: false,
|
|
133
|
+
properties: { member: {
|
|
134
|
+
...MEMBER_VIEW_SCHEMA,
|
|
135
|
+
required: true
|
|
136
|
+
} }
|
|
137
|
+
};
|
|
138
|
+
const MEMBER_LIST_VALUE_SCHEMA = {
|
|
139
|
+
type: "array",
|
|
140
|
+
items: MEMBER_VIEW_SCHEMA
|
|
141
|
+
};
|
|
142
|
+
const SEND_VALUE_SCHEMA = {
|
|
143
|
+
type: "object",
|
|
144
|
+
additionalProperties: false,
|
|
145
|
+
properties: {
|
|
146
|
+
messageId: {
|
|
147
|
+
type: "string",
|
|
148
|
+
required: true
|
|
149
|
+
},
|
|
150
|
+
status: {
|
|
151
|
+
type: "string",
|
|
152
|
+
required: true,
|
|
153
|
+
enum: ["accepted", "queued"]
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
/** `noProgress` is present only on the model-only shortcut that skips the wait. */
|
|
158
|
+
const WAIT_VALUE_SCHEMA = {
|
|
159
|
+
type: "object",
|
|
160
|
+
additionalProperties: false,
|
|
161
|
+
properties: {
|
|
162
|
+
timedOut: {
|
|
163
|
+
type: "boolean",
|
|
164
|
+
required: true
|
|
165
|
+
},
|
|
166
|
+
noProgress: {
|
|
167
|
+
type: "object",
|
|
168
|
+
additionalProperties: false,
|
|
169
|
+
properties: {
|
|
170
|
+
reason: {
|
|
171
|
+
type: "string",
|
|
172
|
+
required: true,
|
|
173
|
+
const: "no-active-peer"
|
|
174
|
+
},
|
|
175
|
+
message: {
|
|
176
|
+
type: "string",
|
|
177
|
+
required: true
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const INTERRUPT_VALUE_SCHEMA = {
|
|
184
|
+
type: "object",
|
|
185
|
+
additionalProperties: false,
|
|
186
|
+
properties: { previousStatus: {
|
|
187
|
+
type: "string",
|
|
188
|
+
required: true,
|
|
189
|
+
enum: [
|
|
190
|
+
"running",
|
|
191
|
+
"idle",
|
|
192
|
+
"inactive"
|
|
193
|
+
]
|
|
194
|
+
} }
|
|
195
|
+
};
|
|
196
|
+
const TASK_LIST_VALUE_SCHEMA = {
|
|
197
|
+
type: "object",
|
|
198
|
+
additionalProperties: false,
|
|
199
|
+
properties: {
|
|
200
|
+
tasks: {
|
|
201
|
+
type: "array",
|
|
202
|
+
required: true,
|
|
203
|
+
items: TASK_VIEW_SCHEMA
|
|
204
|
+
},
|
|
205
|
+
nextCursor: { type: "integer" }
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Declare one canonical output schema with compact model-facing JSON. Every
|
|
210
|
+
* Team result is a fixed record, so the declared schema is what makes the
|
|
211
|
+
* compiler check `execute` against the value the model is promised.
|
|
212
|
+
* @param schema - canonical value schema for one tool.
|
|
213
|
+
* @returns the `output` declaration accepted by {@link defineTool}.
|
|
214
|
+
*/
|
|
215
|
+
function jsonOutput(schema) {
|
|
216
|
+
return {
|
|
217
|
+
schema,
|
|
218
|
+
render: (_args, value) => [{
|
|
219
|
+
type: "text",
|
|
220
|
+
text: JSON.stringify(value)
|
|
221
|
+
}]
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
/** Recover the exact caller guaranteed by Agent-scoped tool discovery. */
|
|
225
|
+
function callingAgent(agent, toolName) {
|
|
226
|
+
/* v8 ignore next 2 -- Team tools are registered only in an exact Agent scope, so discovery supplies this carrier. */
|
|
227
|
+
if (agent === void 0) throw new Error(`${toolName} requires a calling Agent`);
|
|
228
|
+
return agent;
|
|
229
|
+
}
|
|
230
|
+
/** Register the complete Team tool set in one exact Agent scope. */
|
|
231
|
+
function install(agent, ctx, config) {
|
|
232
|
+
const scoped = agent.ctx;
|
|
233
|
+
const disposers = [];
|
|
234
|
+
const register = (disposer) => {
|
|
235
|
+
disposers.push(disposer);
|
|
236
|
+
};
|
|
237
|
+
try {
|
|
238
|
+
register(scoped.systemPrompt.section({
|
|
239
|
+
name: "team:policy",
|
|
240
|
+
order: scoped.systemPrompt.getSectionOrder("TEAM_POLICY"),
|
|
241
|
+
text: () => {
|
|
242
|
+
const membership = ctx.agentTeams.membership(agent);
|
|
243
|
+
return `${POLICY}\n\nYour Team role is ${membership.role}; your Team name is ${membership.name}; Team id is ${membership.id}.`;
|
|
244
|
+
}
|
|
245
|
+
}));
|
|
246
|
+
register(scoped.tools.register(defineTool({
|
|
247
|
+
name: "spawn_teammate",
|
|
248
|
+
description: "Create one named, durable teammate. Only the Team Lead may call this tool.",
|
|
249
|
+
parameters: {
|
|
250
|
+
name: {
|
|
251
|
+
type: "string",
|
|
252
|
+
required: true,
|
|
253
|
+
description: "Unique lower-kebab-case teammate name."
|
|
254
|
+
},
|
|
255
|
+
description: {
|
|
256
|
+
type: "string",
|
|
257
|
+
required: true,
|
|
258
|
+
description: "Short description of the delegated responsibility."
|
|
259
|
+
},
|
|
260
|
+
prompt: {
|
|
261
|
+
type: "string",
|
|
262
|
+
required: true,
|
|
263
|
+
description: "Complete initial task for the teammate."
|
|
264
|
+
},
|
|
265
|
+
context: {
|
|
266
|
+
type: "string",
|
|
267
|
+
enum: ["fresh", "fork"],
|
|
268
|
+
description: "fresh starts without Lead history; fork inherits completed Lead turns. Defaults to fresh."
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
output: jsonOutput(SPAWN_VALUE_SCHEMA),
|
|
272
|
+
async execute(args, exec) {
|
|
273
|
+
const agent = callingAgent(exec.agent, "spawn_teammate");
|
|
274
|
+
const context = args.context ?? "fresh";
|
|
275
|
+
return await ctx.agentTeams.spawnTeammate(agent, {
|
|
276
|
+
name: args.name,
|
|
277
|
+
description: args.description,
|
|
278
|
+
prompt: [{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: args.prompt
|
|
281
|
+
}],
|
|
282
|
+
context,
|
|
283
|
+
provider: context === "fork" ? config.forkProvider : config.freshProvider,
|
|
284
|
+
signal: exec.signal
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
})));
|
|
288
|
+
register(scoped.tools.register(defineTool({
|
|
289
|
+
name: "send_message",
|
|
290
|
+
description: "Send one durable message to another Team member. A running target receives it at the nearest step boundary; an idle target starts a turn; an inactive teammate cold-resumes.",
|
|
291
|
+
parameters: {
|
|
292
|
+
target: {
|
|
293
|
+
type: "string",
|
|
294
|
+
required: true,
|
|
295
|
+
description: "Team member name, or lead."
|
|
296
|
+
},
|
|
297
|
+
message: {
|
|
298
|
+
type: "string",
|
|
299
|
+
required: true,
|
|
300
|
+
description: "Self-contained message for the target."
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
output: jsonOutput(SEND_VALUE_SCHEMA),
|
|
304
|
+
execute(args, exec) {
|
|
305
|
+
return ctx.agentTeams.sendMessage(callingAgent(exec.agent, "send_message"), {
|
|
306
|
+
target: args.target,
|
|
307
|
+
content: [{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: args.message
|
|
310
|
+
}],
|
|
311
|
+
signal: exec.signal
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
})));
|
|
315
|
+
register(scoped.tools.register(defineTool({
|
|
316
|
+
name: "list_agents",
|
|
317
|
+
description: "List the Lead and every durable teammate with current runtime status.",
|
|
318
|
+
parameters: {},
|
|
319
|
+
output: jsonOutput(MEMBER_LIST_VALUE_SCHEMA),
|
|
320
|
+
async execute(_args, exec) {
|
|
321
|
+
return Promise.resolve(ctx.agentTeams.listMembers(callingAgent(exec.agent, "list_agents")));
|
|
322
|
+
}
|
|
323
|
+
})));
|
|
324
|
+
register(scoped.tools.register(defineTool({
|
|
325
|
+
name: "wait_agent",
|
|
326
|
+
description: "Wait for the next teammate status, mailbox, or shared-task change after this call starts. This never wakes inactive members and returns noProgress immediately when no other member is running or provisioning. Re-list after wakeup or timeout instead of polling.",
|
|
327
|
+
parameters: { timeout_ms: {
|
|
328
|
+
type: "integer",
|
|
329
|
+
description: "Wait duration in milliseconds, from 10000 through 3600000. Defaults to 30000."
|
|
330
|
+
} },
|
|
331
|
+
output: jsonOutput(WAIT_VALUE_SCHEMA),
|
|
332
|
+
async execute(args, exec) {
|
|
333
|
+
const caller = callingAgent(exec.agent, "wait_agent");
|
|
334
|
+
const timeoutMs = args.timeout_ms ?? 3e4;
|
|
335
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1e4 || timeoutMs > 36e5) return await ctx.agentTeams.waitForChange(caller, timeoutMs, exec.signal);
|
|
336
|
+
if (!ctx.agentTeams.listMembers(caller).some((member) => member.id !== caller.id && ACTIVE_WAIT_STATUSES.has(member.status))) return {
|
|
337
|
+
timedOut: false,
|
|
338
|
+
noProgress: {
|
|
339
|
+
reason: "no-active-peer",
|
|
340
|
+
message: NO_ACTIVE_PEER_MESSAGE
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
return await ctx.agentTeams.waitForChange(caller, timeoutMs, exec.signal);
|
|
344
|
+
}
|
|
345
|
+
})));
|
|
346
|
+
register(scoped.tools.register(defineTool({
|
|
347
|
+
name: "interrupt_agent",
|
|
348
|
+
description: "Interrupt one teammate's current turn while preserving its pending inbox. Team Lead only.",
|
|
349
|
+
parameters: { target: {
|
|
350
|
+
type: "string",
|
|
351
|
+
required: true,
|
|
352
|
+
description: "Teammate name."
|
|
353
|
+
} },
|
|
354
|
+
output: jsonOutput(INTERRUPT_VALUE_SCHEMA),
|
|
355
|
+
async execute(args, exec) {
|
|
356
|
+
return Promise.resolve(ctx.agentTeams.interrupt(callingAgent(exec.agent, "interrupt_agent"), args.target));
|
|
357
|
+
}
|
|
358
|
+
})));
|
|
359
|
+
register(scoped.tools.register(defineTool({
|
|
360
|
+
name: "team_task_create",
|
|
361
|
+
description: "Create one unowned pending task on the shared Team task board.",
|
|
362
|
+
parameters: {
|
|
363
|
+
subject: {
|
|
364
|
+
type: "string",
|
|
365
|
+
required: true,
|
|
366
|
+
description: "Concise task title."
|
|
367
|
+
},
|
|
368
|
+
description: {
|
|
369
|
+
type: "string",
|
|
370
|
+
required: true,
|
|
371
|
+
description: "Complete task details and acceptance criteria."
|
|
372
|
+
},
|
|
373
|
+
blocked_by: {
|
|
374
|
+
type: "array",
|
|
375
|
+
items: { type: "string" },
|
|
376
|
+
description: "Task ids that must complete first."
|
|
377
|
+
},
|
|
378
|
+
write_scopes: {
|
|
379
|
+
type: "array",
|
|
380
|
+
items: { type: "string" },
|
|
381
|
+
description: "Advisory workspace-relative file or directory prefixes this task expects to modify."
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
output: jsonOutput(TASK_VIEW_SCHEMA),
|
|
385
|
+
async execute(args, exec) {
|
|
386
|
+
return await ctx.agentTeams.createTask(callingAgent(exec.agent, "team_task_create"), {
|
|
387
|
+
subject: args.subject,
|
|
388
|
+
description: args.description,
|
|
389
|
+
...args.blocked_by === void 0 ? {} : { blockedBy: args.blocked_by.map(TeamTaskId) },
|
|
390
|
+
...args.write_scopes === void 0 ? {} : { writeScopes: args.write_scopes }
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
})));
|
|
394
|
+
register(scoped.tools.register(defineTool({
|
|
395
|
+
name: "team_task_list",
|
|
396
|
+
description: "List shared tasks, including readiness, owner, revision, blockers, and write-scope warnings.",
|
|
397
|
+
parameters: {
|
|
398
|
+
status: {
|
|
399
|
+
type: "string",
|
|
400
|
+
enum: [
|
|
401
|
+
"pending",
|
|
402
|
+
"in_progress",
|
|
403
|
+
"completed"
|
|
404
|
+
],
|
|
405
|
+
description: "Optional exact status filter."
|
|
406
|
+
},
|
|
407
|
+
owner: {
|
|
408
|
+
type: "string",
|
|
409
|
+
description: "Optional member-name filter; use unowned for tasks without an owner."
|
|
410
|
+
},
|
|
411
|
+
ready: {
|
|
412
|
+
type: "boolean",
|
|
413
|
+
description: "Optional readiness filter."
|
|
414
|
+
},
|
|
415
|
+
cursor: {
|
|
416
|
+
type: "integer",
|
|
417
|
+
description: "Zero-based result offset. Defaults to 0."
|
|
418
|
+
},
|
|
419
|
+
limit: {
|
|
420
|
+
type: "integer",
|
|
421
|
+
description: "Number of rows, 1 through 100. Defaults to 50."
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
output: jsonOutput(TASK_LIST_VALUE_SCHEMA),
|
|
425
|
+
execute(args, exec) {
|
|
426
|
+
const status = args.status;
|
|
427
|
+
const filtered = ctx.agentTeams.listTasks(callingAgent(exec.agent, "team_task_list")).filter((task) => (status === void 0 || task.status === status) && (args.owner === void 0 || (args.owner === "unowned" ? task.ownerName === void 0 : task.ownerName === args.owner)) && (args.ready === void 0 || task.ready === args.ready));
|
|
428
|
+
const cursor = args.cursor ?? 0;
|
|
429
|
+
const limit = args.limit ?? 50;
|
|
430
|
+
if (!Number.isSafeInteger(cursor) || cursor < 0) throw new Error("cursor must be a non-negative safe integer");
|
|
431
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) throw new Error("limit must be an integer from 1 through 100");
|
|
432
|
+
return Promise.resolve({
|
|
433
|
+
tasks: filtered.slice(cursor, cursor + limit),
|
|
434
|
+
...cursor + limit < filtered.length ? { nextCursor: cursor + limit } : {}
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
})));
|
|
438
|
+
register(scoped.tools.register(defineTool({
|
|
439
|
+
name: "team_task_get",
|
|
440
|
+
description: "Read the complete latest value of one shared task before changing or executing it.",
|
|
441
|
+
parameters: { task_id: {
|
|
442
|
+
type: "string",
|
|
443
|
+
required: true,
|
|
444
|
+
description: "Shared task id."
|
|
445
|
+
} },
|
|
446
|
+
output: jsonOutput(TASK_VIEW_SCHEMA),
|
|
447
|
+
async execute(args, exec) {
|
|
448
|
+
return Promise.resolve(ctx.agentTeams.getTask(callingAgent(exec.agent, "team_task_get"), TeamTaskId(args.task_id)));
|
|
449
|
+
}
|
|
450
|
+
})));
|
|
451
|
+
register(scoped.tools.register(defineTool({
|
|
452
|
+
name: "team_task_update",
|
|
453
|
+
description: "Compare-and-set a shared task action using the latest revision from team_task_get or team_task_list.",
|
|
454
|
+
parameters: {
|
|
455
|
+
task_id: {
|
|
456
|
+
type: "string",
|
|
457
|
+
required: true,
|
|
458
|
+
description: "Shared task id."
|
|
459
|
+
},
|
|
460
|
+
expected_revision: {
|
|
461
|
+
type: "integer",
|
|
462
|
+
required: true,
|
|
463
|
+
description: "Current task revision used as the CAS precondition."
|
|
464
|
+
},
|
|
465
|
+
action: {
|
|
466
|
+
type: "string",
|
|
467
|
+
required: true,
|
|
468
|
+
enum: [
|
|
469
|
+
"claim",
|
|
470
|
+
"release",
|
|
471
|
+
"edit",
|
|
472
|
+
"set_dependencies",
|
|
473
|
+
"complete",
|
|
474
|
+
"reopen",
|
|
475
|
+
"reassign",
|
|
476
|
+
"delete"
|
|
477
|
+
],
|
|
478
|
+
description: "Task transition to apply."
|
|
479
|
+
},
|
|
480
|
+
subject: {
|
|
481
|
+
type: "string",
|
|
482
|
+
description: "Replacement title for edit."
|
|
483
|
+
},
|
|
484
|
+
description: {
|
|
485
|
+
type: "string",
|
|
486
|
+
description: "Replacement details for edit."
|
|
487
|
+
},
|
|
488
|
+
blocked_by: {
|
|
489
|
+
type: "array",
|
|
490
|
+
items: { type: "string" },
|
|
491
|
+
description: "Complete blocker list for set_dependencies."
|
|
492
|
+
},
|
|
493
|
+
write_scopes: {
|
|
494
|
+
type: "array",
|
|
495
|
+
items: { type: "string" },
|
|
496
|
+
description: "Replacement advisory write scopes for edit."
|
|
497
|
+
},
|
|
498
|
+
owner: {
|
|
499
|
+
type: "string",
|
|
500
|
+
description: "Member name for Lead-only reassign; omit to unassign."
|
|
501
|
+
}
|
|
502
|
+
},
|
|
503
|
+
output: jsonOutput(TASK_VIEW_SCHEMA),
|
|
504
|
+
async execute(args, exec) {
|
|
505
|
+
return await ctx.agentTeams.updateTask(callingAgent(exec.agent, "team_task_update"), {
|
|
506
|
+
taskId: TeamTaskId(args.task_id),
|
|
507
|
+
expectedRevision: args.expected_revision,
|
|
508
|
+
action: args.action,
|
|
509
|
+
...args.subject === void 0 ? {} : { subject: args.subject },
|
|
510
|
+
...args.description === void 0 ? {} : { description: args.description },
|
|
511
|
+
...args.blocked_by === void 0 ? {} : { blockedBy: args.blocked_by.map(TeamTaskId) },
|
|
512
|
+
...args.write_scopes === void 0 ? {} : { writeScopes: args.write_scopes },
|
|
513
|
+
...args.owner === void 0 ? {} : { owner: args.owner }
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
})));
|
|
517
|
+
} catch (error) {
|
|
518
|
+
for (const dispose of disposers.reverse()) dispose();
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
return () => {
|
|
522
|
+
for (const dispose of disposers.reverse()) dispose();
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
/** Install Team tools in every live or subsequently published Team member scope. */
|
|
526
|
+
function apply(ctx, config = {}) {
|
|
527
|
+
const resolved = {
|
|
528
|
+
freshProvider: config.freshProvider ?? "spawn",
|
|
529
|
+
forkProvider: config.forkProvider ?? "fork"
|
|
530
|
+
};
|
|
531
|
+
const installed = /* @__PURE__ */ new Map();
|
|
532
|
+
const maybeInstall = (agent) => {
|
|
533
|
+
if (installed.has(agent) || ctx.agentTeams.tryMembership(agent) === void 0) return;
|
|
534
|
+
installed.set(agent, install(agent, ctx, resolved));
|
|
535
|
+
};
|
|
536
|
+
for (const agent of ctx.agents.list()) maybeInstall(agent);
|
|
537
|
+
ctx.on("agent/created", ({ agent }) => {
|
|
538
|
+
maybeInstall(agent);
|
|
539
|
+
});
|
|
540
|
+
ctx.on("agent/disposed", ({ agent }) => {
|
|
541
|
+
installed.get(agent)?.();
|
|
542
|
+
installed.delete(agent);
|
|
543
|
+
});
|
|
544
|
+
ctx.effect(() => () => {
|
|
545
|
+
for (const dispose of installed.values()) dispose();
|
|
546
|
+
installed.clear();
|
|
547
|
+
}, "tool-team.scopedTools()");
|
|
548
|
+
}
|
|
549
|
+
//#endregion
|
|
550
|
+
export { Config, apply, inject, name };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Scoped model-facing tools for the opt-in Agent Teams runtime. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
/** Cordis plugin name. */
|
|
5
|
+
export declare const name = "tool-agent-team";
|
|
6
|
+
/** Services required by the Team tool plugin. */
|
|
7
|
+
export declare const inject: string[];
|
|
8
|
+
/** Tool routing configuration. */
|
|
9
|
+
export interface Config {
|
|
10
|
+
/** Continuable-subagent provider used for fresh teammates. */
|
|
11
|
+
readonly freshProvider?: string;
|
|
12
|
+
/** Continuable-subagent provider used for completed-prefix fork teammates. */
|
|
13
|
+
readonly forkProvider?: string;
|
|
14
|
+
}
|
|
15
|
+
/** Loader schema for the opt-in Team tool plugin. */
|
|
16
|
+
export declare const Config: z<Config>;
|
|
17
|
+
/** Install Team tools in every live or subsequently published Team member scope. */
|
|
18
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepseek-ai/dsh-experimental-tool-agent-team",
|
|
3
|
+
"description": "Scoped model-facing Agent Teams tools over ctx.agentTeams",
|
|
4
|
+
"version": "0.1.5-alpha.2",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/experimental/tool-agent-team"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./src/*": "./src/*",
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"lib/index.js",
|
|
26
|
+
"lib/types/**/*.d.ts"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
|
|
34
|
+
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
|
|
35
|
+
"@deepseek-ai/dsh-experimental-agent-team": "^0.1.5-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
|
|
38
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-alpha.2",
|
|
42
|
+
"@deepseek-ai/dsh-agent-loop": "^0.1.5-alpha.2",
|
|
43
|
+
"@deepseek-ai/dsh-agent-loop-testkit": "^0.1.5-alpha.2",
|
|
44
|
+
"@deepseek-ai/dsh-session": "^0.1.5-alpha.2",
|
|
45
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.5-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-subagent": "^0.1.5-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-session-query": "^0.1.5-alpha.2",
|
|
48
|
+
"@deepseek-ai/dsh-scope": "^0.1.5-alpha.2",
|
|
49
|
+
"@deepseek-ai/dsh-subagent-fork-in-process": "^0.1.5-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-tool-subagent-control": "^0.1.5-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-experimental-agent-team": "^0.1.5-alpha.2",
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.5-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-subagent-spawn-in-process": "^0.1.5-alpha.2"
|
|
57
|
+
}
|
|
58
|
+
}
|