@argszero/cordis-plugin-subagent-wait
v0.1.1
Published
wait_for_agent for the dsh harness: block until a continuable background subagent settles (via the subagent-settled notice) instead of polling list_agents and misreading idle.
Maintainers
Readme
cordis-plugin-subagent-wait
wait_for_agent for the DeepSeek Harness (dsh).
A thin orchestration helper for continuable background subagents: it blocks — under a bounded timeout —
until a specific continuable child is terminal, instead of forcing the parent to re-derive completion by
polling list_agents (whose idle status collapses "between turns" and "waiting on its own children").
Why
list_agents reports a child's status as running | idle | ready. But idle is ambiguous:
- an
idlechild may simply be between turns; - an
idlechild may be blocked-waiting on its own grandchildren — and is not terminal.
So a parent that polls list_agents and treats status !== 'running' as "done" can hang on a child that is
actually waiting on its own descendants. status === 'ready' (only in storage) is also not a terminal
result to collect — it means "resumable, not finished."
wait_for_agent solves this by keying on the settlement signal, not a status read: the harness's
continuation manager publishes a subagent-settled notice (a typed message source, form: notice, carrying
the child's senderSessionId) into the parent agent's inbox exactly when the child settles and is
disposed. This plugin binds to that signal (via the agent/inbox/inserted event) and falls back to a bounded
registry/status read, so a blocked-waiting child is never mistaken for terminal.
Install
npm install @argszero/cordis-plugin-subagent-waitMount the plugin into your dsh profile (it registers the wait_for_agent tool):
# cordis.yml
plugins:
"@argszero/cordis-plugin-subagent-wait": {}Requires the subagent continuation runtime (
@deepseek-ai/dsh-subagentis a peer dependency). The tool is a thin model-facing adapter overctx.subagentsand the live Agent registry.
Version pin. The
@deepseek-ai/dsh-*latestdist-tag is frozen at an old0.0.1-rc.1; the current harness line is published undernext(0.1.2-rc.1). This plugin's peer range (>=0.1.0) resolves tonextand excludes the stalelatest. If you install by tag, prefer:npm install @argszero/cordis-plugin-subagent-wait @deepseek-ai/dsh-tools@next @deepseek-ai/dsh-subagent@next
Usage
After starting a continuable background subagent, call wait_for_agent to block until it settles:
wait_for_agent agent_id="<child session id>" timeout_ms=30000Returns:
{ "settled": true, "status": "settled", "agent_id": "<child session id>" }When the timeout lapses (and the child is still alive), settled is false and status reflects the
observed runtime status (running / idle / ready). A false result means "the child may still be
running" — not "the child failed."
Parameters
| param | type | default | description |
|-------|------|---------|-------------|
| agent_id | string | — (required) | The durable session id of your direct continuable child. |
| timeout_ms | number | 30000 | Max milliseconds to block. Capped at 300000. |
Exported helper
The same logic is available as a parent-side helper for other plugins:
import { waitForAgent } from '@argszero/cordis-plugin-subagent-wait'
import type { Context } from '@deepseek-ai/cordis'
const { settled, status } = await waitForAgent(ctx, parentAgent, childId, {
signal, // caller cancellation
timeoutMs: 30_000, // bounded wait (default 30s)
})
// settled: true → the child disposed (terminal)
// status: 'idle' → timeout, child alive but between turns (NOT terminal)Design notes
- Keyed on settlement, not status. The wait binds to the
subagent-settlednotice carrying the child id, and re-checks registry terminality (disposal) as a fallback. Anidlechild waiting on its own grandchildren never satisfies the wait. - Timeout is mandatory. A model-facing tool must never be left awaiting a child that never settles, so the wait is always bounded.
- Cooperative cancellation. The wait observes the tool's
signaland resolves promptly on abort. - Composes public surface only. It widens no core interface: it consumes
agent/inbox/inserted(a registered typed event),ctx.subagents.listChildren(), and the live Agent registry. The corewait_for_agentprimitive, if upstream adopts one, stays a separate harness concern.
A note on the peer range
>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0Every dsh release published today is a prerelease, and a semver comparator admits
prereleases only when they share its own major.minor.patch tuple. That makes two forms
wrong, and both have to be avoided:
// Matches nothing at all: 0.1.2-rc.1 sorts below 0.1.2, and every other
// prerelease has a different tuple. -> ETARGET, the package cannot be installed.
">=0.1.2"
// Only 0.1.2-rc.1: a user on the 0.1.5 line gets ERESOLVE.
">=0.1.2-rc.1 <0.2.0"
// What we ship: one comparator per supported tuple line.
">=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0"The npm latest tag for @deepseek-ai/dsh is on the 0.1.2 line while next/alpha
point at 0.1.5, so both comparators are needed. test/peer-range.test.mjs fails if
either bad form comes back.
License
MIT
