What Are spawnableAgents in Codebuff? A Complete Guide to Agent Composition

Spawnable agents in Codebuff are a declarative composition mechanism that allows one agent to programmatically launch other agents during execution, creating modular, reusable workflows without code duplication.

In the CodebuffAI/codebuff repository, spawnableAgents serve as the foundation for building complex multi-agent systems. By declaring which child agents can be instantiated at runtime, developers create a composition graph that enables dynamic task delegation, isolated execution contexts, and sophisticated orchestration patterns.

Understanding spawnableAgents in Codebuff

At its core, the spawnableAgents field transforms static agent definitions into dynamic, composable units. When you define an agent in Codebuff, you specify an array of agent references—either fully-qualified IDs (publisher/agentId@version) or short local IDs—that this agent is permitted to instantiate during its execution cycle.

This architecture solves three critical problems in agent development:

  • Reusability: A single "file-picker" agent can serve multiple higher-level agents (code-reviewers, bug-finders, documentation generators) without copying logic
  • Isolation: Each spawned agent operates in its own sandboxed execution context, ensuring failures in child agents don't corrupt parent state
  • Dynamic orchestration: Parent agents can decide at runtime which children to spawn based on user input or intermediate results, enabling adaptive workflows

How spawnableAgents Enable Agent Composition

The composition system operates across three distinct phases: definition, validation, and execution.

Declaring Spawnable Agents in Agent Definitions

In agents/types/agent-definition.ts, the AgentDefinition interface includes the spawnableAgents field as an array of strings. This declaration acts as a whitelist—only agents listed here can be instantiated by this agent at runtime.

// agents/types/agent-definition.ts
export interface AgentDefinition {
  id: string;
  version: string;
  publisher: string;
  displayName: string;
  model: string;
  // Agents that this agent is permitted to spawn
  spawnableAgents: string[]; // e.g., ['my-org/file-picker@0.2.1', 'my-org/code-searcher@0.3.0']
  spawnerPrompt?: string;
  handleSteps: GeneratorFunction;
}

The spawnerPrompt field complements this by describing when the orchestrator should be used, providing context for both the runtime system and UI visualization tools.

Publishing and Validation via Subagent Resolution

When you publish an agent using codebuff publish, the system validates all spawnableAgents references in web/src/app/api/agents/publish/subagent-resolution.ts. This module ensures composition integrity before the agent becomes available in the registry.

The resolveAndValidateSubagents function performs several critical checks:

  1. Reference normalization: Converts short local IDs to fully-qualified IDs (publisher/agentId@version)
  2. Version resolution: Resolves simple references to the latest published version
  3. Existence validation: Ensures referenced agents are either already published or included in the same publish request
  4. Cycle detection: Prevents circular dependencies that could cause infinite spawn loops
// web/src/app/api/agents/publish/subagent-resolution.ts
export async function resolveAndValidateSubagents({
  agents,
  requestedPublisherId,
  getLatestPublishedVersion,
  existsInSamePublisher,
}: {
  agents: Array<{ id: string; version: string; data: AgentDefinition }>;
  requestedPublisherId: string;
  getLatestPublishedVersion: (publisher: string, agentId: string) => Promise<string | null>;
  existsInSamePublisher: (publisher: string, agentId: string, version: string) => boolean;
}): Promise<void> {
  // Validates spawnableAgents array, resolves versions, checks for cycles
  // Implementation spans lines 9-99 in the source file
}

This validation guarantees that only resolvable, published agents can be spawned, maintaining the integrity of the composition graph.

Runtime Execution via the spawn_agents Tool

During execution, the spawnableAgents declaration enables the spawn_agents tool call within the handleSteps generator function. When an agent yields a spawn_agents call, the engine creates separate execution contexts for each child agent.

The runtime behavior defined in agents/types/agent-definition.ts (lines 44-52) specifies that:

  1. The engine creates isolated execution contexts for each spawned agent
  2. Child agents can optionally inherit the parent's system prompt
  3. Results from child agents return to the parent upon completion
  4. The parent continues execution after receiving child outputs
// Example handleSteps implementation showing spawn_agents usage
handleSteps: function* ({ logger, toolResult }) {
  // Determine which agent to spawn based on context
  const task = yield {
    toolName: 'ask_user',
    input: { prompt: 'Select task: (1) pick files, (2) search code' },
  };
  
  if (task === '1') {
    // Spawn file-picker agent declared in spawnableAgents
    yield {
      toolName: 'spawn_agents',
      input: {
        agents: [{ agent_type: 'file-picker', prompt: 'Select relevant files' }],
      },
    };
  }
  
  // Wait for child completion and receive results
  const { toolResult: childOutput } = yield { toolName: 'wait_for_children' };
  logger.info('Child agent completed:', childOutput);
}

This runtime mechanism transforms static spawnableAgents declarations into dynamic, conditional workflow orchestration.

Visualizing Agent Relationships with spawnableAgents

Codebuff provides comprehensive tooling to visualize the composition graphs created by spawnableAgents, aiding debugging and documentation.

Building the Agent Tree

The web/src/lib/agent-tree.ts module constructs an in-memory tree representation of agent spawn relationships. This tree captures:

  • Spawner prompts: Descriptions of when each agent should be used
  • Availability status: Whether referenced agents are published and accessible
  • Cycle detection: Identification of circular dependencies that would cause infinite loops
  • Depth limits: Enforcement of maximum nesting levels to prevent runaway spawning
// web/src/lib/agent-tree.ts
export interface AgentTreeNode {
  publisher: string;
  agentId: string;
  version: string;
  displayName: string;
  spawnerPrompt: string | null;
  children: AgentTreeNode[];
  isAvailable: boolean;
  cycleDetected: boolean;
}

export async function buildAgentTree(params: {
  rootPublisher: string;
  rootAgentId: string;
  rootVersion: string;
  rootDisplayName: string;
  rootSpawnerPrompt: string | null;
  rootSpawnableAgents: string[];
  lookupAgent: (publisher: string, agentId: string, version: string) => Promise<AgentLookupResult | null>;
  maxDepth?: number;
}): Promise<AgentTreeNode> {
  // Implementation builds tree recursively, checks cycles, validates availability
  // Lines 21-34 in agent-tree.ts
}

Generating Mermaid Diagrams

The generateMermaidDiagram function (lines 29-90 in agent-tree.ts) converts the agent tree into Mermaid flowchart syntax, enabling visual documentation of complex agent hierarchies.

// Generate Mermaid diagram from agent tree
const diagram = generateMermaidDiagram(tree);
console.log(diagram);
// Output:
// flowchart TD
//   A[orchestrator<br/>Task Orchestrator] --> B[file-picker<br/>File Picker]
//   A --> C[code-searcher<br/>Code Searcher]
//   style A fill:#f9f,stroke:#333

The diagram generator applies distinct styling for root nodes, unavailable agents, and cyclic references, making it immediately obvious when spawnableAgents references are broken or problematic.

Practical Implementation Examples

Defining an Orchestrator with spawnableAgents

Here's a complete example showing how to define an agent that utilizes spawnableAgents to delegate tasks:

// .agents/orchestrator.ts
import { AgentDefinition } from '../agents/types/agent-definition'

export const orchestrator: AgentDefinition = {
  id: 'orchestrator',
  version: '0.1.0',
  publisher: 'my-org',
  displayName: 'Task Orchestrator',
  model: 'anthropic/claude-opus-4.6',
  // Declare which agents this orchestrator can spawn
  spawnableAgents: [
    'my-org/file-picker@0.2.1',
    'my-org/code-searcher@0.3.0',
  ],
  spawnerPrompt: 'Use this agent to coordinate file selection and code search.',
  handleSteps: function* ({ logger, toolResult }) {
    // Ask user which sub-task to perform
    const { toolResult: choice } = yield {
      toolName: 'ask_user',
      input: { prompt: 'Pick a task: (1) pick files, (2) search code' },
    }

    if (choice === '1') {
      // Spawn the file-picker agent
      yield {
        toolName: 'spawn_agents',
        input: {
          agents: [{ agent_type: 'file-picker', prompt: 'Select files' }],
        },
      }
    } else {
      // Spawn the code-searcher agent
      yield {
        toolName: 'spawn_agents',
        input: {
          agents: [{ agent_type: 'code-searcher', prompt: 'Search code' }],
        },
      }
    }

    // Wait for child completion
    const { toolResult: childOutput } = yield { toolName: 'wait_for_children' }
    logger.info('Child finished:', childOutput)
  },
}

Publishing with Automatic Resolution

When publishing the orchestrator, Codebuff automatically resolves and validates all spawnableAgents references:

// This happens internally when running: codebuff publish .agents/orchestrator.ts

import { resolveAndValidateSubagents } from './web/src/app/api/agents/publish/subagent-resolution'

await resolveAndValidateSubagents({
  agents: [{ id: 'orchestrator', version: '0.1.0', data: orchestrator }],
  requestedPublisherId: 'my-org',
  getLatestPublishedVersion: async (pub, id) => {
    // Returns latest version or null
    return '0.2.1' 
  },
  existsInSamePublisher: (pub, id, ver) => {
    // Checks if agent exists in current publish batch
    return true
  },
})

This validation ensures that only published, available agents can be referenced in spawnableAgents, preventing runtime errors from unresolved dependencies.

Summary

  • Spawnable agents enable one Codebuff agent to programmatically launch other agents via the spawn_agents tool, creating dynamic composition graphs.
  • The spawnableAgents field in agents/types/agent-definition.ts acts as a whitelist of eligible child agents, declared using fully-qualified IDs or local references.
  • During publishing, web/src/app/api/agents/publish/subagent-resolution.ts validates and normalizes these references, ensuring only resolvable agents can be spawned and preventing circular dependencies.
  • At runtime, the spawn_agents tool creates isolated execution contexts for each child agent, allowing parents to delegate subtasks while maintaining fault isolation.
  • The web/src/lib/agent-tree.ts module provides visualization capabilities, building trees of spawn relationships and generating Mermaid diagrams to help developers understand complex agent hierarchies.

Frequently Asked Questions

What is the difference between spawnableAgents and sub-agents?

Spawnable agents are explicitly declared dependencies that a parent agent can instantiate, while sub-agents refer to the actual runtime instances created via the spawn_agents tool. The term "spawnable" emphasizes that these agents are eligible to be launched on demand—the parent chooses at runtime which declared agents to actually spawn, if any. This differs from static parent-child relationships where sub-agents are always instantiated; spawnable agents provide dynamic, conditional composition.

How does Codebuff prevent circular dependencies in spawnableAgents?

Codebuff detects circular dependencies during both the publishing phase and tree visualization. In web/src/app/api/agents/publish/subagent-resolution.ts, the validation logic checks that referenced agents don't create dependency cycles before allowing publication. Additionally, web/src/lib/agent-tree.ts explicitly tracks cycles during tree construction, setting a cycleDetected flag on nodes that would create infinite loops. This prevents runtime deadlocks where agent A spawns B, which spawns C, which attempts to spawn A again.

Can spawnableAgents reference agents from other publishers?

Yes, spawnableAgents can reference agents from any publisher using fully-qualified IDs in the format publisher/agentId@version. The resolution system in web/src/app/api/agents/publish/subagent-resolution.ts handles cross-publisher references by validating that the specified version exists in the target publisher's registry. However, agents can only spawn other agents that have been published and are available; unpublished or private agents from other publishers cannot be referenced unless explicitly shared.

What happens if an agent tries to spawn an agent not listed in spawnableAgents?

The runtime system enforces the spawnableAgents whitelist, preventing agents from spawning undeclared dependencies. If an agent attempts to use the spawn_agents tool with an agent_type not present in its spawnableAgents array, the execution engine rejects the request. This constraint ensures composition integrity—developers must explicitly declare dependencies at definition time, allowing the publishing pipeline to validate availability and prevent runtime errors from missing or incompatible agents.

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 →