bot0 Agent Loop - Implementation Guide
Overview
The agent loop is the core execution engine of the bot0 daemon. It receives user input, calls Claude, handles tool calls, and loops until task completion. This document describes the implemented architecture and how to use it.
Key principle: Receive input → Call LLM → Execute tools → Loop until done.
Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ BOT0 AGENT LOOP │
│ │
│ User Input ──► Agent Loop ──► Claude API ──► Tool Execution ──► Response │
│ │ │ │ │
│ └──────────────┴──────────────┘ │
│ (loops until done) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ COMPONENTS │ │
│ │ │ │
│ │ 1. Tool Registry - Register, list, execute tools │ │
│ │ 2. Basic Tools - shell, read_file, write_file, list_dir, glob│ │
│ │ 3. Bot0Agent - Core loop calling Claude + handling tools │ │
│ │ 4. HTTP Server - Test endpoint for development │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
File Structure
packages/daemon/
├── src/
│ ├── index.ts # Entry point, exports
│ ├── agent/
│ │ ├── index.ts # Exports
│ │ ├── loop.ts # Bot0Agent class - the main agent loop
│ │ └── types.ts # AgentTask, RunOptions, ProgressUpdate types
│ ├── tools/
│ │ ├── index.ts # ToolRegistry class + exports
│ │ ├── types.ts # Tool interface, ToolDefinition
│ │ ├── shell.ts # Execute shell commands
│ │ ├── read-file.ts # Read file contents
│ │ ├── write-file.ts # Write file contents
│ │ ├── list-dir.ts # List directory contents
│ │ └── glob.ts # Search files by pattern
│ └── server/
│ └── http.ts # Simple HTTP server for testing
└── package.json
Core Components
1. Tool Registry
The ToolRegistry manages tool registration and execution:
import { ToolRegistry, shellTool, readFileTool } from '@bot0/daemon'; const registry = new ToolRegistry(); registry.register(shellTool); registry.register(readFileTool); // Get tool definitions for Claude API const definitions = registry.getDefinitions(); // Execute a tool const result = await registry.execute('shell', { command: 'ls -la' });
Methods:
| Method | Description |
|---|---|
register(tool) | Register a new tool |
get(name) | Get a tool by name |
getNames() | List all registered tool names |
getDefinitions() | Get Claude API tool definitions |
execute(name, input) | Execute a tool |
has(name) | Check if tool exists |
2. Tool Interface
All tools implement the Tool interface:
interface Tool { name: string; description: string; inputSchema: Anthropic.Tool['input_schema']; execute: (input: Record<string, unknown>) => Promise<string>; }
Built-in Tools:
| Tool | Name | Description |
|---|---|---|
shellTool | shell | Execute shell commands |
readFileTool | read_file | Read file contents |
writeFileTool | write_file | Write content to files |
listDirTool | list_dir | List directory contents |
globTool | glob | Search files by pattern |
3. Bot0Agent
The main agent class that runs the loop:
import { Bot0Agent, ToolRegistry } from '@bot0/daemon'; const agent = new Bot0Agent(registry, { model: 'claude-sonnet-4-20250514', maxTokens: 8096, }); const result = await agent.run( { id: '123', prompt: 'List the files in the current directory', source: 'local', }, { onProgress: (update) => console.log(update), maxIterations: 50, } );
Agent Loop Flow:
┌─────────────────────────────────────────────────────────────────────────────┐
│ AGENT LOOP FLOW │
│ │
│ 1. User sends prompt │
│ │ │
│ ▼ │
│ 2. Build messages array with system prompt + user message │
│ │ │
│ ▼ │
│ 3. Call Claude API with tools │
│ │ │
│ ├─► stop_reason: "end_turn" ─► Return response (DONE) │
│ │ │
│ └─► stop_reason: "tool_use" ─► Execute tools │
│ │ │
│ ▼ │
│ 4. Add tool results to messages │
│ │ │
│ └─► Loop back to step 3 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4. Types
AgentTask:
interface AgentTask { id: string; // Unique task identifier prompt: string; // User prompt/instruction source: 'local' | 'remote'; context?: string; // Optional context }
RunOptions:
interface RunOptions { onProgress?: (update: ProgressUpdate) => void; maxIterations?: number; // Default: 50 systemPrompt?: string; // Override system prompt }
ProgressUpdate:
interface ProgressUpdate { type: 'thinking' | 'assistant' | 'tool_call' | 'tool_result' | 'error' | 'done'; content?: string; // For assistant responses tool?: string; // Tool name input?: unknown; // Tool input result?: string; // Tool result error?: string; // Error message }
AgentResponse:
interface AgentResponse { success: boolean; response?: Anthropic.Message; text?: string; // Extracted text response error?: string; iterations?: number; // Loop count }
HTTP Test Server
A simple HTTP server for testing the agent:
import { Bot0Agent, ToolRegistry, startTestServer } from '@bot0/daemon'; const registry = new ToolRegistry(); // ... register tools const agent = new Bot0Agent(registry); startTestServer(agent, 7070);
Endpoints:
| Method | Path | Description |
|---|---|---|
GET | /health | Health check + list tools |
GET | /tools | Get tool definitions |
POST | /task | Run a task |
POST /task Request:
{ "prompt": "List files in the current directory", "context": "Optional context string" }
POST /task Response:
{ "success": true, "text": "Here are the files...", "iterations": 2 }
Usage
Development Mode
Start the agent server for testing:
cd packages/daemon export ANTHROPIC_API_KEY=sk-... pnpm dev:agent
Output:
bot0 agent server running on http://localhost:7070
Endpoints:
GET /health - Health check
GET /tools - List available tools
POST /task - Run a task (JSON body: { "prompt": "..." })
Test Commands
# Health check curl http://localhost:7070/health # List available tools curl http://localhost:7070/tools # Run a simple task curl -X POST http://localhost:7070/task \ -H "Content-Type: application/json" \ -d '{"prompt": "List the files in the current directory"}' # Multi-tool task curl -X POST http://localhost:7070/task \ -H "Content-Type: application/json" \ -d '{"prompt": "Create a file called hello.txt with Hello World, then read it back"}'
Creating Custom Tools
Implement the Tool interface:
import type { Tool } from '@bot0/daemon'; export const myCustomTool: Tool = { name: 'my_tool', description: 'Description shown to Claude', inputSchema: { type: 'object', properties: { param1: { type: 'string', description: 'First parameter', }, }, required: ['param1'], }, execute: async (input) => { const param1 = input.param1 as string; // Do something return `Result: ${param1}`; }, }; // Register it registry.register(myCustomTool);
System Prompt
The default system prompt:
You are bot0, an intelligent AI assistant with access to tools that let you
interact with the local system.
You help users accomplish tasks by using the available tools. Be proactive
in solving problems and always explain what you're doing.
Guidelines:
- Use tools to gather information before making decisions
- If a command fails, analyze the error and try an alternative approach
- Be concise but thorough in your explanations
- Always verify your work when possible
Override with RunOptions.systemPrompt.
Error Handling
The agent handles errors gracefully:
-
Tool execution errors - Returned to Claude as error results, allowing it to retry or take alternative action
-
API errors - Caught and returned in
AgentResponse.error -
Max iterations - Prevents infinite loops (default: 50)
const result = await agent.run(task); if (!result.success) { console.error('Task failed:', result.error); console.log('Completed iterations:', result.iterations); }
Programmatic Usage
Import and use in your own code:
import { Bot0Agent, ToolRegistry, shellTool, readFileTool, writeFileTool, listDirTool, globTool, } from '@bot0/daemon'; // Create registry with all tools const registry = new ToolRegistry(); registry.register(shellTool); registry.register(readFileTool); registry.register(writeFileTool); registry.register(listDirTool); registry.register(globTool); // Create agent const agent = new Bot0Agent(registry); // Run a task const result = await agent.run({ id: Date.now().toString(), prompt: 'Find all TypeScript files and count the lines of code', source: 'local', }); console.log('Success:', result.success); console.log('Response:', result.text);
Future Additions
The following features are planned but not yet implemented:
- Hooks system -
preToolUse,postToolUsecallbacks - Context compaction - Summarize long conversations
- Streaming responses - Real-time token streaming
- ctx0 integration - Context protocol support
- Error recovery - Automatic retries with backoff
- Rate limiting - Prevent API abuse
References
- bot0 Daemon - Daemon system design
- Tools - Tool documentation
- bot0 Architecture - Overall system design
- Proactive Engine - Autonomous actions