Examples
This page contains working examples for common patterns and use cases.
Basic Usage
Simple Agent
Create an agent that can use tools and finish when done:
import { Agent, SIMPLE_FINISH_TOOL, DEFAULT_TOOLS } from '@stirrup/stirrup';
import { ChatCompletionsClient } from '@stirrup/stirrup/clients/openai';
const client = new ChatCompletionsClient({
apiKey: process.env.OPENROUTER_API_KEY!,
baseURL: 'https://openrouter.ai/api/v1',
model: 'anthropic/claude-sonnet-4.5',
});
const agent = new Agent({
client,
name: 'assistant',
maxTurns: 10,
tools: DEFAULT_TOOLS,
finishTool: SIMPLE_FINISH_TOOL,
});
await using session = agent.session();
const result = await session.run('What is 2 + 2?');
console.log(result.finishParams?.reason);
With Custom System Prompt
const agent = new Agent({
client,
name: 'code-reviewer',
maxTurns: 10,
tools: DEFAULT_TOOLS,
finishTool: SIMPLE_FINISH_TOOL,
systemPrompt: 'You are an expert code reviewer. Analyze code for bugs, performance issues, and best practices.',
});
Custom Tools
Simple Calculator Tool
import { z } from 'zod';
import type { Tool, ToolResult } from '@stirrup/stirrup';
import { ToolUseCountMetadata } from '@stirrup/stirrup';
const CalculatorParamsSchema = z.object({
expression: z.string().describe('Mathematical expression to evaluate'),
});
const calculatorTool: Tool<typeof CalculatorParamsSchema, ToolUseCountMetadata> = {
name: 'calculator',
description: 'Evaluate mathematical expressions',
parameters: CalculatorParamsSchema,
executor: async (params) => {
try {
// WARNING: eval is dangerous - use a proper math parser in production
const result = eval(params.expression);
return {
content: `Result: ${result}`,
metadata: new ToolUseCountMetadata(1),
};
} catch (error) {
return {
content: `Error: ${error.message}`,
metadata: new ToolUseCountMetadata(1),
};
}
},
};
const agent = new Agent({
client,
tools: [calculatorTool],
finishTool: SIMPLE_FINISH_TOOL,
});
Weather API Tool
import { z } from 'zod';
import type { Tool, ToolResult } from '@stirrup/stirrup';
import { ToolUseCountMetadata } from '@stirrup/stirrup';
const WeatherParamsSchema = z.object({
location: z.string().describe('City name or location'),
unit: z.enum(['celsius', 'fahrenheit']).default('celsius').describe('Temperature unit'),
});
const weatherTool: Tool<typeof WeatherParamsSchema, ToolUseCountMetadata> = {
name: 'get_weather',
description: 'Get current weather for a location',
parameters: WeatherParamsSchema,
executor: async (params): Promise<ToolResult<ToolUseCountMetadata>> => {
// In production, call a real weather API
const temp = params.unit === 'celsius' ? 22 : 72;
const condition = 'Sunny';
return {
content: `Weather in ${params.location}: ${temp}°${params.unit === 'celsius' ? 'C' : 'F'}, ${condition}`,
metadata: new ToolUseCountMetadata(1),
};
},
};
Database Query Tool
import { z } from 'zod';
import type { Tool, ToolResult } from '@stirrup/stirrup';
import { ToolUseCountMetadata } from '@stirrup/stirrup';
const DbQueryParamsSchema = z.object({
query: z.string().describe('SQL query to execute'),
});
const databaseTool: Tool<typeof DbQueryParamsSchema, ToolUseCountMetadata> = {
name: 'db_query',
description: 'Execute a SQL query against the database',
parameters: DbQueryParamsSchema,
executor: async (params) => {
// In production, use a proper database client with parameterized queries
console.log(`Executing query: ${params.query}`);
const results = [
{ id: 1, name: 'Alice', age: 30 },
{ id: 2, name: 'Bob', age: 25 },
];
return {
content: `Query results (${results.length} rows):\n${JSON.stringify(results, null, 2)}`,
metadata: new ToolUseCountMetadata(1),
};
},
};
Sub-Agents
Delegate specialized tasks to sub-agents:
import { Agent, SIMPLE_FINISH_TOOL, CALCULATOR_TOOL, WebToolProvider } from '@stirrup/stirrup';
// Create specialized sub-agents
const researchAgent = new Agent({
client,
name: 'researcher',
maxTurns: 5,
tools: [new WebToolProvider(180_000, process.env.BRAVE_API_KEY)],
finishTool: SIMPLE_FINISH_TOOL,
systemPrompt: 'You are a research specialist. Use web search to find accurate information.',
});
const mathAgent = new Agent({
client,
name: 'mathematician',
maxTurns: 3,
tools: [CALCULATOR_TOOL],
finishTool: SIMPLE_FINISH_TOOL,
systemPrompt: 'You are a math specialist. Calculate precisely using the calculator tool.',
});
// Create coordinator that delegates to sub-agents
const coordinatorAgent = new Agent({
client,
name: 'coordinator',
maxTurns: 10,
tools: [
researchAgent.toTool('Delegate research tasks to the research specialist'),
mathAgent.toTool('Delegate math calculations to the math specialist'),
],
finishTool: SIMPLE_FINISH_TOOL,
systemPrompt: `You are a task coordinator. Delegate tasks to specialized sub-agents:
- Use 'researcher' for web search and research tasks
- Use 'mathematician' for calculations and math problems`,
});
await using session = coordinatorAgent.session();
await session.run('What is the population of Tokyo and what will it be in 5 years with 2% growth?');
Event Monitoring
Basic Event Listeners
const agent = new Agent({
client,
name: 'event-agent',
maxTurns: 5,
tools: DEFAULT_TOOLS,
finishTool: SIMPLE_FINISH_TOOL,
});
// Listen to agent events
agent.on('run:start', ({ task }) => {
console.log('🚀 Agent started');
});
agent.on('turn:start', ({ turn, maxTurns }) => {
console.log(`📍 Turn ${turn + 1}/${maxTurns}`);
});
agent.on('message:assistant', ({ content, toolCalls }) => {
if (content) console.log('💬 Assistant:', content.substring(0, 100));
if (toolCalls) console.log('🔧 Tools:', toolCalls.map(tc => tc.name).join(', '));
});
agent.on('tool:complete', ({ name, success }) => {
console.log(`${success ? '✅' : '❌'} Tool: ${name}`);
});
agent.on('run:complete', ({ result, duration }) => {
console.log(`✅ Completed in ${duration}ms`);
});
await using session = agent.session();
await session.run('What is 2+2?');
Streaming Events
for await (const event of agent.runStream('What is 2+2?')) {
switch (event.type) {
case 'start':
console.log('🚀 Started');
break;
case 'turn:start':
console.log(`📍 Turn ${event.turn + 1}/${event.maxTurns}`);
break;
case 'message':
if (event.message.role === 'assistant') {
console.log('💬 Assistant:', event.message.content);
}
break;
case 'complete':
console.log('✅ Complete');
break;
}
}
Cancellation with AbortController
const controller = new AbortController();
// Cancel after 3 turns
let turnCount = 0;
agent.on('turn:start', () => {
turnCount++;
if (turnCount >= 3) {
console.log('⏰ Cancelling...');
controller.abort('Maximum turns reached');
}
});
await using session = agent.session();
try {
await session.run('Count to 100', { signal: controller.signal });
} catch (error) {
if (error.name === 'AbortError') {
console.log('✅ Successfully cancelled');
}
}
Structured Logging
The easiest way to monitor agent activity:
import { createStructuredLogger } from '@stirrup/stirrup';
const cleanup = createStructuredLogger(agent, {
level: 'debug', // or 'info'
});
await using session = agent.session();
await session.run('Create a chart');
cleanup(); // Stop logging
This automatically shows: - Turn-by-turn progress - Tool calls with parameters - Sub-agent activity (indented) - Token usage per turn - Final summary with totals
File Handling
Saving Output Files
await using session = agent.session({ outputDir: './output' });
const result = await session.run('Create a chart of sales data');
// Files listed in result.finishParams.paths are automatically saved to ./output/
console.log('Saved files:', result.finishParams?.paths);
Multiple Output Directories
// Save research results
await using session1 = agent.session({ outputDir: './research' });
await session1.run('Research topic and save summary');
// Save analysis in different directory
await using session2 = agent.session({ outputDir: './analysis' });
await session2.run('Analyze data and create visualizations');
Code Execution Backends
Local Execution (Default)
import { LocalCodeExecToolProvider } from '@stirrup/stirrup';
const agent = new Agent({
client,
tools: [new LocalCodeExecToolProvider()],
finishTool: SIMPLE_FINISH_TOOL,
});
Docker Execution
import { DockerCodeExecToolProvider } from '@stirrup/stirrup';
const agent = new Agent({
client,
tools: [new DockerCodeExecToolProvider('python:3.12-slim')],
finishTool: SIMPLE_FINISH_TOOL,
});
E2B Cloud Sandboxes
import { E2BCodeExecToolProvider } from '@stirrup/stirrup';
const agent = new Agent({
client,
tools: [new E2BCodeExecToolProvider({
apiKey: process.env.E2B_API_KEY!,
template: 'base',
})],
finishTool: SIMPLE_FINISH_TOOL,
});
Skills
Load skills from a local skills/ directory and make them available in the execution environment:
await using session = agent.session({
skillsDir: 'skills',
inputFiles: ['examples/skills/sample_data.csv'],
outputDir: './output/skills_example',
});
await session.run('Use the data_analysis skill to analyze the CSV and create a chart.');
Next Steps
- Creating Tools - Deep dive into custom tools
- Tool Providers - Managing tool lifecycle
- Sub-Agents - Complex delegation patterns
- Code Execution - Different execution backends