// docs / using itmirrored from packages/coding-agent/docs/subagents.md

Subagents

Orphus bundles @orphus/subagents, an extension for bounded specialist delegation with separate context while the parent remains in control. Use a single agent, chain, or parallel fan-out when isolation or a specialist pass materially helps with locating code, analyzing behavior, researching references, reproducing actual failures, or simplifying code. Keep interactive, exploratory, conceptual, and conversation-led work inline when direct user steering is more useful.

You do not need to install anything separately when you use @orphus/coding-agent.

Start with natural language

Ask Orphus to coordinate subagents in plain language:

Map the authentication flow with focused subagents before we change it.
Run a parallel review composition: one pass for current behavior, one for failure modes, and one for existing patterns.
Research the upstream library behavior online, then compare it with our local implementation.

Orphus decides whether delegation adds value, which specialist fits each bounded part, and whether the work should run as a single child, parallel group, chain, foreground run, or selective background run. Multiple steps, files, tests, validation, or parallelism alone do not require a workflow; clearly delegated long-running autonomous work that needs durable stages, checkpoints, resumability, HIL, gates, retries, or loops is usually better served by a workflow.

Subagent execution is non-interactive

Supported subagent launches start immediately without opening a preview/editor prompt or waiting for terminal input. This applies to single, parallel, chain, foreground, background, fanout, prompt-template, and human-entered /run, /chain, /parallel, and /run-chain execution. Ask any necessary questions in the parent conversation before delegating.

The human slash commands remain registered and continue to use their separate parsing and event-bridge path, including background and fork flags.

Subagents now run and return their results directly. Orphus does not infer acceptance gates from prompt wording, inject acceptance-report instructions into child prompts, parse or strip acceptance-report blocks, or reject completed child runs because changed-file, test, or review evidence is missing. Put any evidence or validation requirements directly in the task text you give the parent or child agent.

Foreground supervisor coordination

When a foreground child sends intercom.ask, intercom.send, or contact_supervisor coordination, Orphus first probes for the exact foreground owner. Only an exact live child reserves the request; Orphus then sends a generation-scoped detach commit and waits for that child to acknowledge it before placing the message in the parent’s model-visible steering queue. This first-refusal ordering also applies when the parent is a busy workflow stage: detach completes before the request enters the stage AgentSession generation boundary, breaking the child-waits-for-reply / stage-waits-for-child cycle. Unmatched and background-child messages retain existing routing—ordinary parents queue until idle, while open workflow stages fall back to their native generation admission. Blocking need_decision and interview_request calls remain actionable through Intercom’s pending/reply tracker, and the exact threaded reply resumes the retained child without delayed duplicate delivery.

Only the matching foreground child can authorize release of the parent subagent tool. For a parallel foreground group, that accepted commit releases foreground supervision for every active sibling as one unit, so a long-running sibling cannot keep a blocking child request trapped behind the aggregate tool call; tasks still waiting behind the concurrency limit are skipped and never launched unsupervised. Children are in-process AgentSession instances governed by the shared Rust control plane: there is no child OS process, idle watchdog, stdout drain, or detached placeholder to recover. A detached call becomes continued through continue_in_background; its canonical child remains live in the jobs widget and later delivers one terminal result. Fire-and-forget intercom.send and progress_update also release foreground supervision promptly, but do not create a reply waiter.

Blocking coordination is race-safe: a session holds at most one outbound reply waiter, and concurrent blocking requests (parallel intercom.ask calls, or intercom.ask racing contact_supervisor) settle atomically. One request wins the reservation; every other concurrent call returns a normal “Already waiting for a reply” tool error without crashing the agent process or disturbing the pending ask. Cancellation and send failures release only their own waiter, and threaded replies still resolve the exact winning request.

Subagent result announcements are also resilient in sessions that never receive an extension session_start (for example non-interactive in-process child sessions): the lazy Intercom runtime initializes from the most recent turn/tool lifecycle context and delivers self-addressed results locally. If no context is available at all, the relay acknowledges the announcement as undelivered — the subagent tool then falls back to returning results inline — instead of recording connection errors in the session transcript.

Intercom connection remains tool-driven. Foreground and background launches do not import the heavy Intercom runtime or connect either the parent or bridged child automatically. If live child-to-parent coordination is needed, the parent model should invoke intercom({ action: "status" }) before launch; the child then connects on its first contact_supervisor or intercom call. Cancellation or session replacement still invalidates the handshake generation, so stale acknowledgements cannot surface or detach a child.

Orphus’s implementation adapts the prompt foreground release and later-result recovery contracts proven in nicobailon/pi-subagents commits 1b55c8c, 589e51e, 68fb528, and 9dfe3df; it retains Atomic’s broker and raw-TypeScript architecture rather than copying upstream’s filesystem transport.

Migration from acceptance gates

If you have older subagent calls, saved chains, or custom agents that used the removed gate fields:

  • Remove acceptance properties from subagent() calls, tasks entries, chain steps, static parallel task items, and dynamic fanout parallel templates. Orphus no longer reads these fields; JSON chain rewrites drop legacy copies.
  • Remove completionGuard: false from agent frontmatter and custom agent definitions. The no-mutation completion guard no longer exists, so the override has no effect and management rewrites strip it.
  • Move validation, command, evidence, review, or residual-risk requirements into the natural-language task text passed to the parent or child agent.

Bundled agents

Orphus currently bundles these agents from @orphus/subagents:

Agent Use it for Edit files?
codebase-locator Find relevant files, directories, tests, configs, and docs for a topic. No
codebase-analyzer Explain how specific code works and trace data flow with file references. No
codebase-pattern-finder Find similar implementations, conventions, and test examples to model after. No
codebase-research-locator Locate prior research/ and specs/ documents related to the task. No
codebase-research-analyzer Extract decisions, constraints, and still-relevant conclusions from prior local docs. No
codebase-online-researcher Research official docs, ecosystem behavior, and open-source source references online; it may persist reusable research notes. Research notes only
debugger Reproduce a concrete failure, prove its root cause, apply the smallest in-scope fix, and rerun the failing scenario. Yes
code-simplifier Simplify recently changed code under its behavior-preservation “doors” rubric. Yes
worker Implement an approved task or handoff, validate the narrow change, and escalate product, architecture, or scope decisions to its supervisor. Yes

The bundled definitions keep their routing and model frontmatter but use compact, outcome-first bodies: role and goal, success criteria, constraints and tool routes, output contract, and stop rules where applicable. Report-producing agents ground progress claims in tool results and return concise evidence rather than narrating internal reasoning. Read-oriented agents inspect and report. debugger, code-simplifier, and worker can edit files, so give them an explicit scope and validation target. The debugger should finish an in-scope diagnosis by applying and validating the fix, not stop at a proposed patch.

Review compositions

Orphus does not bundle a single generic review agent. Instead, compose specialists with distinct angles and let the parent session synthesize their findings before applying any fix.

Common review angles:

Angle Specialist pattern
Current behavior and regressions codebase-analyzer inspects the changed flow and cites file/line evidence.
Failure modes debugger runs in inspect-only mode to reproduce or reason about likely failures without editing.
Fit with project conventions codebase-pattern-finder compares the patch with existing local examples.
Prior decisions codebase-research-locator finds relevant docs, then codebase-research-analyzer extracts applicable constraints.
External API or library conformance codebase-online-researcher checks authoritative sources and version-specific behavior.

Example request:

Review the current diff with fresh-context specialists: analyze correctness, inspect failure modes without editing, and compare the implementation to existing patterns. Synthesize only issues worth fixing now.

Useful prompt templates include /parallel-review, /review-loop, /parallel-research, /parallel-context-build, /parallel-handoff-plan, and /parallel-cleanup. Treat them as reusable compositions, not as separate bundled agent names. Their task templates define the requested outcome, evidence and delegation boundaries, downstream output shape, and an explicit stop rule; preserve those contracts when adapting a template.

Background work and control

Foreground subagents stream progress in the conversation and are the right default when the parent needs the result before proceeding. Use background subagents selectively for genuinely long-running or independently useful bounded delegation; they keep working after control returns and report completion later.

Natural-language examples:

Run the local research scan in the background.
Show me the current async subagent runs.

Tool examples:

subagent({ agent: "codebase-analyzer", task: "Trace the auth flow with file references.", async: true })
subagent({ action: "status" })
subagent({ action: "status", id: "<run-id>" })
subagent({ action: "interrupt", id: "<run-id>" })
subagent({ action: "resume", id: "<run-id>", message: "continue with the test failures" })
subagent({ action: "doctor" })

Use interrupt when you want a resumable stop. Use resume to send a follow-up to a reachable child, or to cold-reload a completed child from its saved session. Use doctor for read-only setup diagnostics.

async: true means do not wait. Orphus admits an in-process child, returns its canonical child path immediately, and tracks the live child through the jobs widget. Intercom detach uses this exact same continuation mechanism, so a foreground child that asks its supervisor to coordinate becomes continued instead of returning a detached placeholder.

async: true does not survive parent exit. The live child is owned by the parent process; quitting Orphus ends any in-flight async run. Only the persisted canonical identity and session file survive, and a later session can list that cold identity and resume it.

Status, interrupt, list, and resume use the Rust registry and status watch for live children; terminal delivery is an in-memory bounded envelope with the artifact and run-history record persisted once. There is no PID polling, result-claim file, stale-run reconciliation, or detached runner process.

Inside workflow stages, completion delivery observes the stage generation boundary. A completion received before the boundary closes is queued through the stage AgentSession and processed before the stage publishes its terminal snapshot. A completion that arrives after close is routed once to the parent/main chat and cannot reopen or append to the completed stage transcript. Producers that are still running do not hold the stage open, so background work remains non-blocking; explicit post-mortem stage chat is still available separately.

The below-editor ORPHUS HARNESS · workers live widget is the live summary for async subagents. It is visible in the normal chat view; when a fullscreen workflow graph is open, hide or leave the graph to return to that widget. Parallel background rows are labelled as Worker N/M so the operator can see who is currently moving, which model/thinking profile it is using, and which tool or activity is live.

Orchestrator model and group policy

Orphus applies the same delegation policy to any parent chat or workflow stage that orchestrates subagents. A named agent uses the model and fallback chain declared by its agent definition, so the orchestrator normally omits the subagent tool’s explicit model argument. An override needs either the user’s exact model request or a documented task-specific reason recorded before launch; model diversity alone is not enough.

If an agent declares no model or fallback policy, the orchestrator consults the role guidance in Model selection, then calls workflow({ action: "models" }) when that tool is available. It may pin only a returned fullId and may add a thinking suffix only when the model entry lists that level. When the catalog tool is unavailable, the catalog is empty, or no recommended model is present, the child stays unpinned and the orchestrator reports the limit instead of inventing a model or inspecting credentials.

cheapestFirst: true on a single call or a parallel task starts that agent’s ladder at its cheapest priced rung instead of the declared primary, and escalates on failure exactly as before. The ladder still decides which models are acceptable; the flag only moves where the walk begins. Price is the registry’s per-million rate for each model, blended 3:1 input to output (agentic turns re-read far more than they emit); models the registry does not price keep their declared order after every priced one, because unknown is not free. This is the fleet model ladder’s “try a spinner first” rule executed by the runtime rather than by hand, and it pairs with handoff: a precise brief is what lets the cheap rung succeed.

Each workflow invocation automatically receives one stable, non-"default" Intercom group as typed admission policy. Its stages and delegated children carry that group across single, parallel, chain, async, and follow-up work unless a call explicitly overrides group. Outside workflows, children inherit the launching session’s resolved group. This isolates workflow runs from unrelated runs and the main chat while contact_supervisor retains its authorized cross-group route.

Context and execution modes

Subagents can run with fresh or forked context:

  • context: "fresh" starts a separate in-process child session with only the task and selected agent context.
  • context: "fork" creates a real branched child session from the parent session leaf. It fails fast if the parent session cannot be forked; it does not silently downgrade to fresh context.

For adversarial review or research, prefer fresh context so the specialist inspects the repository directly. Use forked context when a writer needs the parent conversation history in a separate branch.

Between those two sits handoff: a key→value object of small facts the parent already knows — the decision of record, the files in scope, the acceptance criterion — rendered at the top of the child’s task. It is the third way to give a child context, and the one to reach for when a cheaper model should do the work without the parent’s whole transcript:

Channel The child gets Bound
context: "fork" the parent’s entire session history none — the whole transcript
reads: [...] a [Read from: …] pointer; it reads the files itself the files, at the child’s own tool-call cost
handoff: { … } the facts, inline, in the order given ~2000 characters, enforced by the runtime

The bound is the same boundedRender tiering the room digest uses: keys render in the parent’s order until the budget is spent, then degrade to one line each, and whatever is left is named in a marker (…(+3 keys not shown: alpha, beta, gamma)) rather than silently dropped. Values over 600 characters are cut with a …(+N chars) mark. The render is labelled asserted by the orchestrator, not verified, and not exhaustive, because bounding a handoff’s size does not bound its truth — a child reasoning from the parent’s summary as if it were complete is the failure this label exists to prevent. Large content belongs in a file passed through reads. Non-string values are rejected before the child starts. handoff is accepted on single calls and on parallel tasks[] items; chain steps already have {outputs.name} for the same job.

subagent({
  agent: "worker",
  context: "fresh",
  cheapestFirst: true,
  handoff: {
    decision: "Bound the handoff through boundedRender; do not add a second budget implementation.",
    files: "packages/subagents/src/shared/settings.ts, test/unit/subagents-handoff.test.ts",
    done_when: "npx vitest --run --project unit test/unit/subagents-handoff.test.ts passes",
  },
  task: "Implement the handoff renderer described above.",
})

For parallel implementation work, worktree: true can give each child an isolated git worktree so concurrent edits do not clobber each other.

Fresh child sessions use normal Orphus package discovery when an agent omits extensions, so bundled lightweight MCP, web-access, and Intercom wrappers are available just as they are in the parent. An explicit extensions field (including an empty list) intentionally switches the child to extension-allowlist mode and excludes unlisted builtins; it does not inherit the parent’s normal discovery set.

Top-level parallel calls support up to 50 subagents after expanding each task’s optional count. The extension’s parallel.maxTasks setting defaults to 50 and can enforce a lower task limit; parallel.concurrency independently controls how many of those children run at once, while the Rust turn limiter admits at most four running turns per parent.

Subagent tasks, parallel/chain items, and the top-level call accept a group field that sets the spawned child’s Intercom home group, so same-group subagents can intercom each other while staying isolated from other groups. A named string joins that group; true auto-generates one shared UUID group per parallel set. Precedence is explicit subagent group > inherited current-session group > config > "default". Workflow stages carry their runtime-owned invocation group, so children launched without group automatically join the workflow group; callers do not need to copy or generate an ID. In other sessions, omission inherits that launching session’s resolved group. The child group is applied only when the child has Intercom access (the peer intercom tool or subagent-only contact_supervisor tool); a child without Intercom receives no group. contact_supervisor still reaches the supervisor across group boundaries because Orphus requests a broker capability during typed admission and binds the child’s registration to the issuing supervisor. Foreground and single-child paths use exact child scopes; asynchronous chains use bounded per-child slots so indexes shifted by dynamic fanout remain valid. The lightweight Intercom wrapper lazy-loads the authorization provider; provider failures abort launch, while hosts without a provider omit supervisor metadata instead of exposing a broken channel.

Subagent SINGLE calls, parallel tasks[] items, and chain parallel steps accept an optional name that names the child’s session (the same session name extensions read via getSessionName()). Roundtable rooms key read cursors and attribution by session name, and unnamed in-process children all share the parent’s pid-derived identity — so two children joining the same room would otherwise collide on one cursor. Name each member that will join a room, uniquely per room. Fleet blueprints do this automatically.

When a subagent call, parallel task, chain step, or background run uses a cwd, Orphus validates that working directory before starting the child runtime. Missing or non-directory paths are reported as cwd problems instead of lower-level runtime errors.

Single-agent calls also accept reads: string[] | false. Orphus prepends a [Read from: …] pointer naming those files — the child reads them itself; their content is not inlined — for foreground and background execution through the same in-process session path, including /run agent[reads=a.md+b.md]. Relative entries resolve against the effective child cwd (including a relative top-level cwd resolved from the parent); absolute entries are unchanged. Invalid values fail before the child session starts.

Single-agent calls accept progress: boolean in foreground, background, and revived/resumed mode. progress: true creates a run-scoped progress.md under isolated subagent artifact storage and instructs the child to maintain it without writing progress.md into the child cwd; progress: false disables an agent’s defaultProgress. When progress is omitted, the agent’s default is inherited, except that inherited progress is suppressed for read-only tasks (progress: true still explicitly opts in). Foreground runs remove this run-owned progress storage after the child exits when artifacts: false, including children temporarily detached for intercom coordination. This is separate from includeProgress: true, which only includes detailed runtime progress telemetry in the final tool result and does not create or maintain a file.

subagent({ agent: "worker", task: "Implement the approved fix.", progress: true })
subagent({ agent: "worker", task: "Implement it in the background.", progress: true, async: true })

Nested and fanout boundaries

Child-safety boundaries are enforced by typed admission policy and the bundled subagent extension:

  • Normal child sessions do not receive the subagent tool or the parent-only subagents skill.
  • Child context is filtered to remove parent orchestration artifacts, old control/status messages, and prior parent subagent tool calls/results.
  • Non-fanout children are instructed that they are not the parent orchestrator and must not propose or run subagents.
  • Nested fanout is available only for explicitly authorized agents whose resolved tools include subagent. Authorized fanout children receive narrower instructions that limit delegation to the assigned fanout.
  • The recursion guard has a hard maximum of five delegated subagent levels, and defaults to two. The admitted depth policy may choose a lower value from 0 to 5; deeper admission is refused rather than inherited from process environment state.

Nesting depth: the default, the ceiling, and how to change either

Two different numbers, often confused:

Value Meaning
Default 2 What you get when nothing says otherwise
Ceiling 5 What nothing may exceed, however configured

The default sits below the ceiling deliberately. One level of delegation measurably helps; a second starts to overthink, and the levels beyond that were previously reachable without anyone choosing them. Going deeper should be a decision, not the state you land in by not thinking about it.

To allow deeper nesting, raise the budget the parent runs with — set ORPHUS_SUBAGENT_MAX_DEPTH (clamped to the ceiling), or supply a config depth.

An agent definition’s maxSubagentDepth frontmatter cannot raise it. That value min-clamps against the budget its parent was granted, so it narrows a subtree and never widens one: an agent declaring maxSubagentDepth: 4 under the default of 2 still runs at 2. Use it to make a particular agent shallower than its parent, which is what it is for.

This keeps the parent session responsible for orchestration unless you deliberately choose a fanout-capable custom agent.

Custom agents

Custom agents are Markdown files with YAML frontmatter and a system prompt body. Keep the body outcome-first and locally complete: state the role or goal, observable success criteria, constraints and context-dependent tool routes, required output shape, and stop conditions. Reserve absolute wording for true invariants, request evidence and conclusions rather than private reasoning, and avoid repeated self-check instructions. Common locations are:

Scope Path
User ~/.orphus/agent/agents/**/*.md
Project .orphus/agents/**/*.md

A small custom read-only inspection agent:

---
name: strict-inspector
description: Inspect code for correctness and regressions
tools: read, search, bash
model: anthropic/claude-sonnet-4
fallbackModels: openai/gpt-5-mini
inheritProjectContext: true
---

## Role and goal
Inspect the current diff for correctness and regressions without editing files.

## Success criteria
Cite each actionable issue with file:line evidence and the observed failure or risk.

## Output and stop rule
Return only issues worth fixing now. Stop when the relevant diff and affected call paths have been inspected, or name the evidence you could not access.

If an agent or chain step uses an explicit empty tools: [] allowlist together with outputSchema, Orphus starts the child with only structured_output enabled for the required final answer. It does not omit --tools and accidentally restore default tools. Path-only tool entries remain extension paths and do not create a builtin allowlist by themselves. The typed child session construction registers its schema-backed structured_output tool before explicit allowlists are applied.

Structured output schemas

Chain and parallel steps can declare an outputSchema when the parent needs reliable machine-readable handoff data. Orphus passes that schema directly to a structured_output tool backed by the shared Orphus factory. The child should call structured_output when it is done:

structured_output({
  files: ["src/auth.ts"],
  risks: ["missing regression test"],
})

outputSchema is a plain JSON Schema descriptor object. It may describe object, array, or primitive final values, and the child should pass a JSON value that matches that schema directly. Orphus no longer adds object-root restrictions, sidecar metadata, transcript-finality checks, or duplicate-call guards. The child runtime writes the tool arguments to output.json; the parent validates that captured JSON against the schema, reads it back as result.structuredOutput, and exposes it in named-chain references under outputs.name.structured. If the child exits without calling structured_output, or the captured value fails schema validation, Orphus retries up to three times with a corrective prompt that quotes the exact contract/validation error and reminds the child to call structured_output rather than returning plain JSON.

Children without outputSchema do not receive structured_output from Orphus’s default tool registry. They can still use a custom extension-provided terminating tool if you explicitly add one.

Dynamic fanout collect.outputSchema validates the collected result array after child runs finish.

Fallback models

Agents can define ordered fallbackModels for retryable provider or model failures such as rate limits, quota/usage-limit exhaustion (for example a provider reporting The usage limit has been reached, or usage_limit_reached/insufficient_quota codes), auth problems, unavailable models, network timeouts, or 5xx errors. Orphus tries the requested primary model first, then configured fallbacks, and finally appends the current user-selected model as the last fallback candidate when available. The main chat and workflow stages share one failure classifier, so auth, model-availability, request-incompatibility, and transport signals are handled consistently. Cancellations, safety refusals, and task/tool failures are never retried on another model.

A candidate that cannot serve the current request — for example an HTTP 400/413/422 bad/unprocessable/payload-too-large request, an unsupported tool or parameter, a context-length/context-window overflow, or a too large / invalid_request error — is treated as request/context incompatible and the chain advances to the next candidate rather than stopping. This means that if none of the configured candidates are applicable to the request, Orphus falls back to the currently selected user model instead of failing outright.

Model fallback decisions use structured provider and attempt causes. There is no per-attempt idle watchdog, no child wall-clock kill cap, and no timeout-regex classification: a quiet provider response is allowed to finish, and only an explicit termination or provider failure supplies a retryable cause. Numeric process exit codes are not used as an outcome discriminator.

When registry availability shows that a known candidate provider has no configured auth, Orphus records a skipped model attempt before starting the in-process turn. Unknown/custom providers are still attempted, and the current user-selected model appended as the final fallback is never filtered out by this pre-admission check.

Fallbacks do not retry ordinary task failures, validation failures, tool failures, cancellations, or workflow-code errors. Because a fallback may send the same prompt and context to a different provider, choose models that match your cost, privacy, and data-handling requirements.

Each candidate can also carry its own reasoning effort — see Reasoning levels.

Reasoning levels

Set the reasoning (thinking) effort for each model candidate with a model_name:thinking_effort suffix on model and on every fallbackModels entry. Valid efforts are off, minimal, low, medium, high, xhigh, and max — the same shorthand used by orphus --model sonnet:high. xhigh and max are used only when the selected model’s capability map supports them.

---
name: deep-reviewer
description: Adversarial reviewer for risky diffs
tools: read, search, bash
model: anthropic/claude-sonnet-4:high
fallbackModels: openai/gpt-5:medium, anthropic/claude-haiku-4-5:off
---

Because the effort travels with each model string, every primary and fallback candidate is self-contained: a fallback can run at a different effort than the primary, so a high-effort primary degrades gracefully to a cheaper, lower-effort fallback.

Migrate off the legacy thinking field. The separate thinking: frontmatter field is deprecated. It still works as a default for any candidate that has no suffix, and a suffix always wins, but new agents should encode the effort directly on model and fallbackModels:

-model: openai/gpt-5.5
-fallbackModels: anthropic/claude-opus-4-8
-thinking: xhigh
+model: openai/gpt-5.5:xhigh
+fallbackModels: anthropic/claude-opus-4-8:xhigh

fallbackThinkingLevels exists only as an optional compatibility helper: it is aligned by index to fallbackModels and supplies a fallback candidate’s effort only when that fallback entry has no suffix. Prefer suffixed model strings instead. Attempt metadata reports the resolved model and the effective reasoning effort used for each attempt.

  • Workflows for multi-stage reusable automation.
  • Intercom for cross-session messaging and supervisor escalation.
  • Skills for reusable instructions invoked with /skill:<name>.
  • Settings for user and project configuration.

edit this page on GitHub →