dsh-plugin-mobile-gateway 0.7.2 → 0.7.4
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/PROTOCOL.md +38 -14
- package/README.md +43 -5
- package/cordis.patch.yml +8 -0
- package/docs/dsh-0.1.5-rc.2-compatibility-audit.md +255 -0
- package/docs/dsh-rc2-mobile-integration.md +151 -0
- package/docs/multi-gateway-app-integration.md +124 -0
- package/docs/multi-gateway-phase1-acceptance.md +179 -0
- package/docs/multi-gateway-todo.md +137 -0
- package/docs/runtime-architecture.architecture.json +264 -0
- package/docs/runtime-architecture.html +15001 -0
- package/docs/runtime-architecture.visual-check.html +32 -0
- package/docs/runtime-architecture.visual-check.json +548 -0
- package/docs/typert-remote-gateway-feature-checklist.md +19 -21
- package/lib/client.js +30 -21
- package/lib/dsh-host-adapter.mjs +54 -81
- package/lib/gateway-state.mjs +57 -0
- package/lib/index.mjs +223 -72
- package/lib/session-follower.mjs +134 -0
- package/package.json +3 -3
package/PROTOCOL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh Mobile Gateway — WebSocket 协议参考
|
|
2
2
|
|
|
3
|
-
移动端通过经过设备鉴权的 WebSocket 连接与 dsh 通信:订阅 agent 实时输出、发送文字和图片、处理 Human-in-the-loop 提问与操作审批、查询会话/工作区/历史、调整会话配置。本协议由持久化插件 `dsh-plugin-mobile-gateway`
|
|
3
|
+
移动端通过经过设备鉴权的 WebSocket 连接与 dsh 通信:订阅 agent 实时输出、发送文字和图片、处理 Human-in-the-loop 提问与操作审批、查询会话/工作区/历史、调整会话配置。本协议由持久化插件 `dsh-plugin-mobile-gateway` 实现。当前源码仅适配 DSH 0.1.5-rc.2 / Session format 3,移动端接入变更见 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md)。
|
|
4
4
|
|
|
5
5
|
- **本机端点**:`ws://127.0.0.1:3080/ws/mobile`(与 dsh web GUI 同端口)
|
|
6
6
|
- **局域网端点**:`ws://<电脑的私有局域网 IP>:3081/ws/mobile`(插件独立监听,只提供经过鉴权的 WebSocket)
|
|
@@ -46,7 +46,9 @@ WebUI 中有两个互相独立的开关。它们是本机管理设置,iOS 客
|
|
|
46
46
|
| 开启 | 开启(默认) | 必须使用一次性配对码或长期设备 token,否则返回 `401 Unauthorized` |
|
|
47
47
|
| 开启 | 关闭(仅 Debug) | 仅 DSH 本机监听允许无凭证连接,`hello.authenticated` 为 `false`;独立局域网监听仍返回 `401` |
|
|
48
48
|
|
|
49
|
-
-
|
|
49
|
+
- 未保存用户选择且未显式配置时,移动网关默认关闭。管理界面支持关闭、临时开启、常驻开启,选择会持久化。
|
|
50
|
+
- 临时开启后默认 5 分钟内没有客户端成功建立连接才自动关闭;成功连接后本次运行保持开启,重启重新计时。常驻开启没有该计时器。
|
|
51
|
+
- 启动优先级为持久化选择 > `gatewayMode` > 旧 `gatewayEnabled`;旧配置 true 映射常驻,false 映射关闭。
|
|
50
52
|
- 关闭移动网关会关闭现有连接,WebSocket close code 为 `4004`。
|
|
51
53
|
- 从 Debug 模式重新开启鉴权时,所有无凭证连接会被关闭,close code 为 `4003`。
|
|
52
54
|
- Debug 鉴权开关只影响 DSH 自带的本机监听,并且只在当前 DSH 进程中生效;独立局域网监听始终强制设备鉴权。
|
|
@@ -88,6 +90,26 @@ const pairingText = Buffer.from(JSON.stringify(payload), 'utf8').toString('base6
|
|
|
88
90
|
|
|
89
91
|
长期 token 默认禁止放在 URL query 中,避免被代理日志、浏览器历史和监控系统记录。缺少凭证、凭证无效、配对码过期或重复使用时,HTTP Upgrade 返回 `401 Unauthorized`。
|
|
90
92
|
|
|
93
|
+
#### 多网关身份扩展(当前源码,尚未发布新版本)
|
|
94
|
+
|
|
95
|
+
保持配对 `version: 2`、子协议 `dsh-mobile-v1` 和 `hello.protocol: 3`。以下新增字段不改变已有业务帧语义:
|
|
96
|
+
|
|
97
|
+
| 新字段 | 返回位置 | 约定 |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `gatewayId` | 配对载荷、`paired`、`hello`、`GET /mgw/status` | 持久化 UUID v4;同一网关各访问地址、双通道及重启前后相同 |
|
|
100
|
+
| `gatewayName` | 同上 | 展示名称,允许改变,不能作为身份或鉴权依据 |
|
|
101
|
+
| `endpoints` | 配对载荷、`GET /mgw/status` | 规范化去重的 URL 字符串数组;不在 `hello` 或 `paired` 返回 |
|
|
102
|
+
|
|
103
|
+
配对仍保留 `publicUrl`,且它排在本次 `endpoints` 第一项。其后按本机配对请求 `endpoints`、插件配置 `endpoints`、公网配置、已监听 LAN 地址的顺序合并。每个输入列表及合并结果最多 16 项,每项最多 2048 字符。允许 HTTP(S) 输入并转为 WS(S),拒绝凭证、query、fragment、未指定监听地址及公网明文 WS。
|
|
104
|
+
|
|
105
|
+
`POST /mgw/pair` 可额外传入 `endpoints: string[]`,只用于本次配对,不写入配置。移动端对新地址必须在发送凭证前建立信任;`hello` 的 ID 校验发生在鉴权后,不能替代 TLS 和地址确认。多个网关的 token 独立签发、保存和撤销;App 以网关连接上下文路由原有业务请求。
|
|
106
|
+
|
|
107
|
+
本机管理接口 `POST /mgw/gateway` 接受 `{"mode":"disabled|temporary|persistent"}` 中的一个具体值,例如 `{"mode":"persistent"}`。兼容旧 `{"enabled":true}`(临时开启)与 false(关闭);不能同时传 `mode` 和 `enabled`。返回 `gatewayEnabled`、`gatewayMode`、`waitExpiresAt`、`connectedClients`。参数错误返回 400;保存失败返回 500 且不改变当前运行模式。
|
|
108
|
+
|
|
109
|
+
`GET /mgw/status` 增加身份、地址列表及 `gatewayMode`。模式的持久化不影响配对码有效期和文件传输超时。自动超时关闭若遇到磁盘写入失败,会保持当前进程关闭并记录错误;重启可能仍按旧保存模式运行,需修复存储后再次保存选择。
|
|
110
|
+
|
|
111
|
+
完整配对示例、旧 App 迁移、地址信任与错误处理见 [App 对接说明](docs/multi-gateway-app-integration.md)。
|
|
112
|
+
|
|
91
113
|
#### iOS 对接示例
|
|
92
114
|
|
|
93
115
|
首次配对时,先对二维码/手动字符串执行 Base64URL 解码,再从 JSON 解析 `publicUrl`、`pairingCode` 和 `expiresAt`,并在过期前连接:
|
|
@@ -138,7 +160,7 @@ func connectAuthenticated(publicURL: URL, token: String) -> URLSessionWebSocketT
|
|
|
138
160
|
| type | 参数 | 说明 |
|
|
139
161
|
|---|---|---|
|
|
140
162
|
| `ping` | — | 心跳;回复 `pong` |
|
|
141
|
-
| `subscribe` | `sessionId` |
|
|
163
|
+
| `subscribe` | `sessionId`, `assistantStream?` | 事件流过滤;`assistantStream: true` 启用独立实时流及原子基线(见 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md)),并重放该会话仍待处理的提问与审批(不订阅 = 接收所有会话) |
|
|
142
164
|
| `unsubscribe` | — | 取消过滤 |
|
|
143
165
|
|
|
144
166
|
```json
|
|
@@ -606,7 +628,7 @@ let image = [
|
|
|
606
628
|
| `sessions` | — | 会话列表(`updatedAt/running/blank/cwd/agentPreset`) |
|
|
607
629
|
| `session-archive` | `sessionId` | 将 Session 加入 Host 的完整归档集合(隐藏但不删除) |
|
|
608
630
|
| `session-rename` | `sessionId`, `title` | 写入用户指定的持久化 Session 名称 |
|
|
609
|
-
| `history` | `sessionId`, `beforeSeq?`, `maxMessages?`, `maxBytes?`, `view?` | 历史事件页(见下) |
|
|
631
|
+
| `history` | `sessionId`, `beforeSeq?`, `historyFormatVersion?`, `maxMessages?`, `maxBytes?`, `view?` | 历史事件页(见下) |
|
|
610
632
|
| `attachment` | `sessionId`, `attachmentId` | 读取历史中属于该会话的图片字节 |
|
|
611
633
|
| `file-list` | `sessionId`, `path?`, `requestId?` | 列出会话工作目录内的一层文件与文件夹 |
|
|
612
634
|
| `file-download-open` | `sessionId`, `path`, `requestId` | 打开一个工作目录内的普通文件下载 |
|
|
@@ -660,13 +682,14 @@ WebUI、App 或其他客户端造成的变化通过以下帧主动推送:
|
|
|
660
682
|
- 返回**原始 SessionEvent**(`{type, seq, time, data}`,方案A),可选裁剪
|
|
661
683
|
- 图片不会内联进历史页。`user/message.data.content[]` 中的图片块为 `{ "type":"image", "attachment": ImageAttachmentRef }`;iOS 使用其中的 `attachmentId` 请求图片数据
|
|
662
684
|
- `maxBytes`:单帧字节预算,默认 **4 MiB**;超预算保留最新部分并给出 `nextBeforeSeq` 续页(客户端 16 MiB 上限的安全余量)
|
|
663
|
-
- `view: "conversation"
|
|
664
|
-
- 分页:`hasMore` 为真时用 `beforeSeq: nextBeforeSeq` 请求更早一页
|
|
685
|
+
- `view: "conversation"`:**对话裁剪模式**——隐藏系统消息与 request/header、request/context,移除 Assistant 事件的内嵌 `data.stream`,`tool/result` 嵌套文本截断到 2000 字符
|
|
686
|
+
- 分页:`hasMore` 为真时用 `beforeSeq: nextBeforeSeq, historyFormatVersion: 3` 请求更早一页
|
|
665
687
|
|
|
666
688
|
```json
|
|
667
689
|
→ { "kind": "history", "sessionId": "session-abc", "events": [ ...原始事件... ],
|
|
690
|
+
"historyFormatVersion": 3, "cursor": 200,
|
|
668
691
|
"bytes": 3521, "view": "conversation", "hasMore": true, "nextBeforeSeq": 128,
|
|
669
|
-
"projections": { "asOfSeq":
|
|
692
|
+
"projections": { "asOfSeq": 200, "values": { "tokenUsage": {...}, "contextPressure": {...}, "permissions": {...}, "sessionStats": {...} } } }
|
|
670
693
|
```
|
|
671
694
|
|
|
672
695
|
图片引用结构:
|
|
@@ -949,7 +972,7 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
|
|
|
949
972
|
## 11. 分支(fork)
|
|
950
973
|
|
|
951
974
|
```json
|
|
952
|
-
{ "type": "fork", "sessionId": "session-abc", "atSeq": 42 }
|
|
975
|
+
{ "type": "fork", "sessionId": "session-abc", "atSeq": 42, "historyFormatVersion": 3 }
|
|
953
976
|
→ { "kind": "fork", "sessionId": "session-分支新会话" }
|
|
954
977
|
```
|
|
955
978
|
- `atSeq`:从该消息所在的**完整一轮**分叉(省略 = 最近完成的 turn);进行中的 turn 分叉会报 `fork-unavailable`
|
|
@@ -989,12 +1012,13 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
|
|
|
989
1012
|
### `event` 帧(agent 实时输出)
|
|
990
1013
|
```json
|
|
991
1014
|
{ "kind": "event", "sessionId": "session-abc", "seq": 42, "time": 1786937352,
|
|
992
|
-
"event": { "type": "assistant/
|
|
1015
|
+
"event": { "type": "assistant/message", "turn": 1, "step": 0, "text": "已完成", "reasoning": "", "toolCalls": [] } }
|
|
993
1016
|
```
|
|
994
1017
|
`event.type` 覆盖(精炼字段):
|
|
995
1018
|
- `user/message` → `{text, source, images?: ImageAttachmentRef[]}`
|
|
996
|
-
- `assistant
|
|
997
|
-
- `assistant/
|
|
1019
|
+
- 实时 token 使用独立 `assistant-stream`,不是带持久 seq 的 `assistant/chunk`;详见 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md)。
|
|
1020
|
+
- `assistant/attempt` → `{turn, step, stream[]}`
|
|
1021
|
+
- `assistant/message` → `{turn, step, text, reasoning, toolCalls[], interrupted?, usage?}`
|
|
998
1022
|
- `session/title` → `{title, source?}`
|
|
999
1023
|
- `tool/call` → `{turn, step, callId, name, arguments}`
|
|
1000
1024
|
- `tool/result` → `{turn, step, callId, isError, preview(≤400字符)}`
|
|
@@ -1006,10 +1030,10 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
|
|
|
1006
1030
|
|
|
1007
1031
|
1. Connect → 收到 `hello`
|
|
1008
1032
|
2. `{"type":"sessions"}` → 挑 `sessionId`(或直接下一步自动建)
|
|
1009
|
-
3. `{"type":"subscribe","sessionId":"session-abc"}`
|
|
1033
|
+
3. `{"type":"subscribe","sessionId":"session-abc","assistantStream":true}`,等待 `session-snapshot`
|
|
1010
1034
|
4. `{"type":"message","sessionId":"session-abc","text":"帮我查一下deepseek"}` → `sent`
|
|
1011
|
-
5. 盯着 Messages
|
|
1012
|
-
6. `{"type":"history","sessionId":"session-abc","view":"conversation","maxMessages":60}` → 最近历史(自动分页用 `beforeSeq: nextBeforeSeq`)
|
|
1035
|
+
5. 盯着 Messages 面板:独立 `assistant-stream` 展示 token,`event` 流提供已提交的消息和工具事件
|
|
1036
|
+
6. `{"type":"history","sessionId":"session-abc","view":"conversation","maxMessages":60}` → 最近历史(自动分页用 `beforeSeq: nextBeforeSeq, historyFormatVersion: 3`)
|
|
1013
1037
|
7. `{"type":"session-stats","sessionId":"session-abc"}` → 统计条数据
|
|
1014
1038
|
8. 完事 `{"type":"unsubscribe"}` 或 Disconnect
|
|
1015
1039
|
|
package/README.md
CHANGED
|
@@ -6,21 +6,59 @@
|
|
|
6
6
|
|
|
7
7
|
DeepSeek Harness 的设备鉴权移动网关,支持会话与实时事件、排队消息同步及编辑/删除/Steer、Session 归档和重命名的双向同步、停止当前生成并稍后继续、任务列表和当前 Goal 同步及管理、服务端驱动的命令和技能菜单、Human-in-the-loop、图片及文件传输。安装后,Harness WebUI 左侧边栏会出现“移动设备”入口,可直接开启网关、生成配对二维码和管理可信设备。
|
|
8
8
|
|
|
9
|
-
>
|
|
9
|
+
> 当前源码以 **DSH 0.1.5-rc.2** 为唯一适配基线,使用 Session format 3;不再兼容更早的 Host 版本。
|
|
10
|
+
>
|
|
11
|
+
> 实时流已改为独立 `assistant-stream` 帧,移动端需要按 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md) 更新订阅、缓存与分页处理。当前修改尚未发布。
|
|
12
|
+
>
|
|
13
|
+
> v0.7.3:优化移动网关运行模式下拉框的箭头间距。
|
|
14
|
+
>
|
|
15
|
+
> v0.7.2:新增独立对话/控制连接、空 Session 创建、停止生成与稍后继续、排队消息同步及编辑/删除/Steer,以及 Session 归档和重命名的双向同步。
|
|
10
16
|
|
|
11
17
|
## 协议与 DSH 兼容层
|
|
12
18
|
|
|
13
|
-
移动端连接的是本项目维护的 `dsh-mobile-v1`,不是 DSH 的内部 Remote 协议。插件内部通过独立 Host Adapter 对接 DSH
|
|
19
|
+
移动端连接的是本项目维护的 `dsh-mobile-v1`,不是 DSH 的内部 Remote 协议。插件内部通过独立 Host Adapter 对接 DSH 0.1.5-rc.2 的 Remote Gateway;Session、Workspace、Settings、Commands、Goals 等 namespace 和严格参数只存在于该适配层。
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
配对鉴权和 `dsh-mobile-v1` / `hello.protocol = 3` 保持不变。新版实时 token 不占用持久事件的 `seq`:客户端显式订阅 `assistantStream: true`,接收原子的 `session-snapshot` 和独立增量;普通 `event` 只携带持久事件。未接入新订阅的客户端只能收到持久消息。
|
|
16
22
|
|
|
17
|
-
|
|
23
|
+
历史响应包含 `historyFormatVersion` 和 `cursor`。客户端格式变化时应清理历史缓存、重新安装基线;携带 `beforeSeq` 的分页或携带 `atSeq` 的 fork 必须同时发送 `historyFormatVersion: 3`。
|
|
24
|
+
当前协议接入与验收见 [rc.2 移动端接入说明](docs/dsh-rc2-mobile-integration.md);早期迁移记录见 [Remote Gateway 重构实施计划](docs/remote-gateway-refactor-plan.md)。
|
|
18
25
|
|
|
19
26
|
- WebSocket:`/ws/mobile`
|
|
20
27
|
- 局域网:`ws://<局域网 IP>:3081/ws/mobile`
|
|
21
28
|
- Linux 服务器公网:`wss://<公网 IP>/ws/mobile`
|
|
22
29
|
- 协议文档:[PROTOCOL.md](PROTOCOL.md)
|
|
23
30
|
|
|
31
|
+
## 多网关第一阶段(当前源码)
|
|
32
|
+
|
|
33
|
+
插件提供稳定 `gatewayId`、可配置 `gatewayName`、配对候选地址列表,以及可持久化的“关闭 / 临时开启 / 常驻开启”运行模式。一个 App 可以分别配对不同机器上的网关;客户端多网关管理仍需按 [App 对接说明](docs/multi-gateway-app-integration.md) 实现。本次源码尚未发布新的 npm 版本。
|
|
34
|
+
|
|
35
|
+
在“移动设备”面板选择“常驻开启”,网关会持续接受已授权设备连接,重启后保持。常驻需要 DSH 进程运行、机器未休眠且网络可达,不提供自动发现或网络中转。
|
|
36
|
+
|
|
37
|
+
部署配置示例(对应 mobile-gateway 插件的 `config`):
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
gatewayMode: persistent
|
|
41
|
+
gatewayName: 家里电脑
|
|
42
|
+
requireAuth: true
|
|
43
|
+
endpoints:
|
|
44
|
+
- wss://gateway.example.com/ws/mobile
|
|
45
|
+
- ws://192.168.1.10:3081/ws/mobile
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
运行模式的优先级:**已保存的界面选择 > `gatewayMode` > 旧 `gatewayEnabled`**。没有保存选择时,旧配置 `gatewayEnabled: true` 对应常驻,false 对应关闭。
|
|
49
|
+
|
|
50
|
+
- 关闭:立即断开移动连接,重启后仍关闭。
|
|
51
|
+
- 临时开启:默认 5 分钟没有设备成功连接则关闭并保存关闭状态;成功连接后本次运行保持开启。若以临时模式重启,则重新开始等待首次连接。`gatewayWaitTimeoutMs` 可配置 30 秒至 30 分钟。
|
|
52
|
+
- 常驻开启:没有无人连接关闭计时器。配对码仍默认 5 分钟过期,文件传输超时等独立规则不变。
|
|
53
|
+
|
|
54
|
+
状态默认保存到 `<deviceFile>.gateway.json`,通常为 `~/.dsh/mobile-gateway-devices.json.gateway.json`;可通过 `gatewayStateFile` 单独配置。文件包含随机 UUID v4 身份及用户选择,权限为 `0600`,使用同目录临时文件原子替换;损坏时启动报错,不能静默生成新身份。每个运行实例必须使用独立的设备注册文件和状态文件。
|
|
55
|
+
|
|
56
|
+
升级和迁移机器时应一并保留这两个文件。克隆为新的独立网关时,不复制原实例的状态文件和设备注册文件,让新实例生成新身份并重新配对。不要在运行中删除身份文件来恢复默认模式;如需重新使用启动配置,应停止该实例、备份状态文件、仅将其 `mode` 改为 `null`,保留 `version` 和 `gatewayId` 后重启。
|
|
57
|
+
|
|
58
|
+
`gatewayName` 最多 80 字符,未设置时使用主机名。`endpoints` 是额外候选地址,最多 16 项,每项最多 2048 字符;配对时还会合并首选地址、公网配置与已监听的 LAN 地址,合并超过 16 项会报错。所有地址必须指向同一网关;不得填写 `0.0.0.0` / `::`。地址需要手机实际可达,公网使用 WSS。
|
|
59
|
+
|
|
60
|
+
测试结果与人工步骤见 [验收报告](docs/multi-gateway-phase1-acceptance.md),完成范围见 [多网关待办](docs/multi-gateway-todo.md)。
|
|
61
|
+
|
|
24
62
|
## 配套 iOS 客户端
|
|
25
63
|
|
|
26
64
|
[DeepSeek Harness Mobile](https://github.com/Clarklevis1995/dsh-mobile) 是本仓库的兄弟项目。它是面向 iOS 17+ 的 SwiftUI 原生客户端,支持工作区与会话、工作区内创建文件夹、历史和实时对话、图片、Agent 执行轨迹、Human-in-the-loop,以及由网关配置驱动的命令、技能、模型与权限菜单。
|
|
@@ -69,7 +107,7 @@ dsh web
|
|
|
69
107
|
适用于 DSH 电脑和 iPhone 位于同一个可互访的局域网。
|
|
70
108
|
|
|
71
109
|
1. 打开 WebUI 的“移动设备”。
|
|
72
|
-
2.
|
|
110
|
+
2. 将“网关运行模式”设为“常驻开启”(短期配对也可选“临时开启”)。
|
|
73
111
|
3. 保持“设备鉴权”开启。
|
|
74
112
|
4. 确认面板显示 `ws://<电脑局域网 IP>:3081/ws/mobile`。
|
|
75
113
|
5. 填写设备名称并点击“生成配对二维码”。
|
package/cordis.patch.yml
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
# Secure by default: every mobile WebSocket must authenticate with a
|
|
10
10
|
# paired device credential. Pair/revoke APIs remain local-machine only.
|
|
11
11
|
gatewayEnabled: false
|
|
12
|
+
# Optional startup mode: disabled | temporary | persistent.
|
|
13
|
+
# Saved management-UI choices take precedence over startup config.
|
|
14
|
+
# gatewayMode: persistent
|
|
15
|
+
# gatewayName: Home PC
|
|
16
|
+
# gatewayStateFile: /path/to/instance-gateway.json
|
|
17
|
+
# Optional additional reachable addresses for this SAME gateway:
|
|
18
|
+
# endpoints:
|
|
19
|
+
# - wss://gateway.example.com/ws/mobile
|
|
12
20
|
gatewayWaitTimeoutMs: 300000
|
|
13
21
|
requireAuth: true
|
|
14
22
|
adminLoopbackOnly: true
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# DSH 0.1.5-rc.2 协议兼容性审计
|
|
2
|
+
|
|
3
|
+
核对日期:2026-09-13。本文保留审计与修复过程;当前实现状态以本节为准,后面的合并复核及首次审计为历史记录。
|
|
4
|
+
|
|
5
|
+
## 当前源码修复状态
|
|
6
|
+
|
|
7
|
+
按用户要求,Host 唯一目标为 DSH 0.1.5-rc.2,不再实现旧版分支。基于 `b37b55c` 的本地修改已完成:
|
|
8
|
+
|
|
9
|
+
- 命令保持新版 `submittedAttachments`,目录只读取 `input.attachments`;删除旧 chunkrow 展开。
|
|
10
|
+
- 删除有重复 seq、缺失 turn/step 的全局临时 chunk 转发,新增 `session-follower.mjs`:使用持续 follow 的原子历史/活动生成快照与有序增量。
|
|
11
|
+
- 独立 `assistant-stream` 帧保留 attemptId/revision/index,补齐 turn/step;持久 event 仍使用真实 seq。缺口自动重建,切换/断连/卸载取消订阅,迟到 opening 不污染新订阅。
|
|
12
|
+
- history 输出格式版本/cursor;带 beforeSeq 或 atSeq 的请求必须带格式版本 3,拒绝旧/未标注游标,避免误用迁移前坐标。
|
|
13
|
+
- conversation 历史移除内嵌 stream 和系统/request 元信息;原始视图保留 stream;实时 attempt、中断标识、usage、surface 元信息及工具失败标识已补齐。
|
|
14
|
+
- control baseline 安装 todos/goal,重连和新连接可收到整体投影快照,清除过期值。
|
|
15
|
+
- 新增 decoder 生命周期/缺口/取消测试,以及真实 WebSocket 的独立流、持久消息去重、中途重连、控制基线、历史裁剪和游标版本测试。全量测试通过,gateway dispatch 为 119 项;发布包的 29 端点/34 调用样例契约检查通过。
|
|
16
|
+
|
|
17
|
+
**移动端还需要接入新协议。** 配对无需修改,但独立流展示、缓存重建和游标版本字段不能靠旧客户端自动完成。接入契约见 [rc.2 移动端接入说明](dsh-rc2-mobile-integration.md)。本次修改范围为 gateway 仓库,尚未发布,也未执行新版真实 Host 与 App 全流程联调。
|
|
18
|
+
|
|
19
|
+
## 合并 PR #9 / #10 后的复核(修复前记录)
|
|
20
|
+
|
|
21
|
+
复核提交:`b37b55ce5191d4b1c227899308ada7a67b15bdc5`,版本号仍为 `0.7.3`。相对首次审计,已合入命令适配 PR #9(`bc47e17`)与实时流 PR #10(`82c4da6`)。本节取代下文首次审计中关于当前状态的判断;后面的分析保留作为原始基线。
|
|
22
|
+
|
|
23
|
+
**结论:命令调用层已适配 rc.2;实时流接入了正确来源,但映射存在确定缺陷,尚不能认定流式对话完整兼容。**
|
|
24
|
+
|
|
25
|
+
| 项目 | 合并后状态 | 依据 |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `commands.execute` 参数 | 已适配 rc.2 | 改为 `submittedAttachments`;已有 `parseWireImages()` 会增加 `type: image` |
|
|
28
|
+
| 命令目录附件能力 | 已适配 | `commandAcceptsAttachments()` 同时识别 `input.attachments` 和 `input.images` |
|
|
29
|
+
| 原有 Remote 样例输入 | 全部通过 | 重跑精确 rc.2 发布包检查,29 个端点、34 次调用,无失败 |
|
|
30
|
+
| 实时 token 来源 | 已接入,映射不正确 | 监听了 `agent/assistant-stream`,但序号冲突且缺少 turn/step |
|
|
31
|
+
| 活动生成的重连恢复 | 未适配 | 丢弃 start/end,不保留 attempt 状态,没有读取活动 stream 基线 |
|
|
32
|
+
| 历史内嵌 stream / attempt / 格式标识 | 未改动 | 旧探针复跑仍复现原问题 |
|
|
33
|
+
| 旧 DSH 0.1.2-rc.1 命令兼容 | 出现回退兼容问题 | 无条件发送新版参数,旧描述符只接受 `images` |
|
|
34
|
+
|
|
35
|
+
### 阻断项 A:多个 chunk 与最终消息使用同一 seq
|
|
36
|
+
|
|
37
|
+
位置:`lib/index.mjs:2755`。
|
|
38
|
+
|
|
39
|
+
新版 `session.seq` 是“下一个持久事件的序号”,不是临时 chunk 的序号。chunk 不写入 Session 日志,所以连续 chunk 之间它通常不变;接下来提交的 `assistant/message` 会使用这个相同序号。
|
|
40
|
+
|
|
41
|
+
用当前生产 listener、按 rc.2 帧形状输入 start → 两个 chunk → 最终 message → end,实际得到:
|
|
42
|
+
|
|
43
|
+
```text
|
|
44
|
+
assistant/chunk seq=42 text="Hello"
|
|
45
|
+
assistant/chunk seq=42 text=" world"
|
|
46
|
+
assistant/message seq=42 text="Hello world"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
移动端共享层 `SharedConversationStore.receiveEvent()` 要求 `record.seq > lastSequence`,基线替换还会按 seq 去重。因此该输出不满足现有消费契约,会导致增量被拒绝、同序数据被覆盖或消息无法正确收敛。不能简单对 `session.seq` 加 index 修复,否则仍会占用后续持久序号。
|
|
50
|
+
|
|
51
|
+
应使用独立临时流身份,并在 committed 时与持久 seq 对齐;如果坚持旧移动协议,必须提供完整的虚拟序号及历史/fork 双向映射方案。
|
|
52
|
+
|
|
53
|
+
### 阻断项 B:从 chunk 读取不存在的 turn/step
|
|
54
|
+
|
|
55
|
+
位置:`lib/index.mjs:2748`、`lib/index.mjs:2757`。
|
|
56
|
+
|
|
57
|
+
rc.2 的 start 帧有 `turn/step`,chunk 帧只有 `attemptId/revision/index/time/chunk`。当前代码先过滤掉 start,再读取 `frame.turn/frame.step`,序列化后的两个 chunk 都没有这两个字段。
|
|
58
|
+
|
|
59
|
+
移动端 `ConversationProjection.kt:96` 使用 turn/step 构建消息键,缺失时落到 `-1--1`,无法与带真实 turn/step 的最终消息正确配对。即使修好 seq,这个问题仍单独存在。
|
|
60
|
+
|
|
61
|
+
需要按 session/attempt 保存 start 元信息,在 chunk 时补齐,处理 end/取消/Agent 替换时清理;中途订阅和重连必须从上游快照恢复,不能只依赖此前是否收到过 start。
|
|
62
|
+
|
|
63
|
+
### 旧版本回退兼容
|
|
64
|
+
|
|
65
|
+
PR #9 对目录做了新旧字段兼容,但 execute 仅支持新版参数。用本机精确 `0.1.2-rc.1` 发布描述符复核,当前调用缺少 `images`、多出 `submittedAttachments`。如果还要维持 README 声明的旧 DSH 支持,应增加 Host 能力/版本分支;否则应明确提高最低支持版本。不能把目录双字段识别理解成执行接口也同时兼容。
|
|
66
|
+
|
|
67
|
+
### 复核验证及下一步
|
|
68
|
+
|
|
69
|
+
- 现有 `npm test` 全部通过:115 项 gateway dispatch、认证、LAN、多网关 11 项等。
|
|
70
|
+
- 新增流 listener 在仓库现有测试中没有对应 `agent/assistant-stream` 用例,因此全量测试通过不能排除上述缺陷。
|
|
71
|
+
- 临时探针 `check-post-merge-stream.mjs` 直接执行生产映射函数与 listener,确认重复 seq、turn/step 缺失;输出存于 `post-merge-stream-results.json`。
|
|
72
|
+
- `check-post-merge-old-host.mjs` 验证旧 Host 参数不匹配;`check-event-mapping.mjs` 确认 attempt、工具错误判定、内嵌历史裁剪和格式标识问题仍在。
|
|
73
|
+
- 本次脚本和日志均在 `/private/tmp/dsh-rc2-packs/`;合并后的全量日志为 `gateway-tests-post-merge.log`。未执行新版真实 Host 与真机完整联调。
|
|
74
|
+
|
|
75
|
+
建议先修复实时流两个阻断项并覆盖连续 chunk、最终结算、失败重试、中途重连;随后处理历史代际/缓存和内嵌流精简。普通文件上传、jobs、PTC 详情仍属于后续能力扩展。
|
|
76
|
+
|
|
77
|
+
## 首次审计结论(合并前,历史基线)
|
|
78
|
+
|
|
79
|
+
当前 gateway 不能直接认定为完整兼容 DSH 0.1.5-rc.2。主要问题是命令参数变更、实时 Assistant 流与持久事件分离,以及 Session 历史格式升级后的序号与缓存语义。
|
|
80
|
+
|
|
81
|
+
普通文字/图片 prompt、会话与工作区管理、队列、模型、设置、Goal 和审批的核心入口仍在。无需重写整套网关,也无需因 DSH 升级而更换配对协议。
|
|
82
|
+
|
|
83
|
+
只修命令参数,可以恢复命令调用,但不能恢复逐 token 显示。若要完整支持流式输出、生成中重连和新版轨迹,建议 gateway 与移动端协同增加独立 Assistant 流能力。仅改 gateway、保持旧 App 完整体验,需要另外设计稳定的虚拟序号及双向游标映射,复杂度明显更高。
|
|
84
|
+
|
|
85
|
+
## 核对基线与证据
|
|
86
|
+
|
|
87
|
+
- gateway:`package.json` 为 `0.7.3`,提交 `ee4cf3f75d501107cd75df2023ea15f8bea5e71a`;检查开始时工作区干净。
|
|
88
|
+
- 当前文档声明:DSH `0.1.2-rc.1` / `0.1.3-alpha.1`。本机实际安装的 CLI 及所检查的相关依赖为 `0.1.2-rc.1`。
|
|
89
|
+
- 目标版本的准确 npm 名称:`@deepseek-ai/dsh@0.1.5-rc.2`。
|
|
90
|
+
- 下载了目标版本的 Session/Workspace/Settings Controller、Commands、Goal、Agent Presets、LLM、API Gateway、Session、Agent、User Approval 发布包,检查真实 `typert.host.js` 与类型定义。
|
|
91
|
+
- 官方源码审计固定于 `c291e7961a515f6d7af9304e7fd1d257929aef26`,该源码的 CLI 版本为 `0.1.5-rc.2`。调用契约结论以精确 npm 发布包为准,源码用于追踪行为。
|
|
92
|
+
- 未更新本机 DSH、未启动新版实例、未迁移用户会话。
|
|
93
|
+
|
|
94
|
+
官方入口:[npm 精确版本](https://www.npmjs.com/package/@deepseek-ai/dsh/v/0.1.5-rc.2)、[固定源码提交](https://github.com/deepseek-ai/deepseek-harness/tree/c291e7961a515f6d7af9304e7fd1d257929aef26)。
|
|
95
|
+
|
|
96
|
+
注意三个独立版本:`dsh-mobile-v1` 是移动 WebSocket 子协议,`hello.protocol = 3` 是当前移动握手值,`Session header.version = 3` 是新版 DSH 持久会话格式。后两者数值相同没有兼容含义。
|
|
97
|
+
|
|
98
|
+
## 必须处理的差异
|
|
99
|
+
|
|
100
|
+
### 1. 命令执行参数和命令附件能力改变
|
|
101
|
+
|
|
102
|
+
当前 `lib/dsh-host-adapter.mjs:173` 发送:
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
{ agentId: sessionId, line, images }
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
新版 `commands/execute` 精确参数是:
|
|
109
|
+
|
|
110
|
+
```js
|
|
111
|
+
{
|
|
112
|
+
agentId: sessionId,
|
|
113
|
+
line,
|
|
114
|
+
submittedAttachments: images.map(image => ({ type: 'image', ...image })),
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
无附件也必须传 `submittedAttachments: []`。图片不仅需要改字段名,每项还要增加 `type: 'image'`。
|
|
119
|
+
|
|
120
|
+
新版严格边界拒绝旧参数:缺少 `submittedAttachments`,同时存在多余的 `images`。实际网关会在调用业务方法之前抛出 `gateway/arguments-invalid`。影响范围包括 `command-execute`、通过命令实现的菜单操作,以及 `/permission` 权限切换。
|
|
121
|
+
|
|
122
|
+
此外,命令目录的 `input.images` 改为 `input.attachments`。当前 `lib/index.mjs:1099` 的 `commandUiDescriptor()` 仍读取旧字段,会错误地把支持附件的命令标为不支持图片。适配层应把新版目录映射回现有移动 `ui.images`;普通文件支持再单独增加 capability。
|
|
123
|
+
|
|
124
|
+
兼容旧 DSH 时,应在适配初始化阶段选择版本/能力分支,或者读取可用的描述符。不要遇到任意执行异常就换参数重试有副作用的命令。
|
|
125
|
+
|
|
126
|
+
来源:[Commands 执行实现](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/interaction/commands/src/index.ts)、[附件与目录定义](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/interaction/commands/src/types.ts)。
|
|
127
|
+
|
|
128
|
+
### 2. 实时 token 不再是持久 `session/event`
|
|
129
|
+
|
|
130
|
+
| 项目 | 当前 gateway 假设 | DSH 0.1.5-rc.2 |
|
|
131
|
+
|---|---|---|
|
|
132
|
+
| 实时增量来源 | `session/event` 的 `assistant/chunk` | `agent/assistant-stream` |
|
|
133
|
+
| 增量身份 | 持久事件 `seq` | `attemptId`、`revision`、`index` |
|
|
134
|
+
| 生命周期 | chunk 后接最终消息 | `start` / `chunk` / `end` |
|
|
135
|
+
| 持久结算 | chunk 与 message 各自入日志 | 一个 `assistant/message` 或 `assistant/attempt`,内嵌 `data.stream` |
|
|
136
|
+
| 中途重连 | 当前 history 仅返回持久事件 | `follow({ assistantStream: true })` 可返回活动 attempt 的快照 |
|
|
137
|
+
|
|
138
|
+
`lib/index.mjs:2703` 只监听 `session/event`,所以正常完成后的 `assistant/message` 仍能收到,但生成中的文字、思考和工具参数增量不再到达手机。当前 `sessionSnapshot()` 还会取第一帧即关闭 stream,且没有请求 `assistantStream: true`,不能恢复活动中的生成前缀。
|
|
139
|
+
|
|
140
|
+
需要在 Host Adapter 增加持续 follow 或独立流适配。优先使用上游 `session/follow` 的快照与增量协定处理选中会话;若保留全局 Cordis 监听,则必须自行处理基线、订阅切换和重连竞态,并避免同一持久事件重复转发。
|
|
141
|
+
|
|
142
|
+
建议移动端新增显式选择的独立 Assistant 流帧,保留 `attemptId/revision/index`,处理:
|
|
143
|
+
|
|
144
|
+
- `start`:建立临时展示状态。
|
|
145
|
+
- `chunk`:按 attempt 和连续 index 追加;发现缺口后重新取基线。
|
|
146
|
+
- `end.committed`:用其 `seq` 与持久消息合并,避免重复显示。
|
|
147
|
+
- `end.abandoned`:清理临时 attempt,不伪造已经提交的会话消息。
|
|
148
|
+
- 生成中重连:安装 `snapshot.assistantStream.activeAttempt` 后接续后续帧。
|
|
149
|
+
|
|
150
|
+
不能直接把 `index`、`revision` 或 `startedAfterSeq` 当成旧 `event.seq`。临时流没有独占的持久序号。本项目的移动端共享层 `SharedConversationStore.receiveEvent()` 明确要求新事件 `seq > lastSequence`;随意合成会与去重、分页、fork 坐标冲突。
|
|
151
|
+
|
|
152
|
+
来源:[Agent 流类型](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/core/agent/src/runtime-types.ts)、[Remote 流类型](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/api/session-controller/src/types.ts)、[follow 快照与增量实现](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/api/session-controller/src/history.ts)。
|
|
153
|
+
|
|
154
|
+
### 3. 历史内嵌 stream、失败 attempt 和精简逻辑
|
|
155
|
+
|
|
156
|
+
新版 `SessionHistoryRecord` 只保留 `{ type: 'event', event }`;原来的顶层 `chunks + chunkrow/*` 由事件中的 `data.stream` 替代。内嵌紧凑记录包括 `text-chunks`、`reasoning-chunks`、`tool-call-chunks` 和单个 `chunk`。
|
|
157
|
+
|
|
158
|
+
当前 `expandHistoryRecords()` 对普通 event 直接透传,因此不能说新版历史会全部解码失败:已完成消息的 `data.message` 仍在,基础历史对话可以继续工作。但存在这些缺口:
|
|
159
|
+
|
|
160
|
+
- `lib/index.mjs:266` 对 `assistant/attempt` 落入默认分支,仅发事件类型,丢掉 turn/step、失败或重试 attempt 的流内容。
|
|
161
|
+
- `trimConversationEvent()` 只丢旧顶层 `assistant/chunk`。新版 `assistant/message.data.stream` 和 `assistant/attempt.data.stream` 仍全部进入 `view: conversation`,造成历史包体和序列化成本膨胀,可能更早触发 `maxBytes` 截断。
|
|
162
|
+
- 当前实时 `assistant/message` 映射不带 `interrupted` 和 usage;适配时应明确保留哪些完成/中断元信息,不能靠旧 usage chunk 推导全部状态。
|
|
163
|
+
|
|
164
|
+
改动建议:在适配层区分旧压缩行与新版内嵌 stream;普通对话历史保留最终 message,移除不需要的完整流;轨迹按需提供 attempt 和紧凑记录。不要为了兼容旧 chunk 直接按 `seq + index` 展开新版流,这会占用其他持久事件的真实坐标。
|
|
165
|
+
|
|
166
|
+
来源:[Session 事件定义](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/core/session/src/types.ts)、[Assistant 紧凑流定义](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/llm/llm/src/assistant-stream.ts)。
|
|
167
|
+
|
|
168
|
+
### 4. Session 格式迁移会改变历史 seq
|
|
169
|
+
|
|
170
|
+
目标发布包明确声明 `SESSION_FORMAT_VERSION = 3`。
|
|
171
|
+
|
|
172
|
+
- V1 → V2:把独立 chunk 收进 Assistant 事件,重新分配存续事件序号。
|
|
173
|
+
- V2 → V3:插入 `system/message`,重排序号与同会话引用;替换区间改为 `surfaceOp: { op: 'replace', startSeq, endSeq }`。
|
|
174
|
+
- 同一个 `sessionId` 升级后,历史 `seq` 不保证仍指向原事件。
|
|
175
|
+
|
|
176
|
+
当前 adapter 从快照取出 records/projections,却丢弃 `header.version`。移动端无法通过 history 响应识别格式切换。旧缓存若继续增量合并、沿用 `beforeSeq` 或把旧 `atSeq` 发给 fork,会有漏消息、错误去重或错误定位的风险;若客户端已经全量替换基线,则需用升级用例证明这一点。
|
|
177
|
+
|
|
178
|
+
需要在移动历史/同步契约中传递会话格式或历史代际标识,格式变化时清除该会话缓存和分页游标,重新建立完整基线;旧缓存没有标识时首次连接新版应执行一次重建。fork 必须使用当前代际中取得的真实持久序号。
|
|
179
|
+
|
|
180
|
+
`system/message` 不应被当作普通用户聊天展示;涉及上下文/轨迹时要定义其呈现规则。需要使用 surface 替换的消费者应适配 `startSeq/endSeq`,并保留必要 provenance。上游负责迁移磁盘日志,gateway 无需自行改写 JSONL。
|
|
181
|
+
|
|
182
|
+
来源:[V1 → V2 迁移](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/session/session-format-v1-to-v2/README.md)、[V2 → V3 迁移](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/session/session-format-v2-to-v3/README.md)。
|
|
183
|
+
|
|
184
|
+
## 兼容时应一并修正的现有缺口
|
|
185
|
+
|
|
186
|
+
这些是当前代码与新版有效数据之间的缺口,不全部代表 rc.2 才新增的破坏性变化。
|
|
187
|
+
|
|
188
|
+
| 缺口 | 当前行为 | 建议改动 |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| 工具失败判定 | `buildWireEvent()` 用 `!!data.error` 判断失败 | 新版允许 `tool-result` block 的 `isError: true` 而无结构化 `data.error`;以 block 状态为准,兼容已有错误元数据 |
|
|
191
|
+
| 控制流重连基线 | baseline 只处理 queues,忽略 projections | 安装 baseline 中的 todos/goal 等快照,避免断连期间变化没有后续增量就无法收敛 |
|
|
192
|
+
| 上游数据归一化边界 | 流帧解析与部分投影/目录兼容逻辑散落在 `lib/index.mjs` | 将 DSH 格式判断与数据映射集中到 Host Adapter;index 保留移动连接、鉴权和业务分发 |
|
|
193
|
+
| 能力声明 | 当前能力列表没有表明 Session 格式/Assistant 流模型 | 区分网关可提供能力与客户端主动选择的能力,避免向旧客户端发送其不能处理的新帧 |
|
|
194
|
+
|
|
195
|
+
## 已有核心接口中未发现直接参数阻断的部分
|
|
196
|
+
|
|
197
|
+
用实际 Host Adapter 的样例调用检查了 29 个不同 Remote 端点、34 次调用。除 `commands/execute` 外,其余样例通过发布包参数名和输入 schema 检查。
|
|
198
|
+
|
|
199
|
+
| 领域 | 核对结果 |
|
|
200
|
+
|---|---|
|
|
201
|
+
| Session list/search/create/prompt/attachment/fork/cancel/updateQueue/rename/selectModel/modelCatalog/canOpenWorkspacePath | 当前入口和样例参数仍有效;`session.list` **仍然使用 `_request`** |
|
|
202
|
+
| session.follow/page/control | 调用形状仍有效;历史/流输出和附加能力需按前文适配 |
|
|
203
|
+
| Workspace follow/create/archiveSession | baseline、归档及现有请求形状仍可用 |
|
|
204
|
+
| Settings describe/update | `ns/patch/expectedRevision` 仍可用;可省略 expectedRevision |
|
|
205
|
+
| Commands list | agentId 入参仍可用;目录的附件能力字段必须映射 |
|
|
206
|
+
| Skills、Agent Presets、LLM providers | 当前 namespace 和样例参数仍有效 |
|
|
207
|
+
| Goals edit/pause/resume/clear | `agentId/ref/request` 和 `maxGoalRounds` 仍有效 |
|
|
208
|
+
| 问答与审批 waterfall | `user-questions/request`、`approval/request` 及原回答形状仍在;不能把它们误判为需要迁移到另一个 RPC |
|
|
209
|
+
| Host 集成 | `typertGateway.invoke/stream`、`agentDefaultModel.currentSelection/saveSelection`、WebServer 注册入口仍在 |
|
|
210
|
+
| Web 管理面板与搜索配置 | sidebar/footer 和 shell/overlay slots 仍在;`session-query-sqlite` 与 `first-search` 配置仍有效 |
|
|
211
|
+
|
|
212
|
+
这不是新版真实 Host 全流程通过的证明;样例输入校验不覆盖业务状态、所有数据组合、返回值语义和真实浏览器集成。
|
|
213
|
+
|
|
214
|
+
## 可选的新能力,不阻塞基础兼容
|
|
215
|
+
|
|
216
|
+
1. **普通文件上传**:新版 prompt 支持 `{ type: 'file', receiptId }`,需先通过 `fileUploads/upload` 获取绑定 Agent 的 receipt。现有 gateway 只有图片提交与自身的文件列表/下载,不能等同于 DSH 文件附件上传。完整接入还需移动上传请求、receipt 绑定及文件 block 展示;不能直接把任意主机路径当 receipt。
|
|
217
|
+
2. **后台 jobs**:新版 `session.control` baseline 包含 jobs,增量有 job 状态。当前 gateway 忽略这部分;若要对齐新版后台任务面板,需要新增映射。它与已有 todos 清单不同。
|
|
218
|
+
3. **PTC 轨迹**:上游迁移把 `tool/code-dispatch*` 改为 `tool/ptc-dispatch*`,内置 preset `code` 改为 `ptc`。gateway 动态获取 preset,通常无需硬编码别名;若要呈现 PTC 详情,则需补充具体事件载荷与移动轨迹解释。不能全局替换字符串 `code`。
|
|
219
|
+
|
|
220
|
+
文件来源:[上传服务](https://github.com/deepseek-ai/deepseek-harness/blob/c291e7961a515f6d7af9304e7fd1d257929aef26/packages/client/file-upload/src/index.ts)。其他扩展见上面的 Session 类型及迁移来源。
|
|
221
|
+
|
|
222
|
+
## 推荐实施顺序与文件范围
|
|
223
|
+
|
|
224
|
+
| 顺序 | 范围 | 交付标准 |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| 1 | `lib/dsh-host-adapter.mjs`:命令参数及目录归一化;`test/host-adapter.test.mjs` | 空附件、图片附件、权限命令均通过目标发布包契约,兼容旧版本分支 |
|
|
227
|
+
| 2 | adapter 的历史输出;`lib/index.mjs` 的历史精简、错误/attempt 映射 | 已完成消息、失败 attempt、中断标识、大历史页行为有明确且验证过的移动输出 |
|
|
228
|
+
| 3 | adapter 的持续 Assistant 流;index 订阅/取消/通道分发;`PROTOCOL.md` | 新客户端可恢复实时输出和生成中重连,旧客户端按声明的降级策略运行 |
|
|
229
|
+
| 4 | 历史格式/代际标识;移动端共享历史与会话投影 | 旧格式缓存不会污染新序号,分页/fork/去重使用正确坐标 |
|
|
230
|
+
| 5 | 控制流 baseline 收敛、真实 Host 集成、文档 | todo/goal/queue 重连状态正确,Web 面板与配对在新版可用 |
|
|
231
|
+
| 后续 | 文件上传、jobs、PTC 完整轨迹 | 按新增 capability 单独交付 |
|
|
232
|
+
|
|
233
|
+
基础命令与普通历史适配可以保留 `dsh-mobile-v1` 和现有握手值。独立 Assistant 流可以采用向后兼容的显式订阅扩展;若选择改变既有 `event.seq` 的含义,就必须作为不兼容移动协议处理,不能静默修改。
|
|
234
|
+
|
|
235
|
+
## 验证结果与后续验收清单
|
|
236
|
+
|
|
237
|
+
本次已完成:
|
|
238
|
+
|
|
239
|
+
- `npm test` 全部通过,包括 gateway dispatch 115 项、认证、LAN 与多网关 11 项。首次沙箱运行因无法绑定测试端口退出,允许本地监听后重跑成功。
|
|
240
|
+
- 发布包契约检查:29 个端点、34 次调用;唯一失败项为旧 `commands/execute` 参数。新版空附件和带 `type: image` 的图片样例均通过。
|
|
241
|
+
- 事件映射探针复现:`assistant/attempt` 输出仅剩类型;合法 `isError: true` 且无 `data.error` 的工具失败被输出为 false;conversation 精简仍携带内嵌 stream;history 未输出格式版本。
|
|
242
|
+
|
|
243
|
+
审计脚本与详细输出暂存在 `/private/tmp/dsh-rc2-packs/`:`check-contracts.mjs`、`contract-results.json`、`check-event-mapping.mjs`、`gateway-tests.log`。临时目录可能被系统清理;正式实施时应把相关用例固化到仓库测试,并使用固定版本 fixture 或隔离的真实 Host。
|
|
244
|
+
|
|
245
|
+
正式兼容发布前至少补测:
|
|
246
|
+
|
|
247
|
+
- 旧 DSH 与 0.1.5-rc.2 的命令、附件目录、权限切换回归。
|
|
248
|
+
- 新版 stream start/chunk/end、失败重试、取消、无可见输出、工具参数流。
|
|
249
|
+
- 生成中断网重连、订阅切换、双通道连接;快照与增量无遗漏/重复。
|
|
250
|
+
- V1/V2 会话经真实上游迁移后打开、分页、fork,以及旧移动缓存失效。
|
|
251
|
+
- 内嵌流特别大的 history,conversation 视图确实去除流明细。
|
|
252
|
+
- 工具失败仅存在 block.isError、控制流重连安装完整投影基线。
|
|
253
|
+
- 真实 DSH Host 中插件加载、Web 管理入口、配对、普通消息、审批与重连闭环。
|
|
254
|
+
|
|
255
|
+
本次结论覆盖发布包契约和源码分析;尚未执行最后一组新版真实 Host 联调。
|