Skip to main content
The AI Agents and Prompts surface ships as part of the v4.5 release candidate. Install with @trigger.dev/sdk@rc (or pin 4.5.0-rc.0 or later) to use these features — they aren’t yet on the latest stable, and APIs may still change before the 4.5.0 GA. See supported AI SDK versions and the AI chat changelog for details.
Most chat UIs treat conversations as linear sequences. But real conversations branch — users edit previous messages, regenerate responses, undo exchanges, and explore alternative paths. This pattern shows how to build a branching conversation system using hydrateMessages, chat.history, and custom actions.

Data model

The standard approach (used by ChatGPT, Open WebUI, LibreChat, and others) stores messages as a tree with parent pointers:
A conversation is a tree of nodes. The active branch is resolved by walking from a leaf node up through parentId pointers to the root, then reversing:
Switching branches means changing which leaf is “active” — the same tree, different path.

Backend setup

Store: tree operations

Define helpers that read and write the node tree. Adapt to your database:

Agent: hydration + actions

Frontend

Sending actions

Wire up edit, undo, and branch switching to the transport:

Branch navigation

To show the < 2/3 > sibling switcher, query the tree for siblings at each fork point. This is a frontend concern — the backend exposes the data, the UI navigates it.
The sibling data (which messages share the same parent) needs to come from your database — query it when loading the chat or include it as client data. The agent only returns the active branch via hydrateMessages.

How it works

Design notes

  • Messages are immutable — edits create siblings, not mutations. This preserves full history for analytics and auditing.
  • The tree lives in your database — the agent loads a linear path from it via hydrateMessages. The agent itself doesn’t know about the tree structure.
  • hydrateMessages + onAction + chat.history are the three primitives. Hydration loads the active path, actions modify the tree, and chat.history.set() syncs the accumulator after tree modifications.
  • Frontend owns navigation — the < 2/3 > UI, sibling queries, and branch switching triggers are client-side concerns. The backend just processes actions and returns responses.

See also