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: dsh-crew
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ライセンス。
This server cannot be deployed
Maintenance
Related MCP Connectors
- mcp-serverOAuthai.cdbx
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.
Live SEO workflow tools for Claude Code, Codex, and AI agents.
Turn Claude into a creative studio: DNA-locked characters, images, video, voiceover — 55 tools.
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables Claude Code to delegate tasks to OpenCode subagents asynchronously, with tools for starting tasks, polling status, and fetching results.772 npm2MIT
- 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.602 npm149MIT
- AlicenseAqualityBmaintenanceEnables 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.151MIT