Sessions

Reconnect and Sync

Build a Session client that does not lose streaming events with snapshots and Mutations

Reconnect and Sync

subscribe() only sends future changes and messages() returns a point-in-time snapshot. Combining them in the wrong order creates a race: a Delta can be lost between loading the snapshot and establishing the subscription.

The reliable sequence is subscribe and buffer first, load snapshots second, then merge the buffered events.

const buffered: SessionMutation[] = [];
let snapshot_ready = false;

const unsubscribe = session.subscribe((mutation) => {
  if (!snapshot_ready) {
    buffered.push(mutation);
    return;
  }
  apply_mutation(mutation);
});

const [message_page, info] = await Promise.all([
  session.messages(),
  session.get_info(),
]);

replace_message_state(message_page.items);
replace_session_info(info);

for (const mutation of buffered) {
  apply_mutation(mutation);
}

snapshot_ready = true;

A real UI should process buffered and set snapshot_ready = true in one serialized state transition so a new event cannot arrive in another gap.

Merge rules

MutationMerge strategy
messageUpsert by message_id; apply only a greater revision
partUpsert by part_id in its Assistant Message; apply only a greater revision
deltaAppend delta only to the matching text/reasoning Part
turnUpdate runtime state by turn_id
sessionUpdate Session metadata such as the title

After a disconnect, do not assume Deltas can be replayed. Lost Deltas have already been merged into assistant_message.json or a final Message snapshot, so reloading messages() is what restores correct state.

RemoteSession disconnects

When a remote connection closes:

  1. Retain the current UI snapshot for temporary display, but do not treat it as final truth.
  2. Clean up the old subscription.
  3. Establish a new subscription and buffer Mutations.
  4. Reload messages() and get_info().
  5. Apply buffered events under the merge rules above.

If a client is awaiting turn.finished when the connection breaks, handle its failed result and restore the UI by reloading the Session. Do not synthesize Assistant completion from client-side timers.

Segment history

The initial snapshot loads all Active Messages. To load earlier history, pass the current result's next_before_sequence to read the previous whole Segment. Live Mutations update Active only; do not use them to replace already loaded Segments.

Deduplication

Events from one connection are normally ordered, but a client should still be idempotent:

  • Use message_id + revision to reject old snapshots.
  • Use the parent Message revision for Parts.
  • Append Deltas only while the stream is continuous; after reconnect, prefer the snapshot.
  • mutation_id can deduplicate repeated delivery of the same event.

This approach works for local and remote Sessions. Only the remote transport commonly disconnects.