#### Javascript
```
await view.regenerate(assistantMessageId);
```
## How it works
Every node on the channel carries `parent` and (optionally) `fork-of` or `msg-regenerate` headers. The [conversation tree](https://ably.com/docs/ai-transport/durable-sessions/conversation-tree.md) reads these to build the branching structure: an edit produces a sibling `InputNode` whose `forkOf` points at the original user message's `codec-message-id`; a regenerate produces a same-parent sibling `RunNode` whose `regeneratesCodecMessageId` points at the original assistant message.
The branch decision is anchored to a `codecMessageId` rather than a `runId`. Your UI can render navigation arrows next to each message bubble: the user's prompt for edit forks, the assistant message for regenerate groups.
The view resolves the branch state on demand. Call `view.branchSelection(codecMessageId)` for any message and get back a handle: `{ hasSiblings, siblings, index, selected, select }`. Call the handle's `select(index)` to switch.
## Regenerate
Regenerate creates a sibling run of an assistant message and starts a fresh turn from the same user prompt. The original response stays in the tree. On the client:
### Javascript
```
const { regenerate } = useView();
await regenerate(assistantMessageId);
```
The SDK publishes a `Regenerate` well-known input variant that points at the assistant `codecMessageId`. The agent receives the [invocation](https://ably.com/docs/ai-transport/streaming/runs-and-steps.md#invocations), creates a new run with `regeneratesCodecMessageId` set, and streams the alternative response.
## Edit a user message
Edit replaces a user message and starts a new turn from that point. The original user message and everything below it stays in the tree as a separate branch. On the client:
### Javascript
```
const { edit } = useView();
await edit(messageId, {
kind: 'user-message',
message: {
id: crypto.randomUUID(),
role: 'user',
parts: [{ type: 'text', text: 'Make it 5 days, focused on food.' }],
},
});
```
`edit` delegates to `send`, so it carries the same rule: at most one input in the array may introduce a new message, and the rest must be wire-only references to messages that already exist. An edit that replaces one prompt with two rejects with `InvalidArgument` before anything is published:
### Javascript
```
// Client-side. Throws: an edit replaces one message with one message.
await view.edit(messageId, [
{ kind: 'user-message', message: { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: 'Make it 5 days.' }] } },
{ kind: 'user-message', message: { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: 'And focus on food.' }] } },
]);
```
Put the whole revised prompt in one message instead. The SDK publishes the replacement as a fresh `UserMessage` with the `forkOf` header pointing at the `codecMessageId` being replaced, and the agent forks the run and starts a fresh response from the edited content.
## Navigate between siblings
When a message has siblings, render arrows that switch between them. `branchSelection` returns a `BranchHandle` for any `codecMessageId`:
### Javascript
```
function BranchNav({ codecMessageId, view }) {
const branch = view.branchSelection(codecMessageId);
if (!branch.hasSiblings) return null;
const { siblings, index } = branch;
return (
{index + 1} of {siblings.length}
);
}
```
`branchSelection` always returns a usable object: `hasSiblings: false` for messages that aren't branch anchors, `index: 0` for unknown ids. You can call it on every rendered bubble without conditionals.
When the user selects a different sibling, the view recomputes the visible branch. Every message below the selection point re-renders to reflect the chosen path.
## Side-by-side branches
On the client, multiple views over the same conversation tree have independent branch selections, so different parts of the UI can render different branches simultaneously:
### Javascript
```
const view1 = useCreateView();
const view2 = useCreateView();
// view1 shows branch A, view2 shows branch B; both read the same tree.
```
This is the pattern for comparison UIs where the user wants to see two regenerated responses side by side.
## Agent-side handling
The agent doesn't usually need bespoke branching logic. The invocation arrives, `createRun` returns a run pinned to the right branch, and draining `run.view` walks the ancestor chain along the selected branch automatically:
### Javascript
```
const invocation = Invocation.fromJSON(await req.json());
const run = session.createRun(invocation, {}, { signal: req.signal });
try {
// Rebuild the conversation from run.view before run.start(): draining pages in
// this run's triggering input (otherwise run.start() awaits it arriving live).
while (run.view.hasOlder()) {
await run.view.loadOlder();
}
const conversation = run.view.getMessages().map(({ message }) => message);
await run.start();
const result = streamText({
model: anthropic('claude-sonnet-4-20250514'),
messages: conversation,
abortSignal: run.abortSignal,
});
const { reason } = await run.pipe(result.toUIMessageStream());
await run.end({ reason });
} catch (err) {
await run.end({ reason: 'error' });
throw err;
} finally {
await session.end();
}
```
`run.view` is pinned to this run's branch, so draining it yields the branch in order: the ancestor turns whose runs completed successfully, and the current user input. Pass those messages straight to the LLM.
## Edge cases and unhappy paths
- Editing or regenerating mid-stream cancels nothing automatically. Call `session.cancel(runId)` on the active run first if you do not want both to run.
- Branch selection is per-view. Two devices on the same session can see different branches simultaneously; the tree is shared, the selection is per-view.
- Deeply branched trees can have many siblings. A user can navigate forever; cap or hide branch navigation in your UI if your app has a preferred branch.
- A regenerate against an edited prompt still works. The new branch attaches at the same anchor regardless of what siblings exist.
- The visible branch recomputes when a sibling is selected through the branch handle's `select`. Avoid heavy work in the render path; updates are frequent during streaming.
- An optimistic edit is merged with the published edit. The SDK [reconciles the local insertion with the wire confirmation](https://ably.com/docs/ai-transport/durable-sessions/optimistic-updates.md).
## FAQ
### Does edit overwrite the original message?
No. The original stays in the tree. A new sibling branch is created with the edited content. Users navigate between branches with the `view.branchSelection(codecMessageId)` handle's `select()`.
### How is regenerate different from sending a new prompt?
Regenerate creates a sibling response for the same user prompt. Sending a new prompt adds a new exchange below the current branch. Use regenerate to compare alternative responses to the same question; use a new prompt to continue the conversation.
### Can two clients edit at the same time?
Yes. Each edit creates a sibling, so concurrent edits produce two new siblings. The tree merges cleanly; each device's view selection determines what it renders.
### What if the LLM produces the same response on regenerate?
You get a sibling with the same content. Use temperature or sampling settings to encourage variety, or add a "regenerate again" button.
### How deep can the tree get?
Tree depth is bounded by the conversation length. Branch breadth grows with edits and regenerations. There's no fixed limit; render performance is the practical constraint.
## Related features
- [Conversation tree](https://ably.com/docs/ai-transport/durable-sessions/conversation-tree.md): the data structure that holds branches.
- [Multi-device and fan-out](https://ably.com/docs/ai-transport/streaming/multi-device.md): edits and regenerations sync across devices.
- [Optimistic updates](https://ably.com/docs/ai-transport/durable-sessions/optimistic-updates.md): how local-first edits reconcile with the published tree.
## Related Topics
- [Overview](https://ably.com/docs/ai-transport/durable-sessions.md): A drop-in durable session layer for AI applications. AI Transport holds the conversation and the message state your UI renders, including branching, and you render from its React hooks.
- [Sessions](https://ably.com/docs/ai-transport/durable-sessions/sessions.md): Understand sessions in AI Transport: persistent, shared conversation state that exists independently of any connection, and the ClientSession and AgentSession objects that attach to it.
- [Conversation tree](https://ably.com/docs/ai-transport/durable-sessions/conversation-tree.md): Understand how AI Transport organises messages into a branching conversation tree, and how views give each client its own linear path through it.
- [Optimistic updates](https://ably.com/docs/ai-transport/durable-sessions/optimistic-updates.md): User messages appear instantly in Ably AI Transport. Optimistic insertion with automatic reconciliation when the server confirms.
- [Tool calling](https://ably.com/docs/ai-transport/durable-sessions/tool-calling.md): 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/durable-sessions/human-in-the-loop.md): Add human approval gates to AI agent workflows with Ably AI Transport. Approve tool executions and provide input across devices.
- [Database hydration](https://ably.com/docs/ai-transport/durable-sessions/database-hydration.md): Hydrate an AI conversation from your own database with AI Transport and reconcile it with the live Ably channel, with no gaps and no duplicate messages.
- [Migrate from Streaming](https://ably.com/docs/ai-transport/durable-sessions/move-up-from-streaming.md): What changes in an application already streaming with Ably AI Transport when it moves the conversation into a durable session, and what stays exactly as it is.
## 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.