dsh-remote-subagent: Dispatching tasks from local session to remote machine's DSH subagent

Preface

DSH’s subagents allow a session to offload tasks to another agent for parallel processing, but execution still happens on the local machine. When your agent needs to run on another machine, using that machine’s model configuration, tools, and sandbox, a local subagent won’t help. The usual approach is to open a public port, configure tokens, and write a WebSocket protocol—this means extra exposure surface and a set of infrastructure independent of DSH.

dsh-remote-subagent takes a simpler path: it reuses DSH’s built-in headless entry point, reading the remote dsh headless --json JSON event stream back to the local machine directly via an SSH exec channel. Below is an introduction to what this plugin is, how to install it, how to use it, and its clear boundaries.

What is it

This is a plugin for DSH (DeepSeek Harness), author deepseekv41flash, categorized under Workflow. Currently shows 1 star on the SkillHub directory page and GitHub, version 0.1.0, license MIT.

It solves a problem: a local session needs another machine’s DSH to do the work for it, and wants to see that agent’s thinking, tool calls, and tool results, as well as continue asking questions after it finishes.

The core mechanism is the remote_agent tool. The local session uses it to dispatch tasks to the remote DSH. The remote agent executes using its own model configuration, tools, sandbox, and session storage. The JSON events during the process are streamed back to the local machine in real-time via SSH.

Local Session ── remote_agent tool ──SSH──> Remote dsh headless --json <task>
   ▲                                        │
   └──── Event Stream (thinking / tool_call / tool_result / final) ◄──┘

Choosing dsh headless --json instead of building a custom service offers three benefits:

  • The remote side doesn’t need to open a public port, nor does it require tokens or WebSocket protocols;
  • The remote agent uses its own profile, and the session is stored in the remote $DSH_HOME/sessions, making it visible in the remote DSH Web UI as well;
  • The event stream is the “work process,” so no additional telemetry is needed.

Installation & Enabling

The installation command given in the README is:

# From npm, install to current profile (bundle form)
dsh plugin --profile <profile> add dsh-remote-subagent
# Or install dsh-remote-subagent in DSH Plugin Market/plugin_manager

Dependencies are installed automatically (ssh2, version requirement ^1.17.0). After enabling, the “Remote Agent” icon appears in the left sidebar, and the remote_agent tool appears on the model side.

The plugin’s runtime requirements are written in package.json: Node.js >=20, DSH engine >=0.1.0-rc.6 <0.3.0. The remote side also requires dsh to be in the SSH login PATH, and the headless profile must be available.

Where do machines come from

The plugin does not require you to configure an SSH list twice. It prioritizes reading the dsh-remote registry:

$DSH_HOME/remote-workspaces/machines.json

The default machine resolution order is: plugin config config.machineId → registry currentId → first saved machine → config.machine (used for standalone deployments without dsh-remote). Authentication supports password, private key (including passphrase), and SSH agent.

Host keys use the TOFU (Trust On First Use) strategy: the fingerprint is recorded to $DSH_HOME/dsh-remote-subagent/known_hosts.json on the first connection; every subsequent connection must match, or it will be rejected.

remote_agent Tool

The plugin exposes a remote_agent tool supporting the following actions:

action Description Key Parameters
spawn Start a new remote run (independent remote session) task, machine?, cwd?
wait Block until run ends or timeout (default 30s, max 120s; timeout does not affect remote continuation) runId, waitSeconds?
get Status + recent N process events + result runId, eventLimit?
send Append message to the same remote session (new run, --session-id resume, turn+1) runId, task
list List of recent runs limit?
cancel Terminate remote process (kill -TERM based on pid file) runId
machines List available remote machines —

The reusability semantics of the two actions need to be distinguished clearly:

  • spawn always creates a new remote session, equivalent to the built-in subagent’s spawn;
  • send reuses the same remote session that the run belongs to, equivalent to the subagent’s send_message;
  • Multiple concurrent spawns = multiple remote sessions that do not share context; only one turn can run in the same session at the same time.

Run history and sessionId are persisted locally; after a DSH restart, you can still continue send for the same session.

Panel

Opening the main panel in the left sidebar “Remote Agent”:

  • Left column: New task (select machine + task text + dispatch), run list (status dot / task summary / machine / time elapsed);
  • Right column: Run details (status, remote session id, cwd, turns, event count, token), real-time event timeline (1.5s polling increment, auto-scroll to bottom), result box, terminate / refresh, append message input box.

The data interface behind the panel (GitHub repo is the client side, Desktop uses /api prefix, Web uses raw paths):

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, ...}

Optional Configuration

Plugin configuration is written under config: in the cordis.patch.yml of the current profile. All fields are optional:

Field Default Description
machineId '' Default machine (id/name/host)
machinesFile '' Custom machine registry path
headlessCommand dsh headless Remote command prefix
cwd '' Remote working directory (empty = machine home)
maxConcurrent 4 Parallel run limit
timeoutSeconds 900 Single run limit, terminates remote process on timeout
maxEvents 4000 Max events retained per run
connectTimeoutMs 20000 SSH connection timeout

Typical Usage

The following is a complete call sequence provided in the README: check remote health once, then append a follow-up question:

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:"把监听端口按大小排序再列一次"})

First spawn to get the runId, then wait for it to end or timeout, then use get to fetch the recent process events and results; send appends a message in the same remote session as the previous step, turn +1, allowing the model to see the previous context.

Data & Rollback

The plugin writes two types of data locally:

  • $DSH_HOME/dsh-remote-subagent/runs/<runId>.json: Metadata + event transcript for each run. History persists after restart; runs still running at the moment of restart are marked as failed/interrupted, but the session id is retained, allowing send to resume.
  • $DSH_HOME/dsh-remote-subagent/known_hosts.json: TOFU host fingerprints.

When uninstalling, remove the dsh-remote-subagent bundle from the profile; to clear data, delete $DSH_HOME/dsh-remote-subagent/. The plugin makes no persistent changes to the remote side; it only writes a temporary pid file to /tmp/dsh-remote-subagent-*.pid (cleaned up automatically on normal exit) and occupies the remote session directory.

Test Status

The README records the test results of v0.1.0 (2026-11-04) on a Linux remote (DSH 0.2.0-rc.2, SSH password login):

  • spawn took 11s, generated 23 events, turn 1, including tool_call and tool_result;
  • send continued the same session-*, turn 2 correctly referenced the previous context’s values, and the values in the answer were consistent with turn 1;
  • After DSH restart + plugin hot reload, send could still resume the same remote session (turn 3);
  • After cancel, there were no residual dsh headless processes on the remote side, and the pid file was cleaned up.

The README also mentions offline unit test coverage: machine resolution order and errors, event stream parsing/pagination/resumption/cancellation/persistence, panel routing, SSH references and TOFU fingerprints, and Client slot registration.

Known Boundaries

These limitations come from the README and need to be confirmed against your environment before actual use:

  • The remote side requires dsh to be in the SSH login PATH, and the headless profile must be available; the remote version is too old, or lacks --json / --session-id, it is unavailable;
  • Only POSIX remotes are supported (commands wrapped with sh -c); Windows remotes are not implemented;
  • dsh-remote’s useSshConfig alias machines and ProxyJump jump hosts are not supported; direct connection entries must be used;
  • The remote run’s model / tools / sandbox are completely determined by the remote profile; the local side cannot modify them;
  • wait timeout does not terminate the remote run; interruptions during execution are not supported; you need to wait or cancel before send;
  • The event stream only covers headless single turns; if the remote agent throws work into a background job, subsequent output will not appear in this run’s stream.

Use Cases & Notes

Suitable scenarios are: you have a dedicated machine running DSH (e.g., a server with specific model configurations, specific tools, or an isolated sandbox), and you want the local session to dispatch tasks to it for execution, while viewing the complete execution process in the local panel. It separates the execution environment and interaction environment; the remote side does not need to expose any ports.

Points to note:

  • After the plugin is installed in the current DSH profile, it runs with the permissions of the current dsh process. Before installing third-party plugins, it is recommended to read the source code (GitHub repository) and confirm the license—this plugin is declared as MIT;
  • It depends on dsh-remote’s machines.json as the machine source. If you already use dsh-remote to manage machines, you don’t need to configure them again. Without dsh-remote, you need to use config.machine;
  • “Remote session can append messages” means session content remains on the remote side, so pay attention to the remote storage and cleanup strategy.

Summary

What dsh-remote-subagent does is not complicated: it encapsulates the SSH call to dsh headless --json into a remote_agent tool, paired with event stream readback, run persistence, and a panel. Its value lies in enabling “changing execution environments” without needing to start new services or change protocols, at the cost of only supporting POSIX remotes and the remote behavior being completely determined by the remote profile.

Plugin Directory Page: https://www.skillhub.cn/plugins/deepseekv41flash/dsh-remote-subagent
Repository: https://github.com/deepseekv41flash/dsh-remote-subagent

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

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

Xiaoye