How to Spawn Subagents Using the spawn_agents Tool in Codebuff
The spawn_agents tool enables parent agents to launch multiple child agents in parallel with automatic permission checks, schema validation, and cost tracking.
Codebuff is an open-source AI agent framework that treats every agent as a lightweight runtime capable of delegating tasks to specialized subagents. The spawn_agents tool, defined in common/src/tools/params/tool/spawn-agents.ts and implemented in packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts, provides a safe, version-aware mechanism for parallel agent execution with proper state management and ancestry tracking.
Understanding the spawn_agents Architecture
Tool Schema and Parameters
The tool’s input contract is defined by spawnAgentsParams in the common package. This schema specifies the JSON structure that the LLM must provide when invoking the tool.
// common/src/tools/params/tool/spawn-agents.ts
export const spawnAgentsParams = {
toolName,
endsAgentStep,
description,
inputSchema, // ← agents: [{agent_type, prompt?, params?}]
outputSchema,
} satisfies $ToolParams
The inputSchema expects an agents array where each element contains agent_type (required), an optional prompt, and optional params that must conform to the child agent’s input schema.
Handler Entry Point
The handleSpawnAgents function in the agent-runtime package serves as the orchestration layer. It receives the parsed tool call, extracts the parent runtime context, and loops over each requested child agent.
// packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts
export const handleSpawnAgents = async (params) => {
const { agents } = toolCall.input;
const results = await Promise.allSettled(
agents.map(async ({ agent_type, prompt, params: spawnParams }) => {
// 1️⃣ Validate & fetch child template
const { agentTemplate, agentType } = await validateAndGetAgentTemplate({
...params,
agentTypeStr: agent_type,
parentAgentTemplate,
});
// 2️⃣ Validate prompt/params against child schema
validateAgentInput(agentTemplate, agentType, prompt, spawnParams);
// 3️⃣ Build a fresh AgentState for the sub‑agent
const subAgentState = createAgentState(agentType, agentTemplate,
parentAgentState, {});
// 4️⃣ Execute the sub‑agent and forward chunks
const result = await executeSubagent({
...extractSubagentContextParams(params),
ancestorRunIds: parentAgentState.ancestorRunIds,
userInputId: `${userInputId}-${agentType}${subAgentState.agentId}`,
prompt: prompt || '',
spawnParams,
agentTemplate,
parentAgentState,
agentState: subAgentState,
onResponseChunk,
});
return { …result, agentType, agentName: agentTemplate.displayName };
})
);
// …aggregate costs & build JSON tool result
};
Permission and Validation Flow
Agent Template Validation
Before spawning, the system verifies that the parent agent is authorized to create the requested child type. The validateAndGetAgentTemplate function checks the parent’s spawnableAgents list.
Base agents such as base, base-free, and base-parallel can spawn any agent type. Non-base agents must explicitly declare allowed children in their template configuration. If the child type is not permitted, the runtime throws an error:
Agent type parent-id is not allowed to spawn child agent type codebuff/thinker@2.0.0.
Input Schema Validation
Once permissions are confirmed, validateAgentInput runs the child agent’s Zod inputSchema against the provided prompt and params. This ensures that subagents receive only valid inputs before execution begins.
Creating and Executing Subagents
Agent State Creation
The createAgentState function initializes a fresh AgentState for each subagent. This state includes:
- A unique
agentId - Ancestry tracking via
ancestorRunIds - Filtered message history (removing unfinished tool calls)
- Optional system messages indicating the subagent was spawned
Subagent Execution Loop
The executeSubagent function manages the runtime lifecycle:
- Emits a
subagent_startchunk to the UI - Invokes
loopAgentStepsto run the child agent’s logic - Streams response chunks back to the parent in real-time
- Emits
subagent_finishwhen complete - Returns the final result and
creditsUsedfor cost aggregation
After all subagents complete, handleSpawnAgents sums the creditsUsed from each child into the parent’s credit counter and returns a JSON-encoded report to the calling LLM.
Practical Examples of spawn_agents Usage
Spawning Multiple Agents in Parallel
To delegate tasks to multiple specialized agents simultaneously, provide an array of agent configurations:
{
"tool_name": "spawn_agents",
"tool_call_id": "spawn‑001",
"input": {
"agents": [
{
"agent_type": "commander",
"prompt": "Run the project’s test suite",
"params": { "command": "npm test" }
},
{
"agent_type": "code-searcher",
"params": {
"searchQueries": [{ "pattern": "authenticate", "flags": "-g *.ts" }]
}
}
]
}
}
The handler validates that the parent’s spawnableAgents list contains both commander and code-searcher, creates two AgentState instances, runs them concurrently via Promise.allSettled, and returns a JSON array:
[
{ "agentName": "Commander", "agentType": "commander", "value": { "type": "lastMessage", "value": [...] } },
{ "agentName": "Code‑Searcher", "agentType": "code-searcher", "value": { "type": "lastMessage", "value": [...] } }
]
Using Versioned Agent Types
Parent agents may restrict children to specific versions using publisher-scoped IDs like codebuff/thinker@1.0.0. When the LLM sends "thinker", the runtime resolves it via getMatchingSpawn in spawn-agent-utils.ts and automatically matches the allowed version.
{
"tool_name": "spawn_agents",
"input": {
"agents": [
{ "agent_type": "thinker", "prompt": "Explain the algorithm" }
]
}
}
If the parent’s spawnableAgents contains "codebuff/thinker@1.0.0", the call succeeds. A version mismatch (e.g., requesting @2.0.0 when only @1.0.0 is allowed) yields an error:
Agent type parent-id is not allowed to spawn child agent type codebuff/thinker@2.0.0.
Inline Spawning with spawn_agent_inline
For scenarios requiring direct output rather than a JSON array, use spawn_agent_inline. This variant executes the same validation pipeline (validateAndGetAgentTemplate, validateAgentInput, executeSubagent) but returns the child’s output directly to the parent context.
{
"tool_name": "spawn_agent_inline",
"input": { "agent_type": "commander-lite", "prompt": "List files" }
}
Key Implementation Files
| File | Purpose |
|---|---|
common/src/tools/params/tool/spawn-agents.ts |
Zod schema describing the tool’s input and output format. |
packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts |
Main handler orchestrating validation, state creation, and subagent execution. |
packages/agent-runtime/src/tools/handlers/tool/spawn-agent-utils.ts |
Utility functions for context extraction, permission matching, template validation, state creation, and execution. |
packages/agent-runtime/src/__tests__/spawn-agents-permissions.test.ts |
Test suite demonstrating permission checks and version handling. |
packages/agent-runtime/src/__tests__/spawn-agents-message-history.test.ts |
Tests verifying message history filtering and propagation. |
packages/agent-runtime/src/__tests__/spawn-agents-image-content.test.ts |
Tests ensuring image content handling in subagents. |
Summary
- The
spawn_agentstool enables parallel delegation by accepting an array of agent configurations and executing them viaPromise.allSettled. - Permission checks in
validateAndGetAgentTemplateensure parents can only spawn explicitly allowed child types, with base agents having unrestricted access. - Input validation via
validateAgentInputguarantees that prompts and parameters conform to each child agent’s Zod schema before execution begins. - The
executeSubagentfunction manages the full lifecycle, emittingsubagent_startandsubagent_finishchunks while streaming intermediate responses back to the parent. - Cost aggregation automatically sums
creditsUsedfrom all children into the parent’s credit counter upon completion.
Frequently Asked Questions
What is the difference between spawn_agents and spawn_agent_inline?
The spawn_agents tool returns a JSON array containing results from all spawned children, making it suitable for parallel task delegation where the parent needs to process multiple outputs simultaneously. In contrast, spawn_agent_inline returns the child’s output directly to the parent context as if it were the parent’s own response, which is useful for simple delegation scenarios requiring immediate consumption of the result.
How does Codebuff prevent unauthorized agents from spawning children?
Codebuff implements permission checks in validateAndGetAgentTemplate within spawn-agent-utils.ts. Base agents such as base, base-free, and base-parallel possess unrestricted spawning capabilities, while all other agent types must explicitly declare allowed children in their spawnableAgents configuration. When a spawn request occurs, the system validates the child type against this whitelist and rejects unauthorized attempts with a descriptive error message.
Can subagents spawn their own child agents?
Yes, Codebuff supports nested agent hierarchies through ancestry tracking. When createAgentState initializes a subagent, it copies the parent’s ancestorRunIds and establishes the new agent’s place in the hierarchy. The executeSubagent function then manages the child’s lifecycle independently, allowing it to call spawn_agents itself and create further descendants while maintaining proper context isolation and cost attribution throughout the chain.
How are costs calculated when spawning multiple agents?
Cost tracking occurs through the creditsUsed field returned by each subagent execution. After Promise.allSettled resolves all child promises in handleSpawnAgents, the handler aggregates the creditsUsed values from every subagent and adds the total to the parent agent’s credit counter. This ensures transparent cost accounting where the parent bears the cumulative expense of all delegated work, regardless of how many levels deep the agent hierarchy extends.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →