Streaming protocol
Blazing Agents uses AI SDK UI-message SSE for Session activity and chunked plain text for stateless generation. Use this page when relaying a response, reading it in the SDK, or handling cancellation and failures.
Contract
| Surface | HTTP response | Body | SDK result |
|---|---|---|---|
| New Session Turn | 201; Location contains the new Session ID; text/event-stream; x-vercel-ai-ui-message-stream: v1 | AI SDK UIMessageChunk SSE | sessionId, toUIMessageStream(), toResponse() |
| Resumed Session Turn | 200; no Location; same stream headers | AI SDK UIMessageChunk SSE | same, using the supplied Session ID |
Session Turn with stream: false | 201 on create or 200 on resume; application/json; create still includes Location | { "message": UIMessage } | REST only; the SDK always requests a stream |
| Text generation | 200; text/plain; charset=utf-8 | chunked text | textStream, awaited text, toResponse() |
| Object generation | 200; text/plain; charset=utf-8 | chunked partial JSON text | partialObjectStream, awaited object, toResponse() |
| Tool approval continuation | 200; UI-message SSE headers | persisted continuation chunks | joinToolApprovalContinuation() terminal result |
UI-message streams contain data: records whose values are AI SDK
UIMessageChunk objects, followed by data: [DONE]. They are not
OpenAI-compatible delta streams. Stateless object mode uses the same text wire
format as text mode; the SDK parses partial and final JSON.
The create Session route mints an ss_... ID. result.sessionId reads it from
the Location header before the body is consumed. On resume it resolves to the
ID supplied by the caller. For interactive creation, a failed or aborted first
Turn does not materialize the Session, so attempting to resume that ID returns
not_found.
Chat and Tool approval continuation results allow one body claim: call either
toUIMessageStream() or toResponse(). A second claim throws SDK
stream_error. Completion and object results tee the successful body
internally, so their iterator, awaited final value, and relay are all available
from one result. toResponse() always constructs a new relay response with
status 200; on a newly created Session it does not preserve the upstream
201 or Location header. Read result.sessionId separately.
Failures have two phases:
- A fetch failure before an HTTP exchange is SDK
network_error. - A non-2xx response before streaming starts uses the normal error envelope.
- After a Session stream starts, failure is a native
{ "type": "error", "errorText": "safe prose" }chunk and the HTTP status remains successful. - A failed text/object transport, invalid final JSON, malformed SSE, or invalid
create
Locationbecomes SDKstream_error. Awaittextorobjectwhen the terminal outcome matters.
Cancellation depends on the surface. For chat, an input AbortSignal aborts
the request and canceling the selected decoded or relay stream propagates to
the active Turn. Completion and object inputs also forward AbortSignal; use
it to cancel the Turn rather than relying on cancellation of one tee branch.
Canceling a Tool approval join only detaches that polling response: it does not
cancel the durable continuation. Failed and canceled Turns are metered but do
not commit interactive Session transcript changes; external Tool side effects
are not rolled back. Durable Tasks differ: the worker attaches a fresh Session
before execution and incrementally commits user, assistant, and failure events,
so failed or canceled Task runs can leave transcript and failure history.
A Tool approval decision returns 202 and a continuation identifier. Joining
that continuation returns its terminal SSE stream; it does not reopen the
original HTTP response.
Examples
This is a minimal valid UI-message stream. Production finish chunks also carry the platform's message usage metadata.
data: {"type":"start","messageId":"msg_1"}
data: {"type":"text-start","id":"text_1"}
data: {"type":"text-delta","id":"text_1","delta":"Hello"}
data: {"type":"text-end","id":"text_1"}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]Iterate deltas and await the assembled final text:
const result = await client.completion({
agentId,
prompt: "Write a two-sentence release note.",
});
for await (const delta of result.textStream) {
process.stdout.write(delta);
}
const finalText = await result.text;Forward cancellation from the caller:
const controller = new AbortController();
const result = await client.completion({
agentId,
prompt: "Analyze the report.",
signal: controller.signal,
});
controller.abort();
await result.text;Used by
- SDK generation
- SDK Session continuations
- REST generation
- REST Session Turns
- REST approval continuation
- Generation and streaming
- Sessions and Turns
- Tool approvals
- Build a chat endpoint
- Stream responses into a frontend
Source of truth
packages/core/src/entities/chat.tspackages/sdk/src/generation.tspackages/sdk/src/generation.chat-results.test.tspackages/sdk/src/generation.chat-requests.test.tspackages/sdk/src/generation.chat-errors.test.tspackages/sdk/src/generation.transport-errors.test.tspackages/sdk/src/generation.completion.test.tspackages/sdk/src/generation.object.test.tsservers/api/src/routes/agents/generation.tsservers/api/src/routes/agents/generation.test.tsservers/api/src/routes/sessions/turns.tsservers/api/src/routes/sessions/create-responses.test.tsservers/api/src/routes/sessions/create-errors.test.tsservers/api/src/routes/sessions/resume.test.tsservers/api/src/routes/sessions/tool-approvals.tsservers/api/src/routes/sessions/tool-approvals.test.tsservers/task-worker/src/task-run-execution.tsservers/task-worker/src/run-workflow-lifecycle.test.tsservers/task-worker/src/run-workflow-persistence.test.ts
Related guides
See the capability and guide links under Used by.
Reference
See the implementation inventory under Source of truth.