DSH Plugin Store
Back to home

PerryLink

dsh-background-agents

Interactive long-session background agents for DeepSeek Harness: start a durable continuable child agent, watch its progress in the Web UI sidebar, message it any time, and interrupt it - all through the official subagent seam.

Stars
1
Language
JavaScript
Created
Aug 14, 2026
Updated
Aug 14, 2026
Web UI
GitHub repo

Introduction

dsh-background-agents

Interactive, long-session background agents for DeepSeek Harness. Start a durable child agent that keeps working while you keep talking 鈥?watch its progress, steer it with messages, and stop it, all without leaving your session.

English涓枃Espa帽olPortugu锚s啶灌た啶ㄠ啶︵

license topic: dsh-plugin topic: dsh

DSH's built-in background jobs are fire-and-forget tool executions: you can read output and kill them, but you cannot talk to them. dsh-background-agents upgrades that to full background agent sessions on the official subagent seam 鈥?a continuable child conversation you can message, steer, and interrupt at any time, while an injected progress line after each of its turns keeps you (and the model) in the loop.

What you get

  • background_agent 鈥?start a durable, continuable child agent from any session. It runs in its own context, returns a stable agent id immediately, and keeps its conversation open forever. Optional per-child scoping: tool_filter (removes tools from the child's view 鈥?never grants new ones), persona (a dedicated system-prompt persona), and max_depth (a delegation-depth cap); childProvider/childModel config route its model requests.
  • bg_message 鈥?send it more work, corrections, or wake a settled agent. Delivered through the official FIFO inbox; the agent's answer is its next turn.
  • bg_list 鈥?status of your agents: label, mode, activity (running / idle / ready / settled / archived), message count, last activity time. Recovers persisted children after a restart. recursive: true lists the whole descendant tree with parentId/depth.
  • bg_result 鈥?fetch a child's latest assistant output text plus its activity, beyond the settled-notice summary.
  • bg_stop 鈥?request interruption of the current turn. Fire-and-return: official teardown finishes the job; the agent stays resumable.
  • autoReport 鈥?after every child turn, one throttled progress line is injected into your session (model-visible, plugin-sourced). Its final outcome arrives via the official settled notice. reportDelivery: wakeup makes each line start a parent turn when the parent is idle.
  • Idle archive 鈥?agents quiet past idleTimeoutMinutes are archived with a notice and a stop request; bg_message wakes them back up.
  • backgroundAgents projection 鈥?a session-projection unit that folds the parent log into dashboard rows (agent id, label, activity, last message summary, created time). Everything reconstructs from the durable log 鈥?no separate database.
  • Web UI panel 鈥?a "Background agents" entry in the Web GUI sidebar with live status, one-click jump into the child session, a stop button, and a message button that queues a new turn through the official subagent.prompt RPC.

Quick start

# from the harness checkout or wherever the dsh CLI lives (web or headless)
dsh plugin --profile <name> add "github:PerryLink/dsh-background-agents#v0.3.0"

The bundle patch carries the plugin row, so dsh plugin add composes it into your profile's layer stack (dsh.profile.bundles). Prefer the git source with a pinned ref: the repo commits its build output (lib/), so git installs need no build step and no allowBuilds entry. The package is also published to npm — plain pnpm add dsh-background-agents works (CI publishes every tag push).

The row that lands in your profile (override config per profile in cordis.patch.yml):

- insert:
    - id: background-agents
      name: dsh-background-agents
      config:
        provider: spawn        # the ctx.subagents provider for continuable children

The plugin needs the subagent spine already mounted (any profile built on @deepseek-ai/dsh-base has it: dsh-subagent, dsh-subagent-spawn-in-process, dsh-session-projection).

Then, in any session, just ask the model 鈥?or call the tools directly:

background_agent "watch the repo for test failures and keep me posted" (label: test-watch)
bg_list
bg_message <agentId> "also check the snapshot tests now"
bg_stop <agentId>

Configuration

Every tunable is a validated Config field 鈥?change it in cordis.yml, never in code.

FieldDefaultMeaning
provider(required)ctx.subagents provider name for continuable starts (spawn)
autoReporttrueinject one progress line into the parent after each child turn
reportDeliveryquietquiet appends the line to the parent's next model request; wakeup starts a parent turn when idle (queues when busy)
reportThrottleMs15000minimum gap between two progress injections for one child
reportSummaryMaxChars300hard cap on the injected progress-line text (ellipsized)
resultMaxChars4000hard cap on the bg_result text (ellipsized, flagged truncated)
maxBackgroundAgents4hard cap on non-archived background agents per parent session; the budget is shared by every continuable direct child of the session (including ones the built-in subagent tool started)
idleTimeoutMinutes120idle window after which a quiet child is archived and notified (>= 1)
idleSweepIntervalMs60000archive sweep period
maxLabelChars120display-label cap (ellipsized)
childProvider(inherit)provider route for child model requests
childModel(inherit)model id for child model requests
maxChildDepth(none)config ceiling for a start's max_depth argument
allowedChildTools(none)allowlist for tool_filter names; empty/absent = no limit

How it works 鈥?and why it survives restarts

Everything rides the official subagent seam: startContinuable, followup, interrupt, listChildren 鈥?the plugin performs no lifecycle routing of its own, never touches another session's Agent, and never kills a process tree (stop = request interruption, teardown belongs to the continuation manager).

The plugin writes every fact through one structured channel and one model-visible channel:

  • background-agents/fact structured fact events (v0.3.0+) 鈥?the registered / message / stop / progress / archived facts, appended to the parent log as log-only records with the envelope's ignorable: true marker; readers that do not know the type skip the records instead of refusing the log, so older harness builds and older plugin versions still open parents written by this one;
  • tool/result replay metadata 鈥?the same facts in logs written before v0.3.0 (folded only while a row has no structured provenance);
  • injected user/message notices (model-visible), source { kind: 'plugin', plugin: 'dsh-background-agents' } 鈥?the throttled progress lines and archive notices (canonical `[background-agent ] 鈥 prefix);
  • the official subagent-settled notice 鈥?the child's durable "settled" fact.

The backgroundAgents projection unit folds the structured channel and keeps the legacy folds for pre-v0.3.0 logs (a row switches to structured provenance on its first fact, so a dual-channel log never double-counts). The dashboard value and bg_list facts reconstruct on every reopen without parsing human-readable notice text. When the catalog itself is unavailable (projections or session store missing), bg_list returns an explicit unrecoverable marker 鈥?it never fabricates an empty list.

Not this plugin

ProjectWhat it doesThe boundary
titanwings/dsh-automationScheduled coding tasks in fresh agent sessionsIt owns when tasks run (scheduling). This plugin owns interactive steering of one long-lived conversation 鈥?no scheduler seam, no cron.
vlln/dsh-task-statusStatus bar for background jobs (progress + output tail)It displays tool-level jobs. This plugin creates and steers agent sessions; its dashboard is one panel of it, not the product.
YYTbit/dsh-plugin-agent-dashboardMulti-agent dashboard skillDisplay-oriented. This plugin's rows are actionable: jump into the child session, send messages, stop 鈥?through the official control plane.

How this relates to the built-in subagent tools

The harness core ships its own subagent tools (subagent, send_message, interrupt_agent, and the child-side report tool). This plugin's bg_* tools are their session-scoped companions; both can be mounted together:

Built-in toolThis pluginDifference
subagent (backgroundMode: 'continuable')background_agentSame startContinuable seam; this plugin adds per-child tool_filter/persona/max_depth validation and the per-session cap
send_messagebg_messageSame delivery semantics; bg_message addresses this conversation's background agents and maintains the projection facts
interrupt_agentbg_stopSame interrupt semantics; bg_stop also records a structured stop fact
child-side report toolautoReportThe built-in is called by the child model itself; this plugin injects throttled progress after every child turn automatically

What the core tools lack: bg_list, bg_result, idle archiving, and the per-parent folded panel projection.

Not in scope: scheduled triggering (the schedule seam exists), cross-machine/remote agents, and any change to the official subagent activation contract.

Development

pnpm install        # tooling only; harness packages resolve against a sibling checkout
pnpm run typecheck  # strict TS, node + client programs
pnpm test           # 69 unit + end-to-end tests (real subagent seam, scripted LLM, jsdom panel)
pnpm run build      # lib/index.js (node half) + lib/client.js (web client bundle)
pnpm run gen-aliases  # re-map harness package paths after the checkout moves

A keyless end-to-end demo drives a real parent session and a background child through a deterministic scripted LLM (no API key; dev/ is gitignored 鈥?adapt the paths to your checkout):

$env:DSH_HOME = 'D:/deepseek-harness/Project/Plugins/dsh-background-agents/dev/dsh-home'
pnpm dsh --profile headless --patch dev/cordis.yml "銆愮埗浼氳瘽銆戦┍鍔ㄥ悗鍙?agent 婕旂ず"

The test suite covers the full path 鈥?start, list, message, stop 鈥?against the real SubagentRuntime with the in-process spawn provider and a scripted adapter, plus throttle/cap/archive policy, projection folding, and crash recovery through session-persistence-jsonl.

License

Apache License 2.0 鈥?see LICENSE. Third-party notices: THIRD_PARTY_NOTICES.md.