Dosu LogoDosu Logo
Ask
Join our Discord
assistant-ui's SpacePublic
assistant-ui
Documentsassistant-ui's Space
AG-UI Message History Management
AG-UI Message History Management
Type
Topic
Status
Published
Created
Jul 8, 2026
Updated
Jul 8, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

AG-UI Message History Management#

AgUiThreadRuntimeCore is the central class in @assistant-ui/react-ag-ui that owns the conversation history for a single thread. It maintains a flat, ordered ThreadMessage[] array where message position implies parentage — each message's implicit parent is the one before it . All mutations (append, edit, reload, cancel) converge through a small set of private methods that operate on this array.

Linear Message Architecture#

The runtime stores messages as a plain array — not a tree. The history is "reset to a head" by slicing: resetHead(parentId) truncates this.messages to everything up to and including the given parentId. This is how edits and reloads discard future messages without needing a tree structure.

New messages are pushed to the end:

  • appendEntry calls resetHead then appends the new ThreadMessage.
  • startRun also calls resetHead(parentId) before inserting an assistant placeholder, ensuring the assistant reply always lands at the end of the slice.

The conversion layer keeps the agent's external format and the internal format in sync: toAgUiMessages serializes the internal array into AG-UI Message[] for each agent run, and fromAgUiMessages deserializes AG-UI messages back, folding role: "tool" messages into the preceding assistant message's tool-call parts.

Optimistic Message ID Handling#

Before a run starts, the runtime inserts an assistant placeholder with an __optimistic__-prefixed id . This placeholder :

  • Is given metadata.isOptimistic: true so that the core MessageRepository's evictOffBranchOptimisticMessages mechanism can clean it up if needed.
  • Has status: { type: "running" }.

When the agent emits TEXT_MESSAGE_START (or any event that surfaces a canonical server id via onServerMessageId), reassignAssistantId(oldId, newId) swaps the placeholder into the real id by:

  1. Stripping metadata.isOptimistic from the renamed message so the now-real message is never evicted .
  2. Migrating the pending history-parent entry from oldId → newId in assistantHistoryParents.
  3. If the server id already exists in the array (duplicate delivery), dropping the placeholder entirely.

If no server id arrives before the run settles, updateAssistantMessage detects the terminal status and assigns a stable local id via generateId(), again going through reassignAssistantId to strip the optimistic flag .

Why this matters: Without isOptimistic: true, the core repository would retain both the placeholder node and the server-id node as sibling branches, surfacing a blank "branch 1/2" in the UI on every turn. This was the bug fixed in PR #4317.

Branching Preservation Utilities#

While AgUiThreadRuntimeCore itself is linear, the surrounding infrastructure supports full branching via ExportedMessageRepository (from @assistant-ui/core):

  • ExportedMessageRepository.fromArray(messages) — builds a linear repository from a flat array, each message's parent being the previous one.
  • ExportedMessageRepository.fromBranchableArray(messages, { headId }) — builds a branching tree from messages that carry an explicit parentId, allowing multiple children per node .

The AG-UI runtime integrates with these through the optional ThreadHistoryAdapter passed to AgUiThreadRuntimeCore . On load, the adapter's load() result feeds into applyExternalMessages, which replaces the internal array and resets recordedHistoryIds. Subsequent messages are appended to history via appendHistoryItem, which deduplicates via the recordedHistoryIds set.

Loading persisted AG-UI history — the public fromAgUiMessages export (added in PR #4264) supports the page-load flow:

fetch persisted AG-UI messages
  → fromAgUiMessages(msgs, options) // returns ThreadMessageLike[]
  → ExportedMessageRepository.fromArray(...)
  → history adapter's load()
  → AgUiThreadRuntimeCore.applyExternalMessages()

This reuses the same conversion code path used internally for MESSAGES_SNAPSHOT events .

Thread State Coordination#

Several mechanisms prevent race conditions and phantom state during runs:

Tool result preservation. The RunAggregator rebuilds assistant content entirely from stream events, so frontend-injected tool results (via addToolResult) would be lost on the next update. preserveToolResults merges previously resolved tool-call parts back into each new content snapshot before applying it.

Deferred tool-call continuation. If a TOOL_CALL_RESULT event arrives while a run is still draining (between RUN_FINISHED and stream close), the follow-up run is deferred to pendingResumeMessageId and dispatched after startRun returns , ensuring no two runs ever overlap.

Cross-run tool results. TOOL_CALL_RESULT events for tool calls that belong to a previous run (not the current aggregator) are routed directly to the historical message via applyCrossRunToolResult rather than the active aggregator, avoiding a false auto-resume trigger.

History deduplication. recordedHistoryIds (a Set<string>) prevents double-appending to the ThreadHistoryAdapter when the same message is processed multiple times (e.g., after an id reassignment) .

Interrupt guards. assertNoPendingInterrupts() throws before append, reload, or resume if the last assistant message is in requires-action / interrupt status, ensuring the thread cannot be advanced past an unresolved interrupt.

Documents
AG-UI Message History Management
CHANGELOG
external-store
overview
README
AG-UI Streaming State Management
CHANGELOG
hooks
hooks
index
introduction
overview
part-1
README
README
stockbroker
AI SDK Integration
ai-sdk
assistant-runtime-provider
custom-backend
custom-backend
expo
hooks
hooks
index
index
index
introduction
migration
migration
overview
part-2
README
README
README
README
README
README
README
README
stockbroker
Bedrock Tool Call Streaming
Cloud Message Pagination
index
External History Adapter
adapters
hooks
README
Generative UI Date and Time Controls
In-Memory Thread Lifecycle
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
LangGraph Anthropic Streaming
Local Runtime Message History
CHANGELOG
Markdown Preprocessing and Repair
CHANGELOG
CHANGELOG
MCP App Tool UI Rendering
CHANGELOG
CHANGELOG
CHANGELOG
message
MCP OAuth State Management
MessageRepository Branch Pointer Invariant
Next.js Cache Components
Remote Thread List Runtime
adapters
CHANGELOG
CHANGELOG
custom-backend
custom-backend
hooks
index
README
README
thread-runtime
Render Prop Leakage
Run Cancellation and Abort Signal Propagation
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
README
Run Telemetry Model ID Extraction
SSR-Safe Thread ID Generation
Stream Decoder Buffer Limits
CHANGELOG
CHANGELOG
CHANGELOG
StreamdownTextPrimitive
index
Streaming Markdown Rendering
CHANGELOG
CHANGELOG
index
introduction
part-1
Viewport Auto-Scroll
CHANGELOG
CHANGELOG
overview
perplexity
primitives
AGENTS
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CHANGELOG
CODE_OF_CONDUCT
CONTRIBUTING
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README
README_LANGGRAPH
SECURITY
SPEC
SPEC_AssistantFrame
SPEC_ModelContextRegistry
action-bar
action-bar-more
adapters
artifacts
assistant-if
assistant-modal
assistant-modal
assistant-runtime
assistant-sidebar
attachment
attachment
attachment-runtime
branch-picker
chatgpt
claude
composer
composer-runtime
composition
context-display
error
file
form-demo
grok
heat-graph
image
index
index
index
index
index
index
index
index
index
markdown
mem0
mermaid
message-part
message-part-runtime
message-runtime
message-timing
modal
model-selector
part-3
pick-a-runtime
primitives
quote
reasoning
selection-toolbar
sources
suggestion
syntax-highlighting
thread
thread
thread-list
thread-list
thread-list-item
thread-list-item-more
thread-list-item-runtime
thread-list-runtime
tool-fallback
tool-group
tw-shimmer
v4-legacy
v5-legacy