dsh-remote-bridge 2.8.2 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +39 -32
- package/README.md +59 -29
- package/assets/screenshots/panel-app-download.png +0 -0
- package/assets/screenshots/panel-devices.png +0 -0
- package/assets/screenshots/panel-plugins.png +0 -0
- package/assets/screenshots/panel-remote.png +0 -0
- package/assets/screenshots/panel-roots.png +0 -0
- package/assets/screenshots/panel-tunnel.png +0 -0
- package/docs/api/asyncapi.yaml +8 -1
- package/docs/api/openapi.yaml +76 -2
- package/kotlin-sdk/src/main/kotlin/ai/deepseek/dsh/workspace/DshWorkspaceClient.kt +21 -0
- package/lib/client.cjs +1972 -1595
- package/lib/index.d.ts +72 -8
- package/lib/index.js +111 -10
- package/package.json +1 -1
- package/screenshots.json +2 -0
package/README.en.md
CHANGED
|
@@ -1,56 +1,44 @@
|
|
|
1
1
|
# dsh-remote-bridge
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>
|
|
4
|
+
<strong>You left the house. The desktop is still locked. Take out your phone and keep using that machine.</strong><br>
|
|
5
|
+
Translates DSH's private remote protocol into a versioned REST API so the Android client
|
|
6
|
+
<a href="https://github.com/westanke/dsh-companion">dsh-companion</a> keeps your chats,
|
|
7
|
+
sessions, files and devices on the same machine.
|
|
5
8
|
</p>
|
|
6
9
|
|
|
7
10
|
<p align="center">
|
|
8
|
-
<
|
|
11
|
+
<img src="assets/screenshots/panel-roots.png" alt="The 📱 Phone settings panel: five collapsible cards in the main list, plus an Advanced settings group (Roots / Image reading / Plugins)" width="420">
|
|
9
12
|
</p>
|
|
10
13
|
|
|
11
14
|
<p align="center">
|
|
12
|
-
<
|
|
15
|
+
<code>dsh plugin --profile web add dsh-remote-bridge</code>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="./README.md">中文</a> ·
|
|
20
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.9.1-087f8c">
|
|
13
21
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
22
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
23
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
16
24
|
</p>
|
|
17
25
|
|
|
18
|
-
`dsh-remote-bridge` is the remote-access bridge plugin for DeepSeek Harness (DSH). It translates DSH's private remote protocol into a versioned REST API so dsh-companion ([GitHub](https://github.com/westanke/dsh-companion) · [Gitee](https://gitee.com/westanke/dsh-companion)) and other trusted clients can keep using the same chat, workspace, and file capabilities while away from the machine. On the desktop it provides a **Phone settings** section inside your DSH settings, rendering
|
|
26
|
+
`dsh-remote-bridge` is the remote-access bridge plugin for DeepSeek Harness (DSH). It translates DSH's private remote protocol into a versioned REST API so dsh-companion ([GitHub](https://github.com/westanke/dsh-companion) · [Gitee](https://gitee.com/westanke/dsh-companion)) and other trusted clients can keep using the same chat, workspace, and file capabilities while away from the machine. On the desktop it provides a **Phone settings** section inside your DSH settings, rendering collapsible cards inline: five in the main list (Remote / Public tunnel / Devices / Trash / App download) plus a collapsed-by-default **Advanced settings** group holding Roots, Image reading and Plugins — every card folds on its own and several may stay open at once.
|
|
27
|
+
|
|
28
|
+
**Who it is for**: people who use DSH as their main tool but do not want to be tied to the desk. Remote access is **off by default** — until you enable it, the plugin listens on loopback only, so installing it never exposes the machine to the LAN by itself.
|
|
19
29
|
|
|
20
30
|
The interface is bilingual and defaults to Chinese. Prebuilt packages run on Windows, macOS, and Linux without compiling on the target server.
|
|
21
31
|
|
|
22
32
|
> **Version warning**: the published `v2.0.0` and `v2.0.1` have serious defects (`v2.0.0` fails to read any historical images; `v2.0.1` cannot activate the plugin). Use **`v2.2.0`** (or ≥ `v2.0.4`).
|
|
23
33
|
> `v2.0.2` and `v2.0.3` were **never released** — do not look for installers under those version numbers.
|
|
24
|
-
|
|
25
|
-
> **DSH 0.2.x compatibility**: this branch adds a compatibility layer for DSH `0.2.0-rc.2` on top of
|
|
26
|
-
> upstream `v1.0.0`. DSH 0.2.x replaced `dsh-host-apiproxy` (which provided the `apiProxy` service)
|
|
27
|
-
> with `dsh-api-gateway`, leaving the upstream release unable to start on 0.2.x — it surfaces as
|
|
28
|
-
> "adding an authorized root returns 405". Background, rationale, changes, and test results are in
|
|
29
|
-
> [DSH 0.2.x compatibility notes](./docs/project/DSH-0.2-COMPAT.md).
|
|
30
|
-
>
|
|
31
|
-
> **Adaptation to DSH 0.2.1-rc.2** (rechecked 2026-10-20; see `docs/project/DSH-0.2.1-ADAPTATION.md`):
|
|
32
|
-
> this plugin **deliberately imports no `@deepseek-ai/*` package** — every host service is consumed
|
|
33
|
-
> through a structural type injected by the running Host (see the header comment in
|
|
34
|
-
> `src/host/dsh-0.2-bridge.ts`). Checked one by one against the installed `0.2.1-rc.2` packages:
|
|
35
|
-
> all 10 `inject` service names in `cordis.patch.yml` **still exist with unchanged signatures**, and
|
|
36
|
-
> `engines.dsh` is recognized by the 0.2.1 kernel (`prepareProfileEntries` in
|
|
37
|
-
> `dsh-app-boot/lib/index.js`) — an unmet range only sets that entry to `disabled` and prints to
|
|
38
|
-
> stderr; **the host process does not exit**. So no code change is needed to run on `0.2.1-rc.2`.
|
|
39
34
|
>
|
|
40
|
-
> **
|
|
41
|
-
> services
|
|
42
|
-
>
|
|
43
|
-
> writing a 0.2.1-only service into `inject` once made the plugin fail to activate on 0.2.0).
|
|
44
|
-
> The services that only 0.2.1 provides (**background jobs** `jobController`, the **permission
|
|
45
|
-
> catalog** `permissionPresets`, file upload `fileUploads`, attachment storage `attachments`) are
|
|
46
|
-
> therefore read at runtime through `ctx.reflect.get(name, false)`: **all of them work on
|
|
47
|
-
> 0.2.1-rc.2 and degrade automatically on 0.2.0-rc.2** (the jobs endpoints answer 404 there, the
|
|
48
|
-
> permission catalog falls back to its built-in one) — the plugin itself runs on both kernels.
|
|
49
|
-
> `GET /api/v1/debug/host-services` reports which optional capabilities the current machine has.
|
|
35
|
+
> **DSH 0.2.x compatibility** and the **0.2.1-rc.2 adaptation notes** involve the trade-offs of the
|
|
36
|
+
> injected services and run long, so they live in
|
|
37
|
+
> [Compatibility & known limits](#compatibility--known-limits) — worth a skim before installing.
|
|
50
38
|
|
|
51
39
|
## What it looks like
|
|
52
40
|
|
|
53
|
-
The desktop entry lives **inside your DSH settings**: look for the section titled **📱 Phone settings**, which renders
|
|
41
|
+
The desktop entry lives **inside your DSH settings**: look for the section titled **📱 Phone settings**, which renders collapsible cards inline (five in the main list plus an Advanced settings group) — no second dialog. It is settings-only and contains **no file tree**: the standalone page `/dsh-workspace` and its file tree and CodeMirror editor were deleted in 2.2.0. The reason and the cost are covered in "No file-management UI on the desktop" below.
|
|
54
42
|
|
|
55
43
|
Remote access remains disabled until a local administrator chooses a bind address, port, and initial device permissions.
|
|
56
44
|
|
|
@@ -159,7 +147,7 @@ The entry lives in DSH settings under the title **Phone settings**. It renders s
|
|
|
159
147
|
| Devices | Inspect and revoke device scopes and per-root grants |
|
|
160
148
|
| Image reading | Set the handoff directory (the image-reading path); shows the read-only factory default `.dsh-images` and the fallback directory |
|
|
161
149
|
| Trash | View and restore deleted entries (only deletions made from the phone app appear here) |
|
|
162
|
-
|
|
|
150
|
+
| Plugins | See what this profile has installed, enable or disable it, check for updates, and jump to the kernel's plugin list |
|
|
163
151
|
| App download | Scan to install the Android client; the codes point at both hosts' Releases list pages |
|
|
164
152
|
|
|
165
153
|
The sidebar gear button was **removed**: having the entry in both the sidebar and the settings page gave one thing two starting points. The in-conversation **Files** tab is gone too, so the desktop now has exactly one entry point.
|
|
@@ -402,7 +390,26 @@ inside the zero-change set, and 4 slots retained with 0 removed.
|
|
|
402
390
|
|
|
403
391
|
## Project
|
|
404
392
|
|
|
405
|
-
- Version: `v2.
|
|
393
|
+
- Version: `v2.9.1`
|
|
394
|
+
- **v2.9.0 makes teammates (sub-sessions) readable**: the phone's team panel could list members and
|
|
395
|
+
sub-sessions but could not open a single one, because `sessions.list` filters out every row with
|
|
396
|
+
`origin === 'subagent'` (the bridge's own `dsh-0.2-bridge.ts:2327`, with a comment explaining that
|
|
397
|
+
exposing them "only yields rows that crash on tap"), so any sub-session id hitting `/messages` gets a
|
|
398
|
+
hard 404. Even past that visibility check the kernel's `validateAddress()` demands the
|
|
399
|
+
`{ kind:'subagent', parentSessionId, childSessionId, mode }` discriminated union instead of a plain
|
|
400
|
+
session address. This release adds a **read-only narrow port guarded by membership**:
|
|
401
|
+
`GET /api/v1/chat/sessions/{leadSessionId}/subagents/{subagentId}/messages`. Authorization takes two
|
|
402
|
+
steps — (1) the Lead session must be visible to the device (reusing `requireSession`, not a single rule
|
|
403
|
+
relaxed); (2) membership must be vouched for by a projection the kernel itself publishes
|
|
404
|
+
(`subagentCatalog[].id` or `agentTeam.members[].id`). Only then is history read directly, deliberately
|
|
405
|
+
**without** calling `requireSession(subagentId)` — removing the `origin` filter would put "crash on tap"
|
|
406
|
+
rows back into the list, while loosening `requireSession` would backdoor every session; the narrow port
|
|
407
|
+
has the cleanest boundary. Read-only (non-GET always 405), and `SUBAGENT_NOT_FOUND` deliberately does
|
|
408
|
+
not distinguish "does not exist" from "not your teammate", otherwise id existence becomes a probe
|
|
409
|
+
surface. New capability bit `subagent-history-v1` (it must be separate from `history-v1`, which always
|
|
410
|
+
404s on sub-session ids and therefore cannot advertise this). The same release re-checked DSH
|
|
411
|
+
0.2.1-rc.2 → rc.5: 18 of the 19 packages the bridge depends on are byte-identical, the only diff being
|
|
412
|
+
a single KDoc comment.
|
|
406
413
|
- **v2.7.1 is a defect-fix release**: the kernel's `sessionController.resolveAgent()` returns an
|
|
407
414
|
`{ agent }` / `{ error }` **envelope**, not the Agent itself (`dsh-api-session-controller/lib/index.js:365-368`
|
|
408
415
|
`liveAgent()` and `:221-246` `resolve()` both have that shape). The bridge used to hand the envelope
|
package/README.md
CHANGED
|
@@ -1,50 +1,43 @@
|
|
|
1
1
|
# dsh-remote-bridge
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong
|
|
4
|
+
<strong>人已经出门,电脑还锁着 —— 掏出手机,继续用这台机器。</strong><br>
|
|
5
|
+
把 DSH 内核的私有 remote 协议翻译成版本化 REST,配上 Android 客户端
|
|
6
|
+
<a href="https://github.com/westanke/dsh-companion">dsh-companion</a>,
|
|
7
|
+
聊天、会话、文件、设备全程留在同一台机器上。
|
|
5
8
|
</p>
|
|
6
9
|
|
|
7
10
|
<p align="center">
|
|
8
|
-
|
|
11
|
+
<img src="assets/screenshots/panel-roots.png" alt="DSH 设置里的「📱 手机设置」面板:主列表五张折叠卡片,另有一个高级设置组(根目录/识图/插件)" width="420">
|
|
9
12
|
</p>
|
|
10
13
|
|
|
11
14
|
<p align="center">
|
|
12
|
-
<
|
|
15
|
+
<code>dsh plugin --profile web add dsh-remote-bridge</code>
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="./README.en.md">English</a> ·
|
|
20
|
+
<img alt="Version" src="https://img.shields.io/badge/version-v2.9.1-087f8c">
|
|
13
21
|
<img alt="DSH plugin" src="https://img.shields.io/badge/DeepSeek_Harness-plugin-1f2328">
|
|
14
22
|
<img alt="Platforms" src="https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-586069">
|
|
15
23
|
<img alt="License" src="https://img.shields.io/badge/license-AGPL--3.0-2da44e">
|
|
16
24
|
</p>
|
|
17
25
|
|
|
18
|
-
`dsh-remote-bridge` 是 DeepSeek Harness(DSH)的远程访问桥接插件:它把 DSH 内核的私有 remote 协议翻译成版本化的 REST 接口,让 dsh-companion([GitHub](https://github.com/westanke/dsh-companion) · [Gitee](https://gitee.com/westanke/dsh-companion))等受信任客户端在离开电脑后仍能使用同一套聊天、工作区与文件能力;桌面端在你的 DSH
|
|
26
|
+
`dsh-remote-bridge` 是 DeepSeek Harness(DSH)的远程访问桥接插件:它把 DSH 内核的私有 remote 协议翻译成版本化的 REST 接口,让 dsh-companion([GitHub](https://github.com/westanke/dsh-companion) · [Gitee](https://gitee.com/westanke/dsh-companion))等受信任客户端在离开电脑后仍能使用同一套聊天、工作区与文件能力;桌面端在你的 DSH 设置里提供一个「手机设置」,内联折叠卡片:主列表五张(远程访问 / 外网隧道 / 设备 / 回收站 / App 下载),另有默认收起的「高级设置」组收纳根目录、识图与插件三项,每张卡片各自折叠、可多张同时展开。
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
**它适合谁**:把 DSH 当主力工具、但不想被「必须坐在电脑前」绑住的人。远程访问**默认关闭**,未启用时只监听回环地址——装完不会自动把机器暴露到局域网。
|
|
21
29
|
|
|
22
|
-
|
|
30
|
+
界面默认中文,可随时切换 English。插件支持 Windows、macOS 和 Linux,安装包已经包含编译产物,不要求用户在服务器上重新构建。
|
|
23
31
|
|
|
24
|
-
>
|
|
25
|
-
> 0.2.x 用 `dsh-api-gateway` 替换了提供 `apiProxy` 服务的 `dsh-host-apiproxy`,导致上游版本在
|
|
26
|
-
> 0.2.x 上无法启动(表现为「添加根目录返回 405」)。兼容层的完整说明——基于什么、为什么、改了什么、
|
|
27
|
-
> 测了什么——见 [DSH 0.2.x 兼容性适配说明](./docs/project/DSH-0.2-COMPAT.md)。
|
|
28
|
-
>
|
|
29
|
-
> **对 DSH 0.2.1-rc.2 的适配结论**(2026-10-20 复核,依据 `docs/project/DSH-0.2.1-ADAPTATION.md`):
|
|
30
|
-
> 本插件**刻意不 import 任何 `@deepseek-ai/*` 包**,全部宿主服务经结构类型 + Host 注入消费
|
|
31
|
-
> (见 `src/host/dsh-0.2-bridge.ts` 头注释)。逐一到 `0.2.1-rc.2` 实装包核对:`cordis.patch.yml`
|
|
32
|
-
> 的 10 项 `inject` 服务名**全部仍存在且签名未变**,`engines.dsh` 在 0.2.1 被内核识别
|
|
33
|
-
> (`dsh-app-boot/lib/index.js` 的 `prepareProfileEntries`),不满足时只把该 entry 置
|
|
34
|
-
> `disabled` 并打 stderr,**宿主进程不退出**。故本插件无需改码即可运行于 `0.2.1-rc.2`。
|
|
32
|
+
> **版本警告**:请务必使用 ≥ `v2.0.4` 版本。
|
|
35
33
|
>
|
|
36
|
-
>
|
|
37
|
-
>
|
|
38
|
-
> 一度因为把 0.2.1 才有的服务写进 inject 而让插件在 0.2.0 上整体不激活)。
|
|
39
|
-
> 因此 0.2.1 才提供的服务(**后台任务** `jobController`、**权限档位目录**
|
|
40
|
-
> `permissionPresets`、文件上传 `fileUploads`、附件存储 `attachments`)改为运行时经
|
|
41
|
-
> `ctx.reflect.get(name, false)` 读取:**0.2.1-rc.2 上这些功能全部可用,0.2.0-rc.2 上
|
|
42
|
-
> 自动降级**(如后台任务端点返回 404、权限档位走内置目录),插件本体两种内核都能跑。
|
|
43
|
-
> 用 `GET /api/v1/debug/host-services` 可以查这台机器上哪些可选能力可用。
|
|
34
|
+
> **DSH 0.2.x 兼容性**与 **0.2.1-rc.2 适配结论**涉及注入服务的取舍,篇幅较长,
|
|
35
|
+
> 统一放在 [兼容性与已知限制](#兼容性与已知限制) 一节,安装前建议扫一眼。
|
|
44
36
|
|
|
45
37
|
## 实际效果
|
|
46
38
|
|
|
47
|
-
|
|
39
|
+
|
|
40
|
+
桌面端的入口**就在你的 DSH 设置里**:打开设置,找到标题为「📱 手机设置」的一节,点进去直接内联渲染折叠卡片(主列表五张 + 一个「高级设置」组),不再另开弹窗。它只做设置。
|
|
48
41
|
|
|
49
42
|
远程访问默认关闭。管理员可在本机 WebUI 中设置绑定 IP、端口和新设备的初始权限。
|
|
50
43
|
|
|
@@ -140,7 +133,7 @@ dsh plugin --profile web add ./dsh-remote-bridge-v2.7.1.tgz
|
|
|
140
133
|
|
|
141
134
|
### 桌面端设置面板
|
|
142
135
|
|
|
143
|
-
入口在 DSH
|
|
136
|
+
入口在 DSH 设置里,标题为「手机设置」。设置区**内联渲染**,不另开弹窗;主列表五张折叠卡片平铺,可多张同时展开,默认展开「远程访问」;授权根目录、识图与插件这三项收在「高级设置」组里(根目录排第一,因为没它就没有可授权的落点),点开照旧能改:
|
|
144
137
|
|
|
145
138
|
| 分区 | 用途 |
|
|
146
139
|
| --- | --- |
|
|
@@ -149,7 +142,7 @@ dsh plugin --profile web add ./dsh-remote-bridge-v2.7.1.tgz
|
|
|
149
142
|
| 设备 | 查看设备权限与根目录授权,随时撤销 |
|
|
150
143
|
| 识图 | 设置图片交接目录(识图路径),并只读展示出厂默认 `.dsh-images` 与兜底目录 |
|
|
151
144
|
| 回收站 | 查看与恢复被删除的条目(只列出来自手机 App 的删除记录) |
|
|
152
|
-
|
|
|
145
|
+
| 插件 | 查看本 profile 装了哪些插件、启用或停用、检查更新,并能跳转到内核的插件列表 |
|
|
153
146
|
| App 下载 | 扫码下载 Android 客户端,二维码指向两个站点的 Releases 列表页 |
|
|
154
147
|
|
|
155
148
|
|
|
@@ -372,6 +365,29 @@ dsh plugin --profile web remove dsh-remote-bridge
|
|
|
372
365
|
|
|
373
366
|
## 兼容性与已知限制
|
|
374
367
|
|
|
368
|
+
### DSH 0.2.0 / 0.2.1 两版的内核差异
|
|
369
|
+
|
|
370
|
+
本分支在上游 `v1.0.0` 的基础上增加了对 DSH `0.2.0-rc.2` 的兼容层。0.2.x 用
|
|
371
|
+
`dsh-api-gateway` 替换了提供 `apiProxy` 服务的 `dsh-host-apiproxy`,导致上游版本在 0.2.x 上
|
|
372
|
+
无法启动(表现为「添加根目录返回 405」)。兼容层的完整说明——基于什么、为什么、改了什么、
|
|
373
|
+
测了什么——见 [DSH 0.2.x 兼容性适配说明](./docs/project/DSH-0.2-COMPAT.md)。
|
|
374
|
+
|
|
375
|
+
**对 DSH 0.2.1-rc.2 的适配结论**(2026-10-20 复核,依据 `docs/project/DSH-0.2.1-ADAPTATION.md`):
|
|
376
|
+
本插件**刻意不 import 任何 `@deepseek-ai/*` 包**,全部宿主服务经结构类型 + Host 注入消费
|
|
377
|
+
(见 `src/host/dsh-0.2-bridge.ts` 头注释)。逐一到 `0.2.1-rc.2` 实装包核对:`cordis.patch.yml`
|
|
378
|
+
的 10 项 `inject` 服务名**全部仍存在且签名未变**,`engines.dsh` 在 0.2.1 被内核识别
|
|
379
|
+
(`dsh-app-boot/lib/index.js` 的 `prepareProfileEntries`),不满足时只把该 entry 置
|
|
380
|
+
`disabled` 并打 stderr,**宿主进程不退出**。故本插件无需改码即可运行于 `0.2.1-rc.2`。
|
|
381
|
+
|
|
382
|
+
**两版的功能差异(重要)**:`inject` 里只放**两个版本都存在**的服务 —— cordis 的 inject 是
|
|
383
|
+
**硬依赖**,任何一个服务缺失就整个插件不激活(CI 在 0.2.0-rc.2 基线上实锤过:
|
|
384
|
+
一度因为把 0.2.1 才有的服务写进 inject 而让插件在 0.2.0 上整体不激活)。
|
|
385
|
+
因此 0.2.1 才提供的服务(**后台任务** `jobController`、**权限档位目录**
|
|
386
|
+
`permissionPresets`、文件上传 `fileUploads`、附件存储 `attachments`)改为运行时经
|
|
387
|
+
`ctx.reflect.get(name, false)` 读取:**0.2.1-rc.2 上这些功能全部可用,0.2.0-rc.2 上
|
|
388
|
+
自动降级**(如后台任务端点返回 404、权限档位走内置目录),插件本体两种内核都能跑。
|
|
389
|
+
用 `GET /api/v1/debug/host-services` 可以查这台机器上哪些可选能力可用。
|
|
390
|
+
|
|
375
391
|
### 某些 DSH 构建缺少附件存储服务
|
|
376
392
|
|
|
377
393
|
DSH 的 cordis 注入属性(`ctx.attachments` / `ctx.fileUploads`)是惰性解析的:读取一个没有任何组件提供的服务会抛错。2.0.3 起改为**容错读取**——服务缺席时插件照常激活,只有真正用到该能力的接口返回可读错误:
|
|
@@ -433,7 +449,21 @@ DSH **0.2.1-rc.3** 隔离实例上**实跑验过**,不是静态推断:插件
|
|
|
433
449
|
|
|
434
450
|
## 项目信息
|
|
435
451
|
|
|
436
|
-
- 当前版本:`v2.
|
|
452
|
+
- 当前版本:`v2.9.1`
|
|
453
|
+
- **v2.9.0 是团队成员(子会话)可读版**:手机端的团队面板此前只能看到成员与子会话的名字,点开读不到
|
|
454
|
+
任何内容 —— 因为 `sessions.list` 会把 `origin === 'subagent'` 的行整批滤掉(桥自己的
|
|
455
|
+
`dsh-0.2-bridge.ts:2327`,注释写明「暴露出来只会得到点开就崩的行」),子会话 id 打 `/messages`
|
|
456
|
+
必然 404;即使绕过可见性,内核 `validateAddress()` 也要求用
|
|
457
|
+
`{ kind:'subagent', parentSessionId, childSessionId, mode }` 判别联合寻址,而非普通会话地址。
|
|
458
|
+
这一版新增**受成员关系校验的只读窄口**
|
|
459
|
+
`GET /api/v1/chat/sessions/{leadSessionId}/subagents/{subagentId}/messages`:授权两步 ——
|
|
460
|
+
① Lead 会话必须对设备可见(沿用 `requireSession`,规则一行不放宽);② 成员关系必须由内核自己发布的
|
|
461
|
+
投影背书(`subagentCatalog[].id` 或 `agentTeam.members[].id`)。两步都过才直读历史,刻意不再调
|
|
462
|
+
`requireSession(subagentId)` —— 拆 `origin` 过滤会把「点开就崩」的行放回列表,放宽 `requireSession`
|
|
463
|
+
等于给所有会话开后门,窄口的边界最清晰。只读(非 GET 一律 405),错误码 `SUBAGENT_NOT_FOUND`
|
|
464
|
+
刻意不区分「不存在」与「不是成员」否则 id 是否存在就成了探测面。新增能力位 `subagent-history-v1`
|
|
465
|
+
(必须独立于 `history-v1`,后者对子会话永远 404 探测不到)。同版复核了 DSH 0.2.1-rc.2 → rc.5:
|
|
466
|
+
桥依赖的 19 个包中 18 个零差异,唯一差异是一行 KDoc 注释。
|
|
437
467
|
- **v2.7.1 是缺陷修复版**:内核 `sessionController.resolveAgent()` 返回的是 `{ agent }` / `{ error }`
|
|
438
468
|
**包装对象**而非 Agent 本体(`dsh-api-session-controller/lib/index.js:365-368` 的 `liveAgent()` 与
|
|
439
469
|
`:221-246` 的 `resolve()` 都是这个形状),桥此前把包装对象直接当 Agent 传给 `fileUploads.upload()`,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/api/asyncapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
asyncapi: 3.0.0
|
|
2
2
|
info:
|
|
3
3
|
title: DeepSeek Harness Mobile Events
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.9.1
|
|
5
5
|
description: |
|
|
6
6
|
Live file and authorized DSH chat events. Reconnect clients must refresh
|
|
7
7
|
REST state; unbounded replay is not provided.
|
|
@@ -154,6 +154,13 @@ channels:
|
|
|
154
154
|
> is missing from the set will start echoing a stale `requestId` from
|
|
155
155
|
> whatever request happened to be in flight when it fired.
|
|
156
156
|
|
|
157
|
+
> **子会话历史不走 WS**(2026-10-30):团队成员 / 子会话的历史只能经
|
|
158
|
+
> REST `GET /chat/sessions/{leadSessionId}/subagents/{subagentId}/messages`
|
|
159
|
+
> 读取,本协议**没有**对应帧,也不打算加。原因是普通 `history` 帧对子会话
|
|
160
|
+
> id 必然 404(子会话被 `sessions.list` 刻意滤掉),而那条窄口的授权靠
|
|
161
|
+
> 「Lead 可见 + 内核投影背书成员关系」两步,与 WS 连接级的会话白名单不是
|
|
162
|
+
> 一回事;硬塞进 WS 只会让两套授权语义打架。详见 `docs/project/API.md`。
|
|
163
|
+
|
|
157
164
|
Rules:
|
|
158
165
|
|
|
159
166
|
- Absent or empty string ⇒ **no** `requestId` key in the response at all
|
package/docs/api/openapi.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
openapi: 3.1.0
|
|
2
2
|
info:
|
|
3
3
|
title: DeepSeek Harness Mobile API
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.9.1
|
|
5
5
|
description: Versioned file and chat BFF for mobile and third-party DSH clients. HTTP and HTTPS are both supported.
|
|
6
6
|
servers:
|
|
7
7
|
- url: http://127.0.0.1:3090/api/v1
|
|
@@ -24,7 +24,7 @@ paths:
|
|
|
24
24
|
properties:
|
|
25
25
|
ok: { type: boolean, const: true }
|
|
26
26
|
version: { type: string, const: v1 }
|
|
27
|
-
pluginVersion: { type: string, const: 2.
|
|
27
|
+
pluginVersion: { type: string, const: 2.9.1 }
|
|
28
28
|
/debug/host-services:
|
|
29
29
|
get:
|
|
30
30
|
operationId: hostServices
|
|
@@ -989,6 +989,80 @@ paths:
|
|
|
989
989
|
responses:
|
|
990
990
|
'202': { description: Prompt accepted by DSH. }
|
|
991
991
|
'409': { $ref: '#/components/responses/Error' }
|
|
992
|
+
/chat/sessions/{leadSessionId}/subagents/{subagentId}/messages:
|
|
993
|
+
parameters:
|
|
994
|
+
- name: leadSessionId
|
|
995
|
+
in: path
|
|
996
|
+
required: true
|
|
997
|
+
description: |
|
|
998
|
+
The session that owns the team (the Lead). Its visibility is checked with the
|
|
999
|
+
ordinary session rules -- the narrow endpoint never relaxes them.
|
|
1000
|
+
schema: { type: string }
|
|
1001
|
+
- name: subagentId
|
|
1002
|
+
in: path
|
|
1003
|
+
required: true
|
|
1004
|
+
description: |
|
|
1005
|
+
A subagent / teammate session id. It is *not* required to be listed by
|
|
1006
|
+
`GET /chat/sessions` (subagent sessions are filtered out of that list);
|
|
1007
|
+
membership is verified against the Lead's own `subagentCatalog` /
|
|
1008
|
+
`agentTeam.members` projection instead.
|
|
1009
|
+
schema: { type: string }
|
|
1010
|
+
get:
|
|
1011
|
+
operationId: listSubagentMessages
|
|
1012
|
+
description: |
|
|
1013
|
+
Read the history of a subagent (teammate) session -- a read-only narrow endpoint.
|
|
1014
|
+
|
|
1015
|
+
Why it exists: `GET /chat/sessions/{sessionId}/messages` resolves visibility via
|
|
1016
|
+
the session list, and subagent sessions are deliberately filtered out of that list
|
|
1017
|
+
(the kernel refuses to activate them as ordinary sessions, so exposing them yields
|
|
1018
|
+
rows that crash on open). Every subagent id therefore 404s on the ordinary path.
|
|
1019
|
+
|
|
1020
|
+
Authorization has two steps, both required: (1) the Lead session must be visible to
|
|
1021
|
+
this device under the unchanged rules; (2) membership must be backed by the kernel's
|
|
1022
|
+
own projection for that Lead (`subagentCatalog[].id` or `agentTeam.members[].id`).
|
|
1023
|
+
Guessing a session id does not work -- a miss is a 404.
|
|
1024
|
+
|
|
1025
|
+
Read-only: any non-GET method is `405 METHOD_NOT_ALLOWED`. Writing to a teammate
|
|
1026
|
+
session would blur who is directing whom; the official Web UI likewise only writes
|
|
1027
|
+
through the Lead's model tools.
|
|
1028
|
+
parameters:
|
|
1029
|
+
- name: beforeSeq
|
|
1030
|
+
in: query
|
|
1031
|
+
schema: { type: integer, minimum: 0 }
|
|
1032
|
+
- name: maxMessages
|
|
1033
|
+
in: query
|
|
1034
|
+
schema: { type: integer, minimum: 1 }
|
|
1035
|
+
responses:
|
|
1036
|
+
'200':
|
|
1037
|
+
description: |
|
|
1038
|
+
Identical shape and byte budget as `listMessages`: the oldest events may be
|
|
1039
|
+
dropped (with `hasMore` forced to true) and oversized strings inside one event
|
|
1040
|
+
may be head/tail clamped. `truncated`/`bytes` appear only when something was
|
|
1041
|
+
actually clamped. `historyFormatVersion` is always present.
|
|
1042
|
+
content:
|
|
1043
|
+
application/json:
|
|
1044
|
+
schema:
|
|
1045
|
+
type: object
|
|
1046
|
+
required: [events, hasMore]
|
|
1047
|
+
properties:
|
|
1048
|
+
events:
|
|
1049
|
+
type: array
|
|
1050
|
+
items: { type: object, additionalProperties: true }
|
|
1051
|
+
hasMore: { type: boolean }
|
|
1052
|
+
projections: { type: object, additionalProperties: true }
|
|
1053
|
+
bytes: { type: integer, minimum: 0 }
|
|
1054
|
+
truncated: { type: boolean }
|
|
1055
|
+
historyFormatVersion: { type: integer, minimum: 1 }
|
|
1056
|
+
'404':
|
|
1057
|
+
description: |
|
|
1058
|
+
`SUBAGENT_NOT_FOUND` -- either the subagent is not a member of this team, or
|
|
1059
|
+
the Lead's projections were unavailable (membership unprovable). The two cases
|
|
1060
|
+
are deliberately indistinguishable so the existence of a session id is not
|
|
1061
|
+
turned into a probing surface.
|
|
1062
|
+
content:
|
|
1063
|
+
application/json:
|
|
1064
|
+
schema: { $ref: '#/components/schemas/Error' }
|
|
1065
|
+
'405': { $ref: '#/components/responses/Error' }
|
|
992
1066
|
/chat/sessions/{sessionId}/attachments:
|
|
993
1067
|
parameters:
|
|
994
1068
|
- name: sessionId
|
|
@@ -541,6 +541,27 @@ class DshWorkspaceClient(
|
|
|
541
541
|
|
|
542
542
|
fun listMessages(sessionId: String): JsonElement = executeJsonElement("chat/sessions/$sessionId/messages", "GET", null)
|
|
543
543
|
|
|
544
|
+
/**
|
|
545
|
+
* 读**子会话(团队成员)**的历史 —— 受成员关系校验的只读窄口。
|
|
546
|
+
*
|
|
547
|
+
* 不能复用 [listMessages]:那条路径靠会话列表判定可见性,而子会话被
|
|
548
|
+
* `sessions.list` 刻意滤掉(内核拒绝把它们当普通会话激活),所以任何子会话 id
|
|
549
|
+
* 打进去都是 404。这条窄口改成两步授权:Lead 会话必须可见 + 内核投影
|
|
550
|
+
* (`subagentCatalog` / `agentTeam.members`)必须背书成员关系。只读。
|
|
551
|
+
*/
|
|
552
|
+
fun listSubagentMessages(
|
|
553
|
+
leadSessionId: String,
|
|
554
|
+
subagentId: String,
|
|
555
|
+
beforeSeq: Int? = null,
|
|
556
|
+
maxMessages: Int? = null,
|
|
557
|
+
): JsonElement {
|
|
558
|
+
val query = buildString {
|
|
559
|
+
if (beforeSeq != null) append(if (isEmpty()) "?" else "&").append("beforeSeq=").append(beforeSeq)
|
|
560
|
+
if (maxMessages != null) append(if (isEmpty()) "?" else "&").append("maxMessages=").append(maxMessages)
|
|
561
|
+
}
|
|
562
|
+
return executeJsonElement("chat/sessions/$leadSessionId/subagents/$subagentId/messages$query", "GET", null)
|
|
563
|
+
}
|
|
564
|
+
|
|
544
565
|
fun renameSession(sessionId: String, title: String) {
|
|
545
566
|
val body = json.encodeToString(RenameRequest.serializer(), RenameRequest(title))
|
|
546
567
|
executeUnit("chat/sessions/$sessionId", "PATCH", body)
|