# Conflicts: # packages/core/scope/README.md # packages/session-persistence/session-persistence-jsonl/README.md # packages/session-persistence/session-persistence-sqlite/README.md # packages/subagent/subagent-acp/README.md # packages/subagent/subagent-fork/README.md # packages/subagent/subagent-inprocess/README.md # packages/subagent/subagent/README.md # packages/subagent/tool-subagent/README.md # packages/support/invariants/README.md # packages/support/subagent-mock/README.md # packages/workflow/workflow-workerthread/README.md # packages/workflow/workflow/README.md
@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 use
ownerFinal: true, so their owners control their final presence after prompt/tool assembly while unrelated contributions remain extensible. - 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.
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.