Streaming Protocol
Purpose and Scope
The Flue streaming protocol is the low-level HTTP contract used to read live agent conversation updates and workflow-run events. Most application code should start with the SDK helpers rather than implementing this wire protocol directly: client.agents.observe() provides a materialized live conversation, client.agents.history() reads a one-shot conversation snapshot, and workflow consumers should prefer client.runs.stream() or client.runs.events(). This page documents the routes, offsets, headers, and Server-Sent Events framing those helpers consume so frontend clients, custom SDKs, and infrastructure integrations can interoperate safely.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Flue uses Durable Streams offsets for both agent conversations and workflow-run events. An offset is not a message index or an event number; it is an opaque resume-after token returned by the server. Correct clients treat it as a checkpoint that must be passed back unchanged. This distinction matters because the public UI projection can differ from the canonical physical stream: one physical batch may produce several public chunks, or it may advance the checkpoint without producing a visible update.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Relevant Source Files
apps/docs/src/content/docs/api/streaming-protocol.md— Defines the public API reference for Flue's Durable Streams wire protocol, including agent history and updates views, workflow run reads, offset semantics, response headers, and SSE frame types.
Stream Routes
Flue exposes separate read surfaces for agent conversations and workflow runs. Agent routes are addressed by agent name and instance id, and the views operate on the instance's default conversation. A plain agent GET defaults to view=history, which makes the initial load path simple for clients that only need the current materialized conversation. The updates view is the incremental path and requires an offset so the server can resume strictly after a known checkpoint.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
| Route | Purpose |
|---|---|
GET /agents/:name/:id?view=history | Read one materialized agent conversation snapshot. |
GET /agents/:name/:id?view=updates&offset=... | Read conversation updates after an offset. |
HEAD /agents/:name/:id | Read agent stream metadata. |
GET /runs/:runId | Read workflow-run events. |
HEAD /runs/:runId | Read workflow-run stream metadata. |
Workflow routes are keyed by run id rather than workflow name. A plain workflow-run GET performs a catch-up read and returns a JSON array of versioned workflow events. Unlike agent conversation projections, workflow-run streams retain tail=N for bounded event inspection. That makes workflow reads suitable for both normal resume flows and diagnostic reads where a client wants to inspect only the most recent bounded suffix of a run's event stream.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Agent History and Updates
Agent history returns one JSON FlueConversationSnapshot. The server produces that snapshot by reducing the complete physical stream prefix for the agent instance. The snapshot's offset is the physical agent-instance tail, including records that were omitted from the public conversation projection. A client should store both the projection state and the returned offset together, because the next incremental request depends on the client still having the matching local state.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
The updates view emits ConversationStreamChunk, which is described as the strict UI projection protocol. It carries UI-only operations such as message and part lifecycle updates, tool input, structured output, settlement, and full-snapshot reset operations. The private canonical record schema is intentionally not exposed on the wire. Consumers should therefore update their local rendered conversation from chunks rather than trying to infer or persist the internal event model.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Updates require an offset and resume strictly after it. For one waitable read, use live=long-poll; for a continuous stream, use live=sse. If the local projection state is lost, do not resume from a stored offset alone. Request fresh history instead, because an update suffix can omit earlier message starts, branches, compaction state, or deltas required to reduce the conversation safely. Agent history and updates explicitly do not support tail for the same reason.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Workflow Reads
Workflow run reads are event-oriented rather than conversation-projection-oriented. The documented plain GET /runs/:runId path performs a catch-up read and returns a JSON array of versioned workflow events. Clients can start from the beginning with offset=-1, then continue with the returned stream offset. When a workflow client wants a live connection, it can combine an offset with live=sse to receive new event batches as the run progresses.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
GET /runs/run_01JX...?offset=-1
GET /runs/run_01JX...?offset=0000000000000000_0000000000000005&live=sseThe workflow stream also has metadata and completion semantics that differ from canonical agent conversation streams. Response headers can report whether a workflow event stream is closed, meaning no more events can arrive. Agent canonical conversation streams remain open and do not emit Stream-Closed. This difference helps clients decide whether to keep polling, reconnect, or finalize a run view without applying agent-specific assumptions to workflow state.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Offsets, Headers, and Caching
Offsets are opaque resume-after tokens. Clients must pass returned values back unchanged and must not parse, increment, sort, or synthesize them. For agents, one offset identifies one atomic canonical record batch. SDK checkpoints advance only after every public update derived from that batch has been delivered. A filtered batch may still advance the offset without emitting an update, so clients should treat the offset as the authoritative stream position even when the visible conversation did not change.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
| Header | Meaning |
|---|---|
Stream-Next-Offset | Offset to use for the next read. |
Stream-Up-To-Date | true when the read reached the current tail. |
Stream-Closed | Workflow event streams only: true when no more events can arrive. |
Stream-Cursor | Cursor for long-poll continuation. |
Catch-up responses use Cache-Control: no-store, while SSE responses use Cache-Control: no-cache. That split reflects the different delivery modes: catch-up reads are snapshots or batches that should not be reused by intermediary caches, and SSE connections are ongoing streams that need cache bypass behavior while remaining an open transport. Clients should read the stream headers on every response rather than assuming a fixed cache or completion policy across agent and workflow endpoints.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
SSE Framing and Resume Strategy
SSE responses contain three kinds of output. event: data frames carry a JSON array of conversation chunks or workflow events. event: control frames carry streamNextOffset and may include upToDate; workflow event streams may also include streamClosed. Idle connections can send heartbeat comments. A robust consumer should process data frames in order, update its rendered or stored state, then track the latest streamNextOffset from control frames as the next resume token.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
The most important operational rule is to reconnect with state, not just with an offset. When an agent updates connection starts, the server reconstructs the canonical prefix through the supplied offset. The history response is API-materialized; it is not a persisted conversation snapshot or replay cache. As a result, reconnect cost grows with the physical agent-instance stream, and applications with very large streams should measure reconnect latency and avoid unnecessary reconnect loops.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
Implementation Guidance
For frontend implementations, the safest boot sequence is: request agent history, render the returned FlueConversationSnapshot, store its offset with the local projection, and then open view=updates&offset=... using either live=long-poll or live=sse. If a browser tab, cache, or client store loses the projection, discard the old offset and request history again. For workflows, start with a catch-up run read and then continue streaming events from the returned offset.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md
When writing a custom SDK, mirror the public SDK's separation between materialized agent conversations and workflow event streams. Agent consumers should expose snapshot and observe-style APIs that hide ConversationStreamChunk reduction from most callers. Workflow consumers should expose event streaming and event history APIs that preserve versioned workflow events. Keeping these concepts separate prevents accidental use of tail on agent projections and avoids treating workflow closure as if it applied to long-lived agent conversations.
Sources: apps/docs/src/content/docs/api/streaming-protocol.md