How to Use the set_output Function for Inter-Agent Communication in Codebuff
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:
- Declaration – The agent template registers
set_outputin itstoolNamesarray and defines anoutputSchemathat specifies the expected JSON structure. - Invocation – During execution, the LLM generates a call to
set_outputwith a payload matching the defined schema. - Validation – The runtime's
handleSetOutputfunction validates the payload againstoutputSchemausing Zod parsing. - Retrieval – The parent agent accesses the validated data via
agentState.outputafter 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.
// 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, this tool is mandatory when outputMode is set to "structured_output" or "json".
// 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.
// 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 manages the validation and storage logic. This handler ensures type safety before persisting data to the agent state.
// 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.
// 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_outputtotoolNamesand define anoutputSchemausing Zod to enforce the data structure between agents. - Store structured data: Child agents call
set_outputwith a payload matching the schema before ending their turn. - Validate automatically: The
handleSetOutputhandler inpackages/agent-runtime/src/tools/handlers/tool/set-output.tsvalidates payloads against the schema and stores valid data onagentState.output. - Retrieve safely: Parent agents access
childState.outputafter the child completes, with the runtime enforcing that structured output agents must callset_outputbefore 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, 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.
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 →