Reconnect and Sync
Build a Session client that does not lose streaming events with snapshots and Mutations
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.
Recommended algorithm
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
| Mutation | Merge strategy |
|---|---|
message | Upsert by message_id; apply only a greater revision |
part | Upsert by part_id in its Assistant Message; apply only a greater revision |
delta | Append delta to matching text/reasoning text or Tool input_text |
turn | Update runtime state by turn_id |
session | Update Session metadata such as the title |
After a disconnect, do not assume Deltas can be replayed. Deltas from completed steps are already part of a stable SQLite snapshot; an interrupted unfinished projection may be discarded and closed with an Error Part. Reload messages() to restore authoritative state.
RemoteSession disconnects
When a remote connection closes:
- Retain the current UI snapshot for temporary display, but do not treat it as final truth.
- Clean up the old subscription.
- Establish a new subscription and buffer Mutations.
- Reload
messages()andget_info(). - 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.
History pagination
The initial snapshot loads the most recent Message page. To load earlier history, pass its next_before_sequence with an appropriate limit. Live Mutations update the current snapshot; never let an older revision replace a loaded Message.
Deduplication
Events from one connection are normally ordered, but a client should still be idempotent:
- Use
message_id + revisionto reject old snapshots. - Use the parent Message revision for Parts.
- Append Deltas only while the stream is continuous; validate
tool_inputagainst itstool_call_id; after reconnect, prefer the snapshot. mutation_idcan deduplicate repeated delivery of the same event.
This approach works for local and remote Sessions. Only the remote transport commonly disconnects.