dsh-device-fleet 0.1.0-dev.3 → 0.1.0-dev.30

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/cordis.patch.yml CHANGED
@@ -18,9 +18,9 @@
18
18
  grep: auto
19
19
  fetch: auto
20
20
  run: ask
21
- tool: deny
21
+ tool: auto
22
22
  allowedTools: []
23
23
  promptSection: true
24
24
  autoApproveAsk: false
25
-
26
-
25
+
26
+
@@ -0,0 +1,31 @@
1
+ # 审批收件箱(逻辑层,dev.7)
2
+
3
+ ## 实现
4
+
5
+ - `src/host/approval-inbox.js`:`ApprovalInbox`
6
+ - `request()`:排队、60s(可配)超时 fail-closed、事件 `request/approved/denied/expired/closed`;
7
+ - `list()`:待审列表(来源设备、能力、工具、参数摘要、剩余时间);
8
+ - `approve(id, {scope})`:`once` / `ttl`(TTL 由 Tool Policy 的 `approvalTtlMs` 控制);
9
+ - `deny(id, reason)`;`close()` 时全部失败关闭。
10
+ - `DirectCallHost` / `DelegationHost` 的 `ask` 档改为等待收件箱结果(不再一律拒绝),批准后按 scope 写入 Tool Policy 授权。
11
+ - 新工具:`fleet_approvals` / `fleet_approve` / `fleet_deny`;`fleet_simulate_request`(仅 `approvals.simulation: true`)用于本机无第二台设备时演练。
12
+ - 配置:`approvals: {ttlMs, maxPending, simulation}`。
13
+
14
+ ## 已验证
15
+
16
+ 单元测试(`node test/host.test.mjs`,10/10):
17
+ - 收件箱排队/批准/拒绝/超时/关闭/上限;
18
+ - DirectCallHost 在 `run=ask` 时阻塞等待收件箱批准后才执行;
19
+ - 工具 handler 的列表/批准/拒绝/模拟。
20
+
21
+ 当前 DSH 环境内模拟(dev.7,无需第二台设备):
22
+ 1. `fleet_simulate_request` 创建一条 `run/pwsh` 请求,`fleet_approvals` 显示 `58328ms left`;
23
+ 2. `fleet_approve(id, scope="ttl")` → `approved … (ttl)`,再次 `fleet_approvals` 为 `(no pending approvals)`;
24
+ 3. 再模拟一条 `delegate` 请求,`fleet_deny` → `denied …`;
25
+ 4. 对不存在的 id 调 `fleet_approve` → `Error: [unknown-request]`。
26
+
27
+ ## 未完成
28
+
29
+ - **Client UI 审批面板/通知**(需 Client bundle):当前只能通过工具或后续 UI 应答;Host 侧已发出 `request/approved/denied/expired` 事件,面板可直接消费;
30
+ - `room` 级授权 scope(需 Room 概念落地后再加);
31
+ - 审批结果持久化审计日志(后续与 Room 事件一起做)。
@@ -0,0 +1,38 @@
1
+ # 审批面板 UI(dev.9 → dev.12)
2
+
3
+ ## Host 数据面
4
+
5
+ `src/host/approval-web.js`:
6
+
7
+ - `GET /device-fleet/approvals` → `{ approvals: [...] }`(ApprovalInbox.list);
8
+ - `POST /device-fleet/approvals/decide`,body `{ id, action: "approve"|"deny", scope?, reason? }` → 决策走 ApprovalInbox;
9
+ - 路由注册在插件 apply 中、卸载时 dispose;纯逻辑 `createApprovalApi` 与路由分离便于单测。
10
+
11
+ ## Client 半
12
+
13
+ `client.js`(闭包工厂,id = `dsh-device-fleet`):
14
+
15
+ - **入口**:`sidebar.footer.action`(Settings 上方),显示 `🛰️ 审批`,有待审批时带数字并高亮;不再占用 `shell.overlay` 右下角,避免与任务列表 UI 重叠;
16
+ - **面板**:`shell.overlay` 直接注册的居中模态对话框(遮罩、Esc/点遮罩/收起关闭);
17
+ - 2.5s 轮询 `/device-fleet/approvals`;按钮「允许一次 / 允许 15 分钟(ttl) / 拒绝」POST 到 decide;
18
+ - 共享模块级 store + `ctx.effect` 启动/清理轮询;使用 `--dsw-alias-*` token(带 fallback)。
19
+
20
+ ## 修复记录
21
+
22
+ - dev.10:右下角浮动按钮可用,但会遮挡任务列表(用户截图确认);
23
+ - dev.11:入口迁到 sidebar footer;发现点击无反应;
24
+ - dev.12:修复 `Panel` 的条件 Hook 顺序(关闭时提前 return,打开后才调 `useEffect`,导致 React 渲染报错);同时把 `shell.overlay` 改回直接注册(dev.10 已验证可用路径)。
25
+
26
+ ## 端到端验证(已完成)
27
+
28
+ 1. 刷新页面后 Settings 上方出现「🛰️ 审批」;
29
+ 2. 点击后居中对话框正常打开并显示待审批条目(用户确认);
30
+ 3. `fleet_simulate_request` 造请求 `dae59ce4-…`(run/pwsh,simulated-device-B);
31
+ 4. 用户在面板点击「允许一次」;
32
+ 5. Host `fleet_approvals` 返回 `(no pending approvals)` → **UI → HTTP → ApprovalInbox 写回链路闭环**。
33
+
34
+ ## 限制
35
+
36
+ - 轮询而非 SSE 推送;
37
+ - 路由未额外鉴权(依赖 web 载体本地/会话边界);
38
+ - 面板暂无房间视图(时间线/看板页下一步)。
@@ -1,11 +1,11 @@
1
1
  # 开发版发布记录
2
2
 
3
3
  - 包名:`dsh-device-fleet`
4
- - 版本:`0.1.0-dev.2`
4
+ - 版本:`0.1.0-dev.3`
5
5
  - dist-tag:`dev`(同时也可用精确版本安装)
6
6
  - registry:<https://registry.npmjs.org/dsh-device-fleet>
7
- - tarball:<https://registry.npmjs.org/dsh-device-fleet/-/dsh-device-fleet-0.1.0-dev.2.tgz>
8
- - 本地离线包:`dsh-device-fleet-0.1.0-dev.2.tgz`(仓库根目录,可拷贝安装)
7
+ - tarball:<https://registry.npmjs.org/dsh-device-fleet/-/dsh-device-fleet-0.1.0-dev.3.tgz>
8
+ - 本地离线包:`dsh-device-fleet-0.1.0-dev.3.tgz`(仓库根目录,可拷贝安装)
9
9
  - 发布时间:2026-11-09(Asia/Shanghai)
10
10
  - 内容:网络层 v1/v2 + Host 插件 + 直调 MVP(`fleet_invite/join/list/tools/call/read/fetch/run`);Room/Board/委派/审批 UI 未包含。
11
11
 
@@ -21,7 +21,7 @@ dsh plugin --profile desktop add dsh-device-fleet@dev
21
21
 
22
22
  ## 已知限制
23
23
 
24
- - 严格同版本:两台设备必须安装同一 `0.1.0-dev.2`,DSH 版本也要同一发布线;
24
+ - 严格同版本:两台设备必须安装同一 `0.1.0-dev.3`,DSH 版本也要同一发布线;
25
25
  - `fleet_run` 的 `ask` 审批目前 fail-closed(审批收件箱未实现);
26
26
  - `fleet_fetch` 仅返回文本;
27
27
  - 首次加载会在 `$DSH_HOME/device-fleet/` 自动创建身份与独立 Fleet;加入已有 Fleet 需要先 `fleet_join` 再重启;
@@ -33,4 +33,128 @@ dsh plugin --profile desktop add dsh-device-fleet@dev
33
33
  - 第一次 activate 失败:`@deepseek-ai/schemastery` 只有默认导出(`import Schema from ...`),改为默认导入;
34
34
  - 第二次 activate 失败:Cordis 直接访问未 inject 的服务会抛错 → 可选服务统一走 `ctx.get(name)`;
35
35
  - 最终 `set_plugin include:device-fleet enabled=true` → `application: applied`,8 个 `fleet_*` 工具出现在模型工具表,`fleet_list` 返回本机 device id,`taskui_probe` 仍为 v0.4.2 / tasks=247(与 task-list 无冲突)。
36
- - 已发布 `0.1.0-dev.2`(包含上述激活修复);`0.1.0-dev.1` 会激活失败,不要使用。
36
+ - 已发布 `0.1.0-dev.3`(包含上述激活修复);`0.1.0-dev.1` 会激活失败,不要使用。
37
+ ## 0.1.0-dev.3(本次)
38
+
39
+ - 邀请里新增 `endpoints`(创建者的局域网/热点 IPv4 + direct 端口),加入方重启后自动向这些地址回连,绕开组播发现失败;
40
+ - 新增 `fleet_status`(deviceId/直连端口/发现/连接/roster/hints)与 `fleet_connect(host, port)`(手动直连,成功后会从握手学到对端 deviceId);
41
+ - `fleet_list` 仍是本地视图;配对成功后双方 roster 会自动固定对端公钥。
42
+ ## 0.1.0-dev.20(稳定化)
43
+
44
+ - 直连端口可固定(profile `direct.port`),插件重载不再换端口;
45
+ - 连接断开后自动重试已知地址(不再只在启动时试一次);
46
+ - `fleet_connect` 手动地址会持久化到 `peer-hints.json`,重启后继续回连;
47
+ - 新增 `fleet_delegate`(dev.5):目标设备用 `SubagentProvider` + `delivery:'caller'` 跑 agent 并返回最终结果。
48
+ ## 0.1.0-dev.20(审批收件箱,逻辑层)
49
+
50
+ - 新增 `ApprovalInbox`:ask 档请求排队、超时 fail-closed、approve(once/ttl)/deny、关闭时全部失败关闭;
51
+ - 直调与委派的 ask 档改为等待本机收件箱结果(不再一律拒绝);
52
+ - 新工具:`fleet_approvals` / `fleet_approve` / `fleet_deny`;`fleet_simulate_request`(仅 `approvals.simulation: true`)用于本机无第二台设备时演练流程;
53
+ - 审批面板(Client UI)与通知尚未实现,当前通过工具/后续 UI 应答。
54
+ ## 0.1.0-dev.20(Room 逻辑层,本机模拟)
55
+
56
+ - `RoomService`:当前会话升级为 Room Host;本地模拟参与者(真实 DSH session/agent);
57
+ - 寻址消息:`room_send` 只唤醒被点名成员,Host 会话写权威 `room/message` 事件;
58
+ - 可见性:`room_messages` 提供完整 transcript;未点名成员不运行;
59
+ - Room Board:`room_task_add/update/list`,`dependsOn` 串行交接(前置完成时唤醒下一个 owner),无依赖任务并行不受影响;
60
+ - 工具:`room_create / room_add_local / room_remove_member / room_send / room_messages / room_status / room_task_* / room_close`;
61
+ - 远程参与者传输与 Client UI 未做(下一步)。
62
+ ## 0.1.0-dev.20(审批面板 UI,第一版)
63
+
64
+ - Host 新增本地 HTTP 数据面:GET `/device-fleet/approvals`、POST `/device-fleet/approvals/decide`(仅本机 web 载体,决策仍走 ApprovalInbox);
65
+ - Client half:`client.js` 闭包工厂,注册 `shell.overlay` 浮动「Fleet 审批」按钮 + 面板,轮询待审批列表,支持允许一次 / 允许 15 分钟 / 拒绝;
66
+ - package.json 增加 `dsh.client` 与 `./client` 导出;`files` 增加 client.js;
67
+ - Client 视觉验证需要刷新页面;当前无浏览器控制,仅验证了 bundle 语法与 Host 路由注册。
68
+ ## 0.1.0-dev.20(审批入口不遮挡)
69
+
70
+ - 触发按钮从右下角浮动改为 `sidebar.footer.action`(设置旁边),不再遮挡任务列表/内容面板;
71
+ - 审批 inbox 改为点击后居中的模态对话框(带遮罩,Esc/点遮罩关闭);
72
+ - 轮询在 Client apply 中启动并由 ctx.effect 清理;待审批数量作为按钮徽标;
73
+ - 已实测:面板点击「允许」后 Host `fleet_approvals` 清空。
74
+ ## 0.1.0-dev.20(Phase B:运行时配置 + 配置页)
75
+
76
+ - `RuntimeConfig`:profile patch 为 base,UI 修改写入 `$DSH_HOME/device-fleet/config-overrides.json`,apply 时合并并持久化;
77
+ - 保存即生效:policy/execution/approvals/deviceName/promptSection/autoApproveAsk 直接更新运行时对象;direct/discovery/relay 通过 `FleetNode.reconfigure()` 原地重启网络监听(不重启插件/DSH);`dataDir` 只读;
78
+ - Host:`GET /device-fleet/config`、`POST /device-fleet/config`(校验/清洗后应用);
79
+ - Client:注册 `plugins.row.config`(key `dsh-device-fleet#device-fleet`)与 `plugins.bundle.config`(key `dsh-device-fleet`),配置页含设备/网络、执行上下文、Tool Policy、审批与提示四组表单;
80
+ - 单测 18/18、网络 9/9。
81
+ ## 0.1.0-dev.20(配置页 DSH 化改版)
82
+
83
+ - Host 新增 `GET /device-fleet/options`:本机地址、可用 preset 列表、shell 工具、subagent providers、工作目录候选;
84
+ - 配置保存前会 ensure 工作目录存在(Device Fleet 工作区自动创建);
85
+ - 配置页重做:行式布局(label 左/控件右)、DSH token 配色、分组标题、去掉多余说明文字与 dataDir 展示;
86
+ - 下拉化:直连地址、局域网发现、Relay 模式、工作目录(工作区/默认/DSH 主目录/自定义)、Agent Preset、Shell、委派 Provider、7 项能力权限、开关项;
87
+ - 仍保留文本输入:设备名称、端口、超时、根目录/工具列表、自定义工作目录与 Relay 地址。
88
+ ## 0.1.0-dev.20(房间详情与配置页修复)
89
+
90
+ - 房间详情接口从 prefix 路由改为精确路由 `GET /device-fleet/room?id=...`,修复控制台一直「加载房间…」;客户端加载失败时显示错误文案;
91
+ - 配置页宽行改为「标签在上、控件在下」,修复根目录等标签竖排换行;
92
+ - 保存按钮改为固定品牌蓝底白字,修复按钮看起来空白;
93
+ - 「允许的原始工具」改为「免审批即用的工具」:从 Host options 拉取本机真实工具清单,以复选框选择(不再手输工具名)。
94
+ ## 0.1.0-dev.20(原始工具遵循 DSH 审批)
95
+
96
+ - 移除「免审批即用的工具」白名单;raw tool 调用不再由插件 allowlist 决定;
97
+ - `policy.capabilities.tool` 默认 `auto`:完全走 DSH 工具流水线(target Session 的 pre-execute policy + approval seam);`deny` 禁用 raw tools,`ask` 在 DSH 前加一层本机审批;
98
+ - 新增 `approval/request` 监听:Execution Slot 里 DSH 需要审批的工具调用会转到本设备审批收件箱(面板可处理),不需要审批的工具直接执行;
99
+ - 旧配置里 `tool: deny` + 空 allowedTools 自动迁移为 `auto`;
100
+ - 配置页移除工具复选框列表,「原始工具(遵循 DSH 审批)」保留 自动/询问/拒绝 下拉。
101
+ ## 0.1.0-dev.20(Phase C:交互动作)
102
+
103
+ - Room 用户动作:以用户身份发消息(选择收件人)、看板任务添加/状态更新、关闭房间、移除成员;
104
+ - 设备动作:生成/复制邀请码、手动地址连接(host+port);
105
+ - 审批历史:ApprovalInbox 记录最近 50 条已决请求(approved/denied/expired/closed),Console 审批 Tab 展示历史;
106
+ - 新增 Host 动作路由:`/room/send`、`/room/task`、`/room/close`、`/room/member/remove`、`/pair/invite`、`/pair/connect`、`/approvals/history`。
107
+ ## 0.1.0-dev.20(修复控制台空白)
108
+
109
+ - 根因:FleetConsole 的设备/房间交互误用了从未定义的 `ActionButton` 组件(React 渲染抛 ReferenceError,主面板空白)。
110
+ - 修复:统一改用已有的 `smallButton`。
111
+ - 新增 `scripts/client-smoke.mjs`:用最小 Hooks 渲染器 + mock fetch 实际渲染控制台(初始/设备/房间列表/房间详情),防止这类渲染崩溃再次漏过 `node --check`。
112
+ ## 0.1.0-dev.20(Room 回归 Session)
113
+
114
+ - Room 恢复为「就是普通 Session」的体验:会话输入框下方新增 Room 成员条(composer.dock),显示 Room 成员;普通会话显示「升级为 Room」按钮(Host API `/room/upgrade`,无需工具调用);
115
+ - Host 监听 `session/event` 的 `user/message`:Room Host 会话里用户正常聊天输入会自动投递给房间参与者并唤醒它们(带 `[Room]` 前缀的注入消息不重播,避免循环);
116
+ - 参与者回复投递回 Host 时以 `[Room] <成员>: <内容>` 形式进入正常 Chat 流,不再需要独立的房间仪表盘;
117
+ - Fleet 控制台保留为设备/配对/审批/配置管理入口。
118
+ ## 0.1.0-dev.21(Room 回复地址修复)
119
+
120
+ - 参与者被用户消息唤醒时,回复引导里的目标从无效的 "host" 修正为真实 Host member id(`host:<sessionId>`),修复 worker 回执发给自己/失败的问题。
121
+ ## 0.1.0-dev.22(Room 独立视图,停止 host 转述)
122
+
123
+ - Session 内新增「房间」Conversation View(`conversation.view`,label 房间):展示成员、时间线(含发言者/收件人)、看板与房间操作,输入默认发给非 host 参与者;
124
+ - 移除 Chat 输入的隐式 Room 广播:普通 Chat 只属于 host agent,Room 交流在「房间」页进行,避免用户在 Chat 里说话被自动转发、或 host agent 转述 worker 回复;
125
+ - host 不再接收参与者回复的 Chat 注入(参与者回复只进入房间时间线);
126
+ - 输入框下方成员条提示「房间消息在『房间』页交流」。
127
+ ## 0.1.0-dev.23(Room 聊天气泡视图)
128
+
129
+ - 「房间」Conversation View 重做为聊天式:agent 消息在左(头像 + 名字 + 收件人),用户消息在右;时间戳、Enter 发送、收件人勾选;
130
+ - 参与者消息原文直接作为左侧气泡展示,不再进入 host 的普通 Chat;
131
+ - 看板可展开/收起,支持任务状态更新与添加。
132
+ ## 0.1.0-dev.24(Room @点名)
133
+
134
+ - 房间输入框支持输入 `@` 弹出成员自动补全;选中成员变为「发给」chip,可点击取消;
135
+ - 无点名时默认发给所有非 host 参与者;被点名成员才唤醒;
136
+ - 聊天气泡显示发送者与收件人(worker-c · → host),用户侧为右气泡、agent 侧为左气泡;
137
+ - 后续:把 Room 状态下的 Chat 替换为这套界面(插件增量,不卸载系统 Chat)。
138
+ ## 0.1.0-dev.25(Room 头像)
139
+
140
+ - Room 对话头像改为图片:worker → worker.png(蓝色大肥鱼),host → host.png(偷吃 Token),user → user.png;
141
+ - Host 新增 `/device-fleet/avatar/{host,worker,user}` 精确路由,从插件 `assets/avatars/` 提供 PNG;package `files` 包含 assets。
142
+ ## 0.1.0-dev.26(Room 自动切换 + 头像库)
143
+
144
+ - RoomDock 在检测到会话是 Room 时尝试通过标准 slot 的 `useConversation` 钩子自动切到「房间」Conversation View(不同版本的 shell 若未暴露该 API 则保持手动切换);
145
+ - 头像库:Settings → Plugins → Device Fleet 配置页新增「头像库」,可多选上传 png/jpeg/webp;Host 存到 `$DSH_HOME/.../avatars/`,提供列表/预览/删除接口;
146
+ - Room 成员头像按成员顺序循环分配库中图片,可点「换头像」切换到下一组;未上传时使用内置三张图。
147
+ ## 0.1.0-dev.27(修复插件配置页空白)
148
+
149
+ - 根因:dev.26 头像库区块误用了不存在的 `sectionCard`,FleetConfig 渲染抛 ReferenceError,导致 Settings → Plugins → Device Fleet 配置页消失。
150
+ - 修复:头像库改用普通卡片容器;client smoke 增加配置页真实渲染用例(plugins.row.config)防止回归。
151
+ ## 0.1.0-dev.28(Room 复用 DSH 输入框 + @点名解析)
152
+
153
+ - 移除房间页自带发送栏,房间消息直接使用 DSH 底部输入框;
154
+ - Host 监听 Room Host 会话的 user/message:解析 `@成员名`,被点名成员才加入收件人;未点名默认发给所有参与者;
155
+ - 消息文本会去掉 @mention 标记后写入房间日志;host 本身已收到 Chat 输入,不重复唤醒;
156
+ - 房间页保留成员/气泡/看板,底部提示用 @ 点名。
157
+ ## 0.1.0-dev.29(@补全面板 + host 房间发言)
158
+
159
+ - 房间 Dock 读取 DSH 输入框草稿(标准 `useInput` 钩子),输入 `@` 时在输入框上方弹出补全面板,顶部标注「可发送给:所有人 / 指定 agent」,列出 Room 成员并可点击插入 `@name`;
160
+ - host agent 在 Chat 的 assistant 消息会同步写入 Room 日志(只读记录,不唤醒参与者),房间页左侧出现 host 气泡与 host 头像。
@@ -0,0 +1,44 @@
1
+ # Fleet 控制台 UI(Phase A,dev.13)
2
+
3
+ ## 用户决策(2026-11-09)
4
+
5
+ 1. **配置保存必须即时生效**:不重启插件、不重启 DSH。→ Phase B 采用"运行时配置 + 持久化 overrides"设计(下一阶段实现)。
6
+ 2. 房间 UI 动作身份:以**用户**身份(ADR-0007)。
7
+ 3. 实施顺序:A → B → C → D。
8
+
9
+ ## 本阶段实现
10
+
11
+ ### Host 读模型
12
+
13
+ `src/host/ui-web.js`:
14
+
15
+ - `GET /device-fleet/overview`:`FleetNode.status()` + rooms 摘要 + approvals 列表;
16
+ - `GET /device-fleet/rooms`:房间摘要列表;
17
+ - `GET /device-fleet/rooms/:id`:房间详情(成员、策略、transcript、看板任务)。
18
+
19
+ ### Room 持久化 / rehydrate
20
+
21
+ `RoomService` 新增 `stateFile`(`$DSH_HOME/device-fleet/rooms.json`):
22
+
23
+ - 每次 append(创建/成员/消息/任务/关闭)后原子写快照;
24
+ - 启动时 `load()` 重建 rooms/members/messages/tasks;
25
+ - 成员 agent 不序列化,唤醒时通过 `sessionController.resolveAgent(sessionId)` 懒解析;
26
+ - 插件卸载不再关闭房间(避免重载即销毁)。
27
+
28
+ **验证**:创建 room + worker-c + 任务 + 1 条消息 → 插件重载 → `room_status` 仍显示 2 名成员、1 条消息、1 个任务;`room_task_list` 保留任务。
29
+
30
+ ### Client
31
+
32
+ `client.js` 新增:
33
+
34
+ - `sidebar.panellist` 图标(id `fleet`,label `设备联邦`,圆形网格图标);
35
+ - `main` keyed 面板 `fleet` = Fleet 控制台,三个 Tab:
36
+ - **设备**:本机名称/id、直连地址、relay、发现数量;Fleet 成员列表(在线/离线);回连地址;
37
+ - **房间**:房间列表(成员数/消息数/任务数),选中后展示成员、看板、时间线;
38
+ - **审批**:待审批列表 + 允许一次/15 分钟/拒绝(与快捷面板共用 Host API)。
39
+
40
+ ## 未完成(Phase B/C/D)
41
+
42
+ - 配置页与即时生效(Phase B);
43
+ - 房间交互:以用户身份发消息、看板操作、邀请/移除成员(Phase C);
44
+ - 配对向导、设备操作、审批历史/授权撤销、SSE、通知、i18n、a11y(Phase C/D)。
@@ -0,0 +1,30 @@
1
+ # 首次配对实录(2026-11-09)
2
+
3
+ ## 环境
4
+
5
+ - 设备 A:本机(Windows,DSH desktop profile,`device-fleet` 0.1.0-dev.3)
6
+ - deviceId:`50fdaf90-f0aa-440e-9fa6-c60ba5051001`,deviceName:`desktop`
7
+ - 直连:`0.0.0.0:60506`(热点网段 `192.168.137.1:60506`)
8
+ - allowedRoots:`E:/Dsh_test`
9
+ - 设备 B:另一台设备(同一热点 `192.168.137.0/24`,plugin 0.1.0-dev.3)
10
+ - deviceId:`c2d42572-e5f0-4d7e-9fff-c28d9a668439`
11
+ - 热点网段内网段:A `192.168.137.1/24`,B 为客户端之一。
12
+
13
+ ## 过程
14
+
15
+ 1. dev.3 邀请携带 `endpoints`(A 的 `210.73.18.88:60506` 与 `192.168.137.1:60506`)。
16
+ 2. B 安装 dev.3 → `fleet_join` 新邀请 → 重启;插件读取 enrollment endpoints,自动回连 A。
17
+ 3. 结果:双方握手成功,roster 互固定公钥;A 侧 `fleet_list` 显示 B `online`,`fleet_status.connected` 含 B。
18
+ 4. `fleet_tools(device=B)` 成功(说明 RPC/加密通道正常)。
19
+ 5. `fleet_read(device=B, path=E:/Dsh_test/hello-from-A.txt)` 返回 `denied: no allowed roots configured on this device` —— 策略按设计拦截,B 侧尚未配置 allowedRoots。
20
+
21
+ ## 结论
22
+
23
+ - **网络层 v1 直连 + 配对流程打通**(组播发现在该热点下不可用,邀请 endpoints 自动回连解决了问题)。
24
+ - **Tool Policy 生效**:目标设备未配置允许根时直调被拒。
25
+ - 待配置 B 的 `policy.allowedRoots` / `execution.cwd` 后可双向直调;`fleet_run` 待审批收件箱 UI。
26
+
27
+ ## 复现要点
28
+
29
+ - 邀请里的 endpoints 由 `networkInterfaces()` 的非回环 IPv4 + 当前 direct port 组成;direct 端口每次插件重载会变,重载后需重新生成邀请或手动 `fleet_connect`。
30
+ - 同一账号指纹:邀请携带 `accountFingerprint`;B 登录不同账号会在加载时被标记 mismatch 并拒绝连接。
@@ -0,0 +1,33 @@
1
+ # 跨设备调用实测(2026-11-09,dev.6)
2
+
3
+ 环境:A `desktop` / `50fdaf90-…`(端口固定 60580);B `laptop` / `c2d42572-…`(dev.6,同一热点)。
4
+ 测试文件:A `E:\Dsh_test\hello-from-A.txt`;B `E:\Dsh_test\hello-from-B.txt`。
5
+
6
+ ## 结果
7
+
8
+ | # | 调用 | 结果 | 耗时 |
9
+ |---|---|---|---|
10
+ | 1 | `fleet_read(B, E:/Dsh_test/hello-from-B.txt)` | ✅ 返回 `hello from device B` | 48ms |
11
+ | 2 | `fleet_call(B, tool="read", {file_path:…hello-from-B.txt})` | ✅ 返回同一内容(原始工具直调) | 4ms |
12
+ | 3 | `fleet_fetch(B, hello-from-B.txt)` | ✅ 返回文本 | 1ms |
13
+ | 4 | `fleet_run(B, "echo hello-from-B")` | ⚠️ `approval_denied`(B 策略 run=ask,审批 UI 未实现,fail-closed 正确) | 即时 |
14
+ | 5 | `fleet_delegate(B, "读取 hello-from-B.txt 并原样返回")` | ✅ B 的 agent(spawn 子会话 `1a768f89…`)读文件并回传最终文本 | 2.7s |
15
+ | 6 | `fleet_delegate(B, "用 fleet_read 读 A 的 hello-from-A.txt 并原样返回")` | ✅ B 的 agent 反向调用 A 的 `read`,回传 `hello from device A (fleet test)`(子会话 `515f8c1b…`) | 2.5s |
16
+
17
+ ## 结论
18
+
19
+ - v1 直连 + 端到端加密 + 配对/roster 固定公钥:**通过**;
20
+ - 直调(typed 快捷工具 / `fleet_call` 原始工具 / fetch):**通过**;
21
+ - Tool Policy 与 fail-closed 审批:**按设计生效**;
22
+ - 跨设备委派(B 的 agent 在本机执行并回传;B 的 agent 反向调 A 工具):**通过**;
23
+ - 遗留:审批收件箱 UI(`run`/`delegate` 的 `ask` 档目前只能拒绝或配置 auto);Room/Room Board/设备页 UI 未开始;委派返回文本里 `[reasoning]` 是结果里的 reasoning block 展示,可后续过滤。
24
+
25
+ ## 复现命令(A 侧)
26
+
27
+ ```
28
+ fleet_list()
29
+ fleet_read(device="c2d42572-…", path="E:/Dsh_test/hello-from-B.txt")
30
+ fleet_call(device="c2d42572-…", tool="read", parameters={file_path:"E:/Dsh_test/hello-from-B.txt"})
31
+ fleet_delegate(device="c2d42572-…", task="读取 E:/Dsh_test/hello-from-B.txt 并原样返回")
32
+ fleet_delegate(device="c2d42572-…", task="用 fleet_read 读设备 50fdaf90-… 的 E:/Dsh_test/hello-from-A.txt 并原样返回")
33
+ ```
@@ -0,0 +1,31 @@
1
+ # Room 逻辑层实测(2026-11-09,dev.8,单机模拟参与者)
2
+
3
+ 在当前这台 DSH 内,用两个**真实本地 session/agent** 充当 Room 参与者,验证寻址唤醒与看板串行交接,不依赖第二台设备。
4
+
5
+ ## 流程与结果
6
+
7
+ 1. `room_create` → roomId = 当前会话 `session-269260c9-…`,host 成员 `host:session-269260c9-…`。
8
+ 2. `room_add_local(name="worker-a", cwd=E:/Dsh_test)` → 真实 session `session-d063026e-…`。
9
+ 3. `room_send(to=[worker-a], text="读取 hello-from-A.txt 并用 room_send 回复 host")` → `woke worker-a`;
10
+ - worker-a 的 agent 真实跑了一轮模型 + 本机 `read`,并在 3.4s 内调用 `room_send` 回传 `hello from device A (fleet test)`;
11
+ - transcript 两条消息,完整可见。
12
+ 4. `room_add_local(name="worker-b")`;看板:
13
+ - taskA `读取 hello-from-A.txt` owner=worker-a;
14
+ - taskB `汇总读取结果` owner=worker-b,`dependsOn=[taskA]`;
15
+ - taskC `并行:独立检查` owner=worker-a,无依赖。
16
+ 5. `room_task_update(taskA, done)` → 返回 `handed off to worker-b`;worker-b 被点名唤醒,30s 内完成汇总并 `room_send` 给 host(内容含前序结果、交叉校验、并行任务仍 pending 的说明),同时把自己 taskB 置为 running。
17
+ 6. `room_task_list`:taskA done、taskB running、taskC pending(并行任务未被串行交接影响)。
18
+ 7. `room_close`。
19
+
20
+ ## 结论
21
+
22
+ - Room 生命周期、成员管理、寻址消息、未点名不唤醒、host 权威日志:单机逻辑通过;
23
+ - Room Board `dependsOn` 串行交接 / 并行任务不阻塞:真实 agent 验证通过;
24
+ - 参与者使用的都是真实 DSH agent(各自 session),不是 mock。
25
+
26
+ ## 限制(下一步)
27
+
28
+ - 远程参与者传输(把 local participant 换成远端设备成员)尚未接入;
29
+ - Client 面板/房间视图未做(用户无法在 GUI 直接看 room 时间线/看板);
30
+ - 房间状态在插件内存 + host session 事件里,插件重载后需要 rehydrate(未实现);
31
+ - 房间策略仅存储,尚未强制(invite/join/maxMembers 中 maxMembers 已生效)。
@@ -0,0 +1,93 @@
1
+ # UI 呈现缺口审计(2026-11-09)
2
+
3
+ ## 现状盘点
4
+
5
+ 当前插件已实现的 UI 只有**审批面板**(dev.12):
6
+
7
+ - 入口:`sidebar.footer.action` 的「🛰️ 审批」;
8
+ - 内容:`shell.overlay` 居中对话框,待审批列表 + 允许一次/15 分钟/拒绝;
9
+ - 数据:Host `/device-fleet/approvals`、`/device-fleet/approvals/decide`。
10
+
11
+ 其余**全部**依靠模型工具文本输出或 profile YAML 配置,没有任何可视化:
12
+
13
+ | 设计要素 | 当前呈现方式 | 缺口 |
14
+ |---|---|---|
15
+ | 设备列表 / 在线状态 / 直连地址 / relay / 发现 | `fleet_status` 文本 | 无设备页,无法看设备拓扑与连接路径 |
16
+ | 配对:邀请码、加入、手动地址、设备移除 | `fleet_invite`/`fleet_join`/`fleet_connect` 工具 | 无配对向导,邀请码需在聊天里传 |
17
+ | 插件配置(deviceName/direct/discovery/relay/execution/policy/approvals/prompt) | profile `cordis.patch.yml` | 完全无 UI,改配置要手改 YAML + 重启 |
18
+ | Room 列表 / 房间状态 | `room_status`、`room_messages` 文本 | 无房间列表/视图 |
19
+ | Room 成员(设备 × agent、host/local/remote) | `room_status` JSON | 无成员可视化,看不出"不同设备不同 agent" |
20
+ | Room 时间线(用户可见完整对话) | `room_messages` 文本 | 无时间线 UI,无消息流式更新 |
21
+ | Room 发消息 / 点名 | `room_send` 工具 | 无输入框/@成员操作 |
22
+ | Room Board(任务、owner、状态、依赖、交接) | `room_task_*` 文本 | 无看板视图 |
23
+ | 房间策略(invite/join/maxMembers/boardWrite) | 创建参数 | 无查看/编辑 |
24
+ | 审批历史 / TTL 授权管理 | 收件箱列表(仅 pending) | 无历史、无授权撤销 |
25
+ | 委派(fleet_delegate) | 工具结果文本 | 无历史/运行状态/中断入口 |
26
+ | 直调审计(谁调了哪台设备什么工具) | 会话轨迹里 | 无 Fleet 维度审计页 |
27
+ | 首次配对引导 | 无 | 新设备没有任何向导 |
28
+ | 错误/诊断 | `fleet_status` + 日志 | 无连接诊断页 |
29
+ | 通知 | 无 | 新审批/新消息没有系统通知 |
30
+ | 国际化 | 中文硬编码 | 未接 Client locale 服务 |
31
+ | 多窗口/实时 | 审批 2.5s 轮询 | Room/设备状态无推送,多窗口不同步 |
32
+
33
+ ## 关键基础设施缺口(UI 之前必须先补)
34
+
35
+ 1. **Host 读模型 API 不足**:目前只有 approvals 两条路由;需要
36
+ - `GET /device-fleet/overview`:本机身份/版本/直连/relay/discovery/roster/connected/peerHints;
37
+ - `GET /device-fleet/rooms`、`GET /device-fleet/rooms/:id`:成员、策略、transcript、tasks;
38
+ - `GET /device-fleet/events`(SSE)或长轮询:状态变化推送。
39
+ 2. **配置写路径缺失**:Cordis config 来自 profile patch,运行时不可写。需要确定:
40
+ - 方案 A(推荐):`$DSH_HOME/device-fleet/config-overrides.json` + apply 时合并,UI 写该文件;保存后提示"重载插件生效"(或调用 Host 重新加载);
41
+ - 方案 B:直接改写 profile `cordis.patch.yml`(风险高:破坏注释/结构,需备份)。
42
+ 3. **Room 状态无持久化/rehydrate**:RoomService 是内存 Map,插件重载/DSH 重启后房间消失。UI 之前应先做基于 host session 事件的 rehydrate,否则"房间视图"打开是空的。
43
+ 4. **UI 操作的身份与授权**:UI 里的"发消息/改任务/关房"是以**用户**身份还是 host agent 身份?需要 Room 服务暴露 user 动作并校验房主/成员权限。
44
+ 5. **Slot/设计系统**:已确认可用的官方扩展点:
45
+ - `sidebar.panellist`(全局面板图标)+ `main`(keyed 主面板)→ Fleet 控制台;
46
+ - `settings.section`(设置页)或 `plugins.bundle.config`(插件配置页,key = 包名)→ 配置 UI;
47
+ - `shell.overlay` → 对话框/房间详情;
48
+ - `sidebar.footer.action` → 快捷入口(审批已用)。
49
+ 6. **多设备语义呈现**:房间成员需要 "设备(名称/在线/连接路径)× agent(session/角色)" 两级标签;当前 member 只有 `local:session-…`/`host:…` 字符串,远程成员还没有实体,需要先接远程参与者传输。
50
+
51
+ ## 建议的信息架构
52
+
53
+ ```
54
+ 侧栏
55
+ ├─ 全局面板图标「Fleet」 (sidebar.panellist: id=fleet)
56
+ │ └─ main keyed "fleet" 主面板
57
+ │ ├─ 设备 Tab:本机状态、roster、在线/离线、直连/relay、配对按钮、诊断
58
+ │ ├─ 房间 Tab:房间列表 → 房间详情(成员树、时间线、看板、策略)
59
+ │ └─ 审批 Tab:待审批 + 历史 + 授权管理
60
+ ├─ Settings 上方「🛰️ 审批」快捷入口(已实现)
61
+ └─ Settings → 插件 → Device Fleet 配置页
62
+ (plugins.bundle.config key "dsh-device-fleet" 或 plugins.row.config "dsh-device-fleet#device-fleet")
63
+ 分块:设备/网络 · 执行上下文 · 远端权限(Tool Policy) · 本地审批 · 高级
64
+ ```
65
+
66
+ ## 分阶段实施计划
67
+
68
+ **Phase A:Fleet 控制台(只读)**
69
+ - Host `/overview`、`/rooms`、`/rooms/:id` API;
70
+ - `sidebar.panellist` + `main` 主面板,三个 Tab:设备 / 房间 / 审批(待审批 + 只读时间线/看板);
71
+ - 先解决 Room rehydrate,否则只读视图无内容。
72
+
73
+ **Phase B:配置 UI**
74
+ - `plugins.bundle.config` 注册配置页;
75
+ - 字段分组 + 校验 + 当前值显示;
76
+ - 保存走 overrides 文件(方案 A),保存后提示重载插件;policy 的 roots/tools 用列表编辑器。
77
+
78
+ **Phase C:交互动作**
79
+ - 房间:以用户身份发消息(点名选择成员)、看板任务增改/交接、关房、邀请/移除成员(权限校验);
80
+ - 设备:配对向导(邀请码生成/扫码式复制、手动地址连接、移除设备);
81
+ - 审批:历史列表 + ttl 授权撤销。
82
+
83
+ **Phase D:体验与可靠性**
84
+ - SSE/事件推送替代轮询,多窗口同步;
85
+ - 通知(新审批/新房间消息);
86
+ - Client locale(zh/en),a11y,空状态/引导;
87
+ - 插件重载 rehydrate、房间日志持久补发(ADR-0018)。
88
+
89
+ ## 需要你确认的决策
90
+
91
+ 1. 配置写路径:选 **overrides 文件(推荐)** 还是直接改 profile YAML?
92
+ 2. 房间 UI 动作的身份:以 **用户** 身份(推荐,符合 ADR-0007 用户发言与设备解耦)还是 host agent 身份?
93
+ 3. 先做 Phase A(控制台只读)还是先做 Phase B(配置页)?我建议按 A → B → C → D。
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "dsh-device-fleet",
3
- "version": "0.1.0-dev.3",
3
+ "version": "0.1.0-dev.30",
4
4
  "description": "DeepSeek Harness device fleet: enroll devices, direct-call remote tools, shared Rooms with lazy-woken agents, and delegated subagents across devices.",
5
5
  "type": "module",
6
6
  "main": "src/plugin/index.js",
7
7
  "exports": {
8
8
  ".": "./src/plugin/index.js",
9
+ "./client": "./client.js",
9
10
  "./net": "./src/net/index.js",
10
11
  "./host": "./src/host/index.js",
11
12
  "./package.json": "./package.json"
@@ -16,6 +17,13 @@
16
17
  "dsh": {
17
18
  "bundle": {
18
19
  "patch": "./cordis.patch.yml"
20
+ },
21
+ "client": {
22
+ "platform": "web",
23
+ "immediately": true,
24
+ "inject": [
25
+ "@deepseek-ai/dsh-client-runtime"
26
+ ]
19
27
  }
20
28
  },
21
29
  "peerDependencies": {
@@ -31,6 +39,8 @@
31
39
  },
32
40
  "files": [
33
41
  "src",
42
+ "client.js",
43
+ "assets",
34
44
  "cordis.patch.yml",
35
45
  "README.md",
36
46
  "CONTEXT.md",