Skip to main content
Stream typed events (text, tool_call, tool_result, finish, error) instead of just token text, so a UI can show tool activity as it happens.
text events emitted before a tool_call are now reliably delivered on every provider — OpenAI, Anthropic, Google, xAI, Groq, and any AI-SDK backend. Pre-tool commentary the model produces before running a tool arrives as tokens, not held back until the tool finishes.

Quick Start

1

See tools happening live

Iterate the event stream and switch on event.type to render text, tool calls, and results as they arrive.
2

Cancel a turn

Pass an AbortController signal to stop generation mid-turn — or just break the for await loop, which aborts the upstream provider request automatically.

How It Works

Structured events share the same internal queue as text deltas, so tool activity is reported in its true position in the response.

The Five Event Types

Every event is a member of the AgentEvent discriminated union — switch on type to narrow the fields.
Every tool_result has a matching tool_call with the same callId, emitted before it — even when the tool is denied at the approval gate, unregistered, or receives malformed or non-object arguments. UIs that pair by callId never see an orphan result. Do not pair by position — that only works while exactly one call is ever in flight.

Choosing a Streaming API

Pick the smallest API that gives you what you need.

Common Patterns

Chat UI that shows tools in progress

Track calls in a Map keyed by callId, then flip each card from running to ok or fail on the matching result.

Progress spinner around a slow tool

Narrate tool_call.name in a status line, then clear it on the matching tool_result.

Best Practices

Order is preserved, but positional matching breaks the moment two tools run in one turn. Always key your state on event.callId so a tool_result finds its tool_call.
A failed tool that returns a friendly output looks identical to a success — the output is the same shape either way. Only event.ok === true means the tool succeeded.
The iterator’s return() is the language’s own abort signal — breaking out of the loop stops upstream generation and billing. Pass opts.signal for external cancellation.
JSON scalars, arrays, or null are rejected before execution with Invalid arguments for tool <name>: expected a JSON object. Pass an object so arguments map to named parameters.

Streaming

Token-level text streaming

Callbacks

Lifecycle callbacks

Approval

Denials also emit paired events