Event Mapping
@threadplane/ag-ui reduces AG-UI protocol events into the Agent contract from @threadplane/chat.
This page is the compatibility map. If your backend emits these events with the expected fields, the chat UI can render the run without knowing which runtime produced it.
#Summary
| AG-UI event | Agent field | Behavior |
|---|---|---|
RUN_STARTED | status, isLoading, error, interrupt | Sets status to running, isLoading to true, clears error, and clears any pending interrupt. |
RUN_FINISHED | status, isLoading | Sets status to idle and isLoading to false. |
RUN_ERROR | status, isLoading, error | Sets status to error, stops loading, and stores the event message when present. |
TEXT_MESSAGE_START | messages | Creates or reuses an assistant message slot. |
TEXT_MESSAGE_CONTENT | messages | Appends delta to the message content. |
TEXT_MESSAGE_END | messages | No-op today. Content is already accumulated from deltas. |
REASONING_MESSAGE_START | messages | Creates or reuses an assistant message slot with reasoning. |
REASONING_MESSAGE_CONTENT | messages | Appends delta to message.reasoning. |
REASONING_MESSAGE_CHUNK | messages | Treated the same as REASONING_MESSAGE_CONTENT. |
REASONING_MESSAGE_END | messages | Adds reasoningDurationMs when timing is available. |
TOOL_CALL_START | toolCalls, messages | Adds a running tool call; also links the call to its parent assistant message (via parentMessageId), creating a message slot if needed. |
TOOL_CALL_ARGS | toolCalls | Accumulates partial JSON fragments, keeps the last-good parsed args, and finalizes parsing on TOOL_CALL_END. |
TOOL_CALL_RESULT | toolCalls | Stores the tool result on the matching tool call. |
TOOL_CALL_END | toolCalls | Marks the matching tool call complete. |
STATE_SNAPSHOT | state, messages | Replaces state and merges citations from state.citations. |
STATE_DELTA | state, messages | Applies JSON Patch to state and merges citations from state.citations. |
MESSAGES_SNAPSHOT | messages, toolCalls | Replaces the message list and merges snapshot-embedded tool calls into toolCalls (by id). |
CUSTOM (name: on_interrupt) | interrupt | Sets the interrupt signal to { id, value, resumable: true }. The value is JSON-parsed when it arrives as a string. |
CUSTOM (other names) | events$ | Emits a runtime-neutral custom event. |
| unknown event | none | Ignored. Future protocol events do not crash the adapter. |
#Run lifecycle
The adapter exposes lifecycle through status, isLoading, and error.
RUN_STARTED clears a previous error. RUN_ERROR and onRunFailed both put the agent into the error state.
onRunFailed comes from the AG-UI subscriber API rather than a protocol event. The adapter treats it like a run failure: status = 'error', isLoading = false, and error is the thrown value.
#Messages
Text messages are accumulated by messageId.
The resulting message is:
TEXT_MESSAGE_END does not finalize anything separately. The content already lives in the signal.
MESSAGES_SNAPSHOT replaces the full message array. Use it when the backend is authoritative for the whole conversation.
#Reasoning
Reasoning events write to the same assistant message when they share the same messageId as the final text response.
The message keeps both fields:
The duration is measured in the browser from start to end event. It is useful for display, not billing or tracing.
#Tool calls
Tool calls are stored independently from messages in agent.toolCalls().
The resulting tool call is:
TOOL_CALL_ARGS accepts partial JSON fragments. The reducer appends each delta to an internal buffer for the tool call, parses whenever the buffer becomes valid JSON, and keeps the last-good args while more fragments arrive. On TOOL_CALL_END, it performs one final parse before marking the call complete.
If the accumulated buffer never parses, args stay at the last-good value or {}.
#State
STATE_SNAPSHOT replaces the full state object:
STATE_DELTA applies JSON Patch operations to the current state:
After either state event, the adapter also runs the citations bridge. If state.citations contains entries keyed by message id, those citations are copied onto the matching messages.
See Citations for the expected shape.
#Custom events
CUSTOM events are exposed through events$.
When the custom event name is state_update and the value is an object, the adapter emits:
For every other custom event name, it emits:
Use state for durable UI state. Use events$ for transient events, telemetry hooks, or UI side effects that should not be stored as conversation state.
Every non-on_interrupt CUSTOM event is fanned out to two surfaces, not one. Alongside the events$ emission above, the reducer also appends { name, data } to the AG-UI-specific customEvents() signal documented on injectAgent() and toAgent(). Reach for the signal when you want an accumulated per-run snapshot for reactive rendering (for example, a2ui-partial generative UI); reach for events$ when you want a transient stream for side-effects or telemetry.
#Submit and stop
agent.submit({ message }) performs three steps:
- Builds a local user message.
- Appends it to
messagesand callssource.addMessage(). - Calls
source.runAgent().
If message is omitted, no user message is appended, but runAgent() still runs.
agent.stop() calls source.abortRun(). Cancellation depends on the AG-UI source implementation.
#Activity and subagents
ACTIVITY_SNAPSHOT and ACTIVITY_DELTA events project into the AG-UI-specific subagents() signal. Use that signal for progress cards and nested task views backed by AG-UI activity streams.
#Unsupported protocol areas
The adapter supports CUSTOM on_interrupt events for the runtime-neutral interrupt() signal, and it supports ACTIVITY-backed subagent progress. It does not implement history or time-travel. Unknown protocol events are ignored rather than treated as errors.