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.

Plugin Durable Subagent — DSH Plugin for DeepSeek Harness
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
P

dsh-plugin-durable-subagent

Plugin Durable Subagent

Durable subagent execution for DeepSeek Harness: registers a `durable` subagent provider and a model-facing tool that drive pi-durable-subagents, so a crashed delegation converges on its first outcome instead of running twice.

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

npx -y @deepseek-ai/dsh plugin --profile web add github:Tim-ReJet/dsh-plugin-durable-subagent#42925463575a13f0b39d4dd94d91f19393e47fbe
READMECompatibilityVersions

Compatibility and provenance

Plugin Durable Subagent is published as dsh-plugin-durable-subagent and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
github
Registry updated
10/13/2026

Versions

0.1.0stable
10/13/2026

Related plugins

Loading related plugins…

Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
any
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/14/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.Hindsight Coding Agents@vectorize-io/hindsight-coding-agentsReflect-only Hindsight long-term memory for coding agents (harness-pluggable: opencode, …), with automatic background ingestion (no setup CLI).

README

dsh-plugin-durable-subagent

Durable subagent execution for DeepSeek Harness. It registers a durable provider on ctx.subagents and a model-facing durable_subagent tool, both driving pi-durable-subagents.

The point of the pairing is one guarantee: a delegation the harness accepted before a crash is read back, not run again.

Where the guarantee comes from

DeepSeek Harness durably records execution intent and does not promise exactly-once effects. Its session checkpoint policy states the convention this plugin implements:

"The policy durably records execution intent, not generic exactly-once effects. Side-effecting tools should forward exec.callId as an idempotency key when their provider supports one." — @deepseek-ai/dsh-session-checkpoint-policy

pi-durable-subagents accepts exactly that key as --request <id>: the first submission under an id is decided once, and every later retry carrying the same content gets that first outcome. So the harness supplies durable intent plus the key, and the durable orchestrator supplies the single decision.

The join is a read-before-write state machine. Every attempt reads describe --key <id> first and only submits when the id is undecided, so a process that died between deciding a submission and reporting it leaves a durable decision that the next attempt reads instead of executing. The keyless test suite pins that path; the traced CLI invocation sequence is describe → run → describe with one execution recorded.

Install

dsh plugin --profile web add dsh-plugin-durable-subagent

The bundle patch inserts one row, durable-subagent. It requires pi-durable-subagents on PATH (npm i -g pi-durable-subagents), or a command that names it; without one, every start fails with a message saying so rather than retrying.

Config

FieldDefaultMeaning
providerdurableRegistry name on ctx.subagents.
commandpi-durable-subagentsExecutable, argv[0] only.
commandArgs[]Arguments before the subcommand, for a launcher rather than a bare executable.
home—DSA_HOME for every invocation.
agentworkerThe pi agent definition a delegation runs as unless the call names another.
cwdthe harness process's working directoryWorking directory of every invocation. A run's cwd is part of the content the CLI hashes.
idempotencyuniqueHow the provider mints request ids: unique (one per start) or content (one per identical delegation). The tool always uses the call identity.
attempts3Submission attempts before a start fails.
settleTimeoutMs600000How long a run handle waits for the durable verdict.
pollIntervalMs1000Poll interval while waiting.
timeoutMs120000Per-invocation deadline for the CLI.
enableTooltrueRegister the model-facing tool.
tooldurable_subagentModel-facing tool name.

How a call becomes a durable request

  1. The harness checks the call's intent durably, then runs the tool body with exec.callId.
  2. The tool derives a request id from the calling session id and exec.callId, so a replay of the same call re-derives the same id.
  3. The plugin reads the id's durable state. Only absent and pending proceed to run; running and asking return as they are, and sealed, rejected, and pruned are terminal.
  4. run --request <id> --spec - --cwd <cwd> --json either decides the request, returns the first outcome, or reports 75 (not decided yet), in which case the next attempt reads the durable state again before resubmitting.

The provider path reaches ctx.subagents instead, where no call identity is available (ResolvedSubagentStartRequest carries none). It therefore mints ids from configuration: unique never conflicts but is not replay-stable, content is replay-stable but two identical delegations from one session collide as request-conflict. Pick content only when the delegations are content-distinct.

Model Experience

The tool

durable_subagent takes action (run, status, answer, steer, notify, stop), plus task, agent, to, message, qid, and rev as each action needs.

run returns as soon as the delegation is decided, not when its work finishes, and reports sealed, running, pending, or asking with the request address. status reads that address again; an asking state carries the question id and revision that answer needs. Nothing is polled on the model's behalf, so a long delegation costs no tokens while it runs.

Token effect

A run reply is one short line. Reading a finished delegation returns its output; reading a running one returns a state name. No child transcript enters the parent's history.

KV cache effect

Tool calls append after the reusable history prefix. Nothing here rewrites earlier messages.

Known Limitations and Deferred Work

  • A linked plugin resolves its imports from its own repository. dsh plugin add <path> records a link:, and Node follows the symlink to the plugin's real path, so @deepseek-ai/* is resolved from the plugin directory and never from the profile. The local install must therefore carry a complete closure: npm install resolves this package's harness peers, while npm install --legacy-peer-deps leaves @deepseek-ai/dsh-tools without the nine packages it declares as peers, and the profile then fails to boot with failed to import loader entry durable-subagent (dsh-plugin-durable-subagent): Cannot find package '@deepseek-ai/dsh-scope'. A published install from npm does not have this problem.
  • cwd is part of the submission digest. A retry from a different working directory is a request-conflict rather than the first outcome. The default is stable for the life of the harness process; a per-session cwd is not yet wired.
  • The provider cannot be replay-stable. ResolvedSubagentStartRequest carries no call identity, so only the tool path gets one execution per call. Making the provider path exact needs a durable run identity on the request; that is a harness change.
  • dispose() stops a run but does not await quiescence. The orchestrator is a separate process and owns its own teardown, so the run handle cannot observe the child's exit.
  • No continuable children and no Agent Teams membership. prepareContinuable is deliberately not implemented: spawn_teammate and cold resume need a durable child session this provider does not create.
  • No tool filtering, persona, or delegation depth. The provider reports outputSchema only; a request needing another capability is rejected loudly by the subagent service rather than silently ignored.
  • outputSchema validation is delegated. pi-durable-subagents validates a report against the schema it was handed before sealing, so a sealed data payload is schema-valid; the plugin does not re-validate it.
  • The orchestrator is not supervised. The plugin neither starts nor restarts pi-durable-subagents; it relies on the orchestrator's own lazy start, lock, and idle exit. Running it under ctx.subprocess would make harness disposal kill it.
  • A failure is per call. A workflow reports status: "done" even when every call inside it failed, and / live on the calls. reads them there; the read-only view alone would call a failed delegation completed.

Verification

Phase 0 exercised the real pi-durable-subagents 1.0.39 CLI against a real orchestrator, and mounted the bundle into a real DeepSeek Harness profile.

CheckResult
npm run typecheck against published @deepseek-ai/dsh-subagent/dsh-tools 0.1.1-rc.2, cordis 4.x, schemastery 3.xclean
node --test keyless suite (fixture CLI, no harness and no pi)32 passed
dsh-plugin-doctor10 passed, 0 errors; remaining items are a new repository's age and remote
npm run build + npm pack --dry-runemits lib/*.js + lib/types/*.d.ts; 22 files
describe --key <id> --json reply shapesconfirmed against the real CLI; payloads pinned in test/fixtures/real-replies.json
converge() against the real CLI, after a "crashed" caller left a durable decisionone read, zero submissions; the retry returned the first wid
Identical resubmission under one idexit 0 with the same wid and created: false
Same id, different contentexit 3 request-conflict, state echoed
Bundle installed with dsh plugin --profile web add, composed treerow durable-subagent present with its config
Headless boot with the plugin mountedloader entries activate; the run stops at MISSING_CREDENTIAL, not at the plugin
A model-driven durable_subagent call in a live sessionverified end to end: run → status ×2 → sealed, addressed by request
The same call driven by the wid instead of the request idreturns absent; the render now leads with request= for this reason
A real pi 1.0.2 child completing a model callnot verified: the throwaway pi had no hydrated model catalog and reported Unknown provider: unknown
An orchestrator crash between decision and executionnot verified; the crash window tested is the caller's

Phase 0 evidence

Submitting one id twice against the real CLI:

$ pi-durable-subagents run --request spike-1 --spec spec.json --json
{"request":"spike-1","wid":"01M4THR3CMT54R65BASRA9QX3V","created":true,"spec_digest":"3a8fdf42…"}
$ pi-durable-subagents run --request spike-1 --spec spec.json --json
{"request":"spike-1","wid":"01M4THR3CMT54R65BASRA9QX3V","created":false,"spec_digest":"3a8fdf42…"}

Driving this plugin's own converge() at the real CLI through a shim that logs every invocation, where the first caller submitted and then "died" without reading its reply:

CLI invocations: run -> describe -> describe -> run -> describe -> describe -> run
converge after the crash: running (wid 01M4THR3CMT54R65BASRA9QX3V)

The three run calls are the crashed caller, one fresh-id convergence, and a deliberate conflicting probe. converge issued none of them for the already-decided id.

A workflow whose only call failed reports status: "done":

$ pi-durable-subagents describe --key spike-1 --json
{"state":"sealed","status":"done","calls":[{"status":"failed","ok":false,"error":"lost ×5","output":""}]}

This is why summarize() reads success per call. The run cost nothing: usage was 0 in and 0 out.

Phase 0b evidence

A real DeepSeek Harness headless session, with the bundle mounted and a real pi-durable-subagents 1.0.39 CLI on PATH, was asked to delegate one task. Its own report:

- Request id: df9631829b6-call-00-xnXLGCeOhmS8M4PETs0m0219-e847ef97bab53919
- Final state: sealed
- Succeeded: No — the delegation failed.
- Output / error (verbatim):
  sealed: request=df9631829b6-…-e847ef97bab53919 (wid 01M4TJKGB6Q3ZX7XNCKW5DX2PG) —
  failed: lost ×5; last error: Unknown provider: unknown

Three things came out of this run. The handle is addressable: the model read request= from the render and every status call resolved, where the first attempt (which rendered only the wid) got absent four times. The call-level failure mapping works in production: a workflow whose only call died is reported as failed with the child's own error, not as a completed delegation with empty output. And the request id is now on-grammar — the tool call id was call_00_zkMrBAcVpchoWOd2TzA93108, whose underscores are outside [A-Za-z0-9][A-Za-z0-9._:-]*.

The child itself never reached a model: the throwaway pi 1.0.2 installation had no hydrated model catalog, so it started as unknown/unknown. pi auth check --provider deepseek reported invalid_state both before and after DEEPSEEK_API_KEY was supplied from the harness credential store, which is a property of that fresh installation rather than of this plugin.

License

MIT. pi-durable-subagents is MIT, © purboo, and adapts parts of pi-subagents (MIT, © Nico Bailon), which this plugin neither vendors nor redistributes. DeepSeek Harness is MIT.

output
error
summarize()
status