# Multi-device sessions Your users move between devices and the conversation follows them. AI Transport puts every device on the same channel, so the second tab and the phone see the same session in real time. A multi-device session works because the [session](https://ably.com/docs/ai-transport/concepts/sessions.md?source=llms.txt) is backed by a shared Ably channel, not a single client-to-server HTTP connection. Any device that subscribes to the channel sees every message: user prompts, agent responses, and control signals. Open a second tab, switch to a phone, or share a session with a colleague. ![Diagram showing multi-device co-pilots architecture](https://raw.githubusercontent.com/ably/docs/main/src/images/content/diagrams/ai-transport-multi-device-co-pilots-architecture.png) Fan-out to multiple connected devices is automatic. There's no special configuration: #### Javascript ``` // Client A (laptop): wrap with a TransportProvider for chatId. // Client B (phone): same channel name in its own TransportProvider, different device. // Inside Chat, read the transport from context. const { transport } = useClientTransport(); ``` ## How it works Every client connected to the same Ably channel shares the same durable session. When any participant publishes (a user message, an agent response, a cancel signal), every other participant receives it through their channel subscription. The client transport distinguishes between own turns (started by this client) and observer turns (started by someone else). Both types are tracked, decoded, and added to the conversation tree. The UI updates for every client, regardless of who initiated the action. ## Distinguish own and observer turns The transport separates turns initiated by the current client from those by other participants: | Type | Origin | Handle | | --- | --- | --- | | Own turn | This client sent the HTTP POST that created the turn. | An `ActiveTurn` with a `stream` and a `cancel()` method. | | Observer turn | Another client or agent created the turn. | Lifecycle events and streamed messages only; no direct stream handle. | Both types appear in the conversation tree and the UI. The difference is internal routing: own turns have a dedicated stream, observer turns accumulate from channel messages. ## Track active turns across clients `useActiveTurns` returns a `Map>` of currently streaming turns: ### Javascript ``` const activeTurns = useActiveTurns(); const isAnyoneStreaming = activeTurns.size > 0; const isAgentWorking = activeTurns.has('agent-1'); ``` This is consistent across every connected client. If client A starts a turn, client B's `useActiveTurns` updates immediately. ## Sync with useChat When using Vercel's `useChat`, the `useMessageSync` hook pushes messages from other clients into `useChat`'s state: ### Javascript ``` const { chatTransport } = useChatTransport(); const { messages, setMessages } = useChat({ transport: chatTransport }); useMessageSync({ setMessages }); ``` Without `useMessageSync`, `useChat` only renders messages from its own sends. The sync hook bridges the gap by feeding observer messages into state. ## Handle late joiners A client that connects after the conversation has started loads the full history from the channel: ### Javascript ``` const { nodes, hasOlder, loadOlder } = useView({ limit: 30 }); ``` `useView` loads history on mount. If a response is currently streaming, the late joiner sees it in progress; the lifecycle tracker synthesises missing events so the stream renders correctly. ## Identify the client Each client has a `clientId` that identifies it across the session. Set the client ID through Ably token authentication so it is verified and cannot be spoofed: ### Javascript ``` // In your token endpoint const token = jwt.sign({ 'x-ably-clientId': 'user-123', // ... }, keySecret); ``` The `clientId` is used throughout: turn ownership, cancel scoping (`{ own: true }` filters by the sender's client ID), and active turn tracking. See [authentication](https://ably.com/docs/ai-transport/concepts/authentication.md?source=llms.txt) for the full setup. ## Edge cases and unhappy paths - Two clients sharing the same `clientId` are indistinguishable to the transport. Cancel signals scoped to `{ own: true }` cancel turns from both. Use a unique `clientId` per device when ownership matters. - A late joiner without channel history capability sees the live stream but not the conversation that came before. Capability scoping is part of [authentication](https://ably.com/docs/ai-transport/concepts/authentication.md?source=llms.txt). - A client that loses connectivity mid-stream resumes its own view on reconnect. Other clients' views are unaffected. - Two devices sending messages at the same time create two separate turns on the same session. See [concurrent turns](https://ably.com/docs/ai-transport/features/concurrent-turns.md?source=llms.txt) for the multiplexing model. - A regenerate triggered on one device updates the conversation tree on every device. The visible branch on each device depends on its current view selection. ## FAQ ### Do I need to write any sync code? No. The channel subscription is the sync. For Vercel `useChat`, add `useMessageSync` to bridge observer messages into its state. ### How many clients connect to one session? There is no fixed limit. The channel is the share point; usage scales with the number of subscribers. See [the platform pricing page](https://ably.com/docs/platform/pricing.md?source=llms.txt) for the connection and message rate limits in effect. ### Can two users have different branch selections on the same session? Yes. Each view holds its own selection. The conversation tree is shared; the view is per-participant. See [conversation branching](https://ably.com/docs/ai-transport/features/branching.md?source=llms.txt). ### What stops a stranger from joining my session? Channel capabilities. Issue tokens that scope `subscribe` and `publish` to the specific channel name for authenticated users. See [authentication](https://ably.com/docs/ai-transport/concepts/authentication.md?source=llms.txt) for scoping examples. ### Does presence work across devices? Yes. Each device enters presence with its own `clientId`. See [agent presence](https://ably.com/docs/ai-transport/features/agent-presence.md?source=llms.txt) for the patterns. ## Related features - [Reconnection and recovery](https://ably.com/docs/ai-transport/features/reconnection-and-recovery.md?source=llms.txt): each device reconnects independently. - [Cancellation](https://ably.com/docs/ai-transport/features/cancellation.md?source=llms.txt): cancel from any device. - [History and replay](https://ably.com/docs/ai-transport/features/history.md?source=llms.txt): late joiners load the full conversation. ## Related Topics - [Token streaming](https://ably.com/docs/ai-transport/features/token-streaming.md?source=llms.txt): Stream AI-generated tokens to clients in realtime using AI Transport, with support for message-per-response and message-per-token patterns. - [Cancellation](https://ably.com/docs/ai-transport/features/cancellation.md?source=llms.txt): Cancel AI responses mid-stream with Ably AI Transport. Scoped cancel signals, server-side authorization, and graceful abort handling. - [Reconnection and recovery](https://ably.com/docs/ai-transport/features/reconnection-and-recovery.md?source=llms.txt): AI Transport streams survive connection drops automatically. Clients reconnect and resume from where they left off with no lost tokens. - [History and replay](https://ably.com/docs/ai-transport/features/history.md?source=llms.txt): Load conversation history from Ably channels with AI Transport. Paginated history, gapless continuity, and scroll-back patterns. - [Conversation branching](https://ably.com/docs/ai-transport/features/branching.md?source=llms.txt): Edit user messages, regenerate AI responses, and navigate branches with Ably AI Transport. The full history is preserved as a tree. - [Interruption](https://ably.com/docs/ai-transport/features/interruption.md?source=llms.txt): Let users interrupt AI agents mid-stream with Ably AI Transport. Cancel-then-send and send-alongside patterns for responsive AI interactions. - [Concurrent turns](https://ably.com/docs/ai-transport/features/concurrent-turns.md?source=llms.txt): Run multiple AI turns simultaneously with Ably AI Transport. Independent streams, scoped cancellation, and multi-agent support. - [Tool calling](https://ably.com/docs/ai-transport/features/tool-calling.md?source=llms.txt): Stream tool invocations and results through Ably AI Transport. Server-executed and client-executed tools with persistent state. - [Human-in-the-loop](https://ably.com/docs/ai-transport/features/human-in-the-loop.md?source=llms.txt): Add human approval gates to AI agent workflows with Ably AI Transport. Approve tool executions and provide input across devices. - [Optimistic updates](https://ably.com/docs/ai-transport/features/optimistic-updates.md?source=llms.txt): User messages appear instantly in Ably AI Transport. Optimistic insertion with automatic reconciliation when the server confirms. - [Agent presence](https://ably.com/docs/ai-transport/features/agent-presence.md?source=llms.txt): Show agent status in your AI application with Ably Presence. Display streaming, thinking, idle, and offline states in real time. - [Push notifications](https://ably.com/docs/ai-transport/features/push-notifications.md?source=llms.txt): Notify users when AI agents complete background tasks with Ably Push Notifications. Reach users even when they're offline. - [Chain of thought](https://ably.com/docs/ai-transport/features/chain-of-thought.md?source=llms.txt): Stream reasoning and thinking content alongside responses with Ably AI Transport. Display chain-of-thought in real time. - [Double texting](https://ably.com/docs/ai-transport/features/double-texting.md?source=llms.txt): Handle users sending multiple messages while the AI is streaming with Ably AI Transport. Queue or run messages concurrently. ## Documentation Index To discover additional Ably documentation: 1. Fetch [llms.txt](https://ably.com/llms.txt?source=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.