Events
@flue/sdk delivers conversation data in three vocabularies, at three levels of abstraction:
- Materialized conversation state —
FlueConversationStateandFlueConversationSnapshot: complete, renderable conversations made of messages, parts, and settlements. This is whatobserve()maintains andhistory()returns, and it is the level application code should consume. - Conversation stream chunks —
ConversationStreamChunk: the incremental update protocol thatobserve()reduces into state and thatwait()’sonEventcallback exposes raw. - Activity records —
FlueEvent: the runtime’s low-level activity event union (model turns, tool calls, compaction, logs). NoFlueClientmethod delivers these; the types are exported for first-party presenters such as theflue runCLI renderer.
Each vocabulary is documented on the page that owns it, linked above and mapped export-by-export below. This page adds only what the SDK itself owns: the FlueEventStream iteration surface and the offset and redelivery semantics an SDK consumer sees.
A minimal consumer reads messages and parts from an observation:
import { createFlueClient } from '@flue/sdk';
const client = createFlueClient({ url: 'https://app.example.com/api/support/thread-42' });
const observation = client.observe();
observation.subscribe(() => {
const { conversation } = observation.getSnapshot();
for (const message of conversation?.messages ?? []) {
if (message.display !== 'visible') continue;
for (const part of message.parts) {
if (part.type === 'text') renderText(message.id, part.text, part.state);
}
}
});
Exported types
FlueConversationSnapshot,FlueConversationState,FlueConversationMessage,FlueConversationPart,FlueConversationSettlement— the materialized conversation, documented field-by-field on FlueClient withhistory()andobserve().ConversationStreamChunk— theupdates-view chunk union, documented on the Streaming Protocol reference. Delivered raw bywait()’sonEvent; not stable application API — application code should consume materialized state viaobserve()instead.FlueEvent,AttachedAgentEvent,AgentSubmissionSettledEvent, and the model-turn payload types (LlmMessageand its content blocks,LlmTurnPurpose,ModelRequest,ModelRequestInfo,ModelRequestInput,ModelResponse,PromptUsage) — the runtime event vocabulary, documented on the Events reference. The SDK exports mirror the runtime union exactly, pinned by wire-conformance type tests, so presenters can consume runtime activity without depending on@flue/runtime.IMAGE_DATA_OMITTED— the sentinel replacing raw image bytes in event payloads, exported from both packages and documented on the Events reference.FlueSerializedError,UnsupportedFlueEventVersionError— documented with the rest of the error surface in SDK errors.
FlueEventStream
interface FlueEventStream<T = FlueEvent> extends AsyncIterable<T> {
cancel(reason?: unknown): void;
readonly offset: string;
}
An async iterable of events backed by a Durable Streams connection, with automatic reconnection, offset-based replay, and live tailing. The SDK constructs these internally (wait() consumes a FlueEventStream<ConversationStreamChunk>); the type is exported so first-party presenters can type streams of their own. for await...of is the consumption interface; breaking out of the loop cleans up the underlying connection.
cancel(reason?)— cancels the stream and aborts the underlying connection. Iteration then ends (done: true) rather than throwing.offset— the resume checkpoint. It advances per delivered batch: it moves to a batch’s next-offset only once every event in that batch has been yielded, so resuming from a checkpointed value never skips undelivered events — at worst it re-delivers the batch that was in flight when the checkpoint was taken (at-least-once).
Iterating a stream of FlueEvent values rejects any event whose v is not 3 with UnsupportedFlueEventVersionError; the SDK does not normalize historical event formats.
FlueStreamOptions
interface FlueStreamOptions {
offset?: string;
tail?: number;
live?: LiveMode;
signal?: AbortSignal;
backoffOptions?: BackoffOptions;
}
Options for one event-stream read. LiveMode and BackoffOptions are re-exported from @durable-streams/client.
offset— starting offset. Defaults to'-1'(full history).tail— limits anoffset: '-1'read to at most the most recent number of events.live— live tailing mode:boolean | 'long-poll' | 'sse'. Defaults totrue(long-poll).falsereads to the current end and completes.signal— aborts the stream; iteration ends without throwing.backoffOptions— retry behavior for connection attempts (initialDelay,maxDelay,multiplier, and callbacks, per@durable-streams/client).
Offsets and redelivery
Every conversation read is anchored to a durable-stream offset — an opaque string checkpoint. Offsets surface on FlueConversationSnapshot, AgentSendResult, AgentConversationObservationSnapshot, and FlueEventStream. Treat them as opaque: compare for equality if you must, never parse or arithmetic on them. Chunk position values and FlueEvent.eventIndex values are not offsets — they identify and order items but cannot be used as resume points. The wire-level offset format and coordination headers are specified in the Streaming Protocol reference.
Delivery is at-least-once: when a connection drops mid-batch, the transport reconnects from the pre-batch offset and replays the batch in flight. The SDK’s consumers each absorb this:
observe()dedupes by chunkpositionand rehydrates a fresh snapshot on reconnect rather than resuming incrementally; seeobserve().wait()watches only for the terminalsubmission-settledchunk of its submission, which is idempotent under redelivery. ItsonEventcallback, however, receives the raw stream: it can observe the same chunk more than once after a reconnect, and it receives every chunk of the conversation from the admission offset, not only chunks belonging to the awaited submission. Dedupe bypositionif the distinction matters; preferobserve()for maintained UI state.
Both live modes ('long-poll' and 'sse') carry the same chunks with the same guarantees; SSE trades connection lifetime for lower token-by-token latency.