dsh-remote-subagent:把本地会话里的任务派给远端机器的 DSH 子代理

前言

DSH 的子代理(subagent)能让一个会话把任务丢给另一个 agent 并行处理,但执行仍然落在本机。当你的 agent 需要在另一台机器上跑、用那台机器的模型配置、工具和沙箱时,本地子代理帮不上忙。通常的做法是开一个公网端口、配 token、再写一套 WebSocket 协议——这意味着额外的暴露面和一套独立于 DSH 的基础设施。

dsh-remote-subagent 走的是更简单的一条路:复用 DSH 自带的 headless 入口,通过 SSH 的 exec 通道把远端 dsh headless --json 的 JSON 事件流直接读回本地。下面介绍这个插件是什么、怎么装、怎么用,以及它明确的边界。

这是什么

这是 DSH(DeepSeek Harness)的一个插件,作者 deepseekv41flash,分类属于工作流,目前在 SkillHub 目录页与 GitHub 上显示 1 个星标,版本 0.1.0,许可证 MIT。

它解决一个问题:本地会话需要让另一台机器上的 DSH 替自己干活,并且能看到那个 agent 的思考、工具调用和工具结果,还能在它跑完之后接着追问。

核心机制是 remote_agent 工具。本地会话通过它把任务派给远端 DSH,远端 agent 用自己的模型配置、工具、沙箱和 session 存储执行,过程中的 JSON 事件通过 SSH 实时流回本地。

本地会话 ── remote_agent 工具 ──SSH──> 远端 dsh headless --json <task>
   ▲                                        │
   └──── 事件流(thinking / tool_call / tool_result / final)◄──┘

选择 dsh headless --json 而不是自建服务,是因为它有三点好处:

  • 远端不需要开公网端口,也不需要 token 或 WebSocket 协议;
  • 远端 agent 用它自己的 profile,session 落在远端 $DSH_HOME/sessions,在远端 DSH Web UI 里同样可见;
  • 事件流本身就是「工作过程」,不需要额外埋点。

安装与启用

README 给出的安装命令是:

# 从 npm 安装到当前 profile(bundle 形式)
dsh plugin --profile <profile> add dsh-remote-subagent
# 或在 DSH 插件市场/plugin_manager 里安装 dsh-remote-subagent

依赖会自动安装(ssh2,版本要求 ^1.17.0)。启用后,左侧栏出现「远端代理」图标,模型侧多出 remote_agent 工具。

插件的运行要求写在 package.json 里:Node.js >=20,DSH 引擎 >=0.1.0-rc.6 <0.3.0。远端同样需要 dsh 在 SSH 登录 PATH 上,且 headless profile 可用。

机器从哪来

插件不要求你重复配置一份 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 工具

插件暴露一个 remote_agent 工具,支持以下几个 action:

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 列出可用的远端机器 —

两个动作的复用语义需要区分清楚:

  • spawn 永远创建新的远端会话,等于内置 subagent 的 spawn;
  • send 复用该 run 所属的同一个远端会话,等于 subagent 的 send_message;
  • 多次 spawn 并发 = 多个互不共享上下文的远端会话;同一会话同一时刻只能跑一个 turn。

run 历史和 sessionId 持久化在本地,DSH 重启后依然可以对同一个 session 继续 send。

面板

左侧栏「远端代理」打开主面板:

  • 左列:新建任务(选机器 + 任务文本 + 派活)、run 列表(状态点 / 任务摘要 / 机器 / 用时);
  • 右列:run 详情(状态、远端 session id、cwd、轮次、事件数、token)、实时事件时间线(1.5s 轮询增量、自动滚底)、结果框、终止 / 刷新、追加消息输入框。

面板背后的数据接口(GitHub 仓库为 client 半,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 连接超时

典型用法

下面是 README 提供的一个完整调用序列,读一次远端健康检查,再追加一条追问:

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 拿到 runId,再 wait 等它结束或超时,接着用 get 拉最近若干条过程事件和结果;send 是在上一步的同一个远端会话里追加消息,turn 加一,模型能看到前面的上下文。

数据与回滚

插件在本地写入两类数据:

  • $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 主机指纹。

卸载时从 profile 移除 dsh-remote-subagent bundle;需要清数据就删除 $DSH_HOME/dsh-remote-subagent/。插件对远端没有持久改动,只会在 /tmp/dsh-remote-subagent-*.pid 落临时 pid 文件(正常结束自动清理)并占用远端 session 目录。

实测状态

README 记载了 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 文件被清理。

README 还说明有离线单测覆盖:机器解析顺序与报错、事件流解析/分页/续跑/取消/持久化、面板路由、SSH 引用与 TOFU 指纹、Client 槽位注册。

已知边界

这些限制来自 README,实际使用前需要对照自己的环境确认:

  • 远端需要 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 的流里。

适用场景与注意

适合的场景是:你有一台专门跑了 DSH 的机器(比如带特定模型配置、特定工具或隔离沙箱的服务器),想让本地会话把任务派过去执行,并且希望在本地面板里看到完整的执行过程。它把执行环境和交互环境拆开,远端不需要暴露任何端口。

需要注意的点:

  • 插件安装在当前 DSH profile 里以后,是以当前 dsh 进程的权限运行的。安装第三方插件之前,建议先读一遍源码(GitHub 仓库)并确认许可证——本插件声明为 MIT;
  • 它依赖 dsh-remote 的 machines.json 作为机器来源,如果已经用 dsh-remote 管理机器,不需要重复配置;没有 dsh-remote 时要改用 config.machine;
  • 「远端会话可追加消息」意味着 session 内容会留在远端,注意远端的存储和清理策略。

小结

dsh-remote-subagent 做的事情不复杂:把 dsh headless --json 的 SSH 调用封装成一个 remote_agent 工具,配上事件流回读、run 持久化和面板。它的价值在于让「换一个执行环境」这件事不需要新开服务、不需要改协议,代价是只支持 POSIX 远端,且远端行为完全由远端 profile 决定。

插件目录页:https://www.skillhub.cn/plugins/deepseekv41flash/dsh-remote-subagent
仓库:https://github.com/deepseekv41flash/dsh-remote-subagent

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜