Appearance
Event System
Overview
AgenTree provides a comprehensive event system for monitoring agent execution in real-time. Events are emitted throughout the agent lifecycle, from creation to completion, providing full visibility into the decision-making and execution process.
Event Categories
Agent Lifecycle Events
agentCreated
- Agent instance createdagentStarted
- Agent execution beganagentCompleted
- Agent finished successfullyagentError
- Agent encountered an error
Execution Events
contextLoaded
- Context files/URLs loadedllmCall
- LLM API call initiatedstreamChunk
- Streaming response chunk received
Tool Events
toolCalls
- Tool execution batch (legacy)toolCallStarted
- Individual tool execution startedtoolCallCompleted
- Individual tool execution finished
Hierarchy Events
childCreated
- Child agent created by parent
Basic Monitoring
Simple Progress Tracking
typescript
import { Agent } from 'agentree';
const agent = new Agent({
name: "data-processor",
task: "Process and analyze customer data",
tools: [dataTools]
});
// Track major milestones
agent.on('agentStarted', (data) => {
console.log(`🚀 Started: ${data.name}`);
});
agent.on('agentCompleted', (data) => {
console.log(`✅ Completed: ${data.name} in ${data.executionTime}ms`);
console.log(` Result: ${data.result.result.substring(0, 100)}...`);
});
agent.on('agentError', (data) => {
console.error(`❌ Error in ${data.name}: ${data.error}`);
});
await agent.execute();
Tool Usage Monitoring
typescript
const toolMetrics = new Map();
agent.on('toolCallStarted', (data) => {
const startTime = Date.now();
toolMetrics.set(data.toolCallId, {
name: data.toolName,
startTime,
input: data.toolInput
});
console.log(`🔧 Starting: ${data.toolName}`);
});
agent.on('toolCallCompleted', (data) => {
const metric = toolMetrics.get(data.toolCallId);
if (metric) {
console.log(` ✅ ${data.toolName}: ${data.duration}ms`);
if (data.toolError) {
console.error(` ❌ Error: ${data.toolError}`);
}
}
});
Child Agent Tracking
typescript
const agentHierarchy = new Map();
agent.on('childCreated', (data) => {
agentHierarchy.set(data.childId, {
name: data.childName,
task: data.childTask,
parentId: data.parentId,
depth: data.depth
});
const indent = ' '.repeat(data.depth - 1);
console.log(`${indent}👶 Created: ${data.childName}`);
console.log(`${indent} Task: ${data.childTask}`);
});
Advanced Monitoring
Hierarchical Logging
typescript
class HierarchicalLogger {
private agentInfo = new Map();
constructor(rootAgent: Agent) {
this.setupLogging(rootAgent);
}
private setupLogging(agent: Agent) {
agent.on('agentCreated', (data) => {
this.agentInfo.set(data.id, {
name: data.name,
depth: data.depth,
parentId: data.parentId,
startTime: Date.now()
});
if (data.depth === 0) {
console.log(`🌳 Root Agent: ${data.name}`);
}
});
agent.on('agentStarted', (data) => {
const indent = ' '.repeat(data.depth);
console.log(`${indent}▶️ ${data.name} started`);
});
agent.on('agentCompleted', (data) => {
const indent = ' '.repeat(data.depth);
const info = this.agentInfo.get(data.id);
const totalTime = Date.now() - (info?.startTime || 0);
console.log(`${indent}✅ ${data.name} completed (${data.executionTime}ms total: ${totalTime}ms)`);
});
agent.on('toolCallCompleted', (data) => {
const indent = ' '.repeat(data.depth + 1);
const status = data.toolError ? '❌' : '✅';
console.log(`${indent}${status} ${data.toolName} (${data.duration}ms)`);
});
}
}
const logger = new HierarchicalLogger(agent);
await agent.execute();
Performance Monitoring
typescript
class PerformanceMonitor {
private metrics = {
agents: new Map(),
tools: new Map(),
startTime: Date.now()
};
constructor(agent: Agent) {
this.monitor(agent);
}
private monitor(agent: Agent) {
// Track agent performance
agent.on('agentStarted', (data) => {
this.metrics.agents.set(data.id, {
name: data.name,
depth: data.depth,
startTime: Date.now(),
toolCalls: 0,
llmCalls: 0
});
});
agent.on('agentCompleted', (data) => {
const agentMetric = this.metrics.agents.get(data.id);
if (agentMetric) {
agentMetric.endTime = Date.now();
agentMetric.duration = data.executionTime;
agentMetric.success = data.success;
}
});
// Track tool performance
agent.on('toolCallCompleted', (data) => {
const agentMetric = this.metrics.agents.get(data.id);
if (agentMetric) {
agentMetric.toolCalls++;
}
// Aggregate tool stats
if (!this.metrics.tools.has(data.toolName)) {
this.metrics.tools.set(data.toolName, {
calls: 0,
totalDuration: 0,
errors: 0,
avgDuration: 0
});
}
const toolMetric = this.metrics.tools.get(data.toolName)!;
toolMetric.calls++;
toolMetric.totalDuration += data.duration;
toolMetric.avgDuration = toolMetric.totalDuration / toolMetric.calls;
if (data.toolError) {
toolMetric.errors++;
}
});
// Track LLM calls
agent.on('llmCall', (data) => {
const agentMetric = this.metrics.agents.get(data.id);
if (agentMetric) {
agentMetric.llmCalls++;
}
});
}
public getReport() {
const totalDuration = Date.now() - this.metrics.startTime;
return {
totalDuration,
agents: Array.from(this.metrics.agents.values()),
tools: Array.from(this.metrics.tools.entries()).map(([name, stats]) => ({
name,
...stats
}))
};
}
}
const monitor = new PerformanceMonitor(agent);
await agent.execute();
const report = monitor.getReport();
console.log('\n📊 Performance Report:');
console.log(`Total Duration: ${report.totalDuration}ms`);
console.log(`Agents: ${report.agents.length}`);
console.log('\nTool Performance:');
report.tools.forEach(tool => {
console.log(` ${tool.name}: ${tool.calls} calls, avg ${tool.avgDuration.toFixed(1)}ms`);
});
Real-time Streaming Monitor
typescript
class StreamingMonitor {
private currentContent = '';
private chunkCount = 0;
constructor(agent: Agent) {
this.monitor(agent);
}
private monitor(agent: Agent) {
agent.on('streamChunk', (data) => {
this.chunkCount++;
if (data.chunk.content) {
this.currentContent += data.chunk.content;
// Display content as it arrives
process.stdout.write(data.chunk.content);
}
if (data.chunk.tool_calls) {
console.log(`\n🔧 Tool calls detected in chunk ${this.chunkCount}`);
}
if (data.chunk.done) {
console.log(`\n\n📊 Stream completed: ${this.chunkCount} chunks, ${this.currentContent.length} characters`);
this.reset();
}
});
}
private reset() {
this.currentContent = '';
this.chunkCount = 0;
}
}
const agent = new Agent({
config: {
streaming: true, // Enable streaming
// ...
}
});
const streamMonitor = new StreamingMonitor(agent);
await agent.execute();
Event Filtering
Filter by Agent Depth
typescript
// Monitor only root agents (depth 0)
agent.on('agentCompleted', (data) => {
if (data.depth === 0) {
console.log(`🏁 Root agent completed: ${data.name}`);
}
});
// Monitor only child agents (depth > 0)
agent.on('agentCreated', (data) => {
if (data.depth > 0) {
console.log(`👶 Child agent created: ${data.name} at depth ${data.depth}`);
}
});
// Monitor specific depth level
const TARGET_DEPTH = 2;
agent.on('toolCallCompleted', (data) => {
if (data.depth === TARGET_DEPTH) {
console.log(`🔧 Level ${TARGET_DEPTH} tool: ${data.toolName}`);
}
});
Filter by Agent Name/Type
typescript
// Monitor specific agent types
agent.on('agentCompleted', (data) => {
if (data.name.includes('analyzer')) {
console.log(`📊 Analysis completed: ${data.name}`);
} else if (data.name.includes('writer')) {
console.log(`✍️ Writing completed: ${data.name}`);
}
});
// Monitor specific tools
agent.on('toolCallCompleted', (data) => {
const criticalTools = ['database_query', 'api_call', 'file_write'];
if (criticalTools.includes(data.toolName)) {
console.log(`⚠️ Critical tool executed: ${data.toolName} (${data.duration}ms)`);
}
});
Filter by Execution Time
typescript
// Monitor slow operations
agent.on('toolCallCompleted', (data) => {
if (data.duration > 5000) { // > 5 seconds
console.log(`🐌 Slow tool: ${data.toolName} took ${data.duration}ms`);
}
});
agent.on('agentCompleted', (data) => {
if (data.executionTime > 30000) { // > 30 seconds
console.log(`⏰ Long-running agent: ${data.name} took ${data.executionTime}ms`);
}
});
Error Monitoring
Comprehensive Error Tracking
typescript
class ErrorTracker {
private errors = [];
constructor(agent: Agent) {
this.setupErrorTracking(agent);
}
private setupErrorTracking(agent: Agent) {
// Agent-level errors
agent.on('agentError', (data) => {
this.errors.push({
type: 'agent_error',
agentId: data.id,
agentName: data.name,
depth: data.depth,
error: data.error,
stack: data.stack,
timestamp: data.timestamp
});
console.error(`💥 Agent Error: ${data.name}`);
console.error(` Error: ${data.error}`);
if (data.depth > 0) {
console.error(` Depth: ${data.depth} (child agent failure)`);
}
});
// Tool-level errors
agent.on('toolCallCompleted', (data) => {
if (data.toolError) {
this.errors.push({
type: 'tool_error',
agentId: data.id,
agentName: data.name,
toolName: data.toolName,
toolInput: data.toolInput,
error: data.toolError,
duration: data.duration,
timestamp: data.timestamp
});
console.error(`🔧💥 Tool Error: ${data.toolName} in ${data.name}`);
console.error(` Input: ${JSON.stringify(data.toolInput)}`);
console.error(` Error: ${data.toolError}`);
}
});
}
public getErrorSummary() {
const agentErrors = this.errors.filter(e => e.type === 'agent_error');
const toolErrors = this.errors.filter(e => e.type === 'tool_error');
return {
totalErrors: this.errors.length,
agentErrors: agentErrors.length,
toolErrors: toolErrors.length,
errors: this.errors
};
}
}
const errorTracker = new ErrorTracker(agent);
await agent.execute();
const errorSummary = errorTracker.getErrorSummary();
if (errorSummary.totalErrors > 0) {
console.log(`\n💥 Error Summary: ${errorSummary.totalErrors} total errors`);
console.log(` Agent errors: ${errorSummary.agentErrors}`);
console.log(` Tool errors: ${errorSummary.toolErrors}`);
}
Custom Event Handlers
Event Aggregation
typescript
class EventAggregator {
private events = [];
private startTime = Date.now();
constructor(agent: Agent) {
this.setupAggregation(agent);
}
private setupAggregation(agent: Agent) {
const eventTypes = [
'agentCreated', 'agentStarted', 'agentCompleted', 'agentError',
'contextLoaded', 'llmCall', 'toolCallStarted', 'toolCallCompleted',
'streamChunk', 'childCreated'
];
eventTypes.forEach(eventType => {
agent.on(eventType as any, (data: any) => {
this.events.push({
eventType,
timestamp: data.timestamp || new Date().toISOString(),
agentId: data.id,
agentName: data.name,
depth: data.depth,
data: data
});
});
});
}
public getEventTimeline() {
return this.events.sort((a, b) =>
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
);
}
public getEventStats() {
const stats = {};
this.events.forEach(event => {
stats[event.eventType] = (stats[event.eventType] || 0) + 1;
});
return stats;
}
}
const aggregator = new EventAggregator(agent);
await agent.execute();
console.log('\n📊 Event Statistics:');
console.log(aggregator.getEventStats());
WebSocket Integration
typescript
import WebSocket from 'ws';
class WebSocketMonitor {
private ws: WebSocket;
constructor(agent: Agent, wsUrl: string) {
this.ws = new WebSocket(wsUrl);
this.setupMonitoring(agent);
}
private setupMonitoring(agent: Agent) {
const events = [
'agentCreated', 'agentStarted', 'agentCompleted',
'toolCallCompleted', 'childCreated'
];
events.forEach(eventType => {
agent.on(eventType as any, (data: any) => {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
eventType,
timestamp: new Date().toISOString(),
data
}));
}
});
});
}
public close() {
this.ws.close();
}
}
// Usage with WebSocket server
const wsMonitor = new WebSocketMonitor(agent, 'ws://localhost:8080');
await agent.execute();
wsMonitor.close();
Event Data Reference
Common Properties
All events include these base properties:
typescript
interface BaseEventData {
id: string; // Agent unique identifier
name: string; // Agent name
timestamp: string; // ISO timestamp
depth: number; // Agent depth in hierarchy
parentId?: string; // Parent agent ID (if child)
}
Event-Specific Properties
AgentResultEventData
typescript
interface AgentResultEventData extends BaseEventData {
result: AgentResult;
executionTime: number;
success: boolean;
}
ToolCallCompletedEventData
typescript
interface ToolCallCompletedEventData extends BaseEventData {
toolName: string;
toolInput: any;
toolOutput?: string;
toolError?: string;
duration: number;
toolCallId: string;
}
ChildAgentEventData
typescript
interface ChildAgentEventData extends BaseEventData {
parentId: string;
parentName: string;
childId: string;
childName: string;
childTask: string;
}
Best Practices
1. Selective Monitoring
Don't monitor every event in production:
typescript
// Development: Monitor everything
if (process.env.NODE_ENV === 'development') {
agent.on('streamChunk', (data) => {
process.stdout.write(data.chunk.content || '');
});
}
// Production: Monitor only important events
agent.on('agentError', (data) => {
logger.error(`Agent error: ${data.name} - ${data.error}`);
});
agent.on('agentCompleted', (data) => {
if (data.depth === 0) {
logger.info(`Task completed: ${data.name} in ${data.executionTime}ms`);
}
});
2. Performance Considerations
Avoid expensive operations in event handlers:
typescript
// Good: Lightweight logging
agent.on('toolCallCompleted', (data) => {
console.log(`Tool: ${data.toolName} (${data.duration}ms)`);
});
// Poor: Heavy processing in event handler
agent.on('toolCallCompleted', (data) => {
// Don't do this - blocks execution
await analyzeToolPerformance(data);
await updateDatabase(data);
await sendNotification(data);
});
// Better: Queue for async processing
const eventQueue = [];
agent.on('toolCallCompleted', (data) => {
eventQueue.push(data);
});
// Process queue separately
setInterval(() => {
const events = eventQueue.splice(0);
processEventsAsync(events);
}, 1000);
3. Memory Management
Clean up event listeners for long-running applications:
typescript
class ManagedMonitor {
private cleanup: () => void;
constructor(agent: Agent) {
const handlers = {
agentCompleted: (data) => console.log(`Completed: ${data.name}`),
agentError: (data) => console.error(`Error: ${data.error}`)
};
// Set up handlers
Object.entries(handlers).forEach(([event, handler]) => {
agent.on(event as any, handler);
});
// Store cleanup function
this.cleanup = () => {
Object.entries(handlers).forEach(([event, handler]) => {
agent.off(event as any, handler);
});
};
}
public dispose() {
this.cleanup();
}
}
const monitor = new ManagedMonitor(agent);
await agent.execute();
monitor.dispose(); // Clean up event listeners
Next Steps
- Configuration - Configure event behavior
- Debugging - Use events for debugging
- API Reference - Events - Complete event type definitions