API Reference

All exports from @assistant-ui/gorp.

Types

GorpMessage

Server-to-client envelope.

type GorpMessage = {
  ops: GorpOperation[];
  ack?: number;
};

ops is a deep patch list; ack is the per-session high-water seq the inner has processed. Receiving the same ack twice is harmless.

RelaySerializedState

Snapshot shape returned by GorpRelay.serialize.

type RelaySerializedState<T> = { state: T };

GorpSessionsState

Snapshot shape returned by GorpSessions.serialize.

type GorpSessionsState = {
  sessions: Record<string, { highWater: number }>;
};

GorpClient

Leaf-side replica with an optimistic view layered on top of the latest server-confirmed state.

new GorpClient<T, C>(config: GorpClient.Config<T, C>)

GorpClient.Config<T, C>

{
  initialState: T;
  mutator: (state: T, command: C, seq: number) => void;
  send: (command: C) => void;
}

The mutator must be deterministic in (state, command, seq)GorpClient re-runs it on every replay to rebuild the optimistic view. The shape matches GorpServer.Config so a single function can run on both sides.

send is invoked synchronously on every client.send(cmd) and again for every pending entry when client.resync() is called.

Properties

client.state: DeepReadonly<T>     // optimistic view (committed + replay(pending))
client.pending: readonly C[]
client.firstPendingSeq: number    // pass on (re)connect handshake

Methods

client.send(command: C): void
client.apply(message: GorpMessage): void
client.resync(): void              // re-emit pending via config.send
client.onChange(cb: () => void): () => void
client.isChangedAt(path: string[]): boolean
client.getChangedKeys(path: string[]): string[]

isChangedAt / getChangedKeys are valid synchronously after apply() or send() — they describe what moved since the last public method call.


GorpServer

Authoritative state container. Pair with GorpSessions for the sessioned wire protocol.

new GorpServer<T, C>(config: GorpServer.Config<T, C>)

GorpServer.Config<T, C>

{
  initialState: T;
  mutator: (state: T, command: C, seq: number) => void;
}

Properties

server.state: T          // live mutable proxy — writes queue ops, batched per microtask
server.state = newValue  // root replace — emits a single `set []` op

Mutating array methods (push, pop, splice, …) throw — use state.list = [...state.list, x] instead.

Methods

server.receive(command: C): void
server.subscribe(cb: (env: GorpMessage) => void): () => void

GorpRelay

State mirror + upstream pipe. Same state/receive/subscribe surface as GorpServer, but state changes come from upstream GorpMessage envelopes instead of a local mutator.

new GorpRelay<T, C>(config: GorpRelay.Config<T, C>)

GorpRelay.Config<T, C>

{
  initialState: T;
  send: (command: C) => void;       // upstream transport callback
}

Properties

relay.state: DeepReadonly<T>

Methods

relay.receive(command: C): void                  // forward upstream via config.send
relay.applyUpstream(msg: GorpMessage): void      // inbound from upstream
relay.subscribe(cb: (env: GorpMessage) => void): () => void
relay.serialize(): RelaySerializedState<T>
relay.restore(state: RelaySerializedState<T>): void

GorpSessions

Wraps either a GorpServer or GorpRelay and adds the sessioned wire protocol: per-sessionId dedup, ack flushing, and resume-after-reconnect.

new GorpSessions<C>(inner: GorpServer | GorpRelay)

Methods

sessions.addClient(
  sessionId: string,
  fromSeq: number,
  send: (msg: GorpMessage) => void,
): { receive(command: C): void; remove(): void }

sessions.close(): void
sessions.serialize(): GorpSessionsState
sessions.restore(state: GorpSessionsState): void

addClient sends an initial { ops: [{ set [] state }], ack? } envelope so a reconnecting client splices any already-processed pending without a round-trip. Sessions are retained for 1 hour after their last activity; currently-connected sessions are never pruned.


Functions

appendText

Marker for the append-text op type — streaming text concatenation without retransmitting prior content. Works inside GorpClient's optimistic mutator too.

import { appendText } from "@assistant-ui/gorp";

server.state.messages[id].parts[0].text = appendText(chunk);