Real conversations aren't linear
Picture a developer pair-programming with an AI assistant. The model returns a function that almost works. The developer asks it to try again. The second attempt is worse. They want the first one back.
In a linear chat, that history is gone, or it's a third bubble in the thread that pollutes context for every future turn. The same problem shows up when a user edits a message and wants to compare both phrasings, or when an agent picks a tool that fails and you'd rather not feed the failure back into its next reasoning step.
`@ably/ai-transport` exposes a tree instead. Branches are first-class: they are explicit in the wire format, exposed through the React hooks, and persisted on the channel alongside the linear messages.
The tree model
Every message has three headers: a `msgId`, a `parentId`, and an optional `forkOf`. Together those three fields describe a directed acyclic graph rooted at the start of the conversation. A normal turn appends a child. A regeneration adds a sibling that shares a `parentId` with an existing assistant message and points to it via `forkOf`. An edit does the same one level higher, on a user message.
The SDK then projects that tree into a linear list of messages for rendering. You navigate between siblings at any fork point, and the projection updates. The underlying tree never loses anything.
The contract is on the `View` interface:
A `View` is a windowed, branch-aware projection over the underlying `Tree`. The tree is the source of truth across all clients on the channel. Each view holds its own branch selection, so two users in the same conversation can be looking at different branches at the same time.
How it works in practice
The use-client-transport demo wires the tree primitives into `useView` and `useCreateView`. Three operations carry most of the weight.
Regenerate
When the user wants another shot at the last assistant reply, hand its `msgId` to `regenerate`:
The hook computes `forkOf`, `parent`, and the truncated history from the currently selected branch. The server publishes the new turn under the same parent as the old one, and both versions live side by side in the tree.
Edit
Editing forks at a user message instead of an assistant message. You pass the target `msgId` and the replacement content:
Everything downstream of the edited message stays in the tree, untouched, on a different branch. A user can flip back to the original phrasing whenever they want.
Sibling navigation
The `MessageList` component asks the view, per node, whether siblings exist and which one is selected:
The bubble renders a small `< 1/3 >` navigator when `hasSiblings` is true. Clicking calls `view.select`, which updates the projection and re-emits the visible node list. The swap is purely local. The data is already in the tree, so there is no round trip to the server.


Real-world use cases
Branching can look like a niche UI flourish at first. The three scenarios below are where it earns its weight.
Customer support agent assist
A human agent is on a live customer chat. The AI drafts a reply. The supervisor reviews it, isn't happy, and regenerates. In a linear thread, you've now either overwritten the original draft or left both visible to the customer. Neither is ideal.
With branching, the regeneration is a sibling. The agent sees both drafts side by side, picks one, and only the chosen branch becomes the visible reply. The discarded draft stays in the tree for audit. Because the transport runs on Ably channels, the supervisor and the agent are subscribed to the same conversation in realtime and they're operating on the same tree.
AI agent tool retry
An agent calls a search tool and gets a noisy result. The next reasoning step is going to anchor on that result. You'd like to retry with a different query, or a different tool, without leaving the failed call in the context window the model sees.
A regenerate forks the assistant turn. The original tool call still lives in the tree (you might want it for evals), but the active branch for the next turn is the cleaner attempt. The history sent to the model on the next request is computed from the selected branch, so the failed path doesn't leak into the prompt.
This is harder than it sounds in a linear architecture because tool calls and their outputs are usually pinned together. The tree treats them as part of a single forkable unit.
Content and copywriting tools
An editorial team uses an AI tool to draft intros for an article. Someone types "give me three options." The model returns one. They regenerate twice more.
With a single linear thread, you're scrolling up and down comparing three blocks of text. With a split view, you put two on screen at once via `useCreateView` and flip the third in via `select`. The tree records all three forever, so going back next week to retrieve the rejected drafts is a `getSiblings` call rather than a database archaeology project.
Play The Ably Affair
The Ably Affair is a detective mystery where the investigation itself is a tree. Press a witness one way and you can regenerate to press them another, with the original line of questioning preserved as a sibling you can come back to. Share the room URL and other tabs join the same case, each detective free to chase their own theory down a different fork while the tree stays in sync underneath.
Tradeoffs
Branching is not free. Storing the full tree means you're keeping messages a linear chat would discard, and your UI needs to handle the case where a user has no idea which branch they're on. Sibling navigators help, but they're another affordance to teach.
The storage cost compounds on long, heavily-edited threads, since every regenerate is a permanent sibling. Analytics get an awkward question. "What did the user actually see?" is the active branch. "What did the conversation cost?" sums every branch. Export and replay hit the same fork in the road.
The SDK gives you a tree-shaped data model and a wire format that preserves it. It does not decide which branch to send to the model on the next turn, when to surface alternates in the UI, or what counts as "the conversation" for analytics and export. Those remain product decisions.
Try it yourself
To see code examples and how a working demo is constructed, clone the Ably AI Transport repository and run the use-client-transport demo. In this demo you can create a branched conversation by editing messages in the chat UI.




