25 KiB
AG-UI Protocol Specification -- Event Type Reference
Complete reference for all AG-UI event types, derived from @ag-ui/core Zod schemas.
Base Event Fields
All events extend BaseEventSchema and share these fields:
| Field | Type | Required | Description |
|---|---|---|---|
type |
EventType (string enum) |
Yes | Event type discriminator |
timestamp |
number |
No | Unix timestamp (ms) when event was created |
rawEvent |
any |
No | Original event data if transformed from another format |
Events use .passthrough() so additional fields are preserved through parsing.
Lifecycle Events
RUN_STARTED
Emitted first when an agent begins processing. Establishes the run context.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"RUN_STARTED" |
Yes | |
threadId |
string |
Yes | Conversation thread ID |
runId |
string |
Yes | Unique run ID |
parentRunId |
string |
No | Lineage pointer for branching/time travel |
input |
RunAgentInput |
No | The exact agent input payload for this run |
{
"type": "RUN_STARTED",
"threadId": "thread-abc",
"runId": "run-123"
}
RUN_FINISHED
Emitted when an agent run completes successfully. No further events for this run after this.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"RUN_FINISHED" |
Yes | |
threadId |
string |
Yes | Conversation thread ID |
runId |
string |
Yes | Run ID matching RUN_STARTED |
result |
any |
No | Output data from the run |
{
"type": "RUN_FINISHED",
"threadId": "thread-abc",
"runId": "run-123",
"result": { "summary": "Task completed" }
}
RUN_ERROR
Emitted when an agent encounters an unrecoverable error. Terminates the run.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"RUN_ERROR" |
Yes | |
message |
string |
Yes | Error description |
code |
string |
No | Error code (e.g., "abort", "timeout") |
{
"type": "RUN_ERROR",
"message": "Model API rate limited",
"code": "rate_limit"
}
STEP_STARTED
Emitted when a named step/phase begins within a run. Optional but recommended for progress visibility.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"STEP_STARTED" |
Yes | |
stepName |
string |
Yes | Name of the step (e.g., node name, function name) |
{
"type": "STEP_STARTED",
"stepName": "retrieve_documents"
}
STEP_FINISHED
Emitted when a named step completes. Must match a corresponding STEP_STARTED.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"STEP_FINISHED" |
Yes | |
stepName |
string |
Yes | Name of the step |
{
"type": "STEP_FINISHED",
"stepName": "retrieve_documents"
}
Text Message Events
TEXT_MESSAGE_START
Begins a new streaming text message.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TEXT_MESSAGE_START" |
Yes | |
messageId |
string |
Yes | Unique message ID |
role |
"developer" | "system" | "assistant" | "user" |
No | Defaults to "assistant" |
name |
string |
No | Optional sender name |
{
"type": "TEXT_MESSAGE_START",
"messageId": "msg-1",
"role": "assistant"
}
TEXT_MESSAGE_CONTENT
Delivers a chunk of text content. Multiple events build the complete message.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TEXT_MESSAGE_CONTENT" |
Yes | |
messageId |
string |
Yes | Must match TEXT_MESSAGE_START.messageId |
delta |
string |
Yes | Text chunk (must be non-empty) |
{
"type": "TEXT_MESSAGE_CONTENT",
"messageId": "msg-1",
"delta": "Hello, how can "
}
TEXT_MESSAGE_END
Signals that a text message is complete. No more content for this messageId.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TEXT_MESSAGE_END" |
Yes | |
messageId |
string |
Yes | Must match TEXT_MESSAGE_START.messageId |
{
"type": "TEXT_MESSAGE_END",
"messageId": "msg-1"
}
TEXT_MESSAGE_CHUNK (Convenience)
Auto-expands into TEXT_MESSAGE_START / TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_END via the client's transformChunks pipeline. Simplifies backend implementation.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TEXT_MESSAGE_CHUNK" |
Yes | |
messageId |
string |
No | Required on first chunk; links subsequent chunks |
role |
"developer" | "system" | "assistant" | "user" |
No | Role for the message |
delta |
string |
No | Text content chunk |
name |
string |
No | Optional sender name |
The client transformer handles lifecycle:
- First chunk with a new
messageIdemitsTEXT_MESSAGE_START - Each chunk with
deltaemitsTEXT_MESSAGE_CONTENT TEXT_MESSAGE_ENDis emitted when the stream switches to a newmessageIdor completes
{
"type": "TEXT_MESSAGE_CHUNK",
"messageId": "msg-1",
"role": "assistant",
"delta": "Hello!"
}
Tool Call Events
TOOL_CALL_START
Begins a new tool invocation.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TOOL_CALL_START" |
Yes | |
toolCallId |
string |
Yes | Unique ID for this tool call |
toolCallName |
string |
Yes | Name of the tool being called |
parentMessageId |
string |
No | Links tool call to a parent assistant message |
{
"type": "TOOL_CALL_START",
"toolCallId": "tc-1",
"toolCallName": "searchDatabase",
"parentMessageId": "msg-1"
}
TOOL_CALL_ARGS
Streams argument data for a tool call. Arguments are JSON fragments that concatenate to form the complete arguments object.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TOOL_CALL_ARGS" |
Yes | |
toolCallId |
string |
Yes | Must match TOOL_CALL_START.toolCallId |
delta |
string |
Yes | Argument JSON fragment |
{
"type": "TOOL_CALL_ARGS",
"toolCallId": "tc-1",
"delta": "{\"query\": \"recent orders\"}"
}
TOOL_CALL_END
Signals that a tool call's arguments are complete.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TOOL_CALL_END" |
Yes | |
toolCallId |
string |
Yes | Must match TOOL_CALL_START.toolCallId |
{
"type": "TOOL_CALL_END",
"toolCallId": "tc-1"
}
TOOL_CALL_RESULT
Delivers the result of a tool execution. Sent after the tool has been executed (typically by the client/frontend).
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TOOL_CALL_RESULT" |
Yes | |
messageId |
string |
Yes | Message ID for this result in conversation history |
toolCallId |
string |
Yes | Must match the corresponding TOOL_CALL_START.toolCallId |
content |
string |
Yes | Tool execution output |
role |
"tool" |
No | Defaults to "tool" |
{
"type": "TOOL_CALL_RESULT",
"messageId": "msg-tool-1",
"toolCallId": "tc-1",
"content": "{\"results\": [{\"orderId\": \"123\"}]}",
"role": "tool"
}
TOOL_CALL_CHUNK (Convenience)
Auto-expands into TOOL_CALL_START / TOOL_CALL_ARGS / TOOL_CALL_END via the client's transformChunks pipeline.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"TOOL_CALL_CHUNK" |
Yes | |
toolCallId |
string |
No | Required on first chunk |
toolCallName |
string |
No | Required on first chunk |
parentMessageId |
string |
No | Links to parent message |
delta |
string |
No | Argument JSON fragment |
{
"type": "TOOL_CALL_CHUNK",
"toolCallId": "tc-1",
"toolCallName": "searchDatabase",
"delta": "{\"query\":"
}
State Management Events
STATE_SNAPSHOT
Delivers a complete replacement of the agent's state. Frontend should discard existing state and use this snapshot.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"STATE_SNAPSHOT" |
Yes | |
snapshot |
any |
Yes | Complete state object |
{
"type": "STATE_SNAPSHOT",
"snapshot": {
"documents": [],
"currentStep": "planning",
"progress": 0
}
}
STATE_DELTA
Delivers incremental state updates as RFC 6902 JSON Patch operations. Applied to current state using fast-json-patch.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"STATE_DELTA" |
Yes | |
delta |
Array<JsonPatchOp> |
Yes | Array of JSON Patch operations |
JSON Patch operations:
{ "op": "add", "path": "/key", "value": ... }{ "op": "replace", "path": "/key", "value": ... }{ "op": "remove", "path": "/key" }{ "op": "move", "path": "/to", "from": "/from" }{ "op": "copy", "path": "/to", "from": "/from" }{ "op": "test", "path": "/key", "value": ... }
{
"type": "STATE_DELTA",
"delta": [
{ "op": "replace", "path": "/currentStep", "value": "executing" },
{ "op": "replace", "path": "/progress", "value": 0.5 }
]
}
MESSAGES_SNAPSHOT
Delivers a complete snapshot of the conversation message history. Uses edit-based merge: existing messages present in the snapshot are replaced, activity messages are preserved, messages not in the snapshot are removed, and new messages from the snapshot are appended.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"MESSAGES_SNAPSHOT" |
Yes | |
messages |
Message[] |
Yes | Array of message objects |
{
"type": "MESSAGES_SNAPSHOT",
"messages": [
{ "id": "m1", "role": "user", "content": "Hello" },
{ "id": "m2", "role": "assistant", "content": "Hi there!" }
]
}
Activity Events
ACTIVITY_SNAPSHOT
Delivers a complete snapshot of an activity (structured progress update displayed between chat messages).
| Field | Type | Required | Description |
|---|---|---|---|
type |
"ACTIVITY_SNAPSHOT" |
Yes | |
messageId |
string |
Yes | Activity message ID |
activityType |
string |
Yes | Discriminator (e.g., "PLAN", "SEARCH", "CODE") |
content |
Record<string, any> |
Yes | Structured activity data |
replace |
boolean |
No | Defaults to true. If false, ignored when message exists |
{
"type": "ACTIVITY_SNAPSHOT",
"messageId": "activity-1",
"activityType": "SEARCH",
"content": {
"query": "CopilotKit setup",
"results": [],
"status": "searching"
}
}
ACTIVITY_DELTA
Applies JSON Patch updates to an existing activity message's content.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"ACTIVITY_DELTA" |
Yes | |
messageId |
string |
Yes | Must match an existing activity message |
activityType |
string |
Yes | Activity discriminator |
patch |
Array<JsonPatchOp> |
Yes | RFC 6902 JSON Patch operations |
{
"type": "ACTIVITY_DELTA",
"messageId": "activity-1",
"activityType": "SEARCH",
"patch": [
{ "op": "replace", "path": "/status", "value": "complete" },
{
"op": "add",
"path": "/results/0",
"value": { "title": "Getting Started" }
}
]
}
Reasoning Events
REASONING_START
Marks the beginning of a reasoning process (chain-of-thought).
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_START" |
Yes | |
messageId |
string |
Yes | Reasoning context ID |
{
"type": "REASONING_START",
"messageId": "reasoning-1"
}
REASONING_MESSAGE_START
Begins a streaming reasoning message (visible portion of chain-of-thought).
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_MESSAGE_START" |
Yes | |
messageId |
string |
Yes | Message ID |
role |
"reasoning" |
Yes | Always "reasoning" |
{
"type": "REASONING_MESSAGE_START",
"messageId": "reasoning-msg-1",
"role": "reasoning"
}
REASONING_MESSAGE_CONTENT
Delivers a chunk of reasoning text.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_MESSAGE_CONTENT" |
Yes | |
messageId |
string |
Yes | Must match REASONING_MESSAGE_START.messageId |
delta |
string |
Yes | Reasoning text chunk (must be non-empty) |
{
"type": "REASONING_MESSAGE_CONTENT",
"messageId": "reasoning-msg-1",
"delta": "Let me think about this..."
}
REASONING_MESSAGE_END
Signals a reasoning message is complete.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_MESSAGE_END" |
Yes | |
messageId |
string |
Yes | Must match REASONING_MESSAGE_START.messageId |
{
"type": "REASONING_MESSAGE_END",
"messageId": "reasoning-msg-1"
}
REASONING_MESSAGE_CHUNK (Convenience)
Auto-expands into REASONING_MESSAGE_START / CONTENT / END via client transformer.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_MESSAGE_CHUNK" |
Yes | |
messageId |
string |
No | Required on first chunk |
delta |
string |
No | Reasoning text chunk |
{
"type": "REASONING_MESSAGE_CHUNK",
"messageId": "reasoning-msg-1",
"delta": "Analyzing the request..."
}
REASONING_END
Marks the end of a reasoning process.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_END" |
Yes | |
messageId |
string |
Yes | Must match REASONING_START.messageId |
{
"type": "REASONING_END",
"messageId": "reasoning-1"
}
REASONING_ENCRYPTED_VALUE
Attaches encrypted chain-of-thought to a message or tool call. Used for zero-data-retention (ZDR) scenarios where reasoning must be preserved across turns but not exposed to the client.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"REASONING_ENCRYPTED_VALUE" |
Yes | |
subtype |
"message" | "tool-call" |
Yes | Entity type this reasoning belongs to |
entityId |
string |
Yes | ID of the message or tool call |
encryptedValue |
string |
Yes | Opaque encrypted content blob |
{
"type": "REASONING_ENCRYPTED_VALUE",
"subtype": "message",
"entityId": "msg-1",
"encryptedValue": "eyJhbGciOiJSU0..."
}
Custom / Extension Events
RAW
Passthrough container for events from external systems.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"RAW" |
Yes | |
event |
any |
Yes | Original event data |
source |
string |
No | Source system identifier |
{
"type": "RAW",
"event": { "vendor_type": "langchain_event", "data": {} },
"source": "langchain"
}
CUSTOM
Application-specific extension events with named semantics.
| Field | Type | Required | Description |
|---|---|---|---|
type |
"CUSTOM" |
Yes | |
name |
string |
Yes | Custom event name |
value |
any |
Yes | Event payload |
{
"type": "CUSTOM",
"name": "citation",
"value": { "url": "https://example.com", "title": "Reference" }
}
Deprecated Events (Remove in 1.0.0)
These THINKING events are replaced by REASONING events:
| Deprecated | Replacement |
|---|---|
THINKING_START |
REASONING_START |
THINKING_END |
REASONING_END |
THINKING_TEXT_MESSAGE_START |
REASONING_MESSAGE_START |
THINKING_TEXT_MESSAGE_CONTENT |
REASONING_MESSAGE_CONTENT |
THINKING_TEXT_MESSAGE_END |
REASONING_MESSAGE_END |
The client SDK includes BackwardCompatibility_0_0_45 middleware that auto-converts these.
Transport Encoding
SSE (Server-Sent Events)
Default transport. Content-Type: text/event-stream.
Each event is encoded as:
data: <JSON>\n\n
The @ag-ui/encoder EventEncoder class produces this format:
import { EventEncoder } from "@ag-ui/encoder";
const encoder = new EventEncoder();
const sseString = encoder.encode(event); // "data: {...}\n\n"
Binary (Protobuf)
Optional binary transport using @ag-ui/proto. Content-Type: application/x-ag-ui.
Each message is length-prefixed: 4-byte big-endian uint32 length header followed by protobuf-encoded message bytes.
The EventEncoder auto-detects format from the Accept header:
const encoder = new EventEncoder({ accept: req.headers.accept });
encoder.getContentType(); // "text/event-stream" or "application/x-ag-ui"
encoder.encodeBinary(event); // Uint8Array (SSE bytes or protobuf)
Type Definitions (RunAgentInput)
The input payload sent to the agent on each run:
| Field | Type | Required | Description |
|---|---|---|---|
threadId |
string |
Yes | Conversation thread ID |
runId |
string |
Yes | Unique run ID |
parentRunId |
string |
No | Parent run for branching |
state |
any |
Yes | Current state |
messages |
Message[] |
Yes | Conversation history |
tools |
Tool[] |
Yes | Available tools |
context |
Context[] |
Yes | Additional context |
forwardedProps |
any |
Yes | Pass-through properties |
Message Types
Messages are discriminated by role:
| Role | Fields | Description |
|---|---|---|
developer |
id, role, content, name?, encryptedValue? |
Developer/system instructions |
system |
id, role, content, name?, encryptedValue? |
System messages |
assistant |
id, role, content?, toolCalls?, name?, encryptedValue? |
Agent responses |
user |
id, role, content (string or InputContent[]), name? |
User messages (supports multimodal) |
tool |
id, role, content, toolCallId, error?, encryptedValue? |
Tool results |
activity |
id, role, activityType, content (Record) |
Activity progress |
reasoning |
id, role, content, encryptedValue? |
Reasoning/thinking content |
Tool Definition
interface Tool {
name: string; // Tool name
description: string; // Human-readable description
parameters: any; // JSON Schema for parameters
}
InputContent (Multimodal)
User messages can contain mixed content:
TextInputContent:{ type: "text", text: string }BinaryInputContent:{ type: "binary", mimeType: string, id?: string, url?: string, data?: string, filename?: string }(requires at least one ofid,url, ordata)