# How to Use the set_output Function for Inter-Agent Communication in Codebuff

> Learn to use Codebuff's set_output function for inter-agent communication. Store structured data in agent states for validated parent retrieval.

- Repository: [Codebuff/codebuff](https://github.com/CodebuffAI/codebuff)
- Tags: how-to-guide
- Published: 2026-03-09

---

**Use the `set_output` tool to store structured data in an agent's state, enabling parent agents to retrieve validated results after a child agent completes its execution cycle.**

The `set_output` function is the primary mechanism for structured inter-agent communication in the Codebuff framework. When building multi-agent workflows in the CodebuffAI/codebuff repository, this built-in tool allows child agents to return complex data objects that parent agents can consume reliably, with full JSON schema validation ensuring type safety across agent boundaries.

## Understanding the set_output Workflow

The `set_output` tool operates as a stateful bridge between agent execution layers. Unlike simple message passing, this function persists structured output directly to the agent's state object, making it available to any parent or orchestrator after the agent's turn completes.

The workflow follows four distinct phases:

1. **Declaration** – The agent template registers `set_output` in its `toolNames` array and defines an `outputSchema` that specifies the expected JSON structure.
2. **Invocation** – During execution, the LLM generates a call to `set_output` with a payload matching the defined schema.
3. **Validation** – The runtime's `handleSetOutput` function validates the payload against `outputSchema` using Zod parsing.
4. **Retrieval** – The parent agent accesses the validated data via `agentState.output` after the child finishes.

## Configuring Your Agent for Structured Output

To enable inter-agent communication with `set_output`, you must configure the agent template with specific metadata that tells the runtime how to validate and store the output.

### Defining the outputSchema

The `outputSchema` field uses Zod to enforce the structure of data passed between agents. This schema acts as a contract between parent and child agents.

```typescript
// common/src/templates/initial-agents-dir/examples/03-advanced-file-explorer.ts
import { z } from 'zod'

export const fileAnalyzerAgent = {
  name: 'file-analyzer',
  toolNames: ['set_output', 'read_file', 'end_turn'],
  outputMode: 'structured_output',
  outputSchema: z.object({
    filePath: z.string().describe('Path to the analyzed file'),
    changedLines: z.array(z.number()).describe('Line numbers modified'),
    summary: z.string().describe('Brief description of changes')
  }),
  // ... other agent configuration
}

```

### Registering the Tool

The `set_output` tool must be explicitly included in the agent's `toolNames` array. According to the source in [`common/src/types/dynamic-agent-template.ts`](https://github.com/CodebuffAI/codebuff/blob/main/common/src/types/dynamic-agent-template.ts), this tool is mandatory when `outputMode` is set to `"structured_output"` or `"json"`.

```typescript
// common/src/tools/list.ts
export const TOOL_DEFINITIONS = {
  set_output: {
    name: 'set_output',
    description: 'Store structured output for parent agent consumption',
    parameters: z.object({}) // Runtime validation uses outputSchema instead
  },
  // ... other tools
}

```

## Implementing set_output in Agent Logic

When the LLM generates the agent's execution steps, it must invoke `set_output` with a payload that conforms to the defined schema. The tool call typically appears as the final action before `end_turn`.

```typescript
// Conceptual LLM-generated step within handleSteps
async function analyzeFile(filePath: string) {
  // Perform analysis...
  const changedLines = [12, 13, 14]
  
  // Store structured result for parent
  await callTool('set_output', {
    data: {
      filePath: filePath,
      changedLines: changedLines,
      summary: 'Refactored utility functions'
    }
  })
  
  // Signal completion
  await callTool('end_turn')
}

```

The runtime wraps this call in a `CodebuffToolCall` object with `toolName: 'set_output'` and the provided input object.

## How the Runtime Processes set_output Calls

The `handleSetOutput` function in [`packages/agent-runtime/src/tools/handlers/tool/set-output.ts`](https://github.com/CodebuffAI/codebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/set-output.ts) manages the validation and storage logic. This handler ensures type safety before persisting data to the agent state.

```typescript
// packages/agent-runtime/src/tools/handlers/tool/set-output.ts
export const handleSetOutput = (async ({
  previousToolCallFinished,
  toolCall,
  agentState,
  ...rest
}) => {
  const output = toolCall.input
  const { data } = output ?? {}

  await previousToolCallFinished

  // Retrieve agent template for schema validation
  const agentTemplate = agentState.agentType
    ? await getAgentTemplate({ ...rest, agentId: agentState.agentType })
    : null

  let finalOutput: unknown
  
  if (agentTemplate?.outputSchema) {
    // Strict validation against Zod schema
    try {
      agentTemplate.outputSchema.parse(output)
      finalOutput = output
    } catch {
      // Fallback to data field only
      agentTemplate.outputSchema.parse(data)
      finalOutput = data
    }
  } else {
    // No schema defined - extract data field if present
    const keys = Object.keys(output)
    const hasOnlyDataField = keys.length === 1 && keys[0] === 'data'
    finalOutput = hasOnlyDataField ? data : output
  }

  // Persist to agent state
  agentState.output = finalOutput as Record<string, unknown>

  return { output: jsonToolResult({ message: 'Output set' }) }
}) satisfies CodebuffToolHandlerFunction<'set_output'>

```

The handler performs schema validation using Zod's `parse` method. If the payload fails validation against the full output object, it attempts to validate just the `data` field. This provides flexibility while maintaining type safety.

## Reading Output from Parent Agents

After a child agent completes its execution cycle, the parent retrieves the structured data through the child's `agentState.output` property. The runtime enforces that agents with `set_output` in their tool list must actually call it before ending their turn.

```typescript
// packages/agent-runtime/src/run-agent-step.ts
if (currentAgentState.output === undefined && toolNames.includes('set_output')) {
  // Enforcement: structured output agents must call set_output
  throw new Error(
    "Agent finished without calling set_output while a structured output schema is required"
  )
}

// Parent accessing child output
const childState = await runAgentStep({
  agentId: 'file-analyzer',
  // ... other params
})

const result = childState.output
// result → { filePath: 'src/utils/helper.ts', changedLines: [12, 13, 14] }

```

This pattern enables complex multi-agent workflows where specialized child agents perform discrete tasks and return structured results that parent agents can programmatically process.

## Summary

- **Declare the contract**: Add `set_output` to `toolNames` and define an `outputSchema` using Zod to enforce the data structure between agents.
- **Store structured data**: Child agents call `set_output` with a payload matching the schema before ending their turn.
- **Validate automatically**: The `handleSetOutput` handler in [`packages/agent-runtime/src/tools/handlers/tool/set-output.ts`](https://github.com/CodebuffAI/codebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/set-output.ts) validates payloads against the schema and stores valid data on `agentState.output`.
- **Retrieve safely**: Parent agents access `childState.output` after the child completes, with the runtime enforcing that structured output agents must call `set_output` before finishing.

## Frequently Asked Questions

### What happens if an agent with set_output never calls the tool?

The runtime throws an error. According to the logic in [`packages/agent-runtime/src/run-agent-step.ts`](https://github.com/CodebuffAI/codebuff/blob/main/packages/agent-runtime/src/run-agent-step.ts), if an agent's `toolNames` includes `set_output` but `agentState.output` remains undefined when the turn ends, the system raises: `"Agent finished without calling set_output while a structured output schema is required"`. This ensures that parent agents never receive undefined data when expecting structured output.

### Can set_output be used without an outputSchema?

Yes, but with reduced type safety. If no `outputSchema` is defined in the agent template, `handleSetOutput` stores the raw input or extracts the `data` field if it's the only property present. However, best practices recommend always defining an `outputSchema` when using `set_output` for inter-agent communication to ensure consistent data contracts between parent and child agents.

### How does set_output differ from regular message passing?

Unlike `add_message` or other communication tools that append to conversation history, `set_output` performs structured state persistence. It validates data against Zod schemas, stores results in a dedicated `agentState.output` property, and enforces that the data must be present before the agent can complete its turn. This makes it suitable for programmatic data exchange rather than conversational context building.

### What validation errors can occur when calling set_output?

Validation fails when the payload structure does not match the Zod schema defined in `outputSchema`. The `handleSetOutput` function first attempts to validate the full input object; if that fails, it tries validating just the `data` field. If both attempts fail, the handler returns an error message and does not update `agentState.output`, forcing the agent to retry with valid data before ending its turn.