Sessions

Messages and Parts

Read Active and Segment history and understand Messages, Parts, and sequence

Messages and Parts

session.messages() returns every persisted Message in the current Active set. It does not take a limit; subscribe() complements the snapshot with live changes.

const page = await session.messages();

for (const message of page.items) {
  render_message(message);
}

It returns:

interface SessionMessagePage {
  items: SessionMessage[];
  total: number;
  source: "active" | "segment";
  start_sequence?: number;
  end_sequence?: number;
  next_before_sequence?: number;
  has_more: boolean;
}

total is the total number of real Messages created by the Session, not items.length. source identifies whether the result came from Active or one closed Segment.

Four top-level Message types

typeMeaningTypical presentation
userUser input or in-flight steeringUser bubble and attachments
assistantOne generated model response segmentText, reasoning, tools, files
actionRuntime activity such as a model change or compactionStatus notice
errorA displayable Session or Turn errorError callout and retry entry point

A Session has one top-level Message sequence ordered by sequence. Tool calls are neither a second Timeline nor top-level Messages: they live in an Assistant Message's parts, so text -> tool -> text is persisted in its real generation order.

Assistant Parts

typeKey fieldsMeaning
texttext, stateVisible output, streaming -> done
reasoningtext, stateReasoning output; product decides whether to display it
tooltool_call_id, tool_name, stateTool input, approval, execution, and result
fileurl, media_typeA file generated or referenced by the Assistant
datadata_type, dataStructured UI data

Every Assistant Part has a stable part_id and immutable internal sequence. A tool state change updates the same Part; a UI should not create separate request and result rows.

Order, revision, and visibility

Every Message has a stable message_id, a creation-time sequence that never changes, and a revision that increases with each complete snapshot update. A client must not replace a greater revision with a lower one.

messages() returns only visibility: "visible" Messages by default. Pass include_internal: true for debugging or audit. A Compact Summary is not a Message, consumes no sequence, and never appears in messages().

Loading older history

Call the method with no arguments first to load all Active Messages. When has_more is true, pass next_before_sequence back unchanged to read the immediately preceding whole Segment:

let page = await session.messages();
render_messages(page.items);

while (page.has_more && page.next_before_sequence !== undefined) {
  page = await session.messages({
    before_sequence: page.next_before_sequence,
  });
  prepend_messages(page.items);
}

before_sequence must be a positive integer. Each historical read returns one complete Segment. There is no limit, cursor, offset, or slicing within a Segment. This keeps UI boundaries aligned with physical Compact boundaries and avoids reading cumulative Summaries as Messages.

Combining snapshots with live Mutations

  1. Use messages() to establish the Active state.
  2. Replace the targeted snapshot for a message or part Mutation.
  3. Append new text for a delta Mutation.
  4. Load older Segments one at a time with next_before_sequence.
  5. On disconnect, reload Active and merge by message_id + revision.

See Reconnect and sync for synchronization and Metadata and storage for persistence details.