How the spawn-agents Tool Coordinates Parallel Agent Execution in Freebuff

The spawn_agents tool launches multiple child agents concurrently using Promise.allSettled, shares the parent's execution context, validates each child specification, creates isolated AgentState instances, aggregates results after all agents complete, and combines billing costs back into the parent agent.

The spawn_agents tool is a core orchestration primitive in the CodebuffAI/freebuff agent runtime that enables a parent agent to delegate work to multiple specialized sub-agents in parallel. Understanding how parallel agent execution is coordinated reveals the architectural decisions behind safe, deterministic multi-agent workflows.

Preparing the Shared Execution Context

Before any child agents launch, the tool extracts the parent's runtime dependencies once through extractSubagentContextParams. This function, defined in [spawn-agent-utils.ts](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/spawn-agent-utils.ts#L60-L68), captures:

  • Environment configuration
  • Billing/credit tracking state
  • LLM client instances
  • Other shared infrastructure

This single extraction ensures all children inherit consistent context without redundant setup overhead.

Validating Child Agent Specifications

Each child agent specification undergoes two-phase validation in validateAndGetAgentTemplate and validateAgentInput (same file, lines 71-98):

  1. Template validation — confirms the requested agent_type is allowed in the current runtime and loads its configuration template
  2. Input validation — checks the supplied prompt and params for type correctness and required fields

Failed validations reject that specific child without aborting the entire batch.

Creating Isolated Agent States

For every validated child, createAgentState (lines 51-63) constructs an independent AgentState instance. This isolation prevents cross-contamination: each sub-agent maintains its own message history, tool access, and execution lifecycle while still sharing the parent's underlying infrastructure.

Launching Agents Concurrently with Promise.allSettled

The core parallel coordination happens in [spawn-agents.ts](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts#L89-L96):

const results = await Promise.allSettled(
  agents.map(async ({ agent_type, prompt, params }) => { … })
);

Why Promise.allSettled instead of Promise.all? The settled variant guarantees the parent receives results from every child even when individual agents fail. This design choice supports partial success patterns common in multi-agent workflows—some children may hit LLM rate limits or encounter tool errors while others complete successfully.

Each mapped promise executes this sequence:

  • Create sub-agent state
  • Validate configuration
  • Call executeSubagent to run the core agent loop

Executing Individual Sub-agents

The executeSubagent function ([spawn-agent-utils.ts](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/spawn-agent-utils.ts#L66-L80)) invokes loopAgentSteps, the runtime's standard LLM-backed agent driver. During execution, it emits lifecycle events:

  • subagent_start — sent when a child begins
  • subagent_finish — sent on completion or failure

These events enable real-time monitoring of parallel agent execution from the parent or external observer.

Collecting and Aggregating Results

After all promises settle, the handler builds a structured report (lines 98-115 in [spawn-agents.ts](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/spawn-agents.ts#L98-L115)):

// Results array contains status, value/error, and metadata for each child
const report = results.map((result, index) => ({
  agentName: agents[index].agent_type,
  agentType: agents[index].agent_type,
  value: result.status === 'fulfilled' ? result.value : result.reason,
}));

The tool then aggregates billing costs (lines 18-60), adding any credits consumed by successful or partially-failed sub-agents to the parent's creditsUsed field. This ensures accurate cost attribution across the entire parallel batch.

Practical Usage Examples

Spawning Multiple Agents Simultaneously

await runTool({
  name: 'spawn_agents',
  input: {
    agents: [
      { agent_type: 'code-reviewer', prompt: 'Review this PR', params: {} },
      { agent_type: 'test-runner',   prompt: 'Run tests',    params: {} },
    ],
  },
});

This call launches both agents in parallel. The code reviewer and test runner execute simultaneously, subject only to underlying LLM provider rate limits.

Processing the Returned Report

const { output } = await runTool(...);
const reports = output[0].value; // array of { agentName, agentType, value }

reports.forEach(r => {
  if (r.value instanceof Error) {
    console.error(`${r.agentName} failed:`, r.value.message);
  } else {
    console.log(`${r.agentName} (${r.agentType}):`, r.value);
  }
});

The settled results preserve individual success/failure states while presenting them in a unified structure.

Comparison: spawn_agents vs spawn_agent_inline

Tool Use Case Concurrency Message History
spawn_agents Multiple independent children Parallel Isolated per child
spawn_agent_inline Single child sharing parent context Sequential Shared with parent

The spawn_agent_inline tool ([spawn-agent-inline.ts](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/spawn-agent-inline.ts#L25-L34)) creates state with includeMessageHistory: true and runs synchronously—appropriate when the parent needs to continue a coherent conversation through a single delegate.

await runTool({
  name: 'spawn_agent_inline',
  input: {
    agent_type: 'doc-generator',
    prompt: 'Create API docs',
    params: {},
  },
});

Key Implementation Files

Summary

  • Parallel execution uses Promise.allSettled to run all child agents concurrently while capturing individual success and failure states
  • Shared context is extracted once via extractSubagentContextParams and inherited by all children
  • Validation occurs per-child through validateAndGetAgentTemplate and validateAgentInput, with failures isolated to that agent
  • State isolation via createAgentState ensures independent execution lifecycles
  • Result aggregation combines outputs and costs only after every child settles, producing a deterministic final report
  • Billing transparency adds sub-agent costs to the parent's creditsUsed for accurate accounting

The design prioritizes observability (lifecycle events), fault tolerance (allSettled semantics), and cost accountability—essential properties for production multi-agent systems.

Frequently Asked Questions

What happens if one spawned agent fails?

The failure is captured in the results array with status 'rejected' and the error reason. Other agents continue and complete normally. The parent receives a complete report showing which children succeeded and which failed, enabling targeted retry logic or graceful degradation.

How does parallel agent execution handle rate limits?

Actual concurrency is constrained by the underlying LLM provider's rate limits and the runtime's HTTP client configuration. The Promise.allSettled structure launches all promises simultaneously, but network-level queuing or provider throttling may serialize requests. The runtime does not implement additional concurrency limiting—this is left to infrastructure configuration.

Can spawned agents spawn their own children?

Yes. Since each child receives a full AgentState with tool access including spawn_agents, nested parallel execution is technically possible. However, the current implementation in CodebuffAI/freebuff does not include explicit depth limits, so application-level guardrails should prevent unbounded recursion.

What's the difference between shared and isolated message history?

Shared history (via spawn_agent_inline) preserves the entire conversation thread between parent and child, making it suitable for delegated single tasks that must return to the parent's context. Isolated history (default in spawn_agents) gives each child a clean slate, appropriate for independent work that should not pollute or leak into other agents' contexts.

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 →