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:

  1. Emits a subagent_start chunk to the UI
  2. Invokes loopAgentSteps to run the child agent’s logic
  3. Streams response chunks back to the parent in real-time
  4. Emits subagent_finish when complete
  5. Returns the final result and creditsUsed for 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_agents tool enables parallel delegation by accepting an array of agent configurations and executing them via Promise.allSettled.
  • Permission checks in validateAndGetAgentTemplate ensure parents can only spawn explicitly allowed child types, with base agents having unrestricted access.
  • Input validation via validateAgentInput guarantees that prompts and parameters conform to each child agent’s Zod schema before execution begins.
  • The executeSubagent function manages the full lifecycle, emitting subagent_start and subagent_finish chunks while streaming intermediate responses back to the parent.
  • Cost aggregation automatically sums creditsUsed from 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →