@deepseek-ai/dsh-subagent-inprocess
This package is the shared run driver for the two in-process providers. Spawn passes no session seed; fork passes the parent's completed-turn prefix. Everything else—depth, child creation, optional child customization, result reading, cancellation, and disposal—has one implementation here.
Start contract
startInProcessRun(request, options): Promise<SubagentRun> fulfills only after the child is published in ctx.agents. A rejected start has already quiesced the agent factory's unpublished creation transaction, so the caller never receives a half-created handle.
The driver follows this sequence:
- Validate the parent depth and optional absolute
maxDepth, then derive child depth as parent depth plus one. - Call
parent.ctx.agents.createdirectly, passing the required request signal into the factory's creation transaction. - During that transaction's unpublished setup window, install the requested persona, tool restriction, and structured-output runtime.
- Publish the child, retain the returned
AgentHandle, and drive one task withchild.send(prompt)followed bychild.whenIdle(). - Read the child's own last assistant message and terminal turn reason, excluding any fork seed.
The child gets the parent's working-directory/session lineage and inherits the parent model unless request.agentOptions overrides it. It gets a fresh flat registration scope: parent ownership does not import parent tool restrictions or establish an authority subset.
Cancellation and ownership
The required request signal covers both startup and the live run. Before publication, AgentCreationTransaction observes it, rolls back, and rejects. The factory detaches that creation-only listener before returning; the driver immediately checks the signal once more before installing a minimal live-run listener, closing the handoff race. After publication, abort cancels the child.
After fulfillment, the caller owns the run. Provider-plugin unload does not revoke it. dispose() removes the live abort listener, records cancellation, and delegates to the returned AgentHandle.dispose(), whose memoized quiescence transaction stops the loop, removes the agent and session, and unwinds scoped registrations. Cancellation owns every non-completed in-flight outcome and reports aborted; an already-completed turn remains completed.
Spawn and fork inputs
InProcessRunOptions is { seed?: SessionEvent[] }. Spawn omits it. Fork supplies a balanced completed-turn prefix and records its length so the result reader never mistakes a seeded parent message for child output.
depthOf(agent) reads AgentOptions.subagentDepth, treating absence as top-level depth zero and rejecting malformed stored values. SubagentDepthError reports an attempted child depth above maxDepth; an unrepresentable depth above the safe-integer domain is a RangeError.
Structured output
attachStructuredRuntime(childCtx, schema) installs the whole contract in the child's scope:
- A
structured_outputtool registered with the requested schema validates and stages the model's value. - An order-190 system-prompt section tells the child that the tool call is the terminal answer.
- Both contributions are ordinary child-scoped registrations. An expert
system-prompt/assemblelistener may replace them and therefore owns preserving the structured-output protocol for that child. - A
tools/resultobserver commits a staged value only after that execution's authoritative final tool result succeeds, including the enclosingrun_coderesult for Code Mode sub-dispatch. - A monotonic tool guard blocks later calls after capture, and
agent/turn-stopends the turn after the structured result commits.
A clean turn that never commits the required structured value reports error; the driver does not re-prompt. All registrations ride the child fiber and disappear with it.
Model Experience
| Context surface | What the model sees | Token effect |
|---|---|---|
| Child-agent request | The shared driver sends the task as the child's user message and, when requested, composes persona and global-tool restrictions in the unpublished child's fresh scope; parent restrictions are not inherited. Structured runs add a scoped instruction plus structured_output in the visible schema or Code Mode SDK, then stop after a committed capture. Spawn supplies no history; fork supplies its balanced seed. |
Child input is isolated from the parent and grows through the child's own steps. Optional persona, filtering, and structured-output changes affect only that child; structured output adds fixed instruction and capability tokens for the run. |
| Parent result, indirectly | The driver extracts only the child's own last assistant output or captured structured value; seeded parent messages and intermediate child work do not become the result. | The parent receives one data-dependent result through the consumer; all other child tokens stay in the child session. |
Known Limitations and Deferred Work
- Runs expose no
sendMessage/resume— the optional runtime capabilities are absent on in-process runs. - Structured capture accepts the
defineToolschema subset only — unsupported JSON Schema constructs fail before the child is created; a provider needing a broader schema vocabulary requires a different runtime.