Creating New Agents

Create specialized agents from scratch using TypeScript files in the .agents/ directory.

Types:

  • LLM-based — Use prompts and language models
  • Programmatic — Use TypeScript generator functions with handleSteps

Control Flow:

  • yield 'STEP' — Run one LLM generation step
  • yield 'STEP_ALL' — Run until completion
  • return — End the agent's turn

Accessing Context:

  • agentState — Current agent state and message history
  • prompt — User's prompt to the agent
  • params — Additional parameters passed to the agent

Basic Structure

Create a new TypeScript file in .agents/ directory:

.agents/my-custom-agent.ts

import { AgentDefinition } from './types/agent-definition'
const definition: AgentDefinition = {
id: 'my-custom-agent',
version: '1.0.0',
displayName: 'My Custom Agent',
spawnerPrompt:
'Spawn this agent for specialized workflow tasks requiring custom logic',
model: 'openai/gpt-5.3-codex',
outputMode: 'last_message',
includeMessageHistory: true,
toolNames: ['read_files', 'write_file', 'end_turn'],
spawnableAgents: ['vesper/researcher@0.0.1'],
inputSchema: {
prompt: {
type: 'string',
description: 'What documentation to create or update',
},
},
systemPrompt: `You are a documentation specialist.`,
instructionsPrompt:
"Write documentation based on the user's request. Research existing code and patterns first.",
stepPrompt:
'Continue working on the documentation. Use end_turn when complete.',
}
export default definition

Programmatic Agents

LLM-based agents cover many tasks. Programmatic agents add precise control over complex workflows while still letting you tap into LLMs when you want them.

How It Works

Use TypeScript generator functions with the handleSteps field to control execution:

.agents/code-analyzer.ts

import { AgentDefinition } from './types/agent-definition'
const definition: AgentDefinition = {
id: 'code-analyzer',
displayName: 'Code Analysis Expert',
spawnerPrompt: 'Spawn for deep code analysis and refactoring suggestions',
model: 'openai/gpt-5.3-codex',
toolNames: ['read_files', 'code_search', 'spawn_agents', 'write_file'],
spawnableAgents: ['vesper/thinker@0.0.1', 'vesper/reviewer@0.0.1'],
handleSteps: function* ({ agentState, prompt, params }) {
// First, find relevant files
const { toolResult: files } = yield {
toolName: 'find_files',
input: { query: prompt },
}
// Read the most important files
if (files) {
const filePaths = JSON.parse(files).slice(0, 5)
yield {
toolName: 'read_files',
input: { paths: filePaths },
}
}
// Spawn a thinker for deep analysis
yield {
toolName: 'spawn_agents',
input: {
agents: [
{
agent_type: 'thinker',
prompt: `Analyze the code structure and suggest improvements for: ${prompt}`,
},
],
},
}
// Let the agent generate its response
yield 'STEP_ALL'
},
}
export default definition

Key Concepts

Generator Function Basics

Your handleSteps function receives context and yields actions:

handleSteps: function* ({ agentState, prompt, params }) {
// agentState: Current conversation and agent state
// prompt: What the user asked this agent to do
// params: Additional parameters passed to the agent
}

Yielding Tool Calls

Execute tools and get their results:

const { toolResult, toolError } = yield {
toolName: 'read_files',
input: { paths: ['file1.ts', 'file2.ts'] }
}
if (toolError) {
console.error('Failed to read files:', toolError)
} else {
const fileContent = JSON.parse(toolResult)
}

Control Flow Options

  • yield 'STEP' — Run one LLM generation step
  • yield 'STEP_ALL' — Run until completion
  • return — End the agent's turn