Streaming (SSE)
The Computer Agents API uses Server-Sent Events (SSE) to stream real-time responses from agent executions. This enables you to show progress, tool calls, and results as they happen.
Overview
When you create a thread with messages or send a follow-up message, the API returns an SSE stream with events describing the execution progress.
curl -X POST https://api.computer-agents.com/v1/threads \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{ "role": "user", "content": "Create hello.py" }],
"stream": true
}'Event Format
Each event follows this format:
data: {"type":"event_type","...payload"}Events are newline-separated. Parse each line starting with data: as JSON.
Event Types
thread.created
Emitted when a new thread is created.
{
"type": "thread.created",
"thread": {
"id": "thread_xxx",
"status": "running",
"environmentId": "env_xxx"
}
}response.created
Emitted when the agent starts processing.
{
"type": "response.created",
"response": {
"id": "resp_xxx",
"status": "in_progress"
}
}response.output_item.added
Emitted when a new output item (message, tool call) is added.
{
"type": "response.output_item.added",
"item": {
"type": "message",
"content": ""
}
}response.content_part.added
Emitted when content is added to a message.
{
"type": "response.content_part.added",
"part": {
"type": "text",
"text": "I'll create "
}
}response.content_part.delta
Emitted for incremental content updates (streaming text).
{
"type": "response.content_part.delta",
"delta": {
"text": "a hello.py file"
}
}response.item.completed
Emitted when an output item is fully completed.
{
"type": "response.item.completed",
"item": {
"type": "message",
"role": "assistant",
"content": "I've created hello.py with the following content..."
}
}response.completed
Emitted when the full response is complete.
{
"type": "response.completed",
"response": {
"id": "resp_xxx",
"status": "completed",
"output": [...],
"usage": {
"inputTokens": 500,
"outputTokens": 250
}
}
}stream.completed
Final event indicating the stream has ended.
{
"type": "stream.completed",
"thread": {
"id": "thread_xxx",
"status": "completed"
},
"usage": {
"inputTokens": 500,
"outputTokens": 250,
"totalCost": 0.0075
}
}error
Emitted when an error occurs.
{
"type": "error",
"error": {
"code": "execution_failed",
"message": "Agent execution failed: timeout"
}
}Client Implementation
JavaScript/TypeScript
async function executeWithStream(task: string) {
const response = await fetch('https://api.computer-agents.com/v1/threads', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
messages: [{ role: 'user', content: task }],
stream: true
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || ''; // Keep incomplete line in buffer
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const event = JSON.parse(line.slice(6));
handleEvent(event);
} catch (e) {
console.error('Failed to parse event:', line);
}
}
}
}
}
function handleEvent(event: any) {
switch (event.type) {
case 'thread.created':
console.log('Thread created:', event.thread.id);
break;
case 'response.content_part.delta':
process.stdout.write(event.delta.text);
break;
case 'response.item.completed':
console.log('\nItem completed:', event.item.type);
break;
case 'stream.completed':
console.log('\nDone! Cost:', event.usage.totalCost);
break;
case 'error':
console.error('Error:', event.error.message);
break;
}
}Python
import requests
import json
def execute_with_stream(task: str):
response = requests.post(
'https://api.computer-agents.com/v1/threads',
headers={
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
},
json={
'messages': [{'role': 'user', 'content': task}],
'stream': True
},
stream=True
)
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: '):
try:
event = json.loads(line[6:])
handle_event(event)
except json.JSONDecodeError:
pass
def handle_event(event):
event_type = event.get('type')
if event_type == 'thread.created':
print(f"Thread created: {event['thread']['id']}")
elif event_type == 'response.content_part.delta':
print(event['delta']['text'], end='', flush=True)
elif event_type == 'stream.completed':
print(f"\nDone! Cost: ${event['usage']['totalCost']}")
elif event_type == 'error':
print(f"Error: {event['error']['message']}")React Hook
import { useState, useCallback } from 'react';
interface StreamState {
threadId: string | null;
content: string;
status: 'idle' | 'streaming' | 'completed' | 'error';
error: string | null;
cost: number | null;
}
export function useAgentStream() {
const [state, setState] = useState<StreamState>({
threadId: null,
content: '',
status: 'idle',
error: null,
cost: null
});
const execute = useCallback(async (task: string) => {
setState(s => ({ ...s, status: 'streaming', content: '', error: null }));
try {
const response = await fetch('/api/threads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
messages: [{ role: 'user', content: task }],
stream: true
})
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const event = JSON.parse(line.slice(6));
if (event.type === 'thread.created') {
setState(s => ({ ...s, threadId: event.thread.id }));
} else if (event.type === 'response.content_part.delta') {
setState(s => ({ ...s, content: s.content + event.delta.text }));
} else if (event.type === 'stream.completed') {
setState(s => ({
...s,
status: 'completed',
cost: event.usage.totalCost
}));
} else if (event.type === 'error') {
setState(s => ({
...s,
status: 'error',
error: event.error.message
}));
}
}
}
}
} catch (error) {
setState(s => ({
...s,
status: 'error',
error: error instanceof Error ? error.message : 'Unknown error'
}));
}
}, []);
return { ...state, execute };
}Non-Streaming Mode
Set stream: false to receive a single JSON response instead:
curl -X POST https://api.computer-agents.com/v1/threads \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"messages": [{ "role": "user", "content": "Create hello.py" }],
"stream": false
}'Response:
{
"thread": { ... },
"execution": {
"success": true,
"response": "I've created hello.py...",
"actions": ["Created: hello.py"],
"durationMs": 5234,
"usage": {
"inputTokens": 150,
"outputTokens": 50
}
}
}Non-streaming mode waits for the full execution to complete before responding. Use streaming for better UX with progress updates.
Error Handling
Always handle the error event type:
if (event.type === 'error') {
const { code, message } = event.error;
switch (code) {
case 'budget_exceeded':
showUpgradePrompt();
break;
case 'execution_timeout':
showRetryOption();
break;
default:
showErrorMessage(message);
}
}Common Error Codes
| Code | Description |
|---|---|
budget_exceeded | User budget exhausted |
execution_timeout | Task took too long |
execution_failed | Agent execution error |
rate_limited | Too many requests |
Related
- Threads - Create and manage threads
- Error Handling - Full error reference