Stream task events
Do this when you want to render or follow a run as it happens. Do not build a polling loop againstGET /v1/agents/{agent_id}/tasks/{task_id} for this — the
stream already replays everything from the beginning, so attaching late loses
nothing.
Prerequisites
- A task id and its agent id. See Start a task.
- An API key.
- A client that can hold an open HTTP response.
curl -Nworks;curlwithout-Nbuffers and looks hung.
Choose an endpoint
Steps
Stream live
event_type are the same dotted string, so a consumer can key on either:
event_id across the hand-off. It closes after
task.completed, task.failed, or task.cancelled.
Drop token-level chunks
llm.call.chunk events are high volume — one per token. They are included by
default. Turn them off when you only need structural progress:
Read history instead
?event_type=tool.call. Chunks never appear here — they are
stream-only and are not persisted.
Write the consumer correctly
Two rules keep a consumer from breaking when the vocabulary grows:- Do not switch on every event name. Pass unknown types through. New event types are added without a version bump.
- Only three types are terminal:
task.completed,task.failed,task.cancelled. Stop on those and nothing else.
tool_call_id for tool events,
{execution_id}:{iteration} for LLM events. A later event with the same part id
replaces the earlier one.
Verify
Confirm the stream terminates on its own with a terminal event rather than hanging:Troubleshooting
The connection opens and nothing appears. Without-N, curl buffers the
response. Add -N. If frames still do not arrive, confirm the task actually
dispatched: a task with execution_id: null never started a workflow and will
never emit events.
The stream closes after about 30 minutes with no terminal event. The feed
has a 30-minute wall-clock limit so a stuck task does not tail forever. This is
the feed giving up, not the task ending. Re-attach, or check
GET .../tasks/{task_id} for the current status.
A completed task streams its history and then hangs briefly before closing.
Expected. The reader replays from the database, then attaches to the live tail
and waits for a terminal event it may have already replayed. It closes on the
replayed terminal event.
Events are missing from history but were seen live. The event was published
to the live stream but its database write failed; that failure is recorded and
does not retry. The live tail is also best-effort in the other direction — a
failed publish leaves the event in history only. History is the durable copy;
prefer GET .../events when completeness matters.
A blocked task never emits task.blocked. There is no such event type. A
blocked task emits task.failed on the feed while its row reads blocked. Read
failure_reason from the task to tell them apart.