The Codex simplification concern plus the duplication comment on the spawn
apply, resolved by deletion: the backend-lifetime holds are gone, so the
runtime registers at the first structured run and disposes when the last
settles — a deployment that never passes outputSchema carries no always-on
global state, and there is no per-backend acquisition block left to extract.
The driver spec now drives an INLINE spawn-shaped provider over
startInProcessRun, which removes the spawn/fork devDependencies (the
test-only workspace cycle); plugin-level structured coverage moves to the
backends' own specs (capture through the shipped plugin, mid-run backend
unload, seeded fork capture). tools.md, the driver README, and both backend
READMEs describe the run-scoped lifetime; the module-graph regenerates
without the cycle edges.
Carved out of #170 per review feedback — the foundation the workflow tool
builds on, now standing alone on master:
- dsh-tools: the structured-output JSON Schema subset (StructuredOutputSchema,
assertSupportedOutputSchema, validateStructuredValue) — rejects loud outside
the enforced subset, listing every violation
- dsh-subagent: SubagentStartRequest.outputSchema / SubagentResult.structured
become a real capability; the service rejects a schema'd request whose
provider lacks it
- dsh-subagent-inprocess: the shared structured runtime — one global
structured_output capture tool, a prepend final-assembly listener that
strips the placeholder for plain agents and swaps in the run's own schema
(plus the calling instruction as a trailing section) for structured
children, an agent/turn-continuation veto once captured, and the
capture/nudge loop in the run driver (structuredNudgeRetries, cancellation
honored mid-nudge); lifetime refcounted by backends and live runs
- subagent-spawn / subagent-fork flip outputSchema: true
One deliberate divergence from the #170 revision: the backends do NOT add
'tools' to their plugin inject. Doing so deferred their apply past the todo
plugin, and the delegation tool mirrors provider lifecycle — so the
model-visible tool order of every existing prompt changed, invalidating every
recorded snapshot fixture. The runtime now gates its capture-tool registration
on tools availability itself (sync when live, a scoped inject fiber when the
Loader starts the backend first), keeping this PR byte-invisible to existing
transcripts: all 35 snapshot scenarios pass against master's fixtures
unchanged.
Master's #36 moved declaration output to lib/types (and types/exports/files
point there). The merge applied that to all pre-existing packages, but the
subagent backends introduced on this stack (subagent-inprocess, subagent-spawn,
subagent-fork) still used the old lib/ layout. Bring them onto the new
convention and add them to the single typecheck tsconfig.json references.
The shared run driver lived inside dsh-subagent-spawn, so the spawn package
carried fork-aware seeding logic and dsh-subagent-fork depended backward on
dsh-subagent-spawn — the two in-process backends were not independent.
Move the driver (startInProcessRun, depthOf, SubagentDepthError,
InProcessRunOptions) into a new pure-library package
@deepseek-ai/dsh-subagent-inprocess that registers nothing. spawn and fork now
both depend only on that driver and neither knows about the other; spawn no
longer re-exports it and fork no longer imports from spawn.
Also wire BOTH backends in examples/coding-agent/cordis.yml (config-only): load
dsh-subagent-spawn + dsh-subagent-fork + two dsh-tool-subagent instances with
distinct toolNames (subagent → spawn, subagent_fork → fork), demonstrating that
exposing multiple transports needs no code change.