dsh-plugin-mobile-gateway 0.7.3 → 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 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` 实现(v0.7.2)。
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)
@@ -160,7 +160,7 @@ func connectAuthenticated(publicURL: URL, token: String) -> URLSessionWebSocketT
160
160
  | type | 参数 | 说明 |
161
161
  |---|---|---|
162
162
  | `ping` | — | 心跳;回复 `pong` |
163
- | `subscribe` | `sessionId` | 事件流过滤:之后只收到该会话的 `event`,并重放该会话仍待处理的提问与审批(不订阅 = 接收所有会话) |
163
+ | `subscribe` | `sessionId`, `assistantStream?` | 事件流过滤;`assistantStream: true` 启用独立实时流及原子基线(见 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md)),并重放该会话仍待处理的提问与审批(不订阅 = 接收所有会话) |
164
164
  | `unsubscribe` | — | 取消过滤 |
165
165
 
166
166
  ```json
@@ -628,7 +628,7 @@ let image = [
628
628
  | `sessions` | — | 会话列表(`updatedAt/running/blank/cwd/agentPreset`) |
629
629
  | `session-archive` | `sessionId` | 将 Session 加入 Host 的完整归档集合(隐藏但不删除) |
630
630
  | `session-rename` | `sessionId`, `title` | 写入用户指定的持久化 Session 名称 |
631
- | `history` | `sessionId`, `beforeSeq?`, `maxMessages?`, `maxBytes?`, `view?` | 历史事件页(见下) |
631
+ | `history` | `sessionId`, `beforeSeq?`, `historyFormatVersion?`, `maxMessages?`, `maxBytes?`, `view?` | 历史事件页(见下) |
632
632
  | `attachment` | `sessionId`, `attachmentId` | 读取历史中属于该会话的图片字节 |
633
633
  | `file-list` | `sessionId`, `path?`, `requestId?` | 列出会话工作目录内的一层文件与文件夹 |
634
634
  | `file-download-open` | `sessionId`, `path`, `requestId` | 打开一个工作目录内的普通文件下载 |
@@ -682,13 +682,14 @@ WebUI、App 或其他客户端造成的变化通过以下帧主动推送:
682
682
  - 返回**原始 SessionEvent**(`{type, seq, time, data}`,方案A),可选裁剪
683
683
  - 图片不会内联进历史页。`user/message.data.content[]` 中的图片块为 `{ "type":"image", "attachment": ImageAttachmentRef }`;iOS 使用其中的 `attachmentId` 请求图片数据
684
684
  - `maxBytes`:单帧字节预算,默认 **4 MiB**;超预算保留最新部分并给出 `nextBeforeSeq` 续页(客户端 16 MiB 上限的安全余量)
685
- - `view: "conversation"`:**对话裁剪模式**——丢弃 `assistant/chunk`(token 回放)与 `request/header`(system prompt),`tool/result` 嵌套文本截断到 2000 字符
686
- - 分页:`hasMore` 为真时用 `beforeSeq: nextBeforeSeq` 请求更早一页
685
+ - `view: "conversation"`:**对话裁剪模式**——隐藏系统消息与 request/header、request/context,移除 Assistant 事件的内嵌 `data.stream`,`tool/result` 嵌套文本截断到 2000 字符
686
+ - 分页:`hasMore` 为真时用 `beforeSeq: nextBeforeSeq, historyFormatVersion: 3` 请求更早一页
687
687
 
688
688
  ```json
689
689
  → { "kind": "history", "sessionId": "session-abc", "events": [ ...原始事件... ],
690
+ "historyFormatVersion": 3, "cursor": 200,
690
691
  "bytes": 3521, "view": "conversation", "hasMore": true, "nextBeforeSeq": 128,
691
- "projections": { "asOfSeq": 127, "values": { "tokenUsage": {...}, "contextPressure": {...}, "permissions": {...}, "sessionStats": {...} } } }
692
+ "projections": { "asOfSeq": 200, "values": { "tokenUsage": {...}, "contextPressure": {...}, "permissions": {...}, "sessionStats": {...} } } }
692
693
  ```
693
694
 
694
695
  图片引用结构:
@@ -971,7 +972,7 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
971
972
  ## 11. 分支(fork)
972
973
 
973
974
  ```json
974
- { "type": "fork", "sessionId": "session-abc", "atSeq": 42 }
975
+ { "type": "fork", "sessionId": "session-abc", "atSeq": 42, "historyFormatVersion": 3 }
975
976
  → { "kind": "fork", "sessionId": "session-分支新会话" }
976
977
  ```
977
978
  - `atSeq`:从该消息所在的**完整一轮**分叉(省略 = 最近完成的 turn);进行中的 turn 分叉会报 `fork-unavailable`
@@ -1011,12 +1012,13 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
1011
1012
  ### `event` 帧(agent 实时输出)
1012
1013
  ```json
1013
1014
  { "kind": "event", "sessionId": "session-abc", "seq": 42, "time": 1786937352,
1014
- "event": { "type": "assistant/chunk", "turn": 1, "step": 0, "chunkType": "text-delta", "text": "正在" } }
1015
+ "event": { "type": "assistant/message", "turn": 1, "step": 0, "text": "已完成", "reasoning": "", "toolCalls": [] } }
1015
1016
  ```
1016
1017
  `event.type` 覆盖(精炼字段):
1017
1018
  - `user/message` → `{text, source, images?: ImageAttachmentRef[]}`
1018
- - `assistant/chunk` → `{turn, step, chunkType: text-delta|reasoning-delta|tool-call-delta|usage|finish, text?/tool?/usage?/finish?}`
1019
- - `assistant/message` → `{turn, step, text, reasoning, toolCalls[]}`
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?}`
1020
1022
  - `session/title` → `{title, source?}`
1021
1023
  - `tool/call` → `{turn, step, callId, name, arguments}`
1022
1024
  - `tool/result` → `{turn, step, callId, isError, preview(≤400字符)}`
@@ -1028,10 +1030,10 @@ WebUI 中的“任务”与“进行中的目标”分别对应 DSH 的 `todos`
1028
1030
 
1029
1031
  1. Connect → 收到 `hello`
1030
1032
  2. `{"type":"sessions"}` → 挑 `sessionId`(或直接下一步自动建)
1031
- 3. `{"type":"subscribe","sessionId":"session-abc"}`
1033
+ 3. `{"type":"subscribe","sessionId":"session-abc","assistantStream":true}`,等待 `session-snapshot`
1032
1034
  4. `{"type":"message","sessionId":"session-abc","text":"帮我查一下deepseek"}` → `sent`
1033
- 5. 盯着 Messages 面板:`event` 流实时滚动(chunk → tool/call → tool/result → assistant/message)
1034
- 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`)
1035
1037
  7. `{"type":"session-stats","sessionId":"session-abc"}` → 统计条数据
1036
1038
  8. 完事 `{"type":"unsubscribe"}` 或 Disconnect
1037
1039
 
package/README.md CHANGED
@@ -6,17 +6,22 @@
6
6
 
7
7
  DeepSeek Harness 的设备鉴权移动网关,支持会话与实时事件、排队消息同步及编辑/删除/Steer、Session 归档和重命名的双向同步、停止当前生成并稍后继续、任务列表和当前 Goal 同步及管理、服务端驱动的命令和技能菜单、Human-in-the-loop、图片及文件传输。安装后,Harness WebUI 左侧边栏会出现“移动设备”入口,可直接开启网关、生成配对二维码和管理可信设备。
8
8
 
9
+ > 当前源码以 **DSH 0.1.5-rc.2** 为唯一适配基线,使用 Session format 3;不再兼容更早的 Host 版本。
10
+ >
11
+ > 实时流已改为独立 `assistant-stream` 帧,移动端需要按 [rc.2 接入说明](docs/dsh-rc2-mobile-integration.md) 更新订阅、缓存与分页处理。当前修改尚未发布。
12
+ >
9
13
  > v0.7.3:优化移动网关运行模式下拉框的箭头间距。
10
14
  >
11
- > v0.7.2:新增独立对话/控制连接、空 Session 创建、停止生成与稍后继续、排队消息同步及编辑/删除/Steer,以及 Session 归档和重命名的双向同步;兼容 DSH v0.1.2-rc.1 与 v0.1.3-alpha.1。
15
+ > v0.7.2:新增独立对话/控制连接、空 Session 创建、停止生成与稍后继续、排队消息同步及编辑/删除/Steer,以及 Session 归档和重命名的双向同步。
12
16
 
13
17
  ## 协议与 DSH 兼容层
14
18
 
15
- 移动端连接的是本项目维护的 `dsh-mobile-v1`,不是 DSH 的内部 Remote 协议。插件内部通过独立 Host Adapter 对接 DSH v0.1.2-rc.1 的 Remote Gateway;Session、Workspace、Settings、Commands、Goals 等 namespace 和严格参数只存在于该适配层。
19
+ 移动端连接的是本项目维护的 `dsh-mobile-v1`,不是 DSH 的内部 Remote 协议。插件内部通过独立 Host Adapter 对接 DSH 0.1.5-rc.2 的 Remote Gateway;Session、Workspace、Settings、Commands、Goals 等 namespace 和严格参数只存在于该适配层。
16
20
 
17
- 因此本次从旧 APIProxy 迁移到 Remote Gateway 不要求现有移动端修改:WebSocket 子协议、`hello.protocol = 3`、配对鉴权和已有请求/响应字段保持不变。以后 DSH 调整 Remote API 时,优先只更新插件适配层及契约测试;只有移动协议本身发生不可兼容变化时,才会新增并并行支持新的移动端协议版本。
21
+ 配对鉴权和 `dsh-mobile-v1` / `hello.protocol = 3` 保持不变。新版实时 token 不占用持久事件的 `seq`:客户端显式订阅 `assistantStream: true`,接收原子的 `session-snapshot` 和独立增量;普通 `event` 只携带持久事件。未接入新订阅的客户端只能收到持久消息。
18
22
 
19
- 详细实施边界见 [Remote Gateway 重构实施计划](docs/remote-gateway-refactor-plan.md)。
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)。
20
25
 
21
26
  - WebSocket:`/ws/mobile`
22
27
  - 局域网:`ws://<局域网 IP>:3081/ws/mobile`
@@ -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 联调。
@@ -0,0 +1,151 @@
1
+ # DSH 0.1.5-rc.2 移动端接入
2
+
3
+ 本次源码以 DSH 0.1.5-rc.2 为唯一 Host 基线,不提供旧 Host 分支。配对、设备 token、双连接和 `dsh-mobile-v1` 保持原样;以下是新的实时订阅和历史坐标约定。App 需要同步实现本说明,不能把独立流帧交给旧的 `SessionEvent.seq` reducer。
4
+
5
+ ## 1. 能力与历史缓存
6
+
7
+ `hello` 增加:
8
+
9
+ ```json
10
+ {
11
+ "kind": "hello",
12
+ "protocol": 3,
13
+ "dshVersion": "0.1.5-rc.2",
14
+ "historyFormatVersion": 3,
15
+ "capabilities": ["assistant-stream-v1", "history-format-version", "projection-baseline"]
16
+ }
17
+ ```
18
+
19
+ 示例只列出新增字段和能力;原有能力仍在。`dshVersion` 是本适配器的目标版本声明,非运行时 CLI 版本探测。握手协议值 3 与 Session 格式值 3 是独立概念。
20
+
21
+ 缓存键至少包含 `(gatewayId, sessionId, historyFormatVersion)`。缓存没有格式版本或与服务端不同,必须丢弃该 Session 的旧历史、分页游标、fork 锚点及临时输出,重新安装基线。DSH 自己迁移磁盘日志,客户端不得迁移旧 seq。
22
+
23
+ ## 2. 订阅一个会话
24
+
25
+ 在 conversation 连接(或未拆分的连接)发送:
26
+
27
+ ```json
28
+ { "type": "subscribe", "sessionId": "s1", "assistantStream": true }
29
+ ```
30
+
31
+ 先收到确认:
32
+
33
+ ```json
34
+ { "kind": "subscribed", "sessionId": "s1", "assistantStream": true, "subscriptionId": "<UUID>" }
35
+ ```
36
+
37
+ 再收到原子基线:
38
+
39
+ ```json
40
+ {
41
+ "kind": "session-snapshot",
42
+ "sessionId": "s1",
43
+ "subscriptionId": "<UUID>",
44
+ "streamId": "<本次上游 follow 的 UUID>",
45
+ "historyFormatVersion": 3,
46
+ "cursor": 41,
47
+ "replace": true,
48
+ "view": "conversation",
49
+ "events": [],
50
+ "bytes": 0,
51
+ "hasMore": false,
52
+ "projections": { "asOfSeq": 41, "values": {} },
53
+ "assistantStream": {
54
+ "revision": 2,
55
+ "activeAttempt": {
56
+ "attemptId": "s1:1",
57
+ "turn": 2,
58
+ "step": 3,
59
+ "startedAfterSeq": 41,
60
+ "nextIndex": 1,
61
+ "stream": [{ "type": "text-chunks", "time0": 100, "index": 0, "texts": ["Hello"], "dt": [] }]
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ 这是最新历史窗口和活动生成状态在同一个上游切点的快照。没有正在生成的 attempt 时省略 `activeAttempt`,客户端应清空临时输出。`hasMore` 为 true 时同时返回 `nextBeforeSeq`,按普通历史请求补更早内容;`replace` 指当前订阅基线需要替换,不能把该窗口误认为完整历史。
68
+
69
+ 客户端只处理当前 `subscriptionId`。收到新 snapshot 后更新 `streamId`,清理上一个 stream 的临时状态,并把持久流水位设为 `cursor`,不能使用精简 events 的最大 seq 代替它:精简视图可能隐藏了末尾的系统事件。
70
+
71
+ 未发送 `assistantStream: true` 的连接仍接收持久 `event`,不会收到伪装成持久事件的 token。control 连接不能订阅对话流。
72
+
73
+ ## 3. 临时增量与最终结算
74
+
75
+ 每个独立流帧携带 `sessionId/subscriptionId/streamId`,其根节点没有 `seq`:
76
+
77
+ ```json
78
+ {
79
+ "kind": "assistant-stream",
80
+ "sessionId": "s1",
81
+ "subscriptionId": "<UUID>",
82
+ "streamId": "<UUID>",
83
+ "frame": {
84
+ "type": "chunk",
85
+ "attemptId": "s1:1",
86
+ "revision": 3,
87
+ "index": 1,
88
+ "time": 101,
89
+ "turn": 2,
90
+ "step": 3,
91
+ "chunk": { "type": "text-delta", "index": 0, "text": " world" }
92
+ }
93
+ }
94
+ ```
95
+
96
+ - `start`:含 `attemptId/revision/startedAfterSeq/turn/step`,新建临时消息。
97
+ - `chunk`:原样保留上游 chunk(文字、思考、工具参数、usage、finish 等),网关从 start 或 snapshot 补齐 `turn/step`。
98
+ - `end`:含 `attemptId/revision/index/turn/step/outcome`;`index` 是该 attempt 的 chunk 总数。
99
+ - `end.outcome = { kind: "committed", eventType, seq }`:之前的持久 event 已入流,使用 seq 关联 `assistant/message` 或 `assistant/attempt`,合并/清理临时消息,避免重复显示。
100
+ - `end.outcome = { kind: "abandoned" }`:清理临时 attempt,不生成虚构的持久消息。没有任何 chunk 的 attempt 也可能直接结束。
101
+
102
+ `revision` 属于一次 Agent 生命周期,`index` 属于一次 attempt。不要把其中任何一个写入持久事件 seq。网关检查连续性;App 仍应按当前 streamId/attemptId 去重和隔离展示。
103
+
104
+ 持久帧仍使用 `kind: "event"`,由同一个上游 follow 有序转发,带真实 seq 以及 subscriptionId/streamId。不会再与全局 `session/event` 重复转发。消息附带中断标识 `interrupted` 和可选 usage;失败 attempt 保留 turn/step 与紧凑 stream。`surfaceOp` 和 `sourceEventSeqs` 在存在时位于持久帧根节点。
105
+
106
+ ## 4. 重连、切换与取消
107
+
108
+ - 上游结束、连续性断档或临时失败:发送 `session-stream-reset`,含 `sessionId/subscriptionId/streamId/code/message/retrying`。立即冻结旧 stream 并清理临时输出,等待新 snapshot;网关按退避间隔重新打开 follow。
109
+ - 新 snapshot 使用新的 streamId,可能有重叠历史,必须替换基线后再继续,不能按旧流水位直接追加。
110
+ - 不支持的 Session 格式等永久失败:reset 的 `retrying: false`,App 提示错误;网关不切换到不安全的全局流回退。
111
+ - 手机 WebSocket 断开:重连、重新订阅。网关从上游快照取得已经生成但尚未提交的前缀,随后只发送基线之后的增量。
112
+ - 切换会话或 `unsubscribe`:网关取消旧 follow,迟到的旧 opening 不再发送。App 清除旧 subscriptionId 的临时显示。
113
+ - `session-cancel` 仍走控制连接,最终以持久中断消息/attempt 和 end 为准。
114
+
115
+ ## 5. 历史、分页与 fork
116
+
117
+ 普通 history 首次请求无需游标:
118
+
119
+ ```json
120
+ { "type": "history", "sessionId": "s1", "view": "conversation" }
121
+ ```
122
+
123
+ 返回值新增 `historyFormatVersion: 3`、`cursor`。conversation 视图隐藏 `system/message`、`request/header`、`request/context`,移除 Assistant 事件中的 `data.stream`,保留真实 seq、最终 message、usage 和 interrupted。默认原始视图保留完整内嵌 stream,用于按需读取轨迹。全部为隐藏事件的页仍返回可前进的 nextBeforeSeq。
124
+
125
+ 携带游标必须同时携带读取该游标时的格式版本:
126
+
127
+ ```json
128
+ { "type": "history", "sessionId": "s1", "beforeSeq": 20, "historyFormatVersion": 3 }
129
+ { "type": "fork", "sessionId": "s1", "atSeq": 42, "historyFormatVersion": 3 }
130
+ ```
131
+
132
+ 缺失版本、旧版本或版本不匹配,返回 `code: "history-format-mismatch"`、`resetRequired: true` 和当前 historyFormatVersion,且不会调用上游分页/fork。客户端必须重建缓存后再取新坐标,不能给旧坐标补一个 3 后重试。游标必须是非负安全整数。省略 atSeq 的 fork 仍表示从最近完成轮次分叉。
133
+
134
+ 收到 session-snapshot 后,不要用晚到的普通 history 响应覆盖正在进行的订阅基线;更早历史页只合并到历史部分。这样可以避免独立查询与实时流交错造成回滚。
135
+
136
+ ## 6. 控制基线
137
+
138
+ 连接建立和上游 control baseline 更新时,控制连接收到:
139
+
140
+ ```json
141
+ {
142
+ "kind": "projection-baseline",
143
+ "projections": { "s1": { "asOfSeq": 42, "values": { "todos": [], "goal": null } } }
144
+ }
145
+ ```
146
+
147
+ 这是网关当前持有的 todos/goal 快照,按整体替换处理;缺失 Session/键代表清除旧值。空对象也具有清除语义。后续仍用 `tasks-updated` / `goal-updated` 增量更新。网关也向原有消费者发对应基线值,避免断连期间修改的 Goal 只能等下一次增量才出现。
148
+
149
+ ## 验证范围
150
+
151
+ 仓库测试覆盖 rc.2 的参数封装、两种独立序号、缺口恢复、取消清理、真实 WebSocket 的原子基线/最终结算、重连前缀、历史裁剪、游标版本校验、工具失败与控制投影。真实 rc.2 Host 与 App 联调仍需在客户端接入后执行。普通文件上传、jobs 和 PTC 专用 UI 属于后续能力扩展。
@@ -10,14 +10,11 @@
10
10
 
11
11
  ## 版本基线与兼容性
12
12
 
13
- - [x] 支持 DSH `0.1.2-rc.1` 的 Typert Remote 基础契约。
13
+ - [x] 以 DSH `0.1.5-rc.2` / Session format 3 为唯一适配基线,不提供旧 Host 分支。
14
14
  - [x] 使用独立 Host Adapter 隔离 DSH Remote namespace、method 和严格参数名。
15
- - [x] 保持移动端 `dsh-mobile-v1`、`hello.protocol = 3` 和现有字段兼容。
16
- - [ ] 支持 DSH `0.1.3-alpha.1` 的 Typert Remote 契约。
17
- - [ ] 同时兼容 `0.1.2-rc.1` 与 `0.1.3-alpha.1`,不要求移动端同步升级。
18
- - [ ] 增加 Host Remote 能力探测并在进程生命周期内缓存探测结果。
19
- - [ ] 为 `0.1.3-alpha.1` 建立独立的 fake gateway 契约测试。
20
- - [ ] 建立 RC1 与 Alpha1 双版本回归测试矩阵。
15
+ - [x] 保留移动配对和 `dsh-mobile-v1`、`hello.protocol = 3`;新增独立实时流需移动端接入。
16
+ - [x] rc.2 输入契约、流状态机和真实 WebSocket mock Host 回归测试。
17
+ - [ ] rc.2 真实 Host 与移动 App 完整联调。
21
18
 
22
19
  ## 连接、鉴权与通用传输
23
20
 
@@ -67,16 +64,17 @@
67
64
 
68
65
  ## 实时会话流与状态同步
69
66
 
70
- - [x] 在 `0.1.2-rc.1` 上通过 Host `session/event` 转发实时 SessionEvent。
67
+ - [x] 通过 Host `session/event` 向未启用 follow 的连接转发持久 SessionEvent。
71
68
  - [x] 转发 `todos` projection 的增量更新。
72
69
  - [x] 转发 `goal` projection 的增量更新。
73
70
  - [x] 将 WebUI 或其他客户端写入的 `session/title` 变化实时通知移动端。
74
71
  - [x] 将 Workspace 归档集合变化实时通知移动端。
75
- - [ ] 在 `0.1.3-alpha.1` 上使用 `session.follow({ assistantStream: true })` 接收实时 Assistant 流。
76
- - [ ] 将 `assistant-stream` 的 start/chunk/end 帧转换为现有移动端实时事件。
77
- - [ ] 处理 Assistant Stream reconnect baseline、revision、index 和 settlement。
78
- - [ ] 使用 `session.follow` 持续接收 durable event,而不只读取 opening snapshot。
79
- - [ ] 在断线重连或序号出现缺口时自动补齐 Session 历史。
72
+ - [x] 在 `0.1.5-rc.2` 上使用 `session.follow({ assistantStream: true })` 接收实时 Assistant 流。
73
+ - [x] 将 `assistant-stream` 的 start/chunk/end 转发为独立移动帧,并从 start/基线补齐 turn/step。
74
+ - [x] 处理 Assistant Stream reconnect baseline、revision、index 和 settlement。
75
+ - [x] 使用 `session.follow` 持续接收 durable event,而不只读取 opening snapshot。
76
+ - [x] 上游断流或序号出现缺口时重建 Session 快照;移动连接重建后重新订阅恢复。
77
+ - [x] 安装并向控制连接重放 todos/goal projection baseline。
80
78
  - [x] 处理 `session.control` baseline 中的 queues,并在重连时整体替换。
81
79
  - [ ] 处理 `session.control` baseline 中的 jobs。
82
80
  - [ ] 处理 `session.control` baseline 中除 `todos`、`goal` 外的 projections。
@@ -93,27 +91,27 @@
93
91
 
94
92
  - [x] 读取 Session opening snapshot。
95
93
  - [x] 分页读取更早的历史记录。
96
- - [x] 展开 RC1 `chunkrow/text-chunks` 历史记录。
97
- - [x] 展开 RC1 `chunkrow/reasoning-chunks` 历史记录。
98
- - [x] 展开 RC1 `chunkrow/tool-call-chunks` 历史记录。
94
+ - [x] 读取 format 3 event 记录,保留真实 seq 与内嵌 stream,不展开为虚构序号。
95
+ - [x] 输出历史格式/cursor;拒绝缺失或不匹配版本的分页/fork 游标。
99
96
  - [x] 支持移动端历史帧字节预算。
100
97
  - [x] 支持 conversation 裁剪视图。
101
98
  - [x] 读取历史图片附件字节。
102
99
  - [x] 查询 Session 统计信息。
103
100
  - [x] 查询 Token Usage 与 Context Pressure。
104
- - [ ] 完整适配 Alpha1 的 Assistant attempt 与 compact stream 历史展示。
101
+ - [x] 原始历史和实时事件保留 Assistant attempt,conversation 历史移除内嵌 stream。
102
+ - [ ] 移动端完成独立 Assistant 流及 attempt 明细展示。
105
103
 
106
104
  ## Host 命令与技能
107
105
 
108
106
  - [x] 按 Session 列出 Host 命令。
109
- - [x] 执行 `0.1.2-rc.1` Host 命令。
107
+ - [x] 执行 `0.1.5-rc.2` Host 命令。
110
108
  - [x] 转发 `command/run` 与 `command/done` 生命周期事件。
111
109
  - [x] 提供服务端驱动的命令菜单 UI 描述。
112
110
  - [x] 提供模型和权限的通用二级选择菜单。
113
111
  - [x] 按 Session 列出用户可调用技能。
114
112
  - [x] 区分 Model 可调用技能与仅用户技能。
115
- - [ ] 适配 Alpha1 `commands.execute.submittedAttachments` 参数。
116
- - [ ] 同时识别 RC1 `input.images` 与 Alpha1 `input.attachments` 命令描述。
113
+ - [x] 适配 rc.2 `commands.execute.submittedAttachments` 参数。
114
+ - [x] 识别 rc.2 `input.attachments` 命令描述。
117
115
  - [ ] 支持向 Host 命令附加普通文件 receipt。
118
116
  - [ ] 实时同步 `commands/change`,使移动端命令目录自动刷新。
119
117
 
@@ -290,7 +288,7 @@
290
288
  - [ ] Host Adapter 已封装对应 Remote 调用或流,不在业务层散落 namespace/method。
291
289
  - [ ] 移动端协议请求、响应和错误形态已经定义。
292
290
  - [ ] 对应单元测试或网关端到端测试通过。
293
- - [ ] RC1/Alpha1 相关兼容测试通过,或明确声明该功能只在指定 Host 版本启用。
291
+ - [ ] DSH 0.1.5-rc.2 相关契约和行为测试通过。
294
292
  - [ ] `PROTOCOL.md` 与 README 中面向用户的能力说明已经同步。
295
293
 
296
294
  > 本节是每个新功能的验收模板,不表示当前所有待实现功能已经满足这些条件,因此保持未勾选。