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 theAgentEvent 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 aMap keyed by callId, then flip each card from running to ok or fail on the matching result.
Progress spinner around a slow tool
Narratetool_call.name in a status line, then clear it on the matching tool_result.
Best Practices
Pair by callId, never by position
Pair by callId, never by position
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.Treat ok === false as failure, always
Treat ok === false as failure, always
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.Break the for await loop to cancel
Break the for await loop to cancel
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.Return objects from tools that expect named args
Return objects from tools that expect named args
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.Related
Streaming
Token-level text streaming
Callbacks
Lifecycle callbacks
Approval
Denials also emit paired events

