# 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 | ```json { "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 | ```json { "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"`) | ```json { "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) | ```json { "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 | ```json { "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 | ```json { "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) | ```json { "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` | ```json { "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 `messageId` emits `TEXT_MESSAGE_START` - Each chunk with `delta` emits `TEXT_MESSAGE_CONTENT` - `TEXT_MESSAGE_END` is emitted when the stream switches to a new `messageId` or completes ```json { "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 | ```json { "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 | ```json { "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` | ```json { "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"` | ```json { "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 | ```json { "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 | ```json { "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` | 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": ... }` ```json { "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 | ```json { "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` | Yes | Structured activity data | | `replace` | `boolean` | No | Defaults to `true`. If `false`, ignored when message exists | ```json { "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` | Yes | RFC 6902 JSON Patch operations | ```json { "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 | ```json { "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"` | ```json { "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) | ```json { "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` | ```json { "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 | ```json { "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` | ```json { "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 | ```json { "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 | ```json { "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 | ```json { "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: \n\n ``` The `@ag-ui/encoder` `EventEncoder` class produces this format: ```typescript 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: ```typescript 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 ```typescript 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 of `id`, `url`, or `data`)