Files
deepseek-harness/packages/subagent/subagent-inprocess
Tianyi Cui 6967c584d1 Merge remote-tracking branch 'origin/worktree-agent-scope-design' into codex/package-readme-limitations-audit-20260712
# 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
2026-07-12 23:35:47 +08:00
..

@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:

  1. Validate the parent depth and optional absolute maxDepth, then derive child depth as parent depth plus one.
  2. Call parent.ctx.agents.create directly, passing the required request signal into the factory's creation transaction.
  3. During that transaction's unpublished setup window, install the requested persona, tool restriction, and structured-output runtime.
  4. Publish the child, retain the returned AgentHandle, and drive one task with child.send(prompt) followed by child.whenIdle().
  5. 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_output tool 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/result observer commits a staged value only after that execution's authoritative final tool result succeeds, including the enclosing run_code result for Code Mode sub-dispatch.
  • A monotonic tool guard blocks later calls after capture, and agent/turn-stop ends 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 defineTool schema subset only — unsupported JSON Schema constructs fail before the child is created; a provider needing a broader schema vocabulary requires a different runtime.