# LiveObjects state
An agent can react to what the user is doing, such as the page they're on or the record they've selected, the moment it changes. AI Transport sessions carry Ably LiveObjects, so agents and clients share the same live state over the session they already use.
With LiveObjects state, an agent and its clients read and write one shared object on the session. The agent can act on the user's current state as soon as the client writes it, and can report its own status back for every client to see in realtime. That shared object comes from Ably LiveObjects, exposed through the `session.object` API on both `ClientSession` and `AgentSession`.
## How state sync works
`session.object` is the same `RealtimeObject` the channel exposes, passed through as the channel's LiveObjects API with no wrapper or added behaviour: call `get()` to resolve the object, then read and subscribe to it as you would on a plain Ably channel. On the agent, resolve the object once and let it react to every change:
### Javascript
```
// On the agent: react whenever the user's state changes.
const myObject = await session.object.get();
myObject.subscribe(() => groundNextResponse(myObject.compactJson()));
```
The client works the same object from the other end. As the user moves around the app, the client writes their current state with `myObject.set(...)`, which fires the agent's subscription. Because object state syncs on channel attachment, a client that joins late or reloads has the current state before any conversation history loads.
## Enable LiveObjects
LiveObjects is not part of the default channel mode set, so it needs explicit opt-in. Three things must be in place, or `session.object` operations throw:
- Construct the Ably Realtime client with the `LiveObjects` plugin from `ably/liveobjects`.
- Request the object channel modes on the session, by passing `OBJECT_MODES` as the session's `channelModes` option.
- Grant the connection's token the `object-subscribe` and `object-publish` [capabilities](https://ably.com/docs/ai-transport/setup/authentication.md#capabilities).
Pass the plugin when you create the client, and the modes when you create the session:
### Javascript
```
import * as Ably from 'ably';
import { LiveObjects } from 'ably/liveobjects';
import { createClientSession, OBJECT_MODES } from '@ably/ai-transport';
import { createUIMessageSessionCodec } from '@ably/ai-transport/vercel';
// Without the LiveObjects plugin, session.object throws.
const ably = new Ably.Realtime({ authUrl: '/api/auth/token', plugins: { LiveObjects } });
const session = createClientSession({
client: ably,
channelName: 'conversation-42',
codec: createUIMessageSessionCodec(),
// Opt the session's channel into LiveObjects. The session requests these
// modes on top of the ones AI Transport always needs, so the transport
// itself is unaffected.
channelModes: OBJECT_MODES,
});
```
`OBJECT_MODES` is `['OBJECT_SUBSCRIBE', 'OBJECT_PUBLISH']`. Channel modes replace the default set rather than adding to it, so the session requests `OBJECT_MODES` together with the modes it always needs.
## React to state changes on both sides
Both sides read and write the same object. The client writes the user's current state as they navigate, and renders whatever the agent sends back:
### Javascript
```
const myObject = await session.object.get();
// Publish the user's current view as they navigate.
function onNavigate(path) {
myObject.set('currentPage', path);
}
// Render whatever the agent reports back.
myObject.subscribe(() => renderAgentStatus(myObject.compactJson()?.agentStatus));
```
The agent subscribes to the same state and adapts to it. When the user opens a new page, the subscription fires with the new value, and the agent can ground its next response in the page they're actually looking at. It reports its own progress back through the same object:
### Javascript
```
import { OBJECT_MODES } from '@ably/ai-transport';
import { createAgentSession } from '@ably/ai-transport/vercel';
const session = createAgentSession({
client: ably,
channelName: invocation.sessionName,
channelModes: OBJECT_MODES,
});
await session.connect();
const myObject = await session.object.get();
// Adapt to whatever the user is currently looking at.
myObject.subscribe(() => {
const { currentPage } = myObject.compactJson() ?? {};
// Ground the next response in the page the user moved to.
});
// Report progress back; every subscribed client sees it.
await myObject.set('agentStatus', 'searching flights');
```
Every write reaches all subscribed clients over the same session. Concurrent writes are safe when the operation commutes: two clients calling `LiveCounter.increment` both count, but a `LiveMap.set` on the same key is last-write-wins. Partition writes by key so two writers don't race on one `set`.
## Edge cases and unhappy paths
- A client constructed without the `LiveObjects` plugin throws when you call `session.object`. The session does not suppress the error; construct the client with `plugins: { LiveObjects }`.
- A session created without `channelModes: OBJECT_MODES` attaches without object modes, and object operations fail. Pass the modes when you create the session.
- A token missing the `object-subscribe` or `object-publish` capability fails at the operation site rather than at construction. The server grants only the permitted subset of requested modes. Capability scoping is part of [authentication](https://ably.com/docs/ai-transport/setup/authentication.md#capabilities).
- A `LiveMap.set` on a key two clients write at once is last-write-wins, so one write is lost. Partition writes by key, or use a `LiveCounter` where the values need to merge.
- A read-only client can request `['OBJECT_SUBSCRIBE']` on its own rather than the full `OBJECT_MODES`. Object writes then fail client-side before any publish, which shows the mistake sooner than a token rejection does.
- `channelModes` must stay constant while the session lives. The React provider recreates its session only when `channelName` changes, so changing the modes after mount reverts the channel's mode set without a reattach.
- `ably/react` exports no LiveObjects hooks, so subscribe imperatively in an effect rather than looking for a `useLiveMap` equivalent.
## FAQ
### What belongs in shared state, and what belongs in the conversation?
Put the state both sides act on now in `session.object`: the page the user is on, the record they've selected, a form in progress, a counter. Leave the conversation itself in the message stream, and keep the agent's private reasoning on the server.
### How does the agent react as soon as the state changes?
`session.object.get()` gives you the object, and `subscribe` fires on every change, nested ones included. In the callback the agent reads the latest value with `compactJson()` and works from that.
### Why does session.object throw?
One of the three [requirements](#enable) is missing: the `LiveObjects` plugin on the client, `channelModes: OBJECT_MODES` on the session, or the `object-subscribe` / `object-publish` capabilities on the token.
### Can both the agent and the client write to the same object?
Yes. Both call the same `LiveMap` and `LiveCounter` API on `session.object`. Concurrent writes merge when the operation commutes, as with `LiveCounter.increment`; a `LiveMap.set` on the same key is last-write-wins. Partition writes by key to avoid races.
## Related features
- [LiveObjects](https://ably.com/docs/liveobjects.md): the Ably LiveObjects API, including `LiveMap` and `LiveCounter`.
- [Tool calling](https://ably.com/docs/ai-transport/durable-sessions/tool-calling.md): the agent asks for specific data on demand, where state sync observes it continuously.
- [Sessions](https://ably.com/docs/ai-transport/durable-sessions/sessions.md): `session.object` on the client and agent sessions.
- [Agent presence](https://ably.com/docs/ai-transport/channel/agent-presence.md): the same pass-through pattern for Ably Presence.
## Related Topics
- [Agent presence](https://ably.com/docs/ai-transport/channel/agent-presence.md): Show agent status in your AI application with Ably Presence. Display streaming, thinking, idle, and offline states in realtime.
- [Push notifications](https://ably.com/docs/ai-transport/channel/push-notifications.md): Notify users when AI agents complete background tasks with Ably Push Notifications. Reach users even when they're offline.
## Documentation Index
To discover additional Ably documentation:
1. Fetch [llms.txt](https://ably.com/llms.txt) for the canonical list of available pages.
2. Identify relevant URLs from that index.
3. Fetch target pages as needed.
Avoid using assumed or outdated documentation paths.