dsh-vscode-bridge
DiscoveredCordis plugin bridging the dsh-vscode extension to native DSH services (workspace grouping, session titles, archive, agent presets, permission presets, slash commands, skill catalog, session export) o
dsh-vscode-bridge
中文 | English
适用于 DeepSeek Harness(DSH)的插件:为 dsh-vscode-lite 提供一条窄带、令牌鉴权的 JSON-RPC 通道,直通 DSH 原生服务——工作区分组、会话标题、会话删除、Agent 预设、权限预设、斜杠指令、技能目录、会话日志导出。
ACP 是纯自动化接口,标题、删除、工作区分组、预设、权限模式都不会出现在 ACP 协议上。装载本插件后,扩展可以直接调用 DSH 进程内的原生服务。
为什么需要
通过 ACP 创建的会话从不加入 workspace registry,因此在 DSH Web UI 里全部落入「未分组」。而重命名/删除会话、切换 Agent 预设、切换权限模式,在 DSH 内部都有原生服务——只是没有任何通道把这些服务暴露给外部客户端。
本插件从 DSH 进程内部补上这个缺口:一个 loopback TCP 监听器,持令牌鉴权,扩展通过发现文件找到它。
特性
- 工作区分组修复:在
session/created时把每个新会话 attach 到其 cwd 所属的工作区(不存在则创建)——「未分组」不再增长。 - 原生会话标题:读取任意会话折叠后的标题;通过
sessionTitle.rename重命名任意活会话。 - 会话删除:经 registry 级归档集合实现产品级删除(
workspaceRegistry.archiveSession)。 - Agent 预设:列出预设名册(含生效的默认项)、读取会话当前预设、切换空白会话(会话开跑后返回
agent-preset/locked——上游契约)。 - 权限预设:查询选项列表与会话当前预设并切换——sandbox 模式与 approval 策略立即生效。
- 事件推送:订阅方收到 plan/todo/标题/权限/指令生命周期等会话事件的
bridge.event通知,类型过滤支持精确匹配与前缀匹配(plan/、command/)。 - 原生斜杠指令:列出会话生效的原生指令目录(
/compact、/plan等),并经ctx.commands.execute执行——与 TUI/Web 完全同一条路径,command/run、command/done生命周期事件随推送到达。 - 技能目录:只读列出项目级与用户级技能(名称、描述、来源等稳定字段);技能的真正调用由模型侧 skill 工具完成,目录已在 DSH 内注入。
- 会话日志导出:把会话(含子代理后代)的逻辑日志与引用附件流式打包为宿主侧 ZIP 文件——字节不经 ndjson 通道,大日志内存有界。
- 能力如实上报:
bridge.handshake报告当前部署真实可用的能力;某个可选服务缺失只降级对应方法族,插件整体不受影响。
工作原理
flowchart LR
Ext[dsh-vscode 扩展] -->|扫描匹配 directories| Disc[~/.dsh/vscode-bridge/<pid>.json<br/>权限 0600]
Ext -->|ndjson JSON-RPC 2.0| Tcp[Loopback 监听器<br/>7310-7319 首个空闲端口]
Tcp --> Auth{token 校验}
Auth -->|失败| Reject[-32001 unauthorized]
Auth -->|通过| Core[BridgeCore 分发]
Core --> Svc[workspaceRegistry · sessionTitle · commands<br/>agentPresets · permissionPresets · skills]
Created[session/created] --> Attach[resolveByPath 或 create<br/>attachSession]
Fire[session/event 事件流] --> Push[bridge.event 推送<br/>给匹配的订阅连接]
- 插件绑定配置区间内第一个空闲端口,多个 DSH 进程(每个编辑器窗口一个)无需协调即可共存。
- 插件把
{ port, token, pid, protocolVersion, capabilities, directories }写入$HOME/.dsh/vscode-bridge/<pid>.json(0600,原子写),directories列出进程 cwd、每个活会话 cwd 和每个已知工作区路径供扩展匹配实例;卸载时删除,启动时按 pid 活性回收历史残留。工作区目录不再被写入。 - 每个请求都必须在顶层
token字段携带令牌;loopback + 文件权限就是全部访问边界。 - 借助 WSL2 的
localhostForwarding,Windows 侧的扩展可以透明地连上 WSL 侧的监听器。
安装
兼容 deepseek-harness 0.1.5-rc.2 与 0.1.7-rc.1(@deepseek-ai/dsh-* 包 ≥ 0.1.5-rc.2)。插件未在 peerDependencies 声明 DSH 包,因此不受 0.1.7 的安装/启动 peer 版本强制检查(DSH-0.1.7-J1-01)约束。
插件要装进一个同时携带 dsh-base 与 dsh-acp-app 两个 bundle 的自建 profile。DSH 默认不带现成的 acp profile:dsh plugin 初始化出的自建 profile 只有 dsh-base,需要手动补上 dsh-acp-app bundle,以及 ACP 组合没有的几行服务(workspace、预设注册行,外加 standard 预设挂载所需的子代理模型路由宿主行)。预设注册行随主机版本不同:0.1.5 主机是 agent-presets(@deepseek-ai/dsh-agent-presets,config.default 指定默认预设);0.1.7 主机预设改为声明式(DSH-0.1.7-J1-03),注册行是 agent-preset-registry(@deepseek-ai/dsh-agent-preset-registry,config: { default: standard }),每个预设另需一条 @deepseek-ai/dsh-agent-preset 声明行(可参考 web-app bundle 的 presets/*.patch.yml)。该服务未挂载时桥接照常加载,presets 能力降级为不可用。
从 npm 安装
# profile 不存在时自动初始化(初始仅含 dsh-base bundle)
dsh plugin --profile acp-vscode add dsh-vscode-bridge
从 tarball 安装
git clone https://github.com/EPCN-fla/dsh-vscode-bridge.git
cd dsh-vscode-bridge
pnpm install
pnpm run build
pnpm pack # 产出 dsh-vscode-bridge-<version>.tgz
dsh plugin --profile acp-vscode add ./dsh-vscode-bridge-<version>.tgz
本地开发
开发期把 CLI 直接指向工作副本目录,每次改动后重新构建:
dsh plugin --profile acp-vscode add /absolute/path/to/dsh-vscode-bridge
接线 profile
先把 $DSH_HOME/profiles/acp-vscode/package.json 的 bundle 列表补上 ACP 应用(bundle 随 CLI 安装自带,无需另装依赖):
{
"dsh": {
"profile": {
"bundles": ["@deepseek-ai/dsh-base", "@deepseek-ai/dsh-acp-app"]
}
}
}
再在同目录的 cordis.patch.yml 中注册插件与缺失的服务行:
# 插入 acp 组合未携带的服务行
- insert:
- id: workspace
name: '@deepseek-ai/dsh-workspace'
- id: agent-presets
name: '@deepseek-ai/dsh-agent-presets'
config:
default: standard
# standard 预设的子代理模型路由读这个宿主行(web bundle 自带,acp 组合没有)
- id: subagent-model-selection-settings
name: '@deepseek-ai/dsh-tool-subagent/model-selection-settings'
- id: dsh-vscode-bridge
name: 'dsh-vscode-bridge'
# config: { portStart: 7310, portEnd: 7319 } # 可选覆盖
# 可选:为三档权限补上显示名与描述(base 的默认表只有 sandbox/approval,
# 没有 name/description 元数据)
- id: permission
config:
presets:
read-only:
sandbox: read-only
approval: ask
name: read-only
description: 只读;写入与更大范围的重试需要批准。
workspace-write:
sandbox: workspace-write
approval: ask
name: workspace-write
description: 允许在工作区内写入;更大范围的重试需要批准。
danger-full-access:
sandbox: danger-full-access
approval: never
name: danger-full-access
description: 完全文件访问,不再弹出批准。
最后把扩展的 dsh.profile 设置为 acp-vscode(扩展的「一键安装 bridge」命令会自动完成上述全部步骤)。
通信协议
双向均为每行一个 JSON 对象,标准 JSON-RPC 2.0 信封。
// 请求
{ "jsonrpc": "2.0", "id": 1, "method": "session.setTitle",
"params": { "sessionId": "…", "title": "修复登录流程" }, "token": "…" }
// 成功
{ "jsonrpc": "2.0", "id": 1, "result": { "sessionId": "…", "title": "修复登录流程", "updatedAt": 1700000000500 } }
// 失败(data.code 是稳定的机器可读细节)
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32009, "message": "session \"…\" has already started; its agent preset is fixed",
"data": { "code": "agent-preset/locked", "details": { } } } }
| 方法 | 参数 | 返回 |
|---|---|---|
bridge.handshake | — | { protocolVersion, plugin, version, pid, startedAt, capabilities } |
session.list | { includeStored? } | { sessions: LiveSessionInfo[], storedIncluded, stored? } |
session.get | { sessionId } | 活会话行,或带折叠标题的存储态行 |
session.setTitle | { sessionId, title } | { sessionId, title, updatedAt }——仅限活会话 |
session.delete | { sessionId } | { sessionId, archived: true };有活动(运行中 turn)的会话以 -32009 session/active 拒绝(DSH ≥ 0.1.7),未知会话以 -32004 session/not-found 拒绝 |
session.subscribe | { sessionId?, types? } | { subscribed: true, types };事件以 bridge.event 通知到达 |
session.unsubscribe | — | { subscribed: false } |
preset.list | — | { default, presets: [{ id, trust?, name?, description?, broken?, isDefault }] }——trust('system' | 'user')仅 0.1.5 主机提供,0.1.7 起上游已移除该字段 |
preset.current | { sessionId } | { sessionId, preset } |
preset.select | { sessionId, presetId } | { sessionId, selected };会话开跑后以 agent-preset/locked 失败 |
permission.get | { sessionId? } | { options: [{ value, name, description? }], default, current? } |
permission.set | { sessionId, name } | { sessionId, current } |
workspace.list | — | { workspaces: [{ id, path, title, sessionIds }], archivedSessionIds } |
workspace.attach | { sessionId } | { attached, workspaceId?, reason? }——活会话直接挂载;非活会话回退到存储态头按 cwd 校验挂载(覆盖 ACP 等进程外创建的会话) |
command.list | { sessionId } | { commands: [{ name, description, inputHint?, attachments? }] }——该会话 agent 生效的原生指令目录(按名排序) |
command.run | { sessionId, line, timeoutMs? } | { commandId, kind: 'success' | 'error', text?, sourceEventSeq? }——line 须以 / 开头;处理器级失败以 kind:'error'+text 返回,不作 RPC 错误 |
skill.list | { sessionId? } | { skills: [{ name, description, whenToUse?, source, provider }] }——传 sessionId 按会话头 cwd 解析项目级技能,否则用进程 cwd;无技能返回空数组 |
session.exportZip | { sessionId, destPath? } | { path, fileName, bytes, entries }——含子代理后代的会话日志 ZIP 落盘到 destPath(缺省 <tmpdir>/dsh-session-export/<fileName>);活会话先做持久化屏障 |
订阅的 types 条目默认精确匹配;以 / 结尾时按前缀匹配(plan/ 匹配 plan/update);* 匹配全部。默认推送集合:session/title、permission/preset、sandbox/mode、approval/policy、agent-preset/selected、command/、plan/、todo/。
错误码:标准 JSON-RPC(-32700 解析失败、-32600 非法请求、-32601 未知方法、-32602 参数错误、-32603 内部错误),另有 -32000 服务器错误(data.code 如 command/timeout、command/aborted)、-32001 未授权(token 缺失或错误)、-32002 该 profile 中服务不可用、-32004 会话不存在/非活会话、-32009 冲突(data.code 携带上游错误码,如 agent-preset/locked)。command.run 对未知名称另以 -32602 + data.code: 'command/unknown' 应答。
配置
| 配置 | 默认 | 含义 |
|---|---|---|
host | 127.0.0.1 | 绑定地址。保持 loopback——发现文件令牌就是访问边界 |
portStart / portEnd | 7310 / 7319 | 候选端口区间;第一个空闲端口胜出 |
token | 每次启动随机 | 固定令牌,需要可复现性多于卫生性时使用 |
discoveryDir | $HOME/.dsh/vscode-bridge | 发现目录,内含 <pid>.json |
attachSessions | true | 在 session/created 时把新会话 attach 到其 cwd 的工作区 |
commandTimeoutMs | 180000 | command.run 执行超时;到点 abort 在途指令并返回 command/timeout(compact 需一次 LLM 摘要,默认给足 3 分钟) |
已知限制
- 重命名与权限/预设切换要求活会话。存储态(未运行)会话需先经 ACP resume;存储态标题仍可通过
session.get读取。 - 「删除」即归档:会话从所有分组界面消失,但其事件日志仍保留在磁盘上。物理删除不是 DSH 的公开 API。
- 预设切换仅限空白会话(上游
agent-preset/locked契约):跑过一轮后组合即固定。 agentPresets是可选服务。缺少agent-presetspatch 行时插件照常加载;此时preset.*返回service-unavailable,握手如实报告presets: false。commands/skills/sessionExport同为可选能力。ACP 组合缺对应服务行时插件照常装载,对应方法族返回service-unavailable,握手能力位为false;归档模块只在首次调用时动态解析,解析失败不影响插件装载。- 导出不走 Web 的
/export指令。该指令依赖 ACP 组合没有的connection服务;bridge 绕过指令层直接复用归档模块,由session.exportZip在宿主侧产出 ZIP 文件(含子代理后代日志),ZIP 字节不经 ndjson 通道。 command.run仅限活会话且不带附件。ACP 侧没有暂存回执通道,附件恒以空数组提交;指令声明需要附件时,上游错误文本原样透传为kind:'error'结果。忙会话不预检:compact 自报busy、plan 返回queued。skill.list是只读目录。技能的实际调用由模型侧 skill 工具完成(目录已在 DSH 内注入),bridge 不代替模型触发技能。- 拓扑 3(WSL 扩展宿主 → Windows 侧 dsh)不在 TCP 通道覆盖范围内:Windows 进程发布的发现文件对 WSL 客户端不可达,该方向上 loopback 也不互通。
- 同机多实例按
<pid>.json共存,共用同一工作区目录的多个 DSH 进程各自发布条目;扩展在该目录的匹配条目中取startedAt最新者。
开发
要求 Node >=20(在 Node 24 上开发)。
pnpm install
pnpm run typecheck # tsc --noEmit
pnpm run test # node:test(type-stripped TS)
pnpm run build # 产出 lib/ 与 lib/types/
测试全部位于 tests/:
| 文件 | 覆盖 |
|---|---|
tests/server.test.ts | 端口扫描、ndjson 分帧、超长行断连、监听器生命周期 |
tests/discovery.test.ts | 0600 发现文件的发布/替换/清理、死 pid 残留清扫、不可写目录 |
tests/core.test.ts | token 鉴权、全部 RPC 方法(mock 服务)、上游错误码透传、事件推送过滤、服务降级时的能力上报 |
tests/compose.test.ts | 真实 Cordis 组合:插件加载 → 发现文件 → TCP 握手 → RPC → 干净卸载 |
目录结构
src/
index.ts 插件入口(name/inject/Config/apply,Cordis 接线)
core.ts BridgeCore:attach 逻辑、RPC 分发、事件推送
server.ts ndjson JSON-RPC/TCP 传输层(端口区间扫描)
discovery.ts <pid>.json 发现文件的发布与回收(原子写,0600,残留清扫)
protocol.ts 协议类型、错误码、能力标志
tests/ node:test 测试(type-stripped TS)
本插件遵循 Harness function-plugin 契约:named exports name / inject / Config / apply,无 default export。
许可证
Comments
Loading…
Similar plugins
by ZSeven-W
DeepSeek Harness (DSH) plugin: dispatch work to DSH agents from Claude Code / Codex — native subagent progress, in-host worker sessions with per-tier presets, and a multimodal bridge that lends the te
★ 156
MIT
JavaScript
Sep 24, 2026
dsh plugin --profile web add @zseven-w/dsh-crewby KannaKuron
Agent preset for DeepSeek Harness that layers the self-referential Cordis toolset (dynamic plugins, runtime inspection) and preset-authoring skills onto the PTC Code-Mode preset; materialized files ca
★ 5
↓ 1.2k/wk
MIT
JavaScript
Oct 4, 2026
dsh plugin --profile web add dsh-ptc-cordis-presetby Yazzyk
DeepSeek Harness (dsh) 插件:在 Web GUI 里手动点选文件或目录,屏蔽 agent 对它们的读取、搜索、写入与编辑。A dsh plugin that denies an agent read/search/write/edit access to chosen files and directories, picked from the plugin's own We
★ 0
MIT
JavaScript
Sep 19, 2026
dsh plugin --profile web add dsh-file-shieldby navid-kianfar
Options plate on the DeepSeek Harness composer's + button — pick files and folders from the project or anywhere on the machine, upload from your device, or run a slash command — with inline path chips
★ 0
MIT
TypeScript
Sep 15, 2026
dsh plugin --profile web add @achasoft/dsh-add-assetsby wang-kaopu
Give coding Agents runtime evidence for debugging and verifying DSH / Cordis plugins. 让 Coding Agent 获得用于调试和验证 DSH / Cordis 插件的运行时证据。
★ 0
Apache-2.0
TypeScript
Aug 29, 2026
dsh plugin --profile web add dsh-cordis-devtoolsby PerryLink
Installable DSH bundle: the dsh-plugin-guide plugin-development knowledge base as an on-demand agent skill. Official docs archive (EN/ZH), Cordis primer, 114-repo community archive, 1654 archived Disc
★ 46
Apache-2.0
TypeScript
Oct 5, 2026
dsh plugin --profile web add dsh-plugin-guide