AG-UI Protocol

Runtime options

useAgUiRuntime options, adapters, supported events, thread list.

Reference for the runtime's API surface. Start with quickstart if you have not already.

useAgUiRuntime options

OptionTypeDescription
agentHttpAgentAn AG-UI client agent (from @ag-ui/client). Required.
loggerPartial<Logger>Optional logger overrides. The runtime logs event-parser warnings and run lifecycle events.
showThinkingbooleanWhether to render THINKING_* and REASONING_* events as visible reasoning. Defaults to true.
onError(e: Error) => voidError callback fired on RUN_ERROR events and protocol errors.
onCancel() => voidCancellation callback fired when the user cancels a run.
adaptersUseAgUiRuntimeAdaptersStandard adapter slots (see below).

Adapter slots

AdapterSlotNotes
Attachmentsadapters.attachmentsSee attachment adapter.
Speechadapters.speechText-to-speech. See speech adapter.
Dictationadapters.dictationSpeech-to-text input.
Feedbackadapters.feedbackThumbs up / down. See feedback adapter.
Historyadapters.historyPer-thread message persistence.
Thread listadapters.threadListMulti-thread switching (experimental, see below).

Loading conversation history

If your backend exposes the persisted AG-UI messages of a conversation (for example a GET /agents/state endpoint), use fromAgUiMessages to convert them to assistant-ui messages and return them from the history adapter so the thread is restored on page load:

import { fromAgUiMessages } from "@assistant-ui/react-ag-ui";
import { ExportedMessageRepository } from "@assistant-ui/react";

const runtime = useAgUiRuntime({
  agent,
  adapters: {
    history: {
      async load() {
        const { messages } = await fetch("/agents/state").then((r) => r.json());
        return ExportedMessageRepository.fromArray(fromAgUiMessages(messages));
      },
      async append({ message }) {
        // persist the newly sent message on your backend
      },
    },
  },
});

Messages sent during the session are always forwarded to the agent through the run input, independent of append. A no-op append is therefore only safe when your backend already persists the conversation on its own; otherwise those messages are gone on the next page load.

fromAgUiMessages accepts an optional second argument: pass { showThinking: false } to match a runtime configured with showThinking: false, so imported reasoning messages are dropped at conversion time, the same way a live run never stores them.

fromAgUiMessages reconstructs the text, reasoning, and tool calls of each message. Multimodal user input (image, audio, video, and document parts, as well as legacy binary parts) is restored as attachments on the user message, so a backend that persists multimodal messages shows them again on reload and re-sends them on the next run. Legacy binary parts that only reference a file id are not restored.

Thread list (experimental)

The thread list adapter is currently experimental and may change without notice.

UseAgUiThreadListAdapter lets you back the thread list with your own state.

OptionTypeDescription
threadIdstringThe currently active thread ID.
onSwitchToNewThread() => Promise<void>Called when the user creates a new thread. Reset your thread state here.
onSwitchToThread(threadId: string) => Promise<{ messages, state? }>Called when the user switches threads. Return the persisted messages (and optional opaque state).
const runtime = useAgUiRuntime({
  agent,
  adapters: {
    threadList: {
      threadId: currentThreadId,
      onSwitchToNewThread: async () => {
        setCurrentThreadId(await createThread());
      },
      onSwitchToThread: async (id) => {
        const { messages, state } = await loadThread(id);
        setCurrentThreadId(id);
        return { messages, state };
      },
    },
  },
});

Interrupts (experimental)

The interrupt API is experimental and may change without notice.

When the agent emits RUN_FINISHED with outcome = { type: "interrupt", interrupts: [...] }, the active assistant message is marked requires-action with reason: "interrupt" and the Interrupt[] payload is written to metadata.custom.agui.interrupts. Render an approval / input UI from there.

useAgUiRuntime returns an AgUiAssistantRuntime with two extra methods:

MethodDescription
unstable_getPendingInterrupts(): readonly AgUiInterrupt[]Snapshot of the open interrupts on the most recent assistant message.
unstable_submitInterruptResponses(responses: ResumeEntry[]): Promise<void>Submits one ResumeEntry per open interrupt and resumes the run.

responses must address every open interrupt; missing entries, unknown ids, or expired interrupts (expiresAt) reject before any network call. Each entry is { interruptId, status: "resolved" | "cancelled", payload? }. The next RunAgentInput carries resume: ResumeEntry[].

const runtime = useAgUiRuntime({ agent });
const pending = runtime.unstable_getPendingInterrupts();

await runtime.unstable_submitInterruptResponses(
  pending.map((i) => ({
    interruptId: i.id,
    status: "resolved",
    payload: { approved: true },
  })),
);

Supported events

The runtime parses the AG-UI event stream and maps each event type to assistant-ui state.

EventEffect
RUN_STARTED / RUN_FINISHEDToggles thread isRunning. RUN_FINISHED.outcome is honored (success / interrupt).
RUN_CANCELLEDMarks the in-flight assistant message as cancelled.
RUN_ERRORMarks the message as errored; fires onError.
TEXT_MESSAGE_START / _CONTENT / _ENDStreams text content into an assistant message.
TEXT_MESSAGE_CHUNKAppends a delta without explicit lifecycle.
THINKING_START / _ENDWraps reasoning blocks (when showThinking is on).
THINKING_TEXT_MESSAGE_*Streams thinking text deltas.
REASONING_START / _MESSAGE_* / _ENDStreams structured reasoning per message.
TOOL_CALL_START / _ARGS / _ENDStreams tool calls into the current assistant message.
TOOL_CALL_CHUNKStreams tool deltas without explicit lifecycle.
TOOL_CALL_RESULTAttaches a tool result to a tool call.
STATE_SNAPSHOTReplaces the agent's external state.
STATE_DELTAApplies a JSON-patch-style delta to the agent's state.
MESSAGES_SNAPSHOTReplaces the full message list (used for thread restore).
CUSTOMForwarded to your custom event handling.
RAWUntyped passthrough for unrecognized events.

Feature support

FeatureSupported
Streaming textYes
Thinking / reasoning blocksYes
Tool calls and resultsYes
Tool result handoff (client-side execution)Yes
State snapshots and deltasYes
CancellationYes
Message editingYes
Message reloadYes
Run resumptionYes
Interrupts (human-in-the-loop)Experimental (unstable_* API, see Interrupts)
Multi-threadExperimental (adapters.threadList)
History persistenceVia history adapter