Extract a shared sub-agent core behind every spawn site - #4779
Merged
Conversation
A sub-agent is now one concept stated once (packages/agents/src/subagent.ts):
an async generator of ProcessingMessages whose return value is how the run
settled. CodeAct is the default producer (runSubAgent), any generator with
that shape streams through the same pipe (forwardSubAgentStream — tagging for
UI nesting, forward-failure tolerance, between-rounds abort), recursion goes
through one gate (enterSubAgentDepth), and failure detection — the protocol
error field, the sole-key {error} payload a dying step reports, the
schemaless string-error shape — lives in one settlement function.
Before this, run_subtask and run_search each carried a full copy of the
machinery, ScriptRunner's agent() bridge duplicated the executor half, and
plan_workflow_graph re-implemented the streaming half for the GraphPlanner.
All four now sit on the core: the two tools are thin SubAgentTool subclasses
(declaring only their surface, param translation, and child toolset policy),
ScriptRunner calls runSubAgent directly, and the planner generator rides
forwardSubAgentStream. Tool names, schemas, error codes, event tagging, and
depth semantics are unchanged; structured outputSchema support is now
available to every tool-exposed sub-agent, not just script mode.
New delegation tools are a ~40-line SubAgentTool subclass instead of another
copy of the spawn/stream/settle loop.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017YcuD3JDFqQ55kXdJicHDB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Generalizes the sub-agent concept (until now hand-rolled per spawn site, with the graph planner as one ad-hoc case) into one core module,
packages/agents/src/subagent.ts, so sub-agents become reusable building blocks with streaming and CodeAct built in.The concept, stated once: a sub-agent is an async generator of
ProcessingMessageevents whose return value is how the run settled. CodeAct is the default producer, but any generator with that shape —GraphPlanner.plan(), a future reviewer or researcher — streams through the same pipe and nests in the UI the same way.The core
runSubAgent(opts)outputSchema(structured viafinish(), prose otherwise), yields events, returns aSubAgentOutcome— never throws for run failuressettleStepResult(sr, {hasOutputSchema})step_result.error, the sole-key{error}payload a dying step reports, and (schemaless only) any stringerrorpropertyforwardSubAgentStream(gen, opts)parent_tool_call_id,subtask_depth), tolerates a broken forwarder, honors an abort signal between roundsenterSubAgentDepth(ctx, maxDepth)SUBTASK_DEPTH_KEYSubAgentToolSpawn sites refactored onto it
RunSubtaskToolandRunSearchTool— previously two near-identical ~300-line copies of the depth guard / context copy / single-step task / executor / tagging / settlement machinery — are now thinSubAgentToolsubclasses declaring only what makes each one itself (full-belt inheritance + self-stitching for recursion vs. read-only allowlist + breadth-scaled iteration budget).ScriptRunner'sagent()bridge callsrunSubAgentdirectly and pushes events onto the script channel; its private copy of the executor loop and the{error}-payload detection is gone.plan_workflow_graphdrives the GraphPlanner generator throughforwardSubAgentStream, replacing its hand-rolled tag/forward/abort loop — the proof that non-CodeAct producers ride the same pipe.Compatibility
Tool names, input schemas, error codes (
missing_prompt,max_recursion_depth_reached,subtask_failed,search_no_result, …), event tagging fields, and depth semantics are unchanged; existing tests for all four sites pass unmodified. StructuredoutputSchemasupport is now available to every tool-exposed sub-agent (previously script-mode only). One deliberate unification: a schemaless script-modeagent()result that contains a stringerroramong other keys now settles as failure (previously only the sole-key shape did) — nothing legitimate produces that shape in prose mode.Tests
tests/subagent.test.tscovers settlement, tagging, forward-failure tolerance, abort, and the depth gate.packages/agentssuite: 150 files, 2107 passed. Downstreamchat(32),websocket(2232),cli(564) all pass.Docs: new "Sub-Agent Core" section in
packages/agents/CLAUDE.md.🤖 Generated with Claude Code
https://claude.ai/code/session_017YcuD3JDFqQ55kXdJicHDB
Generated by Claude Code