Skip to content

Write-Ahead Reconciliation

Clients optimistically apply their own actions locally, then reconcile when the server echoes them back alongside any concurrent actions from other clients or the server itself. This provides instant UI feedback while maintaining server-authoritative consistency.

Client-Side State

Each client maintains per-subscription:

  • confirmedState — Last fully server-acknowledged state.
  • pendingActions[] — Optimistically applied but not yet echoed by server.
  • optimisticStateconfirmedState with pendingActions replayed on top (computed, not stored).
confirmedState ──► apply pending[0] ──► apply pending[1] ──► ... ──► optimisticState

The UI always renders optimisticState. Users see their actions reflected immediately.

Reconciliation Algorithm

When the client receives an ActionEnvelope from the server:

  1. Own action echoed (origin.clientId === myId and matches head of pendingActions):

    • Pop from pending, apply to confirmedState.
  2. Foreign action (different origin or server-originated):

    • Apply to confirmedState, rebase remaining pendingActions.
  3. Rejected action (server echoed with rejectionReason present):

    • Remove from pending (optimistic effect reverted). The rejectionReason MAY be surfaced to the user.
  4. Recompute optimisticState from confirmedState + remaining pendingActions.

Example

Time  Server                          Client A                        Client B
─────────────────────────────────────────────────────────────────────────────────
 t1                                   dispatch(turnStarted)
                                      pending: [turnStarted]
                                      optimistic: has activeTurn
 t2   receives turnStarted from A
      applies, broadcast seq=10
 t3                                   receives seq=10 (own echo)
                                      pop from pending
                                      confirmed = optimistic
 t4   agent produces delta
      broadcasts seq=11
 t5                                   receives seq=11 (foreign)        receives seq=11
                                      apply to confirmed               apply to confirmed

Why Rebasing Is Simple

Most session actions are append-only:

  • Add turn, append delta, add tool call, append response part.

Pending actions still apply cleanly to an updated confirmed state because they operate on independent data — the turn the client created still exists; the content it appended is additive.

The rare true conflict (two clients abort the same turn) is resolved by server-wins semantics — the server's echo is the source of truth.

Reconnection

If the transport connection drops:

jsonc
// Client → Server (request)
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "reconnect",
  "params": {
    "clientId": "client-1",
    "lastSeenServerSeq": 42,
    "subscriptions": ["agenthost:/root", "copilot:/<uuid>"]
  }
}

The server MUST include all replayed data in the response. If the gap is within the replay buffer, the response contains missed action envelopes. If the gap exceeds the buffer, the response contains fresh snapshots instead. In both cases, the client resets confirmedState accordingly and clears pendingActions.

Protocol notifications (like sessionAdded/sessionRemoved) are not replayed — the client should re-fetch the session list.

Next Steps

  • Actions — The action types that flow through reconciliation.
  • Subscriptions — How URI-based subscriptions work.
  • Lifecycle — Session creation, handshake, and shutdown.

Released under the MIT License.