DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins

dsh-subagent-bridge

Subagent Bridge

Subagent bridge: lets Codex delegate a task to DeepSeek Harness the way it would spawn a subagent (one call, waits for the answer), plus a codex_dispatch tool for the reverse direction.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-subagent-bridge@0.21.0
READMECompatibilityVersions

Compatibility and provenance

Subagent Bridge is published as dsh-subagent-bridge and currently resolves to version 0.21.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
npm
Registry updated
11/3/2026

Versions

0.21.0stable
11/3/2026
0.19.0stable
11/3/2026

Related plugins

Loading related plugins…

Latest
0.21.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
164.3 kB
Files
25
Surface
any
License
MIT
Source
npm
GitHub
★ 5
Weekly downloads
0
Last push
11/3/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in agents-orchestration.

Headless@deepseek-ai/dsh-headlessThe dsh one-shot bundle: a direct core Agent/Session runner over dsh-base with no Host, HTTP, or browser layerExperimental Agent Team Profile@deepseek-ai/dsh-experimental-agent-team-profileAgent Teams collaboration, tools, and Web UI in one experimental bundleExperimental Ralph Bundle@deepseek-ai/dsh-experimental-ralph-bundleRun explicitly requested repeated work with fresh agents.Subagent Codex@deepseek-ai/dsh-subagent-codexOne-shot Codex subagent provider over the official app-server protocol

README

dsh-subagent-bridge

Let Codex or Claude Code drive DeepSeek Harness sessions as subagents — and let DSH drive them back.

中文说明

The plugin runs inside the Harness host and exposes a loopback MCP endpoint. It uses public host services (sessionController, sessions, workspaceRegistry, userQuestions) — no credential files, no cookie forgery.

Highlights

  • One tool call, one answer. dsh_task opens a session, delivers the message, blocks until the turn ends, and returns only the final assistant text.
  • Parallel workers. dsh_spawn returns immediately; dsh_wait blocks until any of them settles.
  • Per-call model and project. Pin a worker to provider/model and file it under a named workspace without touching profile defaults.
  • Queue or steer. Choose whether a follow-up waits for the running turn or is inserted into it.
  • Unattended by default. Workers are told to assume a reasonable default instead of stopping to ask.
  • Reverse direction. codex_dispatch and claude_dispatch hand work to Codex or Claude Code, with resumable threads.
  • Loop guard. Depth and rate limits stop the two agents from delegating to each other in circles.

Requirements

  • DeepSeek Harness ^0.2.1-0.
  • Codex CLI (codex) and/or Claude Code CLI (claude) on PATH for the reverse direction.

Install

git clone https://github.com/Dogtiti/dsh-subagent-bridge

Open Plugins → Add in DSH and pick that directory. On start the plugin listens on 127.0.0.1:47821 and writes the endpoint file:

{
  "url": "http://127.0.0.1:47821/mcp/<token>",
  "port": 47821,
  "token": "<token>",
  "pid": 12345,
  "startedAt": "2026-10-22T12:30:00.000Z"
}

~/.dsh/bridge/endpoint.json, directory 0700, file 0600.

Connect

Codex

codex mcp add dsh-bridge --url "$(node -p "require(process.env.HOME+'/.dsh/bridge/endpoint.json').url")"
codex mcp list                # verify
codex mcp remove dsh-bridge   # remove

The desktop app and the CLI share ~/.codex/config.toml, so both see it. The token lives in the URL path, so no environment variable is needed — the desktop app cannot receive custom ones when launched from Finder. Authorization: Bearer <token> works too.

Claude Code

claude mcp add --transport http dsh-bridge "<the url above>" --scope user

Usage

// Synchronous: wait for the turn, get the answer
dsh_task({ prompt: "Review the error handling in src/bridge.ts", cwd: "/repo" })

// Asynchronous: hand out work, then wait for all of it
dsh_spawn({ task_name: "audit_api", message: "…", cwd: "/repo" })
dsh_spawn({ task_name: "audit_ui",  message: "…", cwd: "/repo" })
dsh_wait({ targets: ["audit_api", "audit_ui"], timeoutMs: 240000 })

// Continue an existing worker
dsh_followup({ worker: "audit_api", message: "Expand item 3 and attach a patch" })

Tools

ToolPurpose
dsh_taskSynchronous dispatch: create session, deliver, block to end of turn, return the final answer, plus operationId, requestId, usage and the model the worker actually used
dsh_spawnAsynchronous dispatch: returns task_name + sessionId immediately
dsh_followupAnother round on an existing worker, reusing its context
dsh_waitWait on several workers at once; returns when any reaches a terminal state
dsh_closeClose a worker and free its slot (the session stays)
dsh_answerAnswer a worker's question (limited — see below)
dsh_list_workersList workers started by this bridge and their live status
dsh_dispatchDeliver one message to any session; returns requestId and seqAtDispatch
dsh_read_resultLong-poll the assistant text produced after seqAtDispatch
dsh_list_sessionsList session metadata only (id, title, cwd, running, last activity)

dsh_list_sessions never returns message content, and dsh_read_result only works for sessions this bridge has dispatched to.

Model

dsh_task({ prompt: "…", model: "zai-coding-cn/glm-5.3", reasoningEffort: "max" })
dsh_spawn({ task_name: "review", message: "…", model: "opencode-go/deepseek-v4.1-flash" })
  • provider/model; a bare model id keeps the current default provider.
  • reasoningEffort is optional and model-dependent.
  • Omitted means the session is untouched.
  • The switch happens before the first prompt, so the worker's first turn already runs on it, and the profile default model is not modified.

Workspace

DSH groups sessions into projects; a new session lands under "Ungrouped" unless its directory is already a project.

dsh_task({ prompt: "…", cwd: "/repo/clawer", workspace: true })    // create, titled after the directory
dsh_spawn({ task_name: "w1", message: "…", cwd: "/repo/clawer", workspace: "Clawer" })
dsh_task({ prompt: "…", cwd: "/repo/clawer" })                     // reuse only

Missing directories are created first (registration requires an existing directory). An existing project is reused without retitling. The result carries workspace: { id, title, path }. A failure here never fails the dispatch.

Queue or steer

DSH's "sending while busy" has exactly two behaviours:

modeBehaviour
queue (default)Wait for the current turn, then deliver as a new turn
steerInsert into the current turn; the model sees it at the next step
dsh_followup({ worker: "w1", message: "Only touch files under src from here", mode: "steer" })

Two measured facts:

  1. steer does not interrupt a running tool call. A worker inside sleep 90 only sees the message when that call returns (70s in our test). Stopping it is the UI's cancel action, not a delivery mode.
  2. steer does not open a new turn — the turn number stays the same, so the bridge waits for the current turn to end. Waiting for the next one blocks until the timeout.

dsh_task refuses a running session unless mode: "steer" is given, and the error message says so. A steered reply shares the turn with the original task, so the bridge anchors the answer on its own user message rather than the turn's last assistant text.

Unattended workers

A worker that calls ask_user_question waits for a human. When the caller is another agent, nobody is there. So the bridge appends a standing instruction to every delegated task:

(If information is missing, or a choice between options is required, pick the most
 reasonable default, finish the task, and state the assumption in one line. Do not
 hand the decision back to the user and do not call ask_user_question.)

On by default (assumeDefaults: false disables it). It is skipped when the prompt already mentions ask_user_question.

Replace the wording with assumptionPolicy — Chinese, for instance:

config:
  assumptionPolicy: "如果缺少必要信息,直接采用最合理的默认做法做完,并在结果里用一行说明你的假设。"

Questions (pendingQuestion)

If a worker does ask, dsh_wait reports it:

{
  "status": { "w1": "running" },
  "pendingQuestion": {
    "callId": "call_abc",
    "questions": [{ "id": "mode", "question": "Which mode?", "options": [{ "label": "Fast" }] }]
  },
  "next": "blocked on a question: answer it with dsh_answer, or call dsh_wait again"
}

dsh_answer({ worker, answers: [{ id: "mode", selected: ["Fast"] }] }) submits a batch.

Known limit: DSH's userQuestions.answer() only accepts questions that have already timed out into the continued state. A question still blocking inside the tool call belongs to the web UI's answerer, and the bridge cannot answer it (answered: false in practice). Prefer assumeDefaults over waiting for a question.

Reverse direction

Both tools are registered for agents of this deployment.

codex_dispatch

ParameterDescription
promptRequired task text
cwdWorking directory; defaults to the calling session's
resumeCodex thread id to continue
sandboxread-only / workspace-write / danger-full-access

Returns { ok, text, threadId, exitCode, durationMs }. Runs codex exec [resume <id>] --json -o <tmpfile>, reads thread.started from the event stream and the final message from the -o file. sandbox and cwd are ignored on resume — codex exec resume accepts neither.

claude_dispatch

ParameterDescription
promptRequired task text
cwdWorking directory
resumeClaude Code session id
modelModel alias or id; falls back to config, then to the CLI's own setting

Returns { ok, text, sessionId, durationMs }. Runs claude -p --output-format json and reads the single JSON object on stdout. stderr is advisory — the CLI warns about an unrecognised model suffix while still succeeding.

Configuration

- insert:
    - id: subagent-bridge
      name: 'dsh-subagent-bridge'
      config:
        port: 47821
KeyDefaultDescription
enabledtrueMaster switch; when off, no listener and no endpoint file
port47821Loopback port; fixed so the client URL survives restarts
endpointFile~/.dsh/bridge/endpoint.jsonWhere the endpoint and token are written
maxResultChars20000Cap on a single result payload (the head is kept)
taskTimeoutSeconds300dsh_task wait cap (client may override, max 240)
assumeDefaultstrueAppend the decide-and-continue instruction
assumptionPolicy(empty)Replacement text for the instruction; empty uses the built-in English default
workersFile~/.dsh/bridge/workers.jsonWorker registry (0600), written to a temp file and renamed so a crash cannot truncate it
maxHops1Delegation depth limit
dispatchPerMinute10Dispatches allowed per window
dispatchWindowMs60000Window length
maxWorkersPerRoot4Workers per working directory
maxWorkers12Global worker cap
workerTtlMs86400000Records stop counting once stale
waitTimeoutMs240000Default dsh_wait timeout
readWaitSeconds25Default dsh_read_result long-poll

A busy port is reported, not silently replaced: the endpoint file gets an error field and the host log prints subagent-bridge: could not start on port ….

Guardrails

Depth. Both agents can see the same bridge, so a circle is possible: DSH worker → codex_dispatch → that Codex run → dsh_spawn → … Each dispatch computes max(in-flight depth, caller depth) + 1 and refuses beyond maxHops:

codex_dispatch would create delegation depth 2, but maxHops is 1.
Carry the work out directly instead of delegating again.

Rate. dispatchPerMinute (default 10) catches bursts and tight retry loops:

rate limit: 10 dispatches per 60s reached; retry in 23s

Slots. 4 workers per cwd, 12 globally. A finished worker keeps its slot until dsh_close; records older than 24h stop counting. Blocking dsh_task calls are registered but do not consume a slot. Buckets are keyed by cwd because the MCP endpoint is shared and carries no session identity.

Persistence. workers.json keeps the registry and each turn's baseline: workers survive a restart, finished ones replay their cached answer, cold sessions report offline, and the file keeps at most 200 records.

Security. Loopback only; every request needs the token in the path or a bearer header, compared in constant time; the token is generated once and persisted. Holding the endpoint file is equivalent to being able to send messages to sessions — keep its 0600/0700 permissions. DSH's own sandbox and approval rules still apply to anything delivered.

How it works

Codex / any MCP client
  │  POST /mcp/<token>   JSON-RPC (stateless, single JSON response)
  ▼
dsh-subagent-bridge (inside the host process)
  · node:http on 127.0.0.1:47821
  · initialize / tools/list / tools/call / ping
  · sessionController.prompt({ sessionId, requestId, mode, content })
  · sessions.get(id).snapshotEvents() to read text back

The protocol is a stateless subset of streamable HTTP: each POST returns one application/json body and nothing is pushed, so results are collected by long polling. The server stops waiting the moment the client disconnects.

Skills

Two installable skills turn this into behaviour a model follows rather than a tool it might forget:

SkillInstall toPurpose
deepseek-workerCodexWhen and how Codex should delegate to DSH
delegate-to-external-agentDSHWhen DSH should delegate to Codex or Claude Code
mkdir -p ~/.codex/skills && cp -R skills/deepseek-worker ~/.codex/skills/
mkdir -p ~/.dsh/skills  && cp -R skills/delegate-to-external-agent ~/.dsh/skills/

Both are plain SKILL.md files: YAML frontmatter (name + description) plus body. Triggering depends entirely on the description, so they carry bilingual trigger phrases and explicit "don't use" boundaries. Changes take effect without a restart.

Compatibility

  • DSH ^0.2.1-0 (engines.dsh).
  • MCP protocol versions 2025-06-18, 2025-03-26, 2024-11-05; unknown versions fall back to the newest.
  • Requires the public services sessionController, sessions, workspaceRegistry, userQuestions. Missing ones surface as explicit tool errors.

Development

node --test "test/*.test.mjs"
index.js              wiring: apply() + re-exports
src/constants.js      versions, limits, binary candidates, policy text
src/utils.js          JSON-RPC helpers, argument validation, sleep
src/settings.js       defaults and config resolution
src/tools.js          tool schemas
src/policy.js         the standing instruction
src/questions.js      ask_user_question takeover and reporting
src/sessions.js       session reads, waits, model and workspace placement
src/guards.js         depth and rate limits
src/registry.js       worker registry persistence and capacity
src/http.js           loopback server, JSON-RPC, tool dispatch
src/api.js            the bridge API the tools call
src/runners/codex.js  codex exec adapter
src/runners/claude.js claude -p adapter

83 tests: handshake and version negotiation, tool list and argument validation, dispatch call shape, result baselines, long polling, real HTTP round trips (404/405/202 and bearer), codex exec event parsing, binary resolution, success/failure/timeout runs, resume argument split, final-answer-only behaviour, dsh_wait terminal states, slot limits and release, followup rebaselining, registry persistence and restart recovery, maxHops and rate limiting, per-cwd budgets, workspace creation and reuse, queue/steer delivery and waiting, anchors for steered answers, Claude output parsing and resume arguments, assumeDefaults injection, pendingQuestion and dsh_answer.

Related

PluginDescription
dsh-subagent-bridge (this repo)Let Codex drive DSH as a subagent, and DSH drive Codex / Claude Code
dsh-predictPredicts the next instruction as placeholder text; Tab accepts
dsh-input-history↑ / ↓ in the composer to recall previous messages
dsh-quota-meterProvider quota next to the model picker
dsh-provider-iconsProvider logos next to model names

License

MIT © Dogtiti

codexBin
codex
Codex executable; a value with / is resolved strictly
codexSandboxworkspace-writeDefault sandbox for codex_dispatch
codexTimeoutMs300000Per-run timeout
claudeBinclaudeClaude Code executable
claudeModel(empty)Empty means the CLI's own setting
claudeTimeoutMs300000Per-run timeout