Subscribe to a real-time stream of task execution events. The frontend uses this to show live tool calls, phase changes, and completion — zero polling required.
Endpoint
This is an SSE (Server-Sent Events) endpoint. Connect with EventSource:
const es = new EventSource("http://localhost:3001/tasks/events");
es.addEventListener("task:start", (e) => {
const data = JSON.parse(e.data);
console.log(`Run #${data.runNumber} started for task ${data.taskId}`);
});
es.addEventListener("task:tool", (e) => {
const data = JSON.parse(e.data);
console.log(`Tool: ${data.tool}`, data.output);
});
es.addEventListener("task:complete", (e) => {
const data = JSON.parse(e.data);
console.log(`Run completed: ${data.status} (${data.durationMs}ms)`);
});
Event types
task:start
Emitted when a task run begins.
{
"type": "task:start",
"taskId": "abc123",
"runId": "run-456",
"runNumber": 3,
"startedAt": 1709556000000,
"phases": [
{ "id": "research", "description": "Research phase", "status": "pending" },
{ "id": "write", "description": "Write output", "status": "pending" }
]
}
Emitted when a tool call finishes during execution.
{
"type": "task:tool",
"taskId": "abc123",
"runId": "run-456",
"tool": "webSearch",
"input": { "query": "latest AI news March 2026" },
"output": { "results": [...] },
"status": "complete"
}
task:phase
Emitted when a phase changes status.
{
"type": "task:phase",
"taskId": "abc123",
"phase": "Research phase",
"status": "completed"
}
task:complete
Emitted when a run finishes successfully.
{
"type": "task:complete",
"taskId": "abc123",
"runId": "run-456",
"runNumber": 3,
"status": "completed",
"output": "## AI News Summary\n\n...",
"durationMs": 45200,
"toolCount": 7,
"qualityScore": 0.85
}
task:error
Emitted when a run fails.
{
"type": "task:error",
"taskId": "abc123",
"runId": "run-456",
"error": "Rate limit exceeded from search provider"
}
task:steer
Emitted when a steering comment is posted.
{
"type": "task:steer",
"taskId": "abc123",
"comment": "Focus more on open-source models"
}
Connection handling
- The SSE connection stays open indefinitely
- If the connection drops, reconnect with a new
EventSource — events are not replayed
- Events stream for all tasks, not a specific one. Filter by
taskId on the client side
For initial load, use GET /tasks and GET /tasks/:id/runs to fetch existing data. The SSE stream handles everything after that — no polling needed.