The client transport subscribes to an Ably channel, decodes incoming messages through a codec, and builds a conversation tree. It provides views for paginated, branch-aware access to the conversation, and methods for cancellation and turn tracking.
Import from the core entry point:
1
import { createClientTransport } from '@ably/ai-transport'Or use the Vercel entry point which pre-binds the codec.
createClientTransport
Factory function that creates a ClientTransport instance.
1
function createClientTransport(options: ClientTransportOptions): ClientTransportClientTransportOptions
| Property | Required | Type | Description |
|---|---|---|---|
| channel | required | Ably.RealtimeChannel | The Ably channel for the session. |
| codec | required | Codec<TEvent, TMessage> | Codec for encoding and decoding messages. See Codec API. |
| clientId | optional | string | The client ID for this transport instance. Used for scoping cancel signals and identifying user messages. |
| api | optional | string | URL of the API endpoint to send user messages to. Defaults to "/api/chat". |
| headers | optional | Record<string, string> | (() => Record<string, string>) | Additional HTTP headers to include in API requests. Use the function form for dynamic values such as auth tokens. |
| body | optional | Record<string, unknown> | (() => Record<string, unknown>) | Additional fields to merge into the API request body. Use the function form for dynamic values. |
| credentials | optional | RequestCredentials | Credentials mode for the fetch request ('include', 'same-origin', 'omit'). |
| fetch | optional | typeof globalThis.fetch | Custom fetch implementation. Defaults to globalThis.fetch. |
| messages | optional | TMessage[] | Initial messages to seed the conversation tree with. Forms a linear chain. |
| logger | optional | Logger | Logger instance for debug output. |
ClientTransport
The ClientTransport object returned by the factory.
tree
The underlying conversation tree. The tree contains every message received on the channel, organized into branches.
1
const tree = transport.treeview
The default View for the transport. Created automatically when the transport is instantiated.
1
const view = transport.viewcreateView
Create an additional View of the conversation tree. Each view maintains its own branch selection and pagination state.
1
function createView(): View<TEvent, TMessage>Use multiple views when you need different perspectives on the same conversation, for example a main chat panel and a sidebar showing an alternative branch.
cancel
Publish a cancel signal on the channel. The optional filter scopes which turns are cancelled.
1
function cancel(filter?: CancelFilter): Promise<void>| Filter property | Type | Effect |
|---|---|---|
| own | true | Cancel all turns started by this client (default). |
| turnId | string | Cancel one specific turn. |
| clientId | string | Cancel all turns by a specific client. |
| all | true | Cancel all active turns on the channel. |
When called with no argument, cancel() defaults to { own: true }.
waitForTurn
Wait for active turns matching the given filter to complete. Returns a promise that resolves when all matching turns have ended. Resolves immediately if no matching turns are active. Defaults to { own: true }.
1
function waitForTurn(filter?: CancelFilter): Promise<void>on('error')
Subscribe to transport-level errors.
1
2
3
transport.on('error', (error: ErrorInfo) => {
console.error('Transport error:', error)
})close
Close the transport and release resources. Optionally cancel active turns before closing.
1
function close(options?: CloseOptions): Promise<void>View
A View is a paginated, branch-aware projection of the conversation tree. It tracks which branch is selected at each fork point and supports lazy loading of older messages.
getMessages
Return the visible domain messages along the selected branch.
1
function getMessages(): TMessage[]flattenNodes
Return the visible nodes along the selected branch, filtered by the pagination window. Each node wraps the domain message with tree metadata.
1
function flattenNodes(): MessageNode<TMessage>[]hasOlder
Whether there are older messages that can be loaded or revealed.
1
function hasOlder(): booleansend
Send one or more user messages and start a new turn. Messages are optimistically inserted into the tree. The parent is auto-computed from the view's selected branch unless overridden in options. Returns an ActiveTurn handle for the server's response stream.
1
function send(messages: TMessage | TMessage[], options?: SendOptions): Promise<ActiveTurn<TEvent>>regenerate
Regenerate an assistant message. Creates a new turn that forks the target message with no new user messages. Automatically computes forkOf, parent, and truncated history from the view's branch.
1
function regenerate(messageId: string, options?: SendOptions): Promise<ActiveTurn<TEvent>>edit
Edit a user message and regenerate from that point. Creates a new turn that forks the target message with replacement content. Automatically computes forkOf, parent, and history from the view's branch.
1
function edit(messageId: string, newMessages: TMessage | TMessage[], options?: SendOptions): Promise<ActiveTurn<TEvent>>update
Update an existing message and start a continuation turn. The local tree is updated optimistically, then the events are sent to the server in the POST body. The server publishes them to the channel and streams a continuation response.
1
function update(msgId: string, events: TEvent[], options?: SendOptions): Promise<ActiveTurn<TEvent>>loadOlder
Load older messages from channel history. Loads from history if the tree does not have enough messages, then advances the pagination window.
1
function loadOlder(limit?: number): Promise<void>select
Select a sibling at a fork point by index. Updates the view's branch selection. The index is clamped to [0, siblings.length - 1].
1
function select(msgId: string, index: number): voidgetSelectedIndex
Get the index of the currently selected sibling at a fork point.
1
function getSelectedIndex(msgId: string): numbergetSiblings
Get all messages that are siblings at a given fork point, ordered chronologically by serial.
1
function getSiblings(msgId: string): TMessage[]hasSiblings
Whether a message has sibling alternatives at its fork point.
1
function hasSiblings(msgId: string): booleangetNode
Get a node by its message ID, or undefined if not found.
1
function getNode(msgId: string): MessageNode<TMessage> | undefinedgetActiveTurnIds
Get active turn IDs for turns with visible messages, grouped by client ID.
1
function getActiveTurnIds(): Map<string, Set<string>>on
Subscribe to view events. Each handler returns an unsubscribe function.
1
2
3
4
5
6
7
8
// Fired when the visible message list changes (new message, branch switch, window shift)
const unsubscribe = view.on('update', () => { })
// Fired when a raw Ably message arrives for a visible node
view.on('ably-message', (msg: Ably.InboundMessage) => { })
// Fired when a turn starts or ends for a turn with visible messages
view.on('turn', (event: TurnLifecycleEvent) => { })close
Tear down the view. Unsubscribes from tree events and clears internal state.
1
function close(): voidActiveTurn
A handle to an active client-side turn, returned by send(), regenerate(), edit(), and update().
| Property | Type | Description |
|---|---|---|
| stream | ReadableStream<TEvent> | The decoded event stream for this turn. |
| turnId | string | The unique identifier for this turn. |
| cancel | () => Promise<void> | Cancel this specific turn. Publishes a cancel message and closes the local stream. |
SendOptions
Per-send options for customizing the HTTP POST and branching metadata.
| Property | Required | Type | Description |
|---|---|---|---|
| headers | optional | Record<string, string> | Additional HTTP headers for this request. |
| body | optional | Record<string, unknown> | Additional fields to merge into the request body. |
| forkOf | optional | string | The msg-id of the message this send replaces (creates a fork). |
| parent | optional | string | null | The msg-id of the preceding message in the conversation thread. null means the message is a root. If omitted, auto-computed from the last message in the view. |
CloseOptions
| Property | Type | Description |
|---|---|---|
| cancel | CancelFilter | Cancel in-progress turns before closing. Publishes a cancel message to the channel. |
MessageNode
A node in the conversation tree, representing a single domain message.
| Property | Type | Description |
|---|---|---|
| kind | 'message' | Discriminator identifying this as a message node. |
| message | TMessage | The domain message. |
| msgId | string | The x-ably-msg-id of this node. Primary key in the tree. |
| parentId | string | undefined | Parent node's msg-id, or undefined for root messages. |
| forkOf | string | undefined | The msg-id this node forks from, or undefined if first version. |
| headers | Record<string, string> | Full Ably headers for this message. |
| serial | string | undefined | Ably serial for ordering. Absent for optimistic messages. |
TurnLifecycleEvent
A structured event describing a turn starting or ending.
1
2
3
type TurnLifecycleEvent =
| { type: 'x-ably-turn-start'; turnId: string; clientId: string }
| { type: 'x-ably-turn-end'; turnId: string; clientId: string; reason: TurnEndReason }TurnEndReason
1
type TurnEndReason = 'complete' | 'cancelled' | 'error'CancelFilter
Filter for cancel operations. At most one field should be set.
| Property | Type | Description |
|---|---|---|
| turnId | string | Cancel a specific turn by ID. |
| own | boolean | Cancel all turns belonging to the sender's client ID. |
| clientId | string | Cancel all turns belonging to a specific client ID. |
| all | boolean | Cancel all turns on the channel. |