dsh-team 0.2.1
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.md +216 -0
- package/cordis.patch.yml +32 -0
- package/lib/client.js +4967 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +591 -0
- package/lib/index.js +2141 -0
- package/package.json +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 huxint
|
|
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.md
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# dsh-team — DeepSeek Harness 的 Agent Team
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
给 dsh 加一支可以指挥的团队:主会话作为 **leader**,可以派生若干**常驻队友(teammate)**,队友有自己的会话、记忆与工具;成员之间通过**邮箱**互发消息(消息成为收件人的下一个 turn),共享一份**任务列表**;会话视图环里多出一个 **Agent 团队**页签,把花名册、协作关系与消息流画成一间能看见的协作室。设计理念参考 Claude Code 的 agent team(共享任务列表 + 邮箱直连 + 成员自协调),实现完全走 dsh 的能力缝。
|
|
6
|
+
|
|
7
|
+
整个能力是**一个包、一行装配**:宿主半边(`dsh-team`)与浏览器半边(`dsh-team/client`)从同一个 `package.json` 构建。
|
|
8
|
+
|
|
9
|
+
## 能力一览
|
|
10
|
+
|
|
11
|
+
- **具名常驻队友**:`team_spawn` 把一个 `ctx.subagents` 的 continuable 子代变成团队成员——会话、日志、冷恢复、活动驻留与中断都由 harness 负责,队友不占会话树。
|
|
12
|
+
- **成员邮箱**:`team_send` 把消息变成收件人的下一个 turn;投递由 leader 权威执行,队友之间的转发有会话预算。
|
|
13
|
+
- **共享任务列表**:`team_task` 建 / 改 / 结案,`team_list` 看花名册、任务与最近流量。
|
|
14
|
+
- **虚拟工作区**:共享黑板 + 每人一块私有便笺(`team_note` / `team_board`),落进 storage domain,跨重启、不占 turn。
|
|
15
|
+
- **协作室页签**:会话视图环里的第三个页签,实时展示成员座位、走动、消息流、工作区与任务板。
|
|
16
|
+
|
|
17
|
+
## 设计
|
|
18
|
+
|
|
19
|
+
### 队友 = continuable subagent
|
|
20
|
+
|
|
21
|
+
队友必须有会话(要记忆、要日志、要能恢复),所以问题不是"别建会话",而是"别建一个普通会话"。`ctx.subagents.startContinuable()` 建的子代天然满足:
|
|
22
|
+
|
|
23
|
+
- `childSessionMeta` 打上 `origin: 'subagent'` → 会话树不展示,也不参与通用 Host 路由;
|
|
24
|
+
- durable child id + descriptor 由缝持有 → dsh 重启后队友**冷恢复**,团队不丢;
|
|
25
|
+
- 队友的 transcript 仍可读:内置的 subagent 目录里它们是 `continuable` 子代,标签是 `名字 (角色)`,协作室里点一个成员就打开。
|
|
26
|
+
|
|
27
|
+
本插件在这之上只补三样 subagent 缝故意不提供的东西:**具名成员**、**成员之间的投递**、**一份共享任务列表**。
|
|
28
|
+
|
|
29
|
+
### 投递权威永远是 leader 的
|
|
30
|
+
|
|
31
|
+
`ctx.subagents.followup()` 只认 durable 直接父级的权威,而 leader 正是每个队友的直接父级。所以 peer↔peer 消息也是"由 leader 权威执行、消息源里写明真实发送者"的一次投递:
|
|
32
|
+
|
|
33
|
+
- `relation` 决定**谁可以要求**投递(`managed` 只能发给 leader,`peer` 可以直接发给任何成员),
|
|
34
|
+
- 从不决定**谁来执行**投递(永远是 leader 权威)。
|
|
35
|
+
|
|
36
|
+
消息落到收件人自己的日志里,source 是 `{ kind: 'team-message', form: 'relay', senderSessionId, senderName, chainId, hop }`——发送者归属与会话深度都随持久化一起留存。
|
|
37
|
+
|
|
38
|
+
### 队友之间不会聊到天荒地老
|
|
39
|
+
|
|
40
|
+
`team_send` 把消息变成对方的下一个 turn——两个 peer 互相礼貌回复就能永远转下去,而这一切**不在用户的主对话里**,没人踩刹车。所以投递带**会话预算**,是机械约束而不是提示词祈祷:
|
|
41
|
+
|
|
42
|
+
- **一次对话(chain)**:leader 每发一条消息就开一条新链(`hop = 0`);队友发消息时**继承它当前正在处理的那条投递**的链,`hop + 1`。所以"leader 交办 → A 问 B → B 答 A"是同一次对话,而不是三条互不相干的消息。
|
|
43
|
+
- **深度上限**(`maxChainHops`,默认 4):一次对话在队友之间最多转这么多手,超了 `team_send` 直接拒绝。
|
|
44
|
+
- **同一有序对不许来回磨**(`maxChainRoundTrips`,默认 2):一条链里 A→B 最多这么多条。
|
|
45
|
+
- **一字不差的重发直接拒**:同一条链里同一有序对重复同样的内容,对收件人不产生任何新信息。
|
|
46
|
+
- **出口永远开着**:以上三条**只管队友→队友**。发给 leader 从不拒绝。所以预算不会把"有话要说"的成员困住,它只是把话逼回收敛点——拒绝语本身就是"settle it yourself and report to the leader"。
|
|
47
|
+
- **为什么 leader 那一侧不设限**:leader 的每个 turn 都在用户看得见、能中断的主对话里,链走到 leader 就已经收敛了。真正危险的是**看不见的**横向循环。
|
|
48
|
+
- **深度是持久事实**:`hop` 写进投递的 `team-message` 源里,跟着收件人的日志一起存,所以协作室的气泡上能看到"第 n 跳"——第 3 跳起变警告色。深度不是只有拒绝时才存在的东西,它一直是可观测的。
|
|
49
|
+
|
|
50
|
+
链的执行状态(谁在处理哪条链、每条边发过几次)是**进程内**的,随 leader 的 live team 一起建立与丢弃,最多记住最近 64 条链;重启后重新开始——重启本身就断链。
|
|
51
|
+
|
|
52
|
+
### 两个虚拟工作区
|
|
53
|
+
|
|
54
|
+
队友做完一件事,除了发消息告诉别人,还应该有个地方**把它写下来**。所以团队有两个**虚拟**工作区——它们不是文件,也不在用户的真实工作目录里:
|
|
55
|
+
|
|
56
|
+
- **共享黑板**:全队可读可写的具名条目(结论、约定、交接物)。
|
|
57
|
+
- **私有便笺**:每个成员一块只有自己能读写的板子,用来把自己的状态带过 turn 边界。
|
|
58
|
+
|
|
59
|
+
工具是 `team_note`(写/删,`private: true` 走自己的便笺)和 `team_board`(不带 key 读索引,带 key 读全文),leader 和队友都有。
|
|
60
|
+
|
|
61
|
+
**为什么不落在会话日志里**:队友的工具调用只落在**它自己**的日志里,leader 的折叠永远读不到——这正是"任务列表只能 leader 写"这条限制的根源。所以工作区落在 `ctx.storageDomain`(`@deepseek-ai/dsh-storage-domain`):写入先落盘再更新内存,跨重启存活,**队友可以直接写**,而且不占任何人的一个 turn。
|
|
62
|
+
|
|
63
|
+
**这也是防死循环的正向出口**:留一条笔记不花 turn、不花会话预算;给同级发消息两样都花。提示里明确这么讲。
|
|
64
|
+
|
|
65
|
+
**面板看到的是快照**:durable 工作区不在任何会话日志里,所以协作室里的"共享工作区"是**主会话最后一次读或写时**的样子——`team_note` / `team_board` 的结果 meta 带着整份共享索引(只有索引与首行预览,不带正文;私有便笺从不进投影)。面板上写着快照时间,不假装自己是实时的。
|
|
66
|
+
|
|
67
|
+
工作区随团队走:解散整队清掉这支团队的全部区域,解雇一名队友清掉它的私有便笺(`team/changed` 带上 `ended` / `removedMember`)。没有 storage-domain 的部署照常用团队,只是这两个工具**根本不会注册**——没人会看到一个写不下去的工具。
|
|
68
|
+
|
|
69
|
+
### leader 不在场时,队友不失联
|
|
70
|
+
|
|
71
|
+
主会话被卸载(用户关掉、进程重启、residency 回收)而队友还在跑,是常态而不是异常。**投递确实停了**——每次投递都跑在 leader 的父级权威上,没有 live 的 leader 就没有权威——但"投递停了"不等于"你没有团队":
|
|
72
|
+
|
|
73
|
+
- **拒绝语分得清两件事**:`LEADER_AWAY`("团队还在,只是主会话没加载,把结果写进 `team_note`,leader 回来会读到")与 `NO_TEAM`("这里还没有团队,用 `team_spawn` 开一支")。
|
|
74
|
+
- **身份提示段落同样分得清**:花名册读不到时,段落会说"团队还在、主会话没加载、把活收个尾写进黑板";真正被解雇的成员才会看到"团队已经不在了"。段落每次组装都重算,所以 leader 一回来,下一步就自动恢复成完整花名册。
|
|
75
|
+
- **工作区不受影响**:队友的席位(`leaderId` / `memberId` / 名字)是**组装时就捕获**的,不需要每次调用回去问 leader。所以 `team_note` / `team_board` 在 leader 不在场时照常可写可读——这正是把它做成 durable 而不是会话日志的收益。
|
|
76
|
+
|
|
77
|
+
### 只折叠一次
|
|
78
|
+
|
|
79
|
+
rc.6 的 `Session.append` 无法把事件标成 `ignorable`,因此**仓库外插件不能新增会话事件类型**(否则卸载插件后旧日志会拒绝加载)。所以团队的每条持久事实都骑在 harness 已认识的词汇上:
|
|
80
|
+
|
|
81
|
+
- 团队工具自己 `tool/result` 的 `meta`(`presentationMeta` 带**整个实体**,不是增量);
|
|
82
|
+
- `user/message` 的消息源(`team-message` / 内置的 `subagent-report` / `subagent-settled`)。
|
|
83
|
+
|
|
84
|
+
`src/fold.ts` 是唯一的折叠实现,`src/projection.ts` 把它注册成 `team` session projection:宿主算一次,框架负责推给浏览器(历史尾巴基线 + `session/projection` 帧),客户端只决定"现在显示哪个会话的值"。
|
|
85
|
+
|
|
86
|
+
### 工具作用域
|
|
87
|
+
|
|
88
|
+
- leader 侧:`agent/created` 时把 6 个工具注册进**该 agent 自己的 ctx**(普通 subagent 继承全局注册表,但不继承别人的 agent scope,所以看不到)。
|
|
89
|
+
- 队友侧:`registerContinuableSetup` 在子代**未发布的组装窗口**里注册 `team_send`、`team_list`、身份提示段落与(可选的)思考强度;不属于任何团队的子代拿到的是空的 disposer。
|
|
90
|
+
|
|
91
|
+
## 模型看到的工具
|
|
92
|
+
|
|
93
|
+
| 工具 | 谁能用 | 作用 |
|
|
94
|
+
|---|---|---|
|
|
95
|
+
| `team_spawn` | leader | 派生队友:`name` / `task` / `relation`,可选 `role`、`persona`、`model`、`reasoning_effort` |
|
|
96
|
+
| `team_send` | leader + 队友 | 邮箱。收件人写队友名、成员 id 或 `leader`;消息成为对方的下一个 turn,不等回复 |
|
|
97
|
+
| `team_task` | leader | 共享任务列表:不带 `task_id` 是新建,带则更新;`assignee` 支持名字或 id |
|
|
98
|
+
| `team_relation` | leader | `managed` ⇄ `peer` 升降级 |
|
|
99
|
+
| `team_dismiss` | leader | 解雇一个队友;不给参数则解散整队(中断当前工作,transcript 仍可读) |
|
|
100
|
+
| `team_list` | leader + 队友 | 花名册(含 `running` / `idle` / `ready` 实时状态)、任务列表、最近邮箱流量 |
|
|
101
|
+
| `team_note` | leader + 队友 | 往共享黑板(或 `private: true` 的私有便笺)写一条笔记;不给 `text` 即删除 |
|
|
102
|
+
| `team_board` | leader + 队友 | 读工作区:不带 `key` 是索引,带 `key` 是全文 |
|
|
103
|
+
|
|
104
|
+
队友汇报用的是 harness 内置的 `report`(`@deepseek-ai/dsh-tool-subagent-report` 在子代作用域里注册,且不受 `toolFilter` 影响)——汇报会以 `subagent-report` 源落进 leader 日志,被同一份折叠记进消息流。
|
|
105
|
+
|
|
106
|
+
每个写操作的工具卡片都有自己的标题(`Spawn teammate Alice` / `Alice joined the team` / `Message Alice` / `New task: …` / `Team disbanded`),失败时直接把拒绝原因写在卡片上;写操作一律 `isConcurrencySafe: () => false`,不会被并行调度打散。
|
|
107
|
+
|
|
108
|
+
## 模型与思考强度
|
|
109
|
+
|
|
110
|
+
- `model`:走 `AgentOptions.model`,只影响这一个队友;省略则继承 leader 的模型。
|
|
111
|
+
- `reasoning_effort`:不是 `AgentOptions` 的字段(它是请求头状态),所以队友作用域里挂一个 `agent/request` waterfall,把 `reasoningEffort` 钉在**这个队友自己的**每次请求上。派生时就用 `ctx.llm.resolveModelInfo()` 校验该模型是否提供这个强度,**当场失败**而不是等到队友第一次发请求。
|
|
112
|
+
- 强度记在成员事实里(随折叠持久化),冷恢复后仍然生效——subagent descriptor 本身不存这个字段。
|
|
113
|
+
|
|
114
|
+
## 团队协作室(视图页签)
|
|
115
|
+
|
|
116
|
+
团队协作室是**会话视图环里的第三个页签**:`对话 / 轨迹 / Agent 团队`(`src/client/`)。点开是一间**占满整个标签页的 2.5D 办公室**——一个能望进去的盒子:后墙加两面侧墙围着一块向远端收窄的地板,天花板上垂下一盏吊灯;家具都有顶、正、侧三个面。每个成员有自己的工位、自己的电脑,坐在自己的椅子上;要跟谁说话就**站起来走过去**。消息流、共享工作区、任务板收在**右侧竖排的三扇门(dock)**后面,点开哪扇,哪份账本就以一块**磨砂玻璃抽屉**盖在房间右侧展开。
|
|
117
|
+
|
|
118
|
+
- **有团队才有页签**:客户端跟随器发现当前会话的 `team` 投影里有成员,才把这一条 `conversation.view` 注册进去;团队解散就把注册撤回(未知的 view id 会回落到对话页,撤回不会把读者卡住)。普通会话的视图环完全不变。
|
|
119
|
+
- **房间就是整个页面**:协作室在屏幕上时,插件在 `conversation.composer` 链的**最后一位**交出一个空的输入区——房间不跟一张输入卡片分屏,也就不需要什么"剧场模式"。座位是引用计数的:离开页签立刻还回去,两次挂载重叠也只占一个。
|
|
120
|
+
- **平面与透视分离**:`room.ts` 只认识一张 0–100 的**纯几何平面**——桌位、家具占位、寻路、休息角全在上面,**不量 DOM**,画面只是持久状态的一个函数。`stagecraft.ts` 把这层平面投进一个透视盒子(地板向远端收窄、越远越挤、越远越小),外墙的五个面和每个投影点读的是**同一组常数**,墙和地板永远对得齐。移动家具,绕行路线跟着走;换一个投影,布局原地不变。
|
|
121
|
+
- **每张桌子都是一整套**:带木纹的**桌面 + 前裙板 + 侧挡板 + 挡腿板**,上面放着键盘、该席位强调色的马克杯、一沓纸、一盆小植物,桌后立着**显示器**(加大号的屏幕、支架、底座),桌前一把**办公椅**(SVG 画的网面椅背、腰靠、座垫、气杆、五爪脚轮,还会偶尔轻轻上下落一下)。全部是 token 画出来的形状,没有一张贴图。
|
|
122
|
+
- **屏幕在左肩,人在中间**:显示器立在工位后侧、屏幕面向读者;成员背对房间坐在桌前,工作时不需要看清谁的脸。图层从近到远是**椅背 → 坐着的成员 → 桌面和显示器**——显示器站在成员左肩外侧,再大也不会挡住人,人也正好夹在桌子和椅子之间。成员一旦站起来走路、去休息角、或者正在跟人说话,才转过身来露脸。
|
|
123
|
+
- **人是戴海兽头套的人**:一具人形(鞋、裤子、衬衫、两条胳膊)+ 一头**侧面**的鲸鱼或鲨鱼当兜帽——只有侧着才像它。衣服按席位轮流换**衬衫 / T恤 / 毛衣 / Polo / 卫衣 / 束腰外衣 / 马甲 / 外套 / 条纹 / 背带裤**,鞋也轮流换**运动鞋 / 短靴 / 乐福鞋 / 高帮 / 凉鞋**(带鞋带与鞋底边),头发轮流换**刘海 / 丸子头 / 卷发 / 短寸 / 马尾 / 板寸**(发色与肤色各有几档,不跟强调色走),再各带一件小物(眼镜 / 耳机 / 围巾 / 工牌 / 背包)。leader 戴蓝鲸(喷水),队友按席位轮流拿到虎鲸(背鳍 + 白眼斑)、座头鲸(长鳍 + 头瘤)、独角鲸(长牙)、白鲸(额隆)、抹香鲸(方头)、鲨鱼(背鳍 + 鳃裂)。强调色是**品牌 token 旋转出来的**(`hue-rotate`),不写死颜色。背身时头套朝另一边、后脑勺有头发、腿收进椅子里。
|
|
124
|
+
- **走路是真的走**:`routeBetween` 在**家具占位构成的可见性图**上求最短路——每个障碍矩形外扩一圈净空,只在这些角上转弯,所以走的是真的绕行而不是两条写死的通道;`smooth` 再把转角削成小弧线,急转弯变圆角。`walk.ts` 用一个 rAF 循环按**弧长**推进整趟行程:起步加速、收尾减速(而不是每条腿各自匀速),步态相位跟着走过的距离走——走得快摆得快,走得慢摆得慢;朝向除了左右还分**正身 / 背身**。位置直接写在 DOM 节点上,只有转弯和停步才触发 React 渲染,一屋子人同时走也就每帧几次样式写入。
|
|
125
|
+
- **传消息 = 走过去说**:最新一条投递会让发信人从自己的座位**走到收信人桌边**(站在旁边一步,谁也不挡谁),**到了才开口**(头顶一句截短的话),收信人转头听(`···`);这趟差事过去之后它再走回自己的位置。走的是本人,不是复制体——房间里每个成员永远只有一个。
|
|
126
|
+
- **站在哪儿,就是它此刻在做什么**:
|
|
127
|
+
|
|
128
|
+
| 成员的状态 | 房间里的样子 |
|
|
129
|
+
|---|---|
|
|
130
|
+
| 正在跑一个 turn | 坐在自己桌前,屏幕亮着它那份预设画面 |
|
|
131
|
+
| 刚收到一条消息 | 留在自己桌前读(屏幕上就是这条消息) |
|
|
132
|
+
| 自己刚汇报完、名下没有未完成任务 | 起身去休息角(沙发、茶几、绿植、饮水机) |
|
|
133
|
+
| 闲着没事 | 坐着打盹(`zZ`) |
|
|
134
|
+
|
|
135
|
+
- **屏幕上放的是预设画面**:每个席位固定一种——leader 看**仪表盘**,队友轮流拿到**代码、文档、邮件、表格、终端**——像素条全部由主题 token 混出来。它此刻在做的那件事(进行中任务标题,或最近一条发给它的消息)也写在屏幕上;没活干的屏幕是暗的。
|
|
136
|
+
- **不标区域,靠家具说话**:后墙上有**带窗洞进深、能看见云和海**的两扇窗、铝框白板(笔槽里躺着三支笔和一块板擦、贴着两张便利贴、还有没擦干净的旧字迹)、挂钟(秒针真的在走)、一组**高矮不一的**书墙架(配小盆栽和一座奖杯)、海报和月历;左边服务墙上放着打印机、咖啡机和文件柜;右手边是休息角的地毯、沙发、落地灯与饮水机(水桶里的气泡会上浮),门口偶尔还有一只猫路过。房间里没有一个"工作区""休息区"的字牌——看得出来就不用写。
|
|
137
|
+
- **绿植是一个家族**:休息角、书架、窗台和桌上放的都是 SVG 真画的植物——龟背竹、虎尾兰、绿萝(从墙上垂下来)、仙人掌、琴叶榕、棕榈,各有叶脉、明暗两层叶片、上釉陶盆和托盘;每片叶子都铰在着生点上,错开相位轻轻摆动。
|
|
138
|
+
- **闲着会自己找事做**:没事干的成员过一会儿会起身去接水、走到窗边看海、或在植物旁伸懒腰(leader 永远守着第一张桌)。两个成员在同一处站定会自动分开一点,不会叠成一个人。
|
|
139
|
+
- **名字在脚下**:名牌贴在成员脚边的地面上(不会飘到别人桌子上),未完成任务数挂在肩上,运行状态点在身侧。
|
|
140
|
+
- **右侧三扇门,各有计数**:信箱 / 工作区 / 任务板各带一枚数字徽标(消息条数、笔记条数、**未完成**任务数);抽屉关着的时候来了新投递,信箱那扇门会**亮起呼吸圈**,打开即消。同一时刻只开一扇,再点一次或点抽屉的 × 就收回去。整个房间的 sprite 被关在自己的 stacking context 里,dock 和抽屉永远浮在它们上面——哪怕有成员走到休息角,也不会挡住消息流的按钮或抽屉内容。
|
|
141
|
+
- **信箱是日志,不是聊天**(信箱抽屉里):顶上一条**花名册**,每人**一行**——头像、名字、此刻在跑还是闲着、以及它最新的一句话(截短);下面是流量本身,一条一行、越新越靠下,长内容截到一行、整句挂在 `title` 上。全队都写在**同一侧**:右边属于读者,而读者不在这里发言。`汇报` 走成功色边条,`已收工` 虚线弱化,第 3 跳起的 `hop` 徽标变警告色。鼠标停在一行上,对应成员的工位会亮起——**同一份状态的两种投影互相指认**,玻璃抽屉正好让你同时看见两边。
|
|
142
|
+
- **共享工作区是软木板**(工作区抽屉里):每条笔记是一张钉着图钉的便签,微微歪着,鼠标停上去才摆正;作者一栏带着它的小头像,悬停同样点亮本人;标题旁边写着这份快照的时间。
|
|
143
|
+
- **任务是泳道**(任务板抽屉里):待办 / 进行中 / 已完成三条泳道,道头一枚状态色圆点;卡片上指派人带小头像,结案备注跟在旁边。
|
|
144
|
+
- 点任何一个成员开对应 transcript(走 durable 的 subagent 地址,目录没拉取过就刷新后重试),点 leader 回主会话,`aria-current` 标出你正在看的那个。
|
|
145
|
+
- **解散就关灯**:不带参数的 `team_dismiss` 折叠出 `ended`,团队视图折成空,房间和页签一起收走——你此刻正读着某个队友的 transcript 也一样。
|
|
146
|
+
- **全部动效在 `prefers-reduced-motion` 下关闭**(走路的成员直接站到终点、云不飘、猫不路过、气泡不冒,抽屉直接出现)。
|
|
147
|
+
|
|
148
|
+
导航进队友会话时页签不会消失:跟随器认得"当前会话是这支团队的成员",只把"你在这儿"的标记挪过去,同时改订 **leader 的 `team` 投影**——被读的那个队友自己折不出团队,只有盯着 leader,房间才既是活的、又能在团队解散的那一刻关掉。leader 本身没加载时,房间保留它最后看到的样子。
|
|
149
|
+
|
|
150
|
+
## 安装
|
|
151
|
+
|
|
152
|
+
克隆、构建,然后挂进 web profile:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
git clone https://github.com/huxint/dsh-team.git
|
|
156
|
+
cd dsh-team
|
|
157
|
+
pnpm install
|
|
158
|
+
pnpm run build
|
|
159
|
+
dsh plugin --profile web add link:$PWD
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
包自带 `cordis.patch.yml`(`package.json` 里的 `dsh.bundle.patch` 指向它),`plugin add` 装进去即生效。web profile 的 base bundle(`@deepseek-ai/dsh-base`)已经带齐所需的 continuable subagent provider、session projection 与持久化;虚拟工作区还需要挂载 storage-domain(`@deepseek-ai/dsh-web-app` 已组合),没有它团队其余能力照常,只是 `team_note` / `team_board` 不会注册。
|
|
163
|
+
|
|
164
|
+
验证装配与启动:
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
dsh --profile web --dump-config | grep 'id: team'
|
|
168
|
+
dsh --profile web
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
在会话里让主会话调用 `team_spawn` 派生第一名队友,视图环里就会出现 `Agent 团队` 页签。
|
|
172
|
+
|
|
173
|
+
改完源码重跑 `pnpm run build`:宿主行要重启 dsh,客户端 bundle 刷新页面即可。卸载:
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
dsh plugin --profile web remove dsh-team
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## 配置(`cordis.patch.yml` 可覆写)
|
|
180
|
+
|
|
181
|
+
| 键 | 默认 | 含义 |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| `provider` | `spawn` | 派生队友用的 `ctx.subagents` provider(base bundle 提供 `spawn`) |
|
|
184
|
+
| `maxTeammates` | `8` | 单个 leader 的在册成员上限(1–64) |
|
|
185
|
+
| `maxRecentMessages` | `50` | 折叠保留、协作室显示的邮箱条数上限(1–1000) |
|
|
186
|
+
| `maxChainHops` | `4` | 一次队友间对话最多转手几次(1–64);发给 leader 不计入 |
|
|
187
|
+
| `maxChainRoundTrips` | `2` | 一条链里同一有序对最多几条消息(1–64) |
|
|
188
|
+
| `maxWorkspaceEntries` | `32` | 单个工作区区域(共享黑板 / 一块私有便笺)的条目上限(1–500) |
|
|
189
|
+
| `maxNoteChars` | `4000` | 单条笔记正文长度上限(200–200000) |
|
|
190
|
+
|
|
191
|
+
## 已知限制
|
|
192
|
+
|
|
193
|
+
- **peer↔peer 的历史只在成员自己的会话里**:leader 的折叠只看得见 leader 可见的流量,协作室同理——所以横向对话只有被预算拒绝后成员主动上报时才会进入 leader 的视野。
|
|
194
|
+
- **任务列表由 leader 写**:队友的工具调用落在自己的日志里,leader 的持久状态读不到;队友用 `report` 汇报,由 leader 记账。需要队友自己留下的东西,走共享工作区。
|
|
195
|
+
- **协作室里的共享工作区是快照**:队友直接写 durable 工作区,不经过任何会话日志,所以面板显示的是主会话最后一次读写时的索引(面板标了时间)。
|
|
196
|
+
- **输入框是"接管"不是"删除"**:协作室在屏幕上时,插件在 `conversation.composer` 链的最后一位(优先级 100)交出一个空节点。这是链式槽的正门——不碰宿主 DOM——代价是它排在所有其它接管者之后:真有一个待批准的审批要接管输入框,座位仍然归它,你在这个页签里照样能回答被卡住的 agent。
|
|
197
|
+
- **解散清得掉房间,清不掉宿主的子代理目录**:整队解散后本插件的房间与页签一起消失(折叠出 `ended`,团队视图折成空),但 harness 自己的子代理面板("N 个子代理")是由 durable 会话喂出来的,`ctx.subagents` 与会话存储都**没有"释放 / 遗忘一个子代"的接口**——插件没有正门能抹掉那些条目。这也正是"解散之后 transcript 仍然可读"的另一面。
|
|
198
|
+
- **leader 没加载时冷恢复的队友拿不到团队工具**:花名册是从 leader 的日志折出来的,组装那一刻没有 live leader 就没有花名册,这个子代只会当作普通 subagent 组装(已经组装好的队友不受影响,见上)。
|
|
199
|
+
- **Code Mode 下的团队调用不入折叠**:折叠读的是团队工具自己的 `tool/result`;`run_code` 里的嵌套调用不产生这些行(base bundle 默认不含 Code Mode)。
|
|
200
|
+
- **工具成功但结果没落日志**(极端故障)会留下一个孤儿成员:活的花名册有、折叠没有,重启后消失。
|
|
201
|
+
- 不嵌套:队友不能再开自己的团队(`NESTED_TEAM`)。
|
|
202
|
+
|
|
203
|
+
## 开发
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
pnpm run typecheck # 源码 + 测试
|
|
207
|
+
pnpm run test # 折叠 / 投影 / 服务授权矩阵 / 工具契约 / 队友组装 / 会话预算 / 虚拟工作区 / 房间几何与走路 / 客户端跟随与页签 / 协作室与抽屉
|
|
208
|
+
pnpm run build # 宿主 ESM + 浏览器闭包工厂(构建期强制客户端 bundle 纯净性)
|
|
209
|
+
pnpm run check # 三件一起
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
构建与类型针对 npm 上的 `@deepseek-ai/dsh@0.1.0-rc.6`。遵循 harness 的插件纪律:注册即 effect、能力缝三角色、事件全 JSON 整值、模型可见即落日志、配置无硬编码。
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
[MIT](./LICENSE)
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# The dsh-team bundle patch: one row over whatever base layer the profile
|
|
2
|
+
# composes. The package is its own bundle, so `dsh plugin add link:<path>`
|
|
3
|
+
# both installs and activates it — there is no separate bundle package to
|
|
4
|
+
# keep in step.
|
|
5
|
+
#
|
|
6
|
+
# The row needs `@deepseek-ai/dsh-subagent` (with a continuable provider, e.g.
|
|
7
|
+
# `subagent-spawn-in-process`), `dsh-session-projection`, and session
|
|
8
|
+
# persistence — all present in `@deepseek-ai/dsh-base`.
|
|
9
|
+
#
|
|
10
|
+
# The virtual workspaces additionally need a mounted `storage-domain` form
|
|
11
|
+
# (`@deepseek-ai/dsh-web-app` composes one). Without it the team keeps
|
|
12
|
+
# everything else and `team_note` / `team_board` are simply never registered.
|
|
13
|
+
|
|
14
|
+
- insert:
|
|
15
|
+
- id: team
|
|
16
|
+
name: 'dsh-team'
|
|
17
|
+
config:
|
|
18
|
+
# `ctx.subagents` provider that materializes teammates.
|
|
19
|
+
provider: spawn
|
|
20
|
+
# Live roster ceiling per leader session.
|
|
21
|
+
maxTeammates: 8
|
|
22
|
+
# Mailbox rows kept in the durable fold and shown on the stage.
|
|
23
|
+
maxRecentMessages: 50
|
|
24
|
+
# How far one teammate-to-teammate conversation may relay; reaching
|
|
25
|
+
# the leader is never counted and never refused.
|
|
26
|
+
maxChainHops: 4
|
|
27
|
+
# Messages one ordered member pair may exchange inside one conversation.
|
|
28
|
+
maxChainRoundTrips: 2
|
|
29
|
+
# Notes one workspace area (the shared board, or one private pad) holds.
|
|
30
|
+
maxWorkspaceEntries: 32
|
|
31
|
+
# Longest single note body.
|
|
32
|
+
maxNoteChars: 4000
|