Repository navigation
LOU-Y1, LOU-Y3: sub-agents inherit the parent runtime; subagents option with a task tool - #71
Merged
Merged
Conversation
…on with a task tool
LOU-Y3: `subagents` (a record of createAgent() agents with a new
`description` field, or a `{ list, resolve }` catalog) on createAgent()
and AgentExecutor.execute() registers ONE `task` tool
({ agent, prompt, description }) and an "Available sub-agents" prompt
block. A sub-agent runs on the task prompt alone (isolated context); its
final text plus a steps/finish-reason footer becomes the tool result, and
failures (error, maxSteps, abort, unknown name) become error results that
say why. `maxSubagentDepth` (default 1) bounds nesting: a run at the limit
is not offered `task`. Parallel task calls run concurrently (LOU-V3).
LOU-Y1: the `task` tool and createDelegateTool() share one delegation
core (src/execution/delegation.ts). The child inherits the parent run's
abort signal, trace exporter and span parent (invoke_agent under
execute_tool), hooks (ctx.subagent set; a throwing hook halts the whole
run), approval store, toolConcurrency (unless the child sets its own),
sandbox, captureContent/redactContent and onEvent (events tagged with
event.subagent); child token usage rolls into the parent's result.usage.
The executor passes this through the tool's execute options object (no
async-context globals, so the Worker bundle stays free of node:*).
Approvals: a child call that needs approval pauses the WHOLE run. The
child's paused snapshot is stored inside the parent's single approval
record (ExecutionSnapshot.subagent, PendingApproval.subagentPath); the
parent transcript keeps a placeholder result for the task call, so
sibling results of the same turn are kept. resumeAfterApproval()
re-enters the parent's tool call, resumes the child with the decision,
and returns the child's final answer as the tool result (re-pausing if
the child needs another approval), at any depth. If two children pause
in one turn the run pauses on the first and the second gets an error
result. Snapshots now store the agent as configured (before skills /
sub-agents were applied) so resume can re-apply them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts with LOU-V2 (agent.stream), LOU-Y5 (agent directories) and LOU-W5; stream sub-agent events inside the parent's AgentRun with a subagent field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sub-agent usage rolls up through LOU-V5's onDelegatedUsage (the task tool too, and a sub-agent resumed after approval adds only what it spends after the resume). Streamed sub-agent events no longer include their own run.start/run.done, so those still mark the top-level run exactly once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
6 tasks done
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
LOU-Y3:
subagentsoption and thetasktooldescriptiononcreateAgent(); required for an agent used as a sub-agent (clear error if missing, or if the value is not acreateAgent()agent).subagents/maxSubagentDepthoncreateAgent()andAgentExecutor.execute()/stream(). Registers ONEtasktool ({ agent: enum, prompt, description }) plus an "Available sub-agents" prompt block. A catalog islist()ed at the start of each run. A user tool already namedtaskthrows.[sub-agent 'researcher': 3 step(s), finish reason 'stop']). An error,maxStepsrunning out, an abort or an unknown name each become an error result that says why.taskcalls in one turn run in parallel (LOU-V3). Depth is a budget: each sub-agent gets its parent's budget minus one, and a run at 0 is not offeredtask.taskandcreateDelegateTool()share one delegation core (src/execution/delegation.ts).createDelegateTool()keeps its API and{ text, usage }result.LOU-Y1: delegation inherits the parent runtime. Unless the child sets its own, a child run gets the parent's:
signalexporter: the child'sinvoke_agentspan is parented to the parent'sexecute_toolspan (newExecuteOptions.parentSpanId)captureContent/redactContenthooks, with the newctx.subagent({ name, depth, toolCallId, description?, parent? }) so a hook can opt out. A hook that throws inside a sub-agent halts the whole run.approvalStore(see below)toolConcurrencyandsandboxonEvent, with events taggedevent.subagentUsage rolls up through LOU-V5's
onDelegatedUsage. On a streaming parent (agent.stream()), the child's steps, text deltas, tool events and errors are streamed inline with asubagentfield.run.startandrun.donestill mark only the top-level run, once each. The child keeps its own instructions, model, tools, skills andmaxSteps, and sees only the task prompt.Approval inside a child: full durable nested resume, not the simple fallback.
needsApprovalpauses the WHOLE run.ExecutionSnapshot.subagent,PendingApproval.subagentPath), so anyApprovalStoreworks.taskcall, so sibling results from the same turn are kept and never re-run.resumeAfterApproval()re-enters the parent's tool call and resumes the child with the decision. The child's final answer becomes the tool result, and the parent then continues. If the child needs another approval, the run pauses again with a new id. This works at any depth and forcreateDelegateTool()children.Why
The audit found that child agents lose hooks, tracing and approvals. Competitors (open-harness, eve) offer a
task-style sub-agent catalog with isolated context and depth limits.How verified
Ran on the merge with origin/main at
a343df5(LOU-V5). The last merge,90aa72b(agent-forge fix plus one export), was re-checked with tsc, the execution/subagents/createAgent tests anddocs:llms:check.npx tsc --noEmit: cleannpm run lint: 0 errors, 131 warnings, all pre-existing. The files this PR touches have only their 2 existinganywarnings.npm run build && npm run build --workspace=packages/create-loushy-agent: successnpm run test:coverage:Test Files 132 passed (132),Tests 1951 passed | 4 skipped (1955). On one earlier run,http.test.ts > rejects near the configured timeoutfailed on timing (2371ms vs 2000ms) under load. It passed on rerun and is unrelated.npm run fallow: exit 0.No issues found,0 above threshold. It still reports a duplicate (AnthropicProvider/OpenAIProvider) that is already on main.npm run test:types: 4 files passed, no type errorsnpm run docs:verify-snippets:all 84 snippet(s) type-check against src/; 6 also run cleanlynpm run docs:llmsregenerated;docs:llms:checkpassesgit diff --diff-filter=D --name-only origin/main HEAD: emptyNew tests (32):
src/subagents/subagents.test.tsandsrc/execution/subagentInheritance.test.ts. They cover:ctx.subagent, and a hook halting the runonEventand stream forwarding, including nested chainstoolConcurrencyinheritance and overridecreateDelegateTool, no store, and a streaming lead. A JSON round-trip store proves snapshots are serializable.Notes for reviewer
execute(args, options)options object (aWeakMap-bound scope). It does not useAsyncLocalStorage:node:async_hookson the AgentExecutor path breaks the Cloudflare Worker bundle. I hit that first; the wrangler test caught it.AgentExecutor.ts: extensions,parentSpanId, the scope, suspension handling insettleToolBatch, andsavePause, split out ofpauseForApprovaltoolCallExecution.tssandboxGuard.tsresume.ts: the nested branch lives in a newresumeSubagent.tsagentRun.ts/agentEvents.ts: thesubagenttag, and a per-sink step result so parallel children do not clobber each otherskillsthrew "load_skill already registered".sessionId/checkpointStore. A child is not checkpointed on its own, so a crash mid-child re-runs thattaskcall on resume. Also not inherited: the single-run callbacks (onLLMRequest, ...). There is nologgeronExecuteOptionsto inherit.AgentExecutorfor the lead, becausecreateAgent()has noapprovalStoreyet (LOU-D21).loadAgentDir(LOU-Y5) still turnssubagents/<name>/intodelegate_to_<name>tools. It could switch to the nativesubagentsoption.🤖 Generated with Claude Code