dsh-remote-subagent
DSH(DeepSeek Harness)远端子代理插件:本地会话用 remote_agent 工具把任务派给远端 DSH(SSH 上的另一台机器),远端 agent 用它自己的模型、工具与沙箱干活,思考 / 工具调用 / 工具结果 / 最终答复实时流回本地会话,并可在同一远端会话上追加消息——用法与内置 subagent 类似,但执行发生在远端机器里。
本地会话 ── remote_agent 工具 ──SSH──> 远端 dsh headless --json <task>
▲ │
└──── 事件流(thinking / tool_call / tool_result / final)◄──┘
为什么用远端 dsh headless --json
DSH 自带 headless 入口,--json 会把一次 run 的过程按行输出为 JSON 事件;--session-id 可续跑同一个 Session:
dsh headless --json '<task>' # 新远端会话
dsh headless --json --session-id <id> '<msg>' # 同一会话续跑
插件通过 SSH 的 exec 通道直接读这个流,因此:
- 不需要在远端开公网端口,也不需要 token / WebSocket 协议;
- 远端 agent 用它自己的模型配置、工具、沙箱与 session 存储(session 落在远端
$DSH_HOME/sessions,在远端 DSH Web UI 里同样可见); - 追加消息用
--session-id,语义与 subagent 的 send 一致; - 事件流本身就是「工作过程」,不需要额外埋点。
安装
# 从 npm 安装到当前 profile(bundle 形式)
dsh plugin --profile <profile> add dsh-remote-subagent
# 或在 DSH 插件市场/plugin_manager 里安装 dsh-remote-subagent
依赖会自动安装(ssh2)。启用后:左侧栏出现「远端代理」图标,模型侧多出 remote_agent 工具。
机器从哪来
不重复配置 SSH 清单:目标机器读取 dsh-remote 的注册表
$DSH_HOME/remote-workspaces/machines.json
解析顺序:插件 config.machineId → 注册表 currentId → 第一个已保存机器 → config.machine(独立部署无 dsh-remote 时用)。支持密码、私钥(含 passphrase)、SSH agent。主机密钥采用 TOFU:首次连接把指纹记录到 $DSH_HOME/dsh-remote-subagent/known_hosts.json,之后必须匹配。
工具:remote_agent
| action | 作用 | 关键参数 |
|---|---|---|
spawn |
起一次新的远端 run(独立远端会话) | task, machine?, cwd? |
wait |
阻塞等 run 结束或超时(默认 30s,上限 120s;超时不影响远端继续跑) | runId, waitSeconds? |
get |
状态 + 最近 N 条过程事件 + 结果 | runId, eventLimit? |
send |
给同一远端会话追加消息(新 run,--session-id 续跑,turn+1) |
runId, task |
list |
最近的 run 列表 | limit? |
cancel |
终止远端进程(按 pid 文件 kill -TERM) |
runId |
machines |
可用的远端机器 | — |
典型用法:
remote_agent({action:"spawn", task:"在 /opt/app 跑一次只读健康检查:磁盘、内存、监听端口,最后总结"})
→ runId
remote_agent({action:"wait", runId, waitSeconds:120})
remote_agent({action:"get", runId, eventLimit:30})
remote_agent({action:"send", runId, task:"把监听端口按大小排序再列一次"})
复用语义:spawn 永远新建远端会话(等于 subagent 的 spawn);send 复用该 run 所属的同一远端会话(等于 subagent 的 send_message)。多个 spawn 并发 = 多个互不共享上下文的远端会话;同一会话同一时刻只能跑一个 turn。run 历史与 sessionId 持久化在本地,DSH 重启后仍可 send 续跑。
面板(Client 半)
左侧栏「远端代理」→ 主面板:
- 左列:新建任务(选机器 + 任务文本 + 派活)、run 列表(状态点 / 任务摘要 / 机器 / 用时);
- 右列:run 详情(状态、远端 session id、cwd、轮次、事件数、token)、实时事件时间线(1.5s 轮询增量、自动滚底)、结果框、终止 / 刷新、追加消息输入框。
数据接口(同源;Desktop 走 /api 前缀,Web 走裸路径):
GET /dsh-remote-subagent/runs
GET /dsh-remote-subagent/run?id=<run>&from=<seq>&limit=<n>
POST /dsh-remote-subagent/action {action: spawn|send|cancel, ...}
配置(可选)
插件配置在 profile 的 cordis.patch.yml 的 config: 下,全部可省略:
| 字段 | 默认 | 说明 |
|---|---|---|
machineId |
'' |
默认机器(id/名称/host) |
machinesFile |
'' |
自定义机器注册表路径 |
headlessCommand |
dsh headless |
远端命令前缀 |
cwd |
'' |
远端工作目录(空 = 机器 home) |
maxConcurrent |
4 |
并行 run 上限 |
timeoutSeconds |
900 |
单 run 上限,超时自动终止远端进程 |
maxEvents |
4000 |
每个 run 保留的事件上限 |
connectTimeoutMs |
20000 |
SSH 连接超时 |
本地数据
$DSH_HOME/dsh-remote-subagent/runs/<runId>.json:每个 run 的元数据 + 事件 transcript(重启后历史仍在;重启瞬间仍在跑的 run 标记 failed/interrupted,但保留 session id,可send续跑);$DSH_HOME/dsh-remote-subagent/known_hosts.json:TOFU 主机指纹。
实测状态(v0.1.0,2026-11-04)
在一个 Linux 远端(DSH 0.2.0-rc.2,SSH 密码登录)上跑通:
spawn→ 11s / 23 条事件 / turn 1,含tool_call与tool_result;send续跑同一session-*,turn 2 正确引用上一轮上下文,回答数值与 turn 1 一致;- DSH 重启 + 插件热重载后
send仍能续上同一远端会话(turn 3); cancel后远端无残留dsh headless进程,pid 文件清理;- 面板槽位注册(
sidebar.panellistid=remote-agents、mainkey=remote-agents)与三条面板接口的处理器行为均有单测/实测覆盖。
单测覆盖(离线,无网络):机器解析顺序与报错、事件流解析/分页/续跑/取消/持久化、面板路由、SSH 引用与 TOFU 指纹、Client 槽位注册。
已知边界
- 远端需要
dsh在 SSH 登录 PATH 上,且 headless profile 可用;远端版本过老、没有--json/--session-id时不可用; - 只支持 POSIX 远端(命令用
sh -c包装);Windows 远端未实现; - 不支持 dsh-remote 的
useSshConfig别名机器与 ProxyJump 跳板(请用直连条目); - 远端 run 的模型/工具/沙箱完全由远端 profile 决定,本地不能改;
wait超时不会终止远端 run;运行中不支持插话,需wait或cancel后再send;- 事件流覆盖 headless 单 turn;远端 agent 若把活丢进后台 job,其后续输出不在本 run 流里。
回滚
- 卸载:从 profile 移除
dsh-remote-subagentbundle; - 清数据:删除
$DSH_HOME/dsh-remote-subagent/(run 历史 + TOFU 指纹); - 对远端无持久改动:仅
/tmp/dsh-remote-subagent-*.pid(正常结束自动清理)与远端 session 目录。
License
MIT
No comments yet. Be the first to write one.