Sessions
A session is the durable, shared state of a conversation. It outlives any single connection so a user can close their laptop, switch to a phone, or refresh the page without losing the stream.
A session is the complete, persistent state of a conversation, and it exists independently of anything that connects to it. A client closes its tab, an agent process terminates, and the session is unchanged. Your application addresses it by name.
The session holds the branching tree of messages, the state of any agent work in progress, and any partially streamed output. Two clients that build the same session from the same data arrive at the same state.
Materialise a session
Underneath the session is an Ably channel, which carries the ordered, durable log of everything published to the conversation. Streaming covers what the channel supplies on its own. The session is the structured conversation that log produces once it has been materialised into the conversation tree.
A session materialises from one of two sources.
By default it materialises from the channel, which serves as both the live delivery layer and the stored history of the conversation. When your channel's history retention window covers the session's lifetime, the channel alone holds the whole conversation, so it is your only storage.
Alternatively you supply historical messages from your own database, and the channel provides only live and in-progress activity. Use this when your channel's retention window is shorter than the conversation, or when you need to index conversation data in your own systems. Your code joins the two into one list of messages, and database hydration covers how to wire it up.
Materialisation is more than a replay of the log. Some events change how earlier events are interpreted. A cancel signal changes how a run is represented, and an edit forks a sibling prompt that the view selects in place of the original, even though the original messages are still on the channel. The channel keeps the full unedited log, and materialisation applies these events as instructions that reshape what the session contains.
Connect to a session
Your code reaches a session through a session object, either ClientSession or AgentSession.
ClientSessionruns in the browser for as long as the user's tab is open. It subscribes to the conversation and publishes user input and cancel signals.AgentSessionusually runs for one HTTP handler invocation, and it publishes the run lifecycle events and the streamed response.
Neither object owns the conversation. Each one is a connection to a session that lives on the channel, so several agents or clients can attach to and detach from the same session at once and independently.
Both are constructed and then connected, as this client-side example shows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Client-side. The browser fetches a token; never put an API key here.
import * as Ably from 'ably';
import { createClientSession } from '@ably/ai-transport';
import { createUIMessageSessionCodec } from '@ably/ai-transport/vercel';
const ably = new Ably.Realtime({ authUrl: '/auth' });
const session = createClientSession({
client: ably,
channelName: 'conversation-42',
codec: createUIMessageSessionCodec(),
});
await session.connect();
// session.view, session.tree, and session.cancel(...) are now safe to use.Every operation that touches the conversation waits for the connect promise, and throws InvalidArgument when connect() has never been called, which stops a view.send publishing before the subscription is in place. connect() is idempotent, so a component that mounts twice gets the same promise back. Tear a client down with close() and an agent with end(). An agent that passes an in-flight run to another process uses detach() instead, which durable execution covers.
The codec argument is the translation layer between your framework's events and Ably messages. The Vercel codec is bundled, and createClientSession imported from @ably/ai-transport/vercel comes pre-bound with it, so you can leave the argument out. Writing your own is covered in codec architecture.
Share a session across participants
The session is the unit of sharing. A second client joins the session, an agent hydrates the session to build context for a model call, and every published message goes to the session. Every client works whether or not the others are connected, and the state survives each arrival and departure.
Agent lifecycle does not affect the session. An agent hydrates the session, works through a run, and terminates. The session survives because it lives on the channel rather than in the agent's memory, so a different agent instance handles the next run with the same state.
A new client can join at any time. A second client attaching to the conversation hydrates the full session from history and receives live updates from then on.
The session carries two Ably channel features directly. Presence is exposed as session.presence and tells you which participants are currently connected. LiveObjects is exposed as session.object and holds shared mutable state that the user and the agent both read and write, such as the record the user has selected. Your agent can react to each change as it is published, and the user sees the agent's changes in realtime.
Read next
- Conversation tree: how the session's messages form a branching history.
- Runs and steps: the unit of agent work inside a session.
- Multi-device and fan-out: one conversation across a laptop, a phone, and a second tab.
- ClientSession reference: every client-side method and property.
- AgentSession reference: every agent-side method and property.