dsh-mcp
omp-dsh-workers
Run DeepSeek Harness (DSH) workers from your oh-my-pi session. oh-my-pi (OMP) is a terminal coding agent; DeepSeek Harness (dsh) is DeepSeek's agent runtime. The session becomes the director: it hands out briefs with dsh_spawn, each worker runs as a persistent dsh --profile headless session, and worker questions and results return as native messages relayed by a script.
Experimental v0.1: the interfaces are frozen in docs/contracts/, but nothing here has been through a public release cycle yet.
Why
The OMP harness is expensive per task, and a native sub-agent pays that cost on every job. Here it is paid once at the director level; the work runs in DSH, fast and token-frugal, with only a script between them: zero model tokens per task. DSH makes runs persistent (--resume on a real session id). When you do not need DSH, native sub-agents are still the right choice.
Related MCP server: deepseek-subagent-mcp
How it works
Two model levels, deliberately separate:
Level | Who | Model comes from |
1 | Director — your main OMP session in | your OMP session model |
2 | DSH executor — one DSH headless process per run |
|
So @dsh names the executor's inherited model when dsh_spawn has no model — the watcher is code, not a model.
flowchart TD
D["Director<br/>main OMP session, /dvibe on"]
B["dsh-bridge<br/>argv spawn · run registry · steer channel"]
X["DSH headless run<br/>+ resume plugin"]
L["relay.ts<br/>script representative, in-process"]
D -->|"dsh_spawn — brief, label, model"| B
B -->|"dsh --profile headless [--resume]"| X
X -->|"Envelope v1 (last stdout line)"| B
B -->|"pollRun, 1s"| L
L -->|"⟨label⟩ question / result / failure (followUp)"| D
D -->|"dsh_answer — resumes the session"| B
D -.->|"steering: dsh_list → dsh_send / dsh_wait by runId"| B
D -.->|"dsh_kill by runId"| BThe director spawns with dsh_spawn, answers with dsh_answer, steers with dsh_send, waits with dsh_wait and cancels with dsh_kill; dsh_list resolves a label to a runId.
Components
Path | What it is |
| The OMP extension: |
| bridge-core: spawn in its own detached process group, run registry, Envelope v1, steer channel, owner lease and reaping. Node ≥ 22, plain ESM JavaScript, no dependencies and no build step. |
| Cordis plugin in DSH's headless profile: adds |
| Installation: symlinks into the live OMP dir, the DSH profile patch, plugin dependency linking. |
Requirements
oh-my-pi v18 — verified against 18.0.3 / 18.0.4;
@oh-my-pi/*pinned at^18.0.4.DSH ≥ 0.1.1-rc.2 on
PATH, with theheadlessprofile present.bun for the test scripts; Node ≥ 22 for bridge-core.
A model provider configured in your DSH settings; the extension is provider-neutral: it passes a
<provider>/<model>[:<effort>]string to DSH.
DSH is at release-candidate stage. The resume plugin attaches by entry id, so a release renaming those ids makes the patch silently stop applying. After every DSH upgrade re-run dsh --profile headless --help: if --resume is gone, the plugin is not mounted; docs/dsh-update-checklist.md has the full checklist.
Install
The repository is the source of truth; live directories only ever receive symlinks back into it.
1. Link the extension into OMP.
scripts/install-omp-links.sh [--dry-run] [--uninstall] [--omp-dir DIR]Creates a symlink extensions/dsh-task under $OMP_DIR (default $HOME/.omp/agent). Idempotent — a same-source link is left alone, one pointing elsewhere is re-pointed, and a real file at the destination aborts the script. --uninstall removes only links that point here.
2. Mount the resume plugin into the DSH headless profile.
scripts/install-resume-plugin.sh # install
scripts/install-resume-plugin.sh --uninstall # removeBacks up before every change; requires dsh on PATH and ${DSH_HOME:-$HOME/.dsh}/profiles/headless. Then:
Symlinks
@deepseek-aiandcommanderfrom$DSH_MODULESinto the plugin'snode_modules.Adds the plugin:
dsh plugin --profile headless add link:<plugin dir>, after backing uppackage.json.Appends a
cordis.patch.ymlblock that disablesheadless-startup/headless-runnerand insertsheadless-resume-startup/headless-resume-runner.Verifies: success only when
dsh --profile headless --helpmentions--resume.
3. (tests only) scripts/link-plugin-deps.sh links dependencies on its own; bun run test:resume calls it.
Usage
Director mode
/dvibetoggles director mode;/dvibe on//dvibe offare explicit. The model can also switch it via thedvibetool (action: "on" | "off"), which stays in the narrowed toolset.While on, the toolset narrows to
read,todo,dsh_spawn,dsh_answer,dsh_send,dsh_wait,dsh_list,dsh_kill,dvibe, plus a director directive appended to the system prompt. Thedvibetool returns that directive in its result: the model calls it afterbefore_agent_starthas run, so the turn prompt cannot carry the rules.Briefs go into
dsh_spawnverbatim. Worker questions arrive as⟨label⟩messages fromrelay.ts, answered withdsh_answer; results arrive the same way.Delivery is at-least-once: an event is re-announced every 120 s until a matching
message_startproves the followUp entered the turn context; max 3 attempts per event. A deliveredneed_inputstays watched untildsh_answer.Finished handing out work? End the turn: events arrive as messages on their own.
dsh_waitis the synchronous alternative, only when the next step blocks on that specific run and nothing is left to hand out; an envelope read this way never arrives twice.On
/dvibe off, shutdown, or an in-process session switch the previous toolset is restored.
Briefs, models, resume
Give each task a short
labeland, optionally, amodel, both asdsh_spawnparameters; the label finds the run later indsh_list,dsh_answer,dsh_send.Model notation is
<provider>/<model>[:<effort>]; effort levels:off,minimal,low,medium,high,xhigh,max. The suffix after the last:counts as effort only if it is one of them, else the colon belongs to the model name. No whitespace or control characters; provider/model ≤ 200 chars each, spec ≤ 512; malformed spec fails before spawning:error [invalid_model].Without
model, the run inherits the@dshrole from OMP (modelRoles.dsh), falls back to your session's model, then to DSH's own default.Resume: pass
resumeFromRunId; the bridge looks up itssessionId. Do not put arunIdintoresumeSessionId: different identifiers, you getresume_not_found.Resume works once the run has left an envelope on disk; without one
dsh_spawnthrowshas no session to resume— for a run still running as well as one gone (killed early, crashed at startup, swept). Check which before reacting: a new brief for a still-working run duplicates work.The model override is not sticky: a resume without
modelrecomputes the model.dsh_answerhas nomodelparameter at all; to continue on another model usedsh_spawnwithresumeFromRunIdand an explicitmodel.
What the director sees
Tool cards render for humans, separately from the text the model receives: ▶ dsh spawn → <label>, ✓ started <label> (<runId8>) · pid …, then ⏳ still running with last output lines or ✓ completed · model: … · session: … plus first result lines. While runs are tracked, a dsh runs board sits above the editor and the footer shows dsh: N running · M done. The tools' text output is unchanged: it remains the contract.
Tools
Tool | Parameters | Text the caller gets |
|
|
|
|
|
|
|
| the run's result, or |
|
|
|
|
|
|
| — |
|
|
| the run's result (blocking, one-shot) |
dsh_wait timing out is normal: the run stays alive and can be waited on again; aborting a wait never stops the run. pending from dsh_send means the write reached the channel and the run was alive on re-check — not confirmed delivery; wait instead of resending. dsh_task leaves no envelope, so its run cannot be continued; chains go through dsh_spawn.
Lines the director can rely on
These lines go into the tool's text output, not only details, so a director reading plain text can verify executor, continuity and error codes:
model: <provider>/<model>[:<effort>]
session: <sessionId>
error [<code>]: <message>
# and one line per run from dsh_list:
<runId> state=<state> label=<label|-> model=<spec|default> started=<ISO-8601>Error codes
Every failure returns as an explicit error turn with an envelope code, never a partial success.
Envelope code | 意味 |
| DSHバイナリが起動しませんでした。 |
| 実行が失敗の終了コードで終了しました。 |
| 実行が期限までに完了しませんでした。 |
| 実行がキャンセルされました。 |
| DSHが有効なEnvelope v1を返しませんでした。 |
| 再開するセッションが存在しません。 |
| 永続化されたセッションが破損しているか、サポートされていません。 |
| セッションは既に稼働中か、その永続化された準備が予約されています。 |
| 誰も実行のリースを更新しなかったため、ウォッチドッグがそれを回収しました。 |
| 実行が期限を超えて存続し、回収されました。 |
| プロバイダー/モデルがDSHのカタログにありません。 |
| モデルは存在しますが、努力量またはメタデータがそれに適合しません。 |
デフォルト: 実行期限30分、オーナーリース5分。各dsh_waitウィンドウで更新されます。
テスト
bun run test # unit + integration + bridge = 370 tests, no installed DSH needed
bun run test:resume # resume plugin — needs an installed DSHこのツリーで検証された数: 単体195 + 統合11 + ブリッジ164 = 370テスト。DSHがインストールされていなくても合格します。単体テストはbridge-coreをモックし、統合テストとブリッジテストはDSH_BINARYを通じて注入されたフェイクのdshバイナリに対して実行されます。CIはtypecheck、lint、format:check(strict tsc、Biome)の後に、クリーンなHOMEで同じ3つのスイートを実行します。test:resumeは実行時に@deepseek-ai/*をインポートするため、DSHがインストールされている必要があります。
制限事項
孤立した実行は防止されるのではなく、掃除されます。 DSHの実行はOMPセッションより長く存続します。掃除はロード時と30秒ごとに行われます。クリーンな
session_shutdownでは、拡張機能はレジストリをクリアせずに自身の実行を終了します(SIGTERMは同期、SIGKILLはベストエフォート)。実行中のクラッシュからの復旧はありません: ターン途中の死亡は復元されません。再開できるのはDSHセッションのみです。
再開時のコンパクションは、最初の新しいリクエストヘッダーが書き込まれるまで、前回の実行のヘッダーを読み取ります。
エンベロープ内の
modelはベストエフォートです: 最後に準備されたリクエスト設定であり、ディスパッチの証明ではありません。モデルオーバーライドは実行ごとであり、
resumeFromRunIdをまたいで継承されません。ハブのメトリクスはDSHトークンを認識しません。
ステータス、履歴、ライセンス
実験的なv0.1(0.1.0)です。インターフェース契約はdocs/contracts/にあります。docs/dsh-update-checklist.mdはDSHのアップグレードを扱います。ユーザーまたはモデルが読むものはすべて英語です。コード内のコメントとテスト名はロシア語です。MITライセンス。
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Build Apps and run code in 30 languages — sandboxed, with persistent sessions for agent loops.
Shared control plane for AI coding agents — tasks, memory, decisions, file locks. 12 tools.
Work on your own computers from Claude or ChatGPT: run commands, edit and search files.
Live SEO workflow tools for Claude Code, Codex, and AI agents.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables dispatching work to DeepSeek Harness agents from Claude Code/Codex, with native progress UI, tier policy, and vision/image generation through MCP tools.569 npm199MIT
- AlicenseAqualityCmaintenanceEnables AI coding agents like Claude Code or Codex to delegate tasks to a DeepSeek Harness subagent with its own context window, providing tools for task delegation, result waiting, continuation, and supervision with sandboxed execution.6MIT
- AlicenseAqualityBmaintenanceEnables Codex and Claude Code to delegate implementation, research, debugging, and long-log work to DeepSeek Harness, then observe, continue, or cancel those sessions without leaving the primary workflow.153MIT
- AlicenseNot gradedqualityCmaintenanceEnables an interactive Claude Code session to delegate blocking tasks to headless pi and OpenCode workers, and lets all three peers exchange messages and list paired sessions over a shared local file bus. It exposes 18 tools covering sync/async asks, steering, abort, session lifecycle, state inspection, and cross-agent messaging.216 npm1MIT