Skip to Content
AgentsStreaming & Events

Streaming & Events

The Computer Agents SDK uses Server-Sent Events (SSE) for real-time streaming of execution progress. This allows you to show progress, capture intermediate results, and respond to events as they happen.

Basic Streaming

Use the onEvent callback to receive events:

const result = await client.run('Build a REST API', { environmentId: 'env_xxx', onEvent: (event) => { console.log(`Event: ${event.type}`); } });

Or with threads.sendMessage:

const result = await client.threads.sendMessage(threadId, { content: 'Create a user model', onEvent: (event) => { console.log(`Event: ${event.type}`); } });

Event Types

The SDK emits these event types during execution:

response.started

Fired when the agent begins processing.

onEvent: (event) => { if (event.type === 'response.started') { console.log('Agent started processing...'); } }

response.item.completed

Fired when the agent completes a response item (text, tool call, reasoning, or file change).

onEvent: (event) => { if (event.type === 'response.item.completed') { const item = event.item; switch (item.type) { case 'text': console.log('Agent said:', item.content); break; case 'tool_call': console.log('Tool called:', item.name); break; case 'reasoning': console.log('Agent reasoning:', item.content); break; case 'file_change': console.log('File changed:', item.path); break; } } }

response.completed

Fired when the agent finishes its response.

onEvent: (event) => { if (event.type === 'response.completed') { console.log('Final response:', event.response.content); } }

stream.completed

Fired when the entire stream ends, including run metadata.

onEvent: (event) => { if (event.type === 'stream.completed') { const run = event.run; console.log('Run completed:', run.id); console.log('Status:', run.status); console.log('Tokens:', run.tokens?.input, '+', run.tokens?.output); } }

stream.error

Fired if an error occurs during streaming.

onEvent: (event) => { if (event.type === 'stream.error') { console.error('Stream error:', event.error); console.error('Message:', event.message); } }

TypeScript Event Types

Import the event type for full type safety:

import type { MessageStreamEvent } from 'computer-agents'; const handleEvent = (event: MessageStreamEvent) => { switch (event.type) { case 'response.started': // event is ResponseStartedEvent break; case 'response.item.completed': // event is ResponseItemCompletedEvent // event.item is typed break; case 'response.completed': // event is ResponseCompletedEvent // event.response is typed break; case 'stream.completed': // event is StreamCompletedEvent // event.run is typed break; case 'stream.error': // event is StreamErrorEvent // event.error and event.message are typed break; } };

Progress Indicators

Build a progress indicator by tracking events:

let totalItems = 0; let currentPhase = 'starting'; const result = await client.run('Build a complex app', { environmentId: 'env_xxx', onEvent: (event) => { switch (event.type) { case 'response.started': currentPhase = 'processing'; console.log('⏳ Processing...'); break; case 'response.item.completed': totalItems++; const item = event.item; if (item.type === 'tool_call') { console.log(`🔧 [${totalItems}] Tool: ${item.name}`); } else if (item.type === 'text') { console.log(`💬 [${totalItems}] Response chunk`); } break; case 'response.completed': currentPhase = 'complete'; console.log('✅ Response complete'); break; case 'stream.completed': console.log(`\n📊 Stats: ${event.run.tokens?.input} in, ${event.run.tokens?.output} out`); break; case 'stream.error': currentPhase = 'error'; console.log('❌ Error:', event.error); break; } } });

Real-Time UI Updates

For React or other UI frameworks, use state to drive updates:

// React example const [status, setStatus] = useState('idle'); const [items, setItems] = useState([]); const [result, setResult] = useState(null); const executeTask = async (task: string) => { setStatus('running'); setItems([]); try { const response = await client.run(task, { environmentId: envId, onEvent: (event) => { if (event.type === 'response.item.completed') { setItems(prev => [...prev, event.item]); } if (event.type === 'stream.error') { setStatus('error'); } } }); setResult(response); setStatus('complete'); } catch (error) { setStatus('error'); } };

Collecting All Events

Store events for logging or replay:

const events: MessageStreamEvent[] = []; const result = await client.run('Task', { environmentId: 'env_xxx', onEvent: (event) => { events.push(event); } }); // Log all events console.log('Total events:', events.length); console.log('Event types:', events.map(e => e.type)); // Find specific events const toolCalls = events .filter(e => e.type === 'response.item.completed') .filter(e => e.item.type === 'tool_call'); console.log('Tool calls made:', toolCalls.length);

Timeout Configuration

Configure streaming timeout:

const result = await client.run('Long running task', { environmentId: 'env_xxx', timeout: 300000, // 5 minutes (default: 10 minutes) onEvent: (event) => { console.log(event.type); } });

For sendMessage:

const result = await client.threads.sendMessage(threadId, { content: 'Complex task', timeout: 600000 // 10 minutes });

Error Handling in Streams

Handle both stream errors and exceptions:

try { const result = await client.run('Task', { environmentId: 'env_xxx', onEvent: (event) => { if (event.type === 'stream.error') { // Handle stream-level error (task failed) console.error('Task failed:', event.error); } } }); } catch (error) { // Handle connection/API errors if (error instanceof ApiClientError) { console.error('API error:', error.message); } }

Best Practices

1. Always Handle Errors

onEvent: (event) => { if (event.type === 'stream.error') { // Log, notify, or recover logger.error('Stream error', { error: event.error }); } }

2. Use Event Types for UI States

const getStatusFromEvent = (event: MessageStreamEvent) => { switch (event.type) { case 'response.started': return 'processing'; case 'response.completed': return 'complete'; case 'stream.error': return 'error'; default: return 'running'; } };

3. Debounce UI Updates

// For high-frequency updates import { debounce } from 'lodash'; const debouncedUpdate = debounce((item) => { setItems(prev => [...prev, item]); }, 100); onEvent: (event) => { if (event.type === 'response.item.completed') { debouncedUpdate(event.item); } }

4. Track Token Usage

let totalTokens = { input: 0, output: 0 }; onEvent: (event) => { if (event.type === 'stream.completed' && event.run.tokens) { totalTokens.input += event.run.tokens.input; totalTokens.output += event.run.tokens.output; } }

Next Steps

Last updated on