Skip to main content
A Session is an append-only event log, and everything you show a user — the message timeline, tool activity, pending ActionApprovals, the subagent tree, what is in context — is a projection of that one stream. You do not need a second transcript for each surface. You fold the same ordered events into the shape each view needs. This guide names the five projections a Session renders and shows exactly which events each one derives from. The TypeScript SDK implements them as pure, replay-idempotent folds, so a live consumer keeps every view current without re-reading the log after every event.

One stream, five views

Order by seq

seq is the per-Session sequence. Feed events to a projection in seq order; an event at or below the high-water mark is a no-op.

Project, don't copy

A projection derives a view from the canonical event. It never replaces the log, never moves the cursor, and never reaches the network.

Settle on events

Read settlement from status events and their stop_reason, never from stream quiescence. See Resume a stream.
CHECKFU_API_KEY and CHECKFU_WORKSPACE_ID come from Get access; sessionId is the sess_… you are following. createSessionProjections() bundles all five views behind one observe, so a single pass over the stream keeps every view current. Each view is also available on its own — new MessageProjector(), new ContextLedgerProjector() — when a consumer wants only one.

Messages

The readable conversation: user, system, and agent turns in log order. It folds the events whose payload.content is the point of the turn, and represents a screen-withheld agent message as a structural marker rather than the judged text.
Multimodal content (image and document blocks) carries no string in the projection: text is null and multimodal is true, so a renderer reads the canonical event by seq for the block bodies. A withheld agent.message has withheld set and text null — its message.screened verdict is a separate event you can correlate by content digest.

Tool calls

The conversational tool-activity timeline: each agent.tool_use paired with its settling agent.tool_result.
This is the read-side timeline, not the ToolServing loop. The SDK’s ToolServingProjector (used by tools.serve) is the serving state machine that decides when to invoke your handler. Platform-witnessed exec.tool_invocation settlement is a separate governance lane — it correlates by tool_invocation_id, not tool_use_id — so it is documented in the events reference rather than folded here. See Serve tools from your app.

ActionApprovals and questions

Both park a Run on a human, so they share a view. A governed ActionApproval is a durable permission decision; a Question is ordinary input that grants no authority. They never collapse: an ActionApproval is answered through the ActionApprovals API, a Question through user.question_answer.
Key the wait loop off run.requires_action to learn what a Run is parked on (see Answering a parked run); this projection is the read-side summary of those waits.

Multiagent state

The multi-agent view keeps two distinct mechanisms. A projection presents them as separate lists so observed harness-internal activity is never conflated with governed coordinator threads.
Observed subagents are a harness accounting for its own nested work; they are never governed platform resources. Session threads are governed coordinator-roster Agent lifecycles inside one Session and expose only stable sthr_* identities. Each thread has its own filtered event view while the Session event log stays the durable source of truth. See Multiagent threads.

Context ledger

The inspectable accounting of what is in context: token usage, model routing, compaction and condensation boundaries, and definition-version changes. It is the read-side fold of the same events the GET /v1/sessions/{id}/ledger endpoint summarizes, so a live consumer keeps a current ledger without re-reading the endpoint after every event.

Feeding projections durably

A live tail is enough for a transient view. When the projection must survive a process restart — a Slack thread, a ticket, a dashboard backed by its own store — feed it from createSessionObserver, which owns lossless cursor resume, live-event custody, and terminal cleanup:
Events stay in the observer until the cursor you return covers them, so a crash mid-projection replays rather than skips. eventDelta is only the recognized, unsettled delta and can be redelivered; it is not the complete Session prefix. Your provider receipt transaction must update cumulative provider state and its cursor together. Unknown future events do not appear in the typed delta but still contribute to throughSequence, so read the canonical log when their exact envelopes matter. The SDK projectors have no durable snapshot format: use them for a process-lifetime view, or replay the authoritative log under an explicit retention/work policy before publishing a complete view. The full observer contract — coalescing, backoff, and terminal cleanup — is in the SDK reference.

Next steps

Events reference

The envelope, the full event catalog, and the drive events these projections fold.

Resume a stream reliably

The cursor and replay rules every projection inherits.