---
title: API Reference
description: All exports from @assistant-ui/gorp.
---

## Types

### GorpMessage

Server-to-client envelope.

```ts
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`.

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

### GorpSessionsState

Snapshot shape returned by `GorpSessions.serialize`.

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

---

## GorpClient

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

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

### `GorpClient.Config<T, C>`

```ts
{
  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

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

### Methods

```ts
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`](#gorpsessions)
for the sessioned wire protocol.

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

### `GorpServer.Config<T, C>`

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

### Properties

```ts
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

```ts
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.

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

### `GorpRelay.Config<T, C>`

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

### Properties

```ts
relay.state: DeepReadonly<T>
```

### Methods

```ts
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.

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

### Methods

```ts
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.

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

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