dsh-device-fleet 0.0.0-stage → 0.1.0-dev.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.
Files changed (50) hide show
  1. package/CONTEXT.md +123 -0
  2. package/LICENSE +21 -0
  3. package/README.md +95 -2
  4. package/cordis.patch.yml +24 -0
  5. package/docs/adr/0001-fleet-enrollment-over-account-auto-discovery.md +17 -0
  6. package/docs/adr/0002-shared-room-as-coordination-unit.md +17 -0
  7. package/docs/adr/0003-direct-call-and-room-as-two-channels.md +22 -0
  8. package/docs/adr/0004-direct-call-policy-owned-by-target-device.md +23 -0
  9. package/docs/adr/0005-upgrade-session-to-room-host.md +22 -0
  10. package/docs/adr/0006-addressed-messages-and-any-device-speaks.md +16 -0
  11. package/docs/adr/0007-user-speech-decoupled-from-device.md +21 -0
  12. package/docs/adr/0008-extend-global-task-list-as-room-board.md +21 -0
  13. package/docs/adr/0009-room-policy-configured-at-creation.md +21 -0
  14. package/docs/adr/0010-room-policy-v1-fields-and-custom-room-types.md +24 -0
  15. package/docs/adr/0011-room-board-lifecycle-and-hooks.md +27 -0
  16. package/docs/adr/0012-lazy-wake-and-directed-speech.md +22 -0
  17. package/docs/adr/0013-direct-call-as-device-agnostic-functions.md +26 -0
  18. package/docs/adr/0014-fleet-call-unified-entry-with-typed-shortcuts.md +22 -0
  19. package/docs/adr/0015-room-plus-local-teams-and-delegation.md +22 -0
  20. package/docs/adr/0016-delegation-upgrade-reuses-session.md +20 -0
  21. package/docs/adr/0017-network-phases-direct-and-relay.md +21 -0
  22. package/docs/adr/0018-offline-fail-fast-and-durable-room-log.md +21 -0
  23. package/docs/adr/0019-execution-context-configured-locally.md +24 -0
  24. package/docs/adr/0020-approval-inbox-with-scoped-grants.md +22 -0
  25. package/docs/adr/0021-room-visibility-user-full-agents-scoped.md +26 -0
  26. package/docs/adr/0022-tool-adoption-mechanisms.md +23 -0
  27. package/docs/adr/0023-reuse-session-trajectory-for-call-details.md +22 -0
  28. package/docs/adr/0024-strict-version-match-with-reserved-capabilities.md +22 -0
  29. package/docs/notes/design-decisions-summary.md +49 -0
  30. package/docs/notes/dsh-global-task-list-evaluation.md +54 -0
  31. package/docs/notes/network-v1-v2-implementation.md +71 -0
  32. package/docs/notes/step1-host-plugin-and-direct-call.md +57 -0
  33. package/package.json +45 -6
  34. package/src/host/direct-call.js +190 -0
  35. package/src/host/execution-slot.js +82 -0
  36. package/src/host/index.js +6 -0
  37. package/src/host/store.js +128 -0
  38. package/src/host/tool-policy.js +150 -0
  39. package/src/host/tools.js +215 -0
  40. package/src/index.js +3 -0
  41. package/src/net/direct.js +69 -0
  42. package/src/net/discovery.js +105 -0
  43. package/src/net/fleet-node.js +414 -0
  44. package/src/net/identity.js +156 -0
  45. package/src/net/index.js +9 -0
  46. package/src/net/protocol.js +73 -0
  47. package/src/net/relay-client.js +258 -0
  48. package/src/net/relay-server.js +366 -0
  49. package/src/net/secure-channel.js +538 -0
  50. package/src/plugin/index.js +276 -0
package/CONTEXT.md ADDED
@@ -0,0 +1,123 @@
1
+ # DSH Device Fleet
2
+
3
+ 让登录同一 DeepSeek 账号、且都安装互联插件的多台设备上的 agent 与工具互相可用,使用时像在同一台设备上。
4
+
5
+ ## Language
6
+
7
+ **设备 (Device)**:
8
+ 一台运行 DSH 的机器,有自己的 Host、会话、工作区、文件系统与工具集。
9
+ _Avoid_: 节点, 客户端, 实例
10
+
11
+ **Fleet (设备联邦)**:
12
+ 一组经显式入网、互相信任的设备的集合;只有同一 Fleet 的成员可以互联。
13
+ _Avoid_: 账号设备组, 集群, 网络
14
+
15
+ **入网 (Enrollment)**:
16
+ 把一台新设备加入 Fleet 的一次性动作:由已有成员签发邀请,新设备接受后成为成员。
17
+ _Avoid_: 配对, 关联, 绑定
18
+
19
+ **邀请 (Invite)**:
20
+ 一份一次性、会过期的入网凭据,携带 Fleet 标识、会合信息与一次性 token。
21
+ _Avoid_: 配对码, 二维码(后者只是邀请的载体)
22
+
23
+ **成员 (Member)**:
24
+ 已入网的一台设备,在 Fleet 成员表中占一条记录,带设备身份、能力档位与撤销状态。
25
+ _Avoid_: 节点, 对端(对端只是单次连接的视角)
26
+
27
+ **账号 (Account)**:
28
+ 设备本机登录的 DeepSeek 账号;Fleet 要求成员登录同一账号,用于成员校验与账号切换隔离。
29
+ _Avoid_: 用户, 登录, 身份
30
+
31
+ **账号指纹 (Account fingerprint)**:
32
+ 只在 Fleet 内比较、不暴露原始账号 ID 的账号校验值;用于拒绝不同账号的设备,以及账号切换后的旧连接。
33
+ _Avoid_: 账号 ID, 账号哈希, token
34
+
35
+ ## Collaboration
36
+
37
+ **Room (协作房间)**:
38
+ 一个跨设备的共享协作上下文,等价于群聊:一份权威消息历史、成员表与共享任务板,按序广播给所有成员。
39
+ _Avoid_: 群, 会话, 频道, channel
40
+
41
+ **Host (房主设备)**:
42
+ 创建并托管 Room 的设备;房间的权威历史与成员表落在它的一个会话里,Host 只负责排序、广播与持久化,不霸占话语权。
43
+ _Avoid_: 服务端, 服务器, 主控
44
+
45
+ **Participant (参与者)**:
46
+ 一台加入 Room 的设备在房间里的身份。Participant 的发言经桥发到 Host 排序后广播;被点名时该设备上的 agent 才被唤起。
47
+ _Avoid_: 成员(成员是 Fleet 层面的概念), 节点, 协作者
48
+
49
+ **Participant Brief (参与者任务简报)**:
50
+ 按需唤醒一个 Participant 的 agent 时给它的任务上下文,类似 subagent 的初始 prompt,而不是房间的全部历史。
51
+ _Avoid_: 房间历史, 全量上下文, 会话记录
52
+
53
+ **Room Role (房间角色)**:
54
+ Room 成员的职责标签(`host` / `participant`);角色只决定谁托管权威日志,加入、退出、发言使用同一套成员接口。
55
+ _Avoid_: 主从, 等级, 权限
56
+
57
+ **Room Upgrade (房间升级)**:
58
+ 把当前会话显式升级为 Room Host,并可选地把一次既有委派的目标会话绑定为 Participant 的动作;升级保留双方上下文。
59
+ _Avoid_: 转房间, 升格, 建房
60
+
61
+ **Room Policy (房间策略)**:
62
+ 房间创建时解析并记录的一组协作规则(邀请权、加入方式、成员上限、任务板写权限等);来自插件配置/预设,创建时确定,Host 可修改并广播。
63
+ _Avoid_: 权限, 设置, 房规
64
+
65
+ **Room Type (房间类型)**:
66
+ Room Policy 的命名预设(内置或用户保存,如私密/团队/只读),创建房间时解析成一份 Room Policy。
67
+ _Avoid_: 模板, 模式
68
+
69
+ **Addressing (点名)**:
70
+ Room 消息的显式收件人集合;只有被点名的成员会被唤醒,未点名成员不跑模型但共享房间日志。
71
+ _Avoid_: @(只是显示形式), 广播(`*` 是点名的广播特例)
72
+
73
+ **User (用户)**:
74
+ 房间的人工发言者,是账号所有者本人,与从哪台设备发送无关;设备只作为审计来源。
75
+ _Avoid_: 账号(账号是登录身份), 设备用户, 客户端用户
76
+
77
+ **Room Speaker (发言者)**:
78
+ Room 消息的作者,取值为 `user` 或某个 agent 成员;与 Addressing 一起构成消息的完整路由信息。
79
+ _Avoid_: 发送端, 来源, from 字段
80
+
81
+ **Wake (唤醒)**:
82
+ 让某个成员的 agent 在其设备上跑一轮模型的按需动作;只有被点名或被分配任务块时发生。
83
+ _Avoid_: 通知, 启动, 触发
84
+
85
+ **Delegation (委派)**:
86
+ 成员 agent 把一次性任务交给另一台成员设备上的 agent 执行并取回结果;被委派方使用独立的一次性子会话,不进入 Room、不共享房间历史。
87
+ _Avoid_: 协作, 派发, 远程调用(那是工具直调)
88
+
89
+ **Local Team (本地团队)**:
90
+ 成员 agent 在自己设备上使用的 Agent Team / subagent 集合;对 Room 不可见,不被点名、不写 Room Board,只由代表 agent 把结果带回。
91
+ _Avoid_: 小团队, 下属, 子房间
92
+
93
+ ## Shared State
94
+
95
+ **Room Board (房间任务板)**:
96
+ 与 Room 同生命周期的共享结构化状态(目标、Task Block、负责人、依赖、产物指针),由 Host 持久化,成员按切片读取。
97
+ _Avoid_: 任务列表, 面板数据, 全局任务库
98
+
99
+ **Task Block (任务块)**:
100
+ Room Board 上分配给某个成员的一段可独立开始与完成的工作单元;串行块完成后触发下一块交接,并行块互不影响。
101
+ _Avoid_: 任务, 子任务, 工单
102
+
103
+ **Mirror (镜像)**:
104
+ 参与设备上 Room Board 的本地只读副本,用于面板展示与唤醒时的状态读取;写入一律回到 Host。
105
+ _Avoid_: 副本, 缓存, 同步数据
106
+
107
+ ## Execution
108
+
109
+ **Direct Call (直调)**:
110
+ A 的 agent 直接调用目标设备上的一个 tool 并取回结果的一次性调用;不进入 Room、不创建 Participant、不读取目标设备的对话上下文。
111
+ _Avoid_: 远程调用(太泛), RPC, 工具桥
112
+
113
+ **Execution Slot (执行槽)**:
114
+ 目标设备上由插件管理的隐藏执行上下文,只提供 cwd、沙箱与审批路由;不跑模型、不进 Room、不保存对话历史。
115
+ _Avoid_: 影子 agent, 执行会话, 沙箱容器
116
+
117
+ **Tool Policy (工具策略)**:
118
+ 目标设备插件对"其他设备的直调"的授权规则,按设备与工具粒度决定免审批、需本机审批或拒绝;由资源所有者设备制定。
119
+ _Avoid_: 权限位, ACL, 白名单(白名单只是其中一种手段)
120
+
121
+ **Fleet Tool (设备无关能力函数)**:
122
+ 以 `device` 参数指定目标设备的直调工具;统一入口为 `fleet_call(device, tool, parameters)`,另有少量高频 typed 快捷工具(`fleet_read` / `fleet_fetch` / `fleet_run`)。
123
+ _Avoid_: 远程工具, 镜像工具, MCP tool
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-device-fleet contributors
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 CHANGED
@@ -1,3 +1,96 @@
1
- # Temporary Holding Version
1
+ # dsh-device-fleet
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness 的设备联邦插件:把登录同一账号、且都安装本插件的多台设备连成一个 **Fleet**,让 agent 可以直调其他设备的工具、在共享 **Room** 里协作,或把任务委派给另一台设备的 agent。
4
+
5
+ ## 进度
6
+
7
+ - ✅ **网络层 v1**:设备身份、邀请、端到端加密安全通道、直连(手动地址)、UDP 组播发现
8
+ - ✅ **网络层 v2**:自建 Relay(fleetKey 鉴权的信令 + TCP 拼接数据面,不做 NAT 打洞)
9
+ - ✅ **Host 插件 + 直调 MVP(代码完成、单测通过,待真实 DSH 安装联调)**:`fleet_list` / `fleet_tools` / `fleet_call` + `fleet_read` / `fleet_fetch` / `fleet_run`、Tool Policy、Execution Slot、审批 fail-closed
10
+ - ⏳ Room / Room Board、委派、审批收件箱 UI、设备页/配对/房间 UI(下一步)
11
+
12
+ ## 设计文档
13
+
14
+ - 术语表:[CONTEXT.md](./CONTEXT.md)
15
+ - 决策记录:[docs/adr/](./docs/adr/)(17 条,从入网方式到网络分层)
16
+ - 网络实现说明(线格式/Relay 协议/限制):[docs/notes/network-v1-v2-implementation.md](./docs/notes/network-v1-v2-implementation.md)
17
+ - 全局任务列表复用评估:[docs/notes/dsh-global-task-list-evaluation.md](./docs/notes/dsh-global-task-list-evaluation.md)
18
+
19
+ ## 快速开始
20
+
21
+ ```js
22
+ import {
23
+ FleetNode, createFleet, decodeInvite, encodeInvite, generateIdentity, accountFingerprint,
24
+ } from './src/net/index.js'
25
+
26
+ // 第一台设备:创建 Fleet,导出邀请
27
+ const fleet = createFleet({ name: 'home' })
28
+ const accountFp = accountFingerprint(fleet.fleetKey, 'account-user-id')
29
+ const invite = encodeInvite({ fleet, accountFingerprint: accountFp })
30
+
31
+ // 第二台设备:解码邀请,加入
32
+ const joined = decodeInvite(invite)
33
+
34
+ const a = new FleetNode({
35
+ identity: generateIdentity({ name: 'desktop' }),
36
+ fleet,
37
+ accountFingerprint: accountFp,
38
+ direct: { host: '0.0.0.0', port: 0 },
39
+ discovery: true,
40
+ })
41
+ const b = new FleetNode({
42
+ identity: generateIdentity({ name: 'laptop' }),
43
+ fleet: joined.fleet,
44
+ accountFingerprint: joined.accountFingerprint,
45
+ direct: { host: '0.0.0.0', port: 0 },
46
+ discovery: true,
47
+ })
48
+
49
+ a.onRequest(async ({ method, params }) => params) // 示例:eecho
50
+ await a.start()
51
+ await b.start()
52
+
53
+ // 发现/手动地址均可;双向 RPC 已端到端加密
54
+ const result = await b.request(a.identity.deviceId, 'echo', { path: '/tmp/report.md' })
55
+ console.log(result)
56
+ ```
57
+
58
+ ## Relay(v2,跨网兜底)
59
+
60
+ ```js
61
+ import { createRelayServer } from './src/net/relay-server.js'
62
+
63
+ // 在用户自己的一台机器/VPS/NAS 上启动:
64
+ const relay = createRelayServer({
65
+ host: '0.0.0.0',
66
+ fleets: { [fleet.fleetId]: fleet.fleetKey },
67
+ })
68
+ const { port, dataPort } = await relay.ready
69
+ console.log({ port, dataPort })
70
+ ```
71
+
72
+ 两台设备配置 `relay: { host, port, dataPort }` 后,直连不可达时会自动走 Relay;Relay 只看到密文与连接元数据。
73
+
74
+ ## 测试
75
+
76
+ ```sh
77
+ node test/net.test.mjs # 7/7,直连/发现/Relay/鉴权/密钥固定
78
+ node --test test/ # 标准 node:test 入口(受限沙箱可能禁止 spawn)
79
+ ```
80
+
81
+ 覆盖:邀请编解码、直连 RPC 与 roster TOFU、跨 fleet/跨账号拒绝、固定公钥冲突拒绝、Relay 加密 RPC、Relay 鉴权失败、组播发现。
82
+
83
+ ## 安全说明
84
+
85
+ - 业务帧端到端加密(X25519 + HKDF + AES-256-GCM);Relay 只能看到密文与元数据;
86
+ - 设备身份在首次接触时用 `fleetKey` 成员证明校验并固定(TOFU),之后公钥变化即拒绝;
87
+ - 账号指纹 `HMAC(fleetKey, accountUserId)` 只做成员校验与隔离,不是传输凭据;
88
+ - 当前为 v1/v2 原型:TOFU 无轮换/吊销、Relay 控制面未加 TLS、无限流与审计,上线前需补齐。
89
+
90
+ ## 下一步
91
+
92
+ 1. Host 插件接入:身份/邀请/Fleet 配置持久化到 `$DSH_HOME`,把网络层挂到 Cordis `apply`;
93
+ 2. 工具面:`fleet_list` / `fleet_tools` / `fleet_call` + `fleet_read` / `fleet_fetch` / `fleet_run`;
94
+ 3. Room:会话升级、Room Board(扩展 `dsh-global-task-list`)、点名消息与惰性唤醒;
95
+ 4. 委派:`fleet_delegate` + 一次性子会话 + 委派升级为 Room;
96
+ 5. UI:设备页/配对/审批队列/房间视图;Host/Client bundle 发布。
@@ -0,0 +1,24 @@
1
+ # dsh-device-fleet bundle patch — mounts the Host plugin. The settings below are
2
+ # safe defaults; real values (relay address, allowed roots, execution cwd) are
3
+ # meant to be edited in the profile or through the plugin settings page.
4
+ - insert:
5
+ - id: device-fleet
6
+ name: 'dsh-device-fleet'
7
+ config:
8
+ deviceName: !!js process.env.DSH_DEVICE_NAME ?? 'device'
9
+ direct:
10
+ host: '0.0.0.0'
11
+ port: 0
12
+ discovery: true
13
+ policy:
14
+ allowedRoots: []
15
+ capabilities:
16
+ read: auto
17
+ list: auto
18
+ grep: auto
19
+ fetch: auto
20
+ run: ask
21
+ tool: deny
22
+ allowedTools: []
23
+ promptSection: true
24
+ autoApproveAsk: false
@@ -0,0 +1,17 @@
1
+ # 使用显式入网,而不是"同账号自动互联"
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ DSH 运行时没有"账号 → 设备目录"或"验证对端账号"的端点;插件只能在本机读到自己登录的账号 ID。仅凭"同账号 + 都装插件"无法发现对端,也无法证明对端身份;要实现零配置自动互联必须引入服务端协调。
8
+
9
+ ## Decision
10
+ 采用显式的 Fleet 入网:首台设备创建 Fleet 并签发一次性邀请,其他设备接受邀请后成为成员;账号身份只作为成员校验与隔离条件(账号指纹比对、切换账号即断连),不作为发现或传输通道。
11
+
12
+ ## Considered Options
13
+ - 零配置自动互联(需自建或官方服务端做账号-设备绑定)——一期成本与运维/隐私面过大,否决。
14
+ - 局域网内自动出现"待确认设备"、点击确认后入网——作为未来可选增强保留。
15
+
16
+ ## Consequences
17
+ 一期无需服务端;跨网络发现需要会合点(Hub 设备或 Relay);同账号但未入网的设备不会被自动接纳。
@@ -0,0 +1,17 @@
1
+ # 跨设备协作以共享 Room 为协调单位
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 三个以上 agent 在同一件事上协作时,两两配对会退化成 N(N-1)/2 条互不相干的会话:没有共同历史,协作者之间只能经发起者转发,发起者成为瓶颈与单点;星型转发也同样没有"所有人看到同一份上下文"的群聊感。
8
+
9
+ ## Decision
10
+ 引入 Room 作为跨设备协作的协调单位:由一台设备(Host)托管唯一一份权威消息历史、成员表与共享任务板,所有 Participant 共享并按序广播;任意 Participant 可以直接对其他 Participant 说话,不必经过发起者。Participant 的 agent 按需懒创建,唤醒时只拿任务简报(Participant Brief)与定向消息,不继承房间全量历史。
11
+
12
+ ## Considered Options
13
+ - 两两配对的常驻会话——只在 2 台设备时等价于 Room,3 台以上无共同历史,否决作为协调单位(保留为其实现细节:2 人房 = 1:1)。
14
+ - 星型 Lead + workers(仅发起者↔协作者)——实现最简单,但没有群聊感,发起者是单点,否决。
15
+
16
+ ## Consequences
17
+ 多设备协作需要 Host 角色与广播/排序/持久化;房间历史与各 agent 的上下文解耦,token 成本可控;Host 设备离线时房间需要降级或迁移(一期先按离线只读处理)。
@@ -0,0 +1,22 @@
1
+ # 直调与协作分成两条通道
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 把"只是调用对方一个工具"塞进 Room 或 Participant 会话,会让一次纯工具调用带上对话上下文、会话生命周期与模型唤醒的包袱;反过来,把"多 agent 协作"做成一次性工具调用又丢掉了共同历史与任务状态。两者需要不同的生命周期与上下文预算。
8
+
9
+ ## Decision
10
+ 分成两条通道:
11
+ - **Direct Call(直调)**:A 的 agent 直接调用目标设备上的 tool 并取回结果;不创建 Room/Participant、不读取目标设备对话上下文;目标设备插件按"设备 × 工具"配置决定免审批 / 需本机审批 / 拒绝。
12
+ - **Room(协作)**:只有需要目标设备的 agent 参与时才创建 Participant,给任务简报 + 定向消息,共享房间历史与任务板。
13
+
14
+ 直调遇到有状态工具(执行代码、写文件)时,目标设备使用插件管理的隐藏 **Execution Slot** 提供 cwd、沙箱与审批路由;它不跑模型、不进 Room、不保存对话历史。
15
+
16
+ ## Considered Options
17
+ - 所有直调都走 Room 的 Participant 会话——上下文与生命周期过重,否决。
18
+ - 直调只允许无状态工具(跑代码必须升级为 agent 任务)——限制过强,否决。
19
+ - 所有直调统一走执行槽——纯读也带上下文,偏重,否决。
20
+
21
+ ## Consequences
22
+ 直调与协作可以独立演进,token 与上下文成本可控;直调的执行安全由目标设备插件的工具策略负责;执行槽对用户不可见,但会出现在设备内部审计里。
@@ -0,0 +1,23 @@
1
+ # 直调授权由资源所有者设备决定
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 直调(Direct Call)让 A 的 agent 使用 B 的工具。B 的文件、机器与风险都属于 B,A 不能给自己授权;但逐次弹窗会让一个多步任务反复打断。
8
+
9
+ ## Decision
10
+ 直调的工具策略由**目标设备(资源所有者)**的插件按"设备 × 工具"配置,默认分级:
11
+ - 只读工具:免审批,受目标设备配置的目录根与白名单限制;
12
+ - 执行类(bash/pwsh/terminal/子进程)与写文件:需目标设备本机确认;
13
+ - 危险类(plugin_manager、schedule_*、CUA、交互式终端等):默认拒绝,须在目标设备设备页显式开启。
14
+
15
+ 审批在目标设备的插件界面/队列中显示来源设备、agent 会话、工具名、参数预览、cwd 与超时;超时 fail-closed;允许"本次任务内记住(TTL)"。A 侧不能代 B 授权,审批结果记入目标设备审计。
16
+
17
+ ## Considered Options
18
+ - 审批转发到 A 的用户——把 B 的资源授权交给 A 侧的人,违背资源所有者原则,否决。
19
+ - 每台设备一个总开关——粒度太粗,表达不了"读可以、跑代码要问",否决。
20
+ - 入网即全授权——最省事也最危险,否决。
21
+
22
+ ## Consequences
23
+ "读"与"执行/写"需要分开配置;直调的可用性依赖目标设备有人/有策略回应审批;TTL 授权会短暂扩大目标设备的暴露面,需要审计与撤销。
@@ -0,0 +1,22 @@
1
+ # 会话可就地升级为 Room Host
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 用户往往已经在与本地 agent 对话时才提出"叫上另一台设备一起"的需求。若必须新建会话或切换设备,上下文要搬运、体验割裂。DSH 插件已具备向已有会话日志追加自定义事件并用投影渲染的能力(Agent Team 是现成先例),因此房间状态可以附着在一个已有会话上。
8
+
9
+ ## Decision
10
+ 当前会话可被显式升级为 Room Host:向会话日志追加 `room/*` 事件并挂载 room 投影,保留原有对话上下文。此后以**统一成员接口**添加任意成员(B、C、D……):每台设备对应一个 Participant,Host 只是带 `host` 角色的成员,添加/退出/发言走同一套调用,界面上的名称区别不改变语义。规则:
11
+ - 一个会话最多承担一个 Room 的 `host` 角色;
12
+ - 已是 Participant 的会话不能升级为 Host(另开会话);
13
+ - fork 一个 Host 会话时,子会话不继承 Host 角色或成员关系;
14
+ - Host 会话关闭/归档 → 房间进入只读或结束,Participant 收到通知;
15
+ - 升级与添加成员都是显式动作,不能隐式发生。
16
+
17
+ ## Considered Options
18
+ - 总是新建 Room 会话——上下文需搬运,体验割裂,否决。
19
+ - 由目标设备建房、本机以远端身份加入——当前会话不升级,仍有切换感,否决。
20
+
21
+ ## Consequences
22
+ 房间生命周期绑定 Host 会话,fork/关闭/归档需要额外规则;成员模型保持统一,房间规模与设备命名不影响实现语义。
@@ -0,0 +1,16 @@
1
+ # Room 消息必须点名,且每个成员设备都能发言
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 用户会在不同设备之间切换使用(可能这次对 A 说,下次对 B 说),因此"只有发起者/Host 能发言、其他人只能被动接受"不成立;同时,若每条消息都唤醒所有 agent,会造成 token 浪费、互相打断与重复工作。
8
+
9
+ ## Decision
10
+ - **每条 Room 消息都带显式收件人**(成员 id 集合;`*` 表示广播,但需显式授权/确认,不是默认行为)。只有被点名的成员被唤醒;未点名成员不跑模型,但仍共享房间权威日志。
11
+ - **Room 视图在所有成员设备上可用**:Host 广播权威日志,成员设备的 UI 订阅展示;从任何设备发言使用同一套接口,从而支持用户"人在这台、下一句换那台"的无感切换。
12
+ - 发言者身份分为两类并都进入权威日志:**用户**(人在某台成员设备上直接发言)与 **agent**(成员设备上的 agent 发言)。
13
+ - Host 的 agent 天然是成员之一,但不再垄断发言;Host 的会话只负责托管日志与排序,未点名时不会因日志追加而被唤醒。
14
+
15
+ ## Consequences
16
+ 成员设备需要房间视图与收发通道;Host 需要广播与排序;"点名"成为协议字段而不是 UI 装饰;未点名成员保持静默。
@@ -0,0 +1,21 @@
1
+ # 用户发言与设备解耦,消息 = 内容 + 发言者 + 点名
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 用户会在 A、B 等设备之间切换发言。如果把"用户"绑定到具体设备,同一人会被拆成多个发言者/接收者,通知、审计与房间状态都会出现无意义的重复;而一条用户消息本质上是无状态的指令或信息,真正决定它被谁处理的是收件人。
8
+
9
+ ## Decision
10
+ - 用户是跨设备的**单一发言者身份**;设备只是消息的传输来源,仅作审计元数据,不进入房间状态与路由语义。
11
+ - 一条 Room 消息由三部分组成:**内容**、**发言者**(`user` 或某个 agent 成员)、**点名**(收件成员集合)。消息本身不携带设备状态。
12
+ - 用户从任何成员设备直接发言,都以同一 `user` 身份进入权威日志,并直接唤醒被点名的 agent;本机 agent 不参与转发、不消耗本机 token。
13
+ - 用户也可点名本机 agent,让它以该设备上的 agent 成员身份参与。
14
+
15
+ ## Considered Options
16
+ - 每个设备一个"用户身份"——同一人被拆分,通知/审计/状态重复,否决。
17
+ - 用户消息一律由本机 agent 代理转发——每次多一次模型调用,也无法精准点名,否决。
18
+ - 只有 Host 设备能发言——违背"任意设备无感交流",否决。
19
+
20
+ ## Consequences
21
+ 房间可以只维护一份人工发言者视角;同一用户的房间视图在各设备一致。未来若支持多人房间,需要引入真正的多用户身份,一期不做。
@@ -0,0 +1,21 @@
1
+ # 复用并扩展 dsh-global-task-list 作为 Room 任务板
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ Room 需要结构化的共享状态(目标、任务、负责人、产物指针),否则被唤醒的 agent 只能反复读聊天记录。`dsh-global-task-list`(v0.4.2,作者自己维护、可改源码)已有高度对齐的数据模型(status/steps/progress/parentId/dependsOn/surface)、持久化 `storageDomain`、成熟的面板 UI 与生成式 surface 渲染;但它刻意设计为**本机、session-independent**,没有共享数据源与 room/owner 维度。
8
+
9
+ ## Decision
10
+ 走扩展路线(不 fork、不重写):
11
+ - 在 `task_ui` 记录上增加 **room 作用域**(`roomId`)、**负责人成员**(owner member)与**版本号**(用于跨设备 CAS);
12
+ - 增加 **Host 侧程序化 API**(Host service 或 Typert Remote)供 Fleet 插件读写,而不是复用未认证的 `/task-ui` HTTP 路由;
13
+ - **Room Host 权威**;参与设备把房间任务**镜像**到本地 `task_ui`(只读,写入回到 Host);面板按 room 分组并标记只读镜像;
14
+ - agent 工具面沿用现有 `task_*` 并增加可选 `roomId`,或新增 `room_task_*`(命名在工具面设计中再定)。
15
+
16
+ ## Considered Options
17
+ - Fork TASK_SCHEMA 与面板组件进 Fleet 插件——要维护 ~48KB 面板与 CSS 的副本,且失去后续改进,否决。
18
+ - Room 板只存 `{deviceId, taskId}` 指针、插件零改动——没有统一视图、状态滞后,仅可作 PoC 过渡,否决。
19
+
20
+ ## Consequences
21
+ 个人任务库与房间任务板共用一份存储与面板,需要明确的 room 过滤与只读镜像规则;schema 增加字段(domain 后端描述符不随 schema 变,无需版本升级);跨设备更新需要 CAS 与冲突提示;扩展后的插件成为 Fleet 的依赖项。
@@ -0,0 +1,21 @@
1
+ # Room 由显式命令创建,并携带可配置的 Room Policy
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ Room 只在用户显式命令下创建(升级当前会话或新建房间),因此创建之前用户可以在插件设置里预设"房间类型与权限";不同协作场景需要不同规则(私密房间只让 Host 拉人,团队房间允许任意成员邀请并自动通过)。把这些行为硬编码会逼用户改代码。
8
+
9
+ ## Decision
10
+ - 插件提供**默认 Room Policy 与命名预设(Room Type)**,例如私密、团队、只读;
11
+ - 创建房间(升级当前会话或新建)时,把所选类型解析成一份**策略快照**,写进房间创建事件并广播给成员;
12
+ - 邀请权、加入方式(自动/需确认)、成员上限、任务板写权限等协作规则由该策略决定,而不是硬编码;
13
+ - 默认取插件配置,创建时可覆盖;Host 可在创建后修改协作类策略(记录并广播),但不追溯驱逐既有成员;
14
+ - **边界**:Room Policy 只管协作规则;资源访问(直调某设备的工具、读写其文件)仍由各设备的 Tool Policy 决定,两者不重叠。
15
+
16
+ ## Considered Options
17
+ - 硬编码"仅 Host 邀请 + 自动加入"——无法按场景配置,否决。
18
+ - 全局唯一策略——无法同时支持私密房间与团队房间,否决。
19
+
20
+ ## Consequences
21
+ 房间事件需要持久化策略快照;房间 UI 要显示当前策略;策略修改要审计;实现上必须明确 Room Policy 与设备 Tool Policy 的分界,避免"房间允许"被误当成"资源允许"。
@@ -0,0 +1,24 @@
1
+ # Room Policy v1:可配置协作规则与可保存的 Room Type
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 不同协作场景需要不同规则;而且 Room 由用户显式命令创建,用户应当在创建前就能在插件里配好"房间类型与权限",而不是被硬编码规则限制。当前 Fleet 是**单用户**模型(同一账号 = 同一个人),成员即用户自己的设备。
8
+
9
+ ## Decision
10
+ v1 Room Policy 字段:
11
+ - `invite`: `host-only | members`(谁能邀请新成员)
12
+ - `join`: `auto | confirm`(被邀请设备自动加入,还是需要本机确认)
13
+ - `maxMembers`: 默认 8
14
+ - `boardWrite`: `members | host`(谁能改共享任务板)
15
+ - `remove`: `members | host-only`(成员可主动移除他人,或只有 Host 能移除)
16
+
17
+ Room Type 是命名预设(内置私密/团队/只读),用户可**自定义并保存**;创建房间时解析成一份 Room Policy 快照,写入房间创建事件并广播。Host 可在创建后修改协作类策略并广播;修改不追溯驱逐已有成员,`join` 变更只影响后续加入。
18
+
19
+ ## Considered Options
20
+ - 只做内置硬编码策略——不满足"创建前按场景配置",否决。
21
+ - 策略放到设备级 Tool Policy——混淆了协作规则与资源访问,否决。
22
+
23
+ ## Consequences
24
+ 需要插件设置页管理自定义 Room Type;房间事件持久化策略快照;成员移除/加入规则变更需要广播与审计;"成员可移除他人"依赖单用户 Fleet 假设,未来支持多人房间时必须重新设计。
@@ -0,0 +1,27 @@
1
+ # Room Board 与房间同生命周期,更新由钩子驱动
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ Room Board 要解决"被唤醒的 agent 不必读全量聊天记录就能进入状态"以及"多 agent 串行/并行推进同一件事时状态不丢"。若沿用本机全局任务库,生命周期与房间不一致;若每次变化广播给所有成员,又会造成重复唤醒与 token 浪费。
8
+
9
+ ## Decision
10
+ - **Room Board 与 Room 同生命周期**:随房间创建/归档;Host 权威、参与设备只读镜像(扩展 `dsh-global-task-list` 的 `task_ui`,加 `roomId`)。
11
+ - **Room 中至少存在一个 agent**(Host 的 agent 即成员);每个成员的执行单元是 **Task Block**。
12
+ - **任务按块分配**:
13
+ - 串行块:上一块完成后,其负责人更新 Room Board,Host 将下一块标记为 ready 并向下一块的负责人发一条点名消息(唤醒它接手);
14
+ - 并行块:各成员只更新自己块的状态,不阻塞/不改变其他块;下一阶段开始时读取当前 Board 即可了解全貌。
15
+ - **更新由钩子驱动**:
16
+ - 成员被唤醒领取一块任务时,任务简报里包含该块 + 相关 Board 切片,并提醒开始执行时标记状态;
17
+ - 成员结束一轮时,Host 检查其负责块的 Board 状态;若明显未更新,发一条(最多一次)提醒消息;
18
+ - 任何成员开始新块前自行读取 Board(拉取),不要求全量推送。
19
+ - 其他成员不需要在每次变化时被唤醒;只在被点名(含"下一块 ready")时唤醒。
20
+
21
+ ## Considered Options
22
+ - 沿用全局个人任务库、不区分房间——生命周期不一致,个人任务与房间任务互相污染,否决。
23
+ - 每次 Board 变化广播唤醒所有成员——token 浪费、互相打断,否决。
24
+ - 完全依赖模型自觉更新——task-list 的经验表明会漏更新,否决。
25
+
26
+ ## Consequences
27
+ 需要在 turn 边界做 Board 一致性检查与有限提醒;串行交接会产生点名唤醒消息;Board 需要版本号/CAS 防止覆盖;参与设备的镜像是只读的,写入统一回 Host。
@@ -0,0 +1,22 @@
1
+ # 惰性唤醒与定向发言
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 多设备协作里最贵的资源是"唤醒一个 agent 跑一轮模型":既花 token,也占用该设备用户的时间与注意力。广播式协作会唤醒不必要的 agent,导致重复工作与互相打断。
8
+
9
+ ## Decision
10
+ - **按需唤醒**:只有被点名(Addressing)或被分配 Task Block 的 agent 才被唤醒;未点名成员不启动、不跑模型。
11
+ - **Participant agent 惰性创建**:加入房间时只建立成员记录与只读 Board 镜像;该成员第一次被点名/领取任务时才创建它的本地会话并注入 Participant Brief。直调工具不经过 agent,走 Execution Slot。
12
+ - **广播不是默认行为**:`*` 需要显式授权/确认(受 Room Policy 约束)。
13
+ - **定向发言**:任何成员都可以向任意一个或多个成员说话;相同内容发给多人 = 一条带收件人集合的消息;对每个成员说不同的话 = 多条消息(可用一次批量调用按顺序追加,保持审计与顺序)。
14
+ - 离线成员的消息持久在 Host 日志;唤醒是尽力而为,成员回归时按"自上次已读"补摘要(离线投递细节待定)。
15
+
16
+ ## Considered Options
17
+ - 全员广播、每条消息唤醒所有成员——token 与注意力最差,否决。
18
+ - 加入房间即创建每个成员的 agent——房间多几个不活跃成员就多几个空闲会话,否决。
19
+ - 由发件人本地转发、不经 Host——没有统一顺序与审计,否决。
20
+
21
+ ## Consequences
22
+ 首次唤醒多一次会话创建成本;协议需要 wake/assignment 事件与 turn/end 上报;批量定向消息要保证顺序与可审计;Room Policy 需要约束广播权限。
@@ -0,0 +1,26 @@
1
+ # 直调工具面:设备无关的能力函数 + device 参数
2
+
3
+ ## Status
4
+ amended by ADR-0014 —— 能力目录改为 `fleet_call` 统一入口 + 少量 typed 快捷工具;本文中"设备无关、device 参数、Execution Slot 映射、稳定错误码"仍然有效。
5
+
6
+ ## Status
7
+ accepted
8
+
9
+ ## Context
10
+ 把每台设备的远程工具都镜像到本地,会让工具表随"设备数 × 工具数"膨胀,设备上下线还会改变 schema、打掉 KV cache;而纯通用的 `remote_call(device, tool, args)` 又要求模型先查目录,不够"原生 function calling"。
11
+
12
+ ## Decision
13
+ 直调工具注册为**与设备无关的固定函数**,统一带 `device` 参数(设备 id 或唯一名):
14
+ - `fleet_list()` 返回成员 id/名称/在线状态/能力/Tool Policy 摘要;
15
+ - 能力函数按固定目录、在所有设备上同构注册,例如 `fleet_read(device, path, ...)`、`fleet_list_dir(device, path)`、`fleet_grep(device, pattern, path)`、`fleet_fetch(device, path, ...)`、`fleet_run(device, command, cwd?, timeoutMs?)`;
16
+ - `device` 在 schema 中用字符串而不是枚举:schema 保持稳定(KV cache 友好),运行时按 Fleet 成员表校验;
17
+ - 目标设备把能力映射到本地 tool(如 `fleet_read` → `read`),以 Execution Slot 执行;是否免审/需审批/拒绝由该设备的 Tool Policy 决定;
18
+ - 未纳入目录的冷门工具可通过可选的 `fleet_call(device, tool, args)` 兜底(默认关闭)。
19
+
20
+ ## Considered Options
21
+ - 每设备镜像工具——schema 随设备变化、工具表膨胀,否决。
22
+ - 纯通用 `remote_call` + 目录查询——每次新工具都要先查一次,不够原生,否决。
23
+ - MCP 桥接——无 wait/cancel 语义、本构建无 server SDK,不作为核心路径,否决。
24
+
25
+ ## Consequences
26
+ 需要一张"能力 → 本地工具"映射表与稳定错误码(`device_not_found` / `offline` / `unsupported` / `denied` / `approval_timeout` / `tool_failed`);能力目录按插件版本固定,设备间能力差异靠运行时校验;模型需要先用 `fleet_list` 拿到 device id。
@@ -0,0 +1,22 @@
1
+ # fleet_call 统一执行入口 + 少量 typed 快捷工具
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 纯 typed 能力目录要求每台设备、每个新工具都同步进插件工具表;纯 `fleet_call` 又让模型拿不到目标工具的精确 JSON Schema,复杂参数会在目标设备校验失败、白跑一次往返,且必须先查目录。
8
+
9
+ ## Decision
10
+ 直调只有一个执行原语:`fleet_call(device, tool, parameters)`。
11
+ - `fleet_tools(device)` 返回目标设备**可被远端调用**的工具名、JSON Schema 与 Tool Policy 摘要;
12
+ - `fleet_call` 的 `parameters` 在目标设备按其真实 schema 校验,错误原样回传;
13
+ - 另注册少量高频 **typed 快捷工具**:`fleet_read` / `fleet_fetch` / `fleet_run`,它们只是 `fleet_call` 的薄包装,提供精确 schema 与更好的 function-calling 保真度;
14
+ - 任何设备新增的工具无需插件升级即可通过 `fleet_call` 使用(仍受该设备 Tool Policy 约束);快捷工具集合可配置。
15
+
16
+ ## Considered Options
17
+ - 纯 typed 能力目录——新增工具要改插件,否决。
18
+ - 纯 `fleet_call`——参数保真度差、发现往返多,否决。
19
+ - 动态按需镜像(查目录后临时注册 typed 工具)——会动态改工具表、打掉 KV cache,复杂度高,否决。
20
+
21
+ ## Consequences
22
+ 工具表固定为 `fleet_list` / `fleet_tools` / `fleet_call` + 三个快捷工具;模型对未 typed 工具需先 `fleet_tools` 发现;审计与限额统一在一条执行路径上做。
@@ -0,0 +1,22 @@
1
+ # 协作形态:Room 之下的本地团队与跨设备委派
2
+
3
+ ## Status
4
+ accepted
5
+
6
+ ## Context
7
+ 两台设备要"看起来无缝衔接、agent 自由运行",同时每个 agent 也应保留自己原有的组织能力(本地 Agent Team、subagent)。把这些都塞进 Room 会让房间成员爆炸、上下文互相污染;完全隔离又失去跨设备合作。
8
+
9
+ ## Decision
10
+ 采用"房间 + 逐 agent 私有团队 + 跨设备委派"的三层模型:
11
+ - **Room**:跨设备协作的共享层。每个成员设备在房间里只有一个**代表 agent**(Participant),共享 Room Board、点名与消息。
12
+ - **Local Team**:每个代表 agent 在自己设备上仍可自由使用本地 Agent Team / subagent 干活;这些本地成员**对 Room 不可见、不被点名、不写 Room Board**,只由代表 agent 把结果带回房间。
13
+ - **Delegation(跨设备委派)**:成员 agent 可把一次性任务交给另一台成员设备上的 agent 执行并取回结果;被委派方使用**独立的一次性子会话**(不进入 Room、不共享房间历史、不可续聊),对应 DSH 外部 `SubagentProvider` 的 one-shot 语义。
14
+ - **边界**:跨设备委派有深度上限(默认 1,禁止 A→B→A 回环);被委派的远程子 agent 不能创建/加入 Room,也不能再唤醒第三方成员;Room 成员之间才谈"合作",委派只谈"派活"。
15
+
16
+ ## Considered Options
17
+ - 所有协作都塞进 Room——成员与上下文膨胀,否决。
18
+ - 禁止 agent 使用本地团队——与"agent 自由运行"相悖,否决。
19
+ - 远程子 agent 直接复用目标设备 Room 里的代表会话——会抢上下文与写锁,否决。
20
+
21
+ ## Consequences
22
+ 需要区分"房间成员"与"委派子会话"两种远程 agent 身份;委派结果只回到委派者;深度与回环需要协议层限制;本地团队的成本与行为仍由各设备自己负责。