# MCP, A2A, and AG-UI Source: https://docs.ag-ui.com/agentic-protocols Understanding how AG-UI complements and works with MCP and A2A ## Agentic Protocols The agentic ecosystem is rapidly organizing around a family of open, complementary protocols — each addressing a distinct layer of interaction. AG-UI has emerged as the 3rd leg of the AI protocol landscape: ![AI Protocol Stack](https://mintcdn.com/tawkitai/p1KIIAcVU0goF39y/images/ai-protocol-stack.png?fit=max\&auto=format\&n=p1KIIAcVU0goF39y\&q=85\&s=934df9d29866b7e61d3f95a650cf456a) You can connect your application to agents directly via **AG-UI**, **MCP**, and **A2A**. * **MCP** (Model Context Protocol) Connects agents to tools and to context — but those tools are themselves becoming agentic. * **A2A** (Agent to Agent) Connects agents to other agents. * **AG-UI (Agent–User Interaction)** Connects agents to users (through user-facing applications). You can think of AG-UI as the **"kitchen sink" protocol** — informed by bottom-up, real-world needs for building best-in-class agentic applications. These three agentic protocols are complementary and have distinct technical goals; a single agent can and often does use all 3 simultaneously. ## AG-UI Handshakes with MCP and A2A AG-UI contributors have recently added handshakes, allowing AG-UI to "front for" agents through MCP and A2A protocols, which allows AG-UI client apps and libraries to seamlessly use MCP and A2A supporting agents. AG-UI's mandate is to support the full set of building blocks required by modern agentic applications. ## Generative UI Specs Recently several [generative ui specs](./concepts/generative-ui-specs) (including MCP-UI, Open JSON UI, and A2UI) have been released which allow agents to deliver UI widgets through the interaction protocols. AG-UI works with all of these. Visit our [generative ui specs page](./concepts/generative-ui-specs) to learn more. # Agents Source: https://docs.ag-ui.com/concepts/agents Learn about agents in the Agent User Interaction Protocol # Agents Agents are the core components in the AG-UI protocol that process requests and generate responses. They establish a standardized way for front-end applications to communicate with AI services through a consistent interface, regardless of the underlying implementation. ## What is an Agent? In AG-UI, an agent is a class that: 1. Manages conversation state and message history 2. Processes incoming messages and context 3. Generates responses through an event-driven streaming interface 4. Follows a standardized protocol for communication Agents can be implemented to connect with any AI service, including: * Large language models (LLMs) like GPT-4 or Claude * Custom AI systems * Retrieval augmented generation (RAG) systems * Multi-agent systems ## Agent Architecture All agents in AG-UI extend the `AbstractAgent` class, which provides the foundation for: * State management * Message history tracking * Event stream processing * Tool usage ```typescript theme={null} import { AbstractAgent } from "@ag-ui/client" class MyAgent extends AbstractAgent { run(input: RunAgentInput): RunAgent { // Implementation details } } ``` ### Core Components AG-UI agents have several key components: 1. **Configuration**: Agent ID, thread ID, and initial state 2. **Messages**: Conversation history with user and assistant messages 3. **State**: Structured data that persists across interactions 4. **Events**: Standardized messages for communication with clients 5. **Tools**: Functions that agents can use to interact with external systems ## Agent Types AG-UI provides different agent implementations to suit various needs: ### AbstractAgent The base class that all agents extend. It handles core event processing, state management, and message history. ### HttpAgent A concrete implementation that connects to remote AI services via HTTP: ```typescript theme={null} import { HttpAgent } from "@ag-ui/client" const agent = new HttpAgent({ url: "https://your-agent-endpoint.com/agent", headers: { Authorization: "Bearer your-api-key", }, }) ``` ### Custom Agents You can create custom agents to integrate with any AI service by extending `AbstractAgent`: ```typescript theme={null} class CustomAgent extends AbstractAgent { // Custom properties and methods run(input: RunAgentInput): RunAgent { // Implement the agent's logic } } ``` ## Implementing Agents ### Basic Implementation To create a custom agent, extend the `AbstractAgent` class and implement the required `run` method: ```typescript theme={null} import { AbstractAgent, RunAgent, RunAgentInput, EventType, BaseEvent, } from "@ag-ui/client" import { Observable } from "rxjs" class SimpleAgent extends AbstractAgent { run(input: RunAgentInput): RunAgent { const { threadId, runId } = input return () => new Observable((observer) => { // Emit RUN_STARTED event observer.next({ type: EventType.RUN_STARTED, threadId, runId, }) // Send a message const messageId = Date.now().toString() // Message start observer.next({ type: EventType.TEXT_MESSAGE_START, messageId, role: "assistant", }) // Message content observer.next({ type: EventType.TEXT_MESSAGE_CONTENT, messageId, delta: "Hello, world!", }) // Message end observer.next({ type: EventType.TEXT_MESSAGE_END, messageId, }) // Emit RUN_FINISHED event observer.next({ type: EventType.RUN_FINISHED, threadId, runId, }) // Complete the observable observer.complete() }) } } ``` ## Agent Capabilities Agents in the AG-UI protocol provide a rich set of capabilities that enable sophisticated AI interactions: ### Interactive Communication Agents establish bi-directional communication channels with front-end applications through event streams. This enables: * Real-time streaming responses character-by-character * Immediate feedback loops between user and AI * Progress indicators for long-running operations * Structured data exchange in both directions ### Tool Usage Agents can use tools to perform actions and access external resources. Importantly, tools are defined and passed in from the front-end application to the agent, allowing for a flexible and extensible system: ```typescript theme={null} // Tool definition const confirmAction = { name: "confirmAction", description: "Ask the user to confirm a specific action before proceeding", parameters: { type: "object", properties: { action: { type: "string", description: "The action that needs user confirmation", }, importance: { type: "string", enum: ["low", "medium", "high", "critical"], description: "The importance level of the action", }, details: { type: "string", description: "Additional details about the action", }, }, required: ["action"], }, } // Running an agent with tools from the frontend agent.runAgent({ tools: [confirmAction], // Frontend-defined tools passed to the agent // other parameters }) ``` Tools are invoked through a sequence of events: 1. `TOOL_CALL_START`: Indicates the beginning of a tool call 2. `TOOL_CALL_ARGS`: Streams the arguments for the tool call 3. `TOOL_CALL_END`: Marks the completion of the tool call Front-end applications can then execute the tool and provide results back to the agent. This bidirectional flow enables sophisticated human-in-the-loop workflows where: * The agent can request specific actions be performed * Humans can execute those actions with appropriate judgment * Results are fed back to the agent for continued reasoning * The agent maintains awareness of all decisions made in the process This mechanism is particularly powerful for implementing interfaces where AI and humans collaborate. For example, [CopilotKit](https://docs.copilotkit.ai/) leverages this exact pattern with their [`useCopilotAction`](https://docs.copilotkit.ai/guides/frontend-actions) hook, which provides a simplified way to define and handle tools in React applications. By keeping the AI informed about human decisions through the tool mechanism, applications can maintain context and create more natural collaborative experiences between users and AI assistants. ### State Management Agents maintain a structured state that persists across interactions. This state can be: * Updated incrementally through `STATE_DELTA` events * Completely refreshed with `STATE_SNAPSHOT` events * Accessed by both the agent and front-end * Used to store user preferences, conversation context, or application state ```typescript theme={null} // Accessing agent state console.log(agent.state.preferences) // State is automatically updated during agent runs agent.runAgent().subscribe((event) => { if (event.type === EventType.STATE_DELTA) { // State has been updated console.log("New state:", agent.state) } }) ``` ### Multi-Agent Collaboration AG-UI supports agent-to-agent handoff and collaboration: * Agents can delegate tasks to other specialized agents * Multiple agents can work together in a coordinated workflow * State and context can be transferred between agents * The front-end maintains a consistent experience across agent transitions For example, a general assistant agent might hand off to a specialized coding agent when programming help is needed, passing along the conversation context and specific requirements. ### Human-in-the-Loop Workflows Agents support human intervention and assistance: * Agents can request human input on specific decisions * Front-ends can pause agent execution and resume it after human feedback * Human experts can review and modify agent outputs before they're finalized * Hybrid workflows combine AI efficiency with human judgment This enables applications where the agent acts as a collaborative partner rather than an autonomous system. ### Conversational Memory Agents maintain a complete history of conversation messages: * Past interactions inform future responses * Message history is synchronized between client and server * Messages can include rich content (text, structured data, references) * The context window can be managed to focus on relevant information ```typescript theme={null} // Accessing message history console.log(agent.messages) // Adding a new user message agent.messages.push({ id: "msg_123", role: "user", content: "Can you explain that in more detail?", }) ``` ### Metadata and Instrumentation Agents can emit metadata about their internal processes: * Reasoning steps through custom events * Performance metrics and timing information * Source citations and reference tracking * Confidence scores for different response options This allows front-ends to provide transparency into the agent's decision-making process and help users understand how conclusions were reached. ## Using Agents Once you've implemented or instantiated an agent, you can use it like this: ```typescript theme={null} // Create an agent instance const agent = new HttpAgent({ url: "https://your-agent-endpoint.com/agent", }) // Add initial messages if needed agent.messages = [ { id: "1", role: "user", content: "Hello, how can you help me today?", }, ] // Run the agent agent .runAgent({ runId: "run_123", tools: [], // Optional tools context: [], // Optional context }) .subscribe({ next: (event) => { // Handle different event types switch (event.type) { case EventType.TEXT_MESSAGE_CONTENT: console.log("Content:", event.delta) break // Handle other events } }, error: (error) => console.error("Error:", error), complete: () => console.log("Run complete"), }) ``` ## Agent Configuration Agents accept configuration through the constructor: ```typescript theme={null} interface AgentConfig { agentId?: string // Unique identifier for the agent description?: string // Human-readable description threadId?: string // Conversation thread identifier initialMessages?: Message[] // Initial messages initialState?: State // Initial state object } // Using the configuration const agent = new HttpAgent({ agentId: "my-agent-123", description: "A helpful assistant", threadId: "thread-456", initialMessages: [ { id: "1", role: "system", content: "You are a helpful assistant." }, ], initialState: { preferredLanguage: "English" }, }) ``` ## Agent State Management AG-UI agents maintain state across interactions: ```typescript theme={null} // Access current state console.log(agent.state) // Access messages console.log(agent.messages) // Clone an agent with its state const clonedAgent = agent.clone() ``` ## Conclusion Agents are the foundation of the AG-UI protocol, providing a standardized way to connect front-end applications with AI services. By implementing the `AbstractAgent` class, you can create custom integrations with any AI service while maintaining a consistent interface for your applications. The event-driven architecture enables real-time, streaming interactions that are essential for modern AI applications, and the standardized protocol ensures compatibility across different implementations. # Core architecture Source: https://docs.ag-ui.com/concepts/architecture Understand how AG-UI connects front-end applications to AI agents Agent User Interaction Protocol (AG-UI) is built on a flexible, event-driven architecture that enables seamless, efficient communication between front-end applications and AI agents. This document covers the core architectural components and concepts. ## Design Principles AG-UI is designed to be lightweight and minimally opinionated, making it easy to integrate with a wide range of agent implementations. The protocol's flexibility comes from its simple requirements: 1. **Event-Driven Communication**: Agents need to emit any of the 16 standardized event types during execution, creating a stream of updates that clients can process. 2. **Bidirectional Interaction**: Agents accept input from users, enabling collaborative workflows where humans and AI work together seamlessly. The protocol includes a built-in middleware layer that maximizes compatibility in two key ways: * **Flexible Event Structure**: Events don't need to match AG-UI's format exactly—they just need to be AG-UI-compatible. This allows existing agent frameworks to adapt their native event formats with minimal effort. * **Transport Agnostic**: AG-UI doesn't mandate how events are delivered, supporting various transport mechanisms including Server-Sent Events (SSE), webhooks, WebSockets, and more. This flexibility lets developers choose the transport that best fits their architecture. This pragmatic approach makes AG-UI easy to adopt without requiring major changes to existing agent implementations or frontend applications. ## Architectural Overview AG-UI follows a client-server architecture that standardizes communication between agents and applications: ```mermaid theme={null} flowchart LR subgraph "Frontend" App["Application"] Client["AG-UI Client"] end subgraph "Backend" A1["AI Agent A"] P["Secure Proxy"] A2["AI Agent B"] A3["AI Agent C"] end App <--> Client Client <-->|"AG-UI Protocol"| A1 Client <-->|"AG-UI Protocol"| P P <-->|"AG-UI Protocol"| A2 P <-->|"AG-UI Protocol"| A3 class P mintStyle; classDef mintStyle fill:#E0F7E9,stroke:#66BB6A,stroke-width:2px,color:#000000; style App rx:5, ry:5; style Client rx:5, ry:5; style A1 rx:5, ry:5; style P rx:5, ry:5; style A2 rx:5, ry:5; style A3 rx:5, ry:5; ``` * **Application**: User-facing apps (i.e. chat or any AI-enabled application). * **AG-UI Client**: Generic communication clients like `HttpAgent` or specialized clients for connecting to existing protocols. * **Agents**: Backend AI agents that process requests and generate streaming responses. * **Secure Proxy**: Backend services that provide additional capabilities and act as a secure proxy. ## Core components ### Protocol layer AG-UI's protocol layer provides a flexible foundation for agent communication. * **Universal compatibility**: Connect to any protocol by implementing `run(input: RunAgentInput) -> Observable` The protocol's primary abstraction enables applications to run agents and receive a stream of events: ```typescript theme={null} // Core agent execution interface type RunAgent = () => Observable class MyAgent extends AbstractAgent { run(input: RunAgentInput): RunAgent { const { threadId, runId } = input return () => from([ { type: EventType.RUN_STARTED, threadId, runId }, { type: EventType.MESSAGES_SNAPSHOT, messages: [ { id: "msg_1", role: "assistant", content: "Hello, world!" } ], }, { type: EventType.RUN_FINISHED, threadId, runId }, ]) } } ``` ### Standard HTTP client AG-UI offers a standard HTTP client `HttpAgent` that can be used to connect to any endpoint that accepts POST requests with a body of type `RunAgentInput` and sends a stream of `BaseEvent` objects. `HttpAgent` supports the following transports: * **HTTP SSE (Server-Sent Events)** * Text-based streaming for wide compatibility * Easy to read and debug * **HTTP binary protocol** * Highly performant and space-efficient custom transport * Robust binary serialization for production environments ### Message types AG-UI defines several event categories for different aspects of agent communication: * **Lifecycle events** * `RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR` * `STEP_STARTED`, `STEP_FINISHED` * **Text message events** * `TEXT_MESSAGE_START`, `TEXT_MESSAGE_CONTENT`, `TEXT_MESSAGE_END` * **Tool call events** * `TOOL_CALL_START`, `TOOL_CALL_ARGS`, `TOOL_CALL_END` * **State management events** * `STATE_SNAPSHOT`, `STATE_DELTA`, `MESSAGES_SNAPSHOT` * **Special events** * `RAW`, `CUSTOM` ## Running Agents To run an agent, you create a client instance and execute it: ```typescript theme={null} // Create an HTTP agent client const agent = new HttpAgent({ url: "https://your-agent-endpoint.com/agent", agentId: "unique-agent-id", threadId: "conversation-thread" }); // Start the agent and handle events agent.runAgent({ tools: [...], context: [...] }).subscribe({ next: (event) => { // Handle different event types switch(event.type) { case EventType.TEXT_MESSAGE_CONTENT: // Update UI with new content break; // Handle other event types } }, error: (error) => console.error("Agent error:", error), complete: () => console.log("Agent run complete") }); ``` ## State Management AG-UI provides efficient state management through specialized events: * `STATE_SNAPSHOT`: Complete state representation at a point in time * `STATE_DELTA`: Incremental state changes using JSON Patch format (RFC 6902) * `MESSAGES_SNAPSHOT`: Complete conversation history These events enable efficient client-side state management with minimal data transfer. ## Tools and Handoff AG-UI supports agent-to-agent handoff and tool usage through standardized events: * Tool definitions are passed in the `runAgent` parameters * Tool calls are streamed as sequences of `TOOL_CALL_START` → `TOOL_CALL_ARGS` → `TOOL_CALL_END` events * Agents can hand off to other agents, maintaining context continuity ## Events All communication in AG-UI is based on typed events. Every event inherits from `BaseEvent`: ```typescript theme={null} interface BaseEvent { type: EventType timestamp?: number rawEvent?: any } ``` Events are strictly typed and validated, ensuring reliable communication between components. # Capabilities Source: https://docs.ag-ui.com/concepts/capabilities Dynamic capability discovery for agents in the AG-UI protocol # Capabilities Agents in the AG-UI protocol can declare what they support at runtime through **capability discovery**. This allows clients to query an agent and adapt their behavior based on what features are available — without guessing or hardcoding assumptions. ## How It Works `AbstractAgent` declares an optional `getCapabilities()` method that returns a typed snapshot of everything the agent currently supports. It is implemented by agent subclasses that know their own capabilities — the LangGraph and ADK integrations, or your own — not by the bare `HttpAgent`, which is a transport and has no discovery endpoint to ask. The optional call below returns `undefined` for an agent that does not implement it: ```typescript theme={null} // Any AbstractAgent subclass that implements getCapabilities(); a plain // HttpAgent does not, and would yield undefined here. const agent = new LangGraphAgent({ url: "https://my-agent.example.com/api" }) const capabilities = await agent.getCapabilities?.() if (capabilities?.tools?.supported) { console.log(`Agent provides ${capabilities.tools.items?.length} tools`) } if (capabilities?.reasoning?.supported) { // Show reasoning UI toggle } ``` ### Key Principles * **Discovery only** — the agent declares what it can do, there is no negotiation * **Dynamic** — returns the current state at the time of the call (e.g., if tools are added, the next call reflects them) * **Optional** — agents that don't implement it return `undefined` * **Absent = unknown** — only declare what you support, omitted fields mean the capability is not declared ## The AgentCapabilities Interface Capabilities are organized into typed categories, each representing a different aspect of agent functionality: ```typescript theme={null} interface AgentCapabilities { /** Agent identity and metadata. */ identity?: IdentityCapabilities /** Supported transport mechanisms (SSE, WebSocket, binary, etc.). */ transport?: TransportCapabilities /** Tools the agent provides and tool calling configuration. */ tools?: ToolsCapabilities /** Output format support (structured output, MIME types). */ output?: OutputCapabilities /** State and memory management (snapshots, deltas, persistence). */ state?: StateCapabilities /** Multi-agent coordination (delegation, handoffs, sub-agents). */ multiAgent?: MultiAgentCapabilities /** Reasoning and thinking support (chain-of-thought, encrypted thinking). */ reasoning?: ReasoningCapabilities /** Multimodal input/output support organized by direction (input vs output). */ multimodal?: MultimodalCapabilities /** Execution control and limits (code execution, timeouts, iteration caps). */ execution?: ExecutionCapabilities /** Human-in-the-loop support (approvals, interventions, feedback). */ humanInTheLoop?: HumanInTheLoopCapabilities /** Integration-specific capabilities not covered by the standard categories. */ custom?: Record } ``` The `custom` field is an escape hatch for integration-specific capabilities that don't fit into the standard categories. ## Capability Categories ### Identity Basic metadata about the agent. Useful for discovery UIs, agent marketplaces, and debugging. Set these when you want clients to display agent information or when multiple agents are available and users need to pick one. ```typescript theme={null} interface IdentityCapabilities { /** Human-readable name shown in UIs and agent selectors. */ name?: string /** The framework or platform powering this agent (e.g., "langgraph", "mastra", "crewai"). */ type?: string /** What this agent does — helps users and routing logic decide when to use it. */ description?: string /** Semantic version of the agent (e.g., "1.2.0"). Useful for compatibility checks. */ version?: string /** Organization or team that maintains this agent. */ provider?: string /** URL to the agent's documentation or homepage. */ documentationUrl?: string /** Arbitrary key-value pairs for integration-specific identity info. */ metadata?: Record } ``` ### Transport Declares which transport mechanisms the agent supports. Clients use this to pick the best connection strategy. Only set flags to `true` for transports your agent actually handles — omit or set `false` for unsupported ones. ```typescript theme={null} interface TransportCapabilities { /** Set `true` if the agent streams responses via SSE. Most agents enable this. */ streaming?: boolean /** Set `true` if the agent accepts persistent WebSocket connections. */ websocket?: boolean /** Set `true` if the agent supports the AG-UI binary protocol (protobuf over HTTP). */ httpBinary?: boolean /** Set `true` if the agent can send async updates via webhooks after a run finishes. */ pushNotifications?: boolean /** Set `true` if the agent supports resuming interrupted streams via sequence numbers. */ resumable?: boolean } ``` ### Tools Tool calling capabilities. Distinguishes between tools the agent itself provides (listed in `items`) and tools the client passes at runtime via `RunAgentInput.tools`. Enable this when your agent can call functions, search the web, execute code, etc. ```typescript theme={null} interface ToolsCapabilities { /** Set `true` if the agent can make tool calls at all. Set `false` to explicitly * signal tool calling is disabled even if items are present. */ supported?: boolean /** The tools this agent provides on its own (full JSON Schema definitions). * These are distinct from client-provided tools passed in `RunAgentInput.tools`. */ items?: Tool[] /** Set `true` if the agent can invoke multiple tools concurrently within a single step. */ parallelCalls?: boolean /** Set `true` if the agent accepts and uses tools provided by the client at runtime. */ clientProvided?: boolean } ``` ### Output Output format support. Enable `structuredOutput` when your agent can return responses conforming to a JSON schema, which is useful for programmatic consumption. ```typescript theme={null} interface OutputCapabilities { /** Set `true` if the agent can produce structured JSON output matching a provided schema. */ structuredOutput?: boolean /** MIME types the agent can produce (e.g., `["text/plain", "application/json"]`). * Omit if the agent only produces plain text. */ supportedMimeTypes?: string[] } ``` ### State State and memory management capabilities. These tell the client how the agent handles shared state and whether conversation context persists across runs. ```typescript theme={null} interface StateCapabilities { /** Set `true` if the agent emits `STATE_SNAPSHOT` events (full state replacement). */ snapshots?: boolean /** Set `true` if the agent emits `STATE_DELTA` events (JSON Patch incremental updates). */ deltas?: boolean /** Set `true` if the agent has long-term memory beyond the current thread * (e.g., vector store, knowledge base, or cross-session recall). */ memory?: boolean /** Set `true` if state is preserved across multiple runs within the same thread. * When `false`, state resets on each run. */ persistentState?: boolean } ``` ### Multi-Agent Multi-agent coordination capabilities. Enable these when your agent can orchestrate or hand off work to other agents. ```typescript theme={null} interface MultiAgentCapabilities { /** Set `true` if the agent participates in any form of multi-agent coordination. */ supported?: boolean /** Set `true` if the agent can delegate subtasks to other agents while retaining control. */ delegation?: boolean /** Set `true` if the agent can transfer the conversation entirely to another agent. */ handoffs?: boolean /** List of sub-agents this agent can invoke. Helps clients build agent selection UIs. */ subagents?: Array<{ name: string; description?: string }> } ``` ### Reasoning Reasoning and thinking capabilities. Enable these when your agent exposes its internal thought process (e.g., chain-of-thought, extended thinking). ```typescript theme={null} interface ReasoningCapabilities { /** Set `true` if the agent produces reasoning/thinking tokens visible to the client. */ supported?: boolean /** Set `true` if reasoning tokens are streamed incrementally (vs. returned all at once). */ streaming?: boolean /** Set `true` if reasoning content is encrypted (zero-data-retention mode). * Clients should expect opaque `encryptedValue` fields instead of readable content. */ encrypted?: boolean } ``` ### Multimodal Multimodal input and output support, organized into `input` and `output` sub-objects so clients can independently query what the agent accepts versus what it produces. Clients use this to show/hide file upload buttons, audio recorders, image pickers, etc. ```typescript theme={null} interface MultimodalInputCapabilities { /** Set `true` if the agent can process image inputs (e.g., screenshots, photos). */ image?: boolean /** Set `true` if the agent can process audio inputs (speech, recordings). */ audio?: boolean /** Set `true` if the agent can process video inputs. */ video?: boolean /** Set `true` if the agent can process PDF documents. */ pdf?: boolean /** Set `true` if the agent can process arbitrary file uploads. */ file?: boolean } interface MultimodalOutputCapabilities { /** Set `true` if the agent can generate images as part of its response. */ image?: boolean /** Set `true` if the agent can produce audio output (text-to-speech, audio files). */ audio?: boolean } interface MultimodalCapabilities { /** Modalities the agent can accept as input (images, audio, video, PDFs, files). */ input?: MultimodalInputCapabilities /** Modalities the agent can produce as output (images, audio). */ output?: MultimodalOutputCapabilities } ``` ### Execution Execution control and limits. Declare these so clients can set expectations about how long or how many steps an agent run might take. ```typescript theme={null} interface ExecutionCapabilities { /** Set `true` if the agent can execute code (e.g., Python, JavaScript) during a run. */ codeExecution?: boolean /** Set `true` if code execution happens in a sandboxed/isolated environment. * Only meaningful when `codeExecution` is `true`. */ sandboxed?: boolean /** Maximum number of tool-call/reasoning iterations the agent will perform per run. * Helps clients display progress or set timeout expectations. */ maxIterations?: number /** Maximum wall-clock time (in milliseconds) the agent will run before timing out. */ maxExecutionTime?: number } ``` ### Human-in-the-Loop Human-in-the-loop interaction support. Enable these when your agent can pause execution to request human input, approval, or feedback before continuing. ```typescript theme={null} interface HumanInTheLoopCapabilities { /** Set `true` if the agent supports any form of human-in-the-loop interaction. */ supported?: boolean /** Set `true` if the agent can pause and request explicit approval before * performing sensitive actions (e.g., sending emails, deleting data). */ approvals?: boolean /** Set `true` if the agent allows humans to intervene and modify its plan mid-execution. */ interventions?: boolean /** Set `true` if the agent can incorporate user feedback (thumbs up/down, corrections) * to improve its behavior within the current session. */ feedback?: boolean /** Set `true` if the agent participates in the AG-UI interrupt protocol (emits * `RunFinished` with `outcome: { type: "interrupt", interrupts: [...] }`, * accepts `RunAgentInput.resume`). */ interrupts?: boolean /** Set `true` if tool-call interrupts accept `editedArgs` in the resume payload. * Only meaningful when `interrupts` is `true`. */ approveWithEdits?: boolean } ``` > [!NOTE] > > See [Interrupts](/concepts/interrupts) for the full protocol specification. ## Implementing getCapabilities() ### Custom Agents Implement `getCapabilities()` on your agent subclass, returning only the capabilities you actually support: ```typescript theme={null} import { AbstractAgent, AgentCapabilities } from "@ag-ui/client" class MyAgent extends AbstractAgent { async getCapabilities(): Promise { return { identity: { name: "my-agent", description: "A custom agent with tool support", version: "1.0.0", }, transport: { streaming: true, }, tools: { supported: true, items: this.getRegisteredTools(), clientProvided: true, }, state: { snapshots: true, deltas: true, }, } } // ... run() implementation } ``` ### Dynamic Capabilities Since `getCapabilities()` returns a live snapshot, it reflects the agent's current state: ```typescript theme={null} const agent = new MyAgent(config) let caps = await agent.getCapabilities() console.log(caps.tools?.items?.length) // 5 // Register more tools at runtime agent.registerTool(newTool) caps = await agent.getCapabilities() console.log(caps.tools?.items?.length) // 6 ``` ## Client Usage Patterns ### Adaptive UI Render UI components based on what the agent supports: ```typescript theme={null} const capabilities = await agent.getCapabilities?.() // Only show reasoning panel if supported if (capabilities?.reasoning?.supported) { showReasoningPanel() } // Only show sub-agent selector if available if (capabilities?.multiAgent?.subagents?.length) { showSubAgentSelector(capabilities.multiAgent.subagents) } // Only show approval UI if HITL is supported if (capabilities?.humanInTheLoop?.approvals) { enableApprovalWorkflow() } ``` ### Feature Gating Disable features the agent doesn't support instead of failing at runtime: ```typescript theme={null} const capabilities = await agent.getCapabilities?.() // Gate on an explicit declaration: an omitted field means undeclared, not unsupported. const canUseStructuredOutput = capabilities?.output?.structuredOutput === true const canStream = capabilities?.transport?.streaming === true ``` ### Custom Capabilities Access integration-specific capabilities via the `custom` field: ```typescript theme={null} const capabilities = await agent.getCapabilities?.() const rateLimit = capabilities?.custom?.rateLimit as | { maxRequestsPerMinute: number } | undefined if (rateLimit) { configureThrottling(rateLimit.maxRequestsPerMinute) } ``` # Events Source: https://docs.ag-ui.com/concepts/events Understanding events in the Agent User Interaction Protocol # Events The Agent User Interaction Protocol uses a streaming event-based architecture. Events are the fundamental units of communication between agents and frontends, enabling real-time, structured interaction. ## Event Types Overview Events in the protocol are categorized by their purpose: | Category | Description | | - | - | | Lifecycle Events | Monitor the progression of agent runs | | Text Message Events | Handle streaming textual content | | Tool Call Events | Manage tool executions by agents | | State Management Events | Synchronize state between agents and UI | | Activity Events | Represent ongoing activity progress | | Subagent Events | Track subagents and attribute their output | | Special Events | Support custom functionality | | Draft Events | Proposed events under development | ## Base Event Properties All events share a common set of base properties: | Property | Description | | - | - | | `type` | The specific event type identifier | | `timestamp` | Optional timestamp indicating when the event was created | | `rawEvent` | Optional field containing the original event data if transformed | | `metadata` | Optional extra information attached to the event | ### Metadata `metadata` is an optional, open-by-key object for attaching extra information to an event — token usage, a trace id, a finish reason. It is declared once on the base event, so every event type carries it. Consumers merge an event's metadata into the message that event builds, key by key, with the last write winning. That is what lets a producer send token usage on the final event of a message rather than having to know it up front. See [Metadata](/concepts/metadata) for the full rules: the reserved `ag-ui` key, which events do and do not merge, how tool calls carry their own, and how the object behaves across transports. Most events additionally accept an optional `subagentRunId`, identifying the subagent that produced them. An event without it belongs to the parent agent. See [Subagents](/concepts/subagents). ## Lifecycle Events These events represent the lifecycle of an agent run. A typical agent run follows a predictable pattern: it begins with a `RunStarted` event, may contain multiple optional `StepStarted`/`StepFinished` pairs, and concludes with either a `RunFinished` event (success) or a `RunError` event (failure). Lifecycle events provide crucial structure to agent runs, enabling frontends to track progress, manage UI states appropriately, and handle errors gracefully. They create a consistent framework for understanding when operations begin and end, making it possible to implement features like loading indicators, progress tracking, and error recovery mechanisms. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Run begins Agent->>Client: RunStarted opt Sending steps is optional Note over Agent,Client: Step execution Agent->>Client: StepStarted Agent->>Client: StepFinished end Note over Agent,Client: Run completes alt Agent->>Client: RunFinished else Agent->>Client: RunError end ``` The `RunStarted` and either `RunFinished` or `RunError` events are mandatory, forming the boundaries of an agent run. Step events are optional and may occur multiple times within a run, allowing for structured, observable progress tracking. ### RunStarted Signals the start of an agent run. The `RunStarted` event is the first event emitted when an agent begins processing a request. It establishes a new execution context identified by a unique `runId`. This event serves as a marker for frontends to initialize UI elements such as progress indicators or loading states. It also provides crucial identifiers that can be used to associate subsequent events with this specific run. | Property | Description | | - | - | | `threadId` | ID of the conversation thread | | `runId` | ID of the agent run | | `parentRunId` | (Optional) Lineage pointer for branching/time travel. If present, refers to a prior run within the same thread, creating a git-like append-only log | | `input` | (Optional) The exact agent input payload that was sent to the agent for this run. May omit messages already present in history; compactEvents() will normalize | ### RunFinished Signals the end of an agent run. Every run terminates with either `RunFinished` or `RunError`. `RunFinished` has an optional `outcome` discriminated union: * **omitted** — legacy producer that has not yet adopted the interrupt-aware lifecycle. Treated as a normal completion. * `outcome: { type: "success" }` — the run completed normally. The optional `result` stays at the root of the event for back-compat. * `outcome: { type: "interrupt", interrupts: [...] }` — the run paused for human input. The non-empty `interrupts` array lives inside the outcome variant. The client resumes by starting a new run whose `RunAgentInput` includes a `resume` array addressing every open interrupt. > [!NOTE] > > For the full interrupt lifecycle — the `Interrupt` type, contract rules, error > handling, reason taxonomy, and worked examples — see > [Interrupts](/concepts/interrupts). ### RunError Signals an error during an agent run. The `RunError` event indicates that the agent encountered an error it could not recover from, causing the run to terminate prematurely. This event provides information about what went wrong, allowing frontends to display appropriate error messages and potentially offer recovery options. After a `RunError` event, no further processing will occur in this run. | Property | Description | | - | - | | `message` | Error message | | `code` | Optional error code | ### StepStarted Signals the start of a step within an agent run. The `StepStarted` event indicates that the agent is beginning a specific subtask or phase of its processing. Steps provide granular visibility into the agent's progress, enabling more precise tracking and feedback in the UI. Steps are optional but highly recommended for complex operations that benefit from being broken down into observable stages. The `stepName` could be the name of a node or function that is currently executing. | Property | Description | | - | - | | `stepName` | Name of the step | ### StepFinished Signals the completion of a step within an agent run. The `StepFinished` event indicates that the agent has completed a specific subtask or phase. When paired with a corresponding `StepStarted` event, it creates a bounded context for a discrete unit of work. Frontends can use these events to update progress indicators, show completion animations, or reveal results specific to that step. The `stepName` must match the corresponding `StepStarted` event to properly pair the beginning and end of the step. | Property | Description | | - | - | | `stepName` | Name of the step | ## Text Message Events These events represent the lifecycle of text messages in a conversation. Text message events follow a streaming pattern, where content is delivered incrementally. A message begins with a `TextMessageStart` event, followed by one or more `TextMessageContent` events that deliver chunks of text as they become available, and concludes with a `TextMessageEnd` event. This streaming approach enables real-time display of message content as it's generated, creating a more responsive user experience compared to waiting for the entire message to be complete before showing anything. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Message begins Agent->>Client: TextMessageStart loop Content streaming Agent->>Client: TextMessageContent end Note over Agent,Client: Message completes Agent->>Client: TextMessageEnd ``` The `TextMessageContent` events each contain a `delta` field with a chunk of text. Frontends should concatenate these deltas in the order received to construct the complete message. The `messageId` property links all related events, allowing the frontend to associate content chunks with the correct message. ### TextMessageStart Signals the start of a text message. The `TextMessageStart` event initializes a new text message in the conversation. It establishes a unique `messageId` that will be referenced by subsequent content chunks and the end event. This event allows frontends to prepare the UI for an incoming message, such as creating a new message bubble with a loading indicator. The `role` property identifies whether the message is coming from the assistant or potentially another participant in the conversation. | Property | Description | | - | - | | `messageId` | Unique identifier for the message | | `role` | Role of the message sender ("developer", "system", "assistant", "user", "tool") | ### TextMessageContent Represents a chunk of content in a streaming text message. The `TextMessageContent` event delivers incremental parts of the message text as they become available. Each event contains a small chunk of text in the `delta` property that should be appended to previously received chunks. The streaming nature of these events enables real-time display of content, creating a more responsive and engaging user experience. Implementations should handle these events efficiently to ensure smooth text rendering without visible delays or flickering. | Property | Description | | - | - | | `messageId` | Matches the ID from `TextMessageStart` | | `delta` | Text content chunk (non-empty) | ### TextMessageEnd Signals the end of a text message. The `TextMessageEnd` event marks the completion of a streaming text message. After receiving this event, the frontend knows that the message is complete and no further content will be added. This allows the UI to finalize rendering, remove any loading indicators, and potentially trigger actions that should occur after message completion, such as enabling reply controls or performing automatic scrolling to ensure the full message is visible. | Property | Description | | - | - | | `messageId` | Matches the ID from `TextMessageStart` | ### TextMessageChunk Convenience event that expands to Start → Content → End automatically. The `TextMessageChunk` event lets you omit explicit `TextMessageStart` and `TextMessageEnd` events. The client stream transformer expands chunks into the standard triad: * First chunk for a message must include `messageId` and will emit `TextMessageStart` (role defaults to `assistant` when not provided). * Each chunk with a `delta` emits a `TextMessageContent` for the current `messageId`. * `TextMessageEnd` is emitted automatically when the stream switches to a new message ID or when the stream completes. | Property | Description | | - | - | | `messageId` | Optional unique identifier for the message; required on the first chunk of a message | | `role` | Optional role of the sender ("developer", "system", "assistant", "user") | | `delta` | Optional text content of the message | ## Tool Call Events These events represent the lifecycle of tool calls made by agents. Tool calls follow a streaming pattern similar to text messages. When an agent needs to use a tool, it emits a `ToolCallStart` event, followed by one or more `ToolCallArgs` events that stream the arguments being passed to the tool, and concludes with a `ToolCallEnd` event. This streaming approach allows frontends to show tool executions in real-time, making the agent's actions transparent and providing immediate feedback about what tools are being invoked and with what parameters. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Tool call begins Agent->>Client: ToolCallStart loop Arguments streaming Agent->>Client: ToolCallArgs end Note over Agent,Client: Tool call completes Agent->>Client: ToolCallEnd Note over Agent,Client: Tool execution result Agent->>Client: ToolCallResult ``` The `ToolCallArgs` events each contain a `delta` field with a chunk of the arguments. Frontends should concatenate these deltas in the order received to construct the complete arguments object. The `toolCallId` property links all related events, allowing the frontend to associate argument chunks with the correct tool call. ### ToolCallStart Signals the start of a tool call. The `ToolCallStart` event indicates that the agent is invoking a tool to perform a specific function. This event provides the name of the tool being called and establishes a unique `toolCallId` that will be referenced by subsequent events in this tool call. Frontends can use this event to display tool usage to users, such as showing a notification that a specific operation is in progress. The optional `parentMessageId` allows linking the tool call to a specific message in the conversation, providing context for why the tool is being used. | Property | Description | | - | - | | `toolCallId` | Unique identifier for the tool call | | `toolCallName` | Name of the tool being called | | `parentMessageId` | Optional ID of the parent message | ### ToolCallArgs Represents a chunk of argument data for a tool call. The `ToolCallArgs` event delivers incremental parts of the tool's arguments as they become available. Each event contains a segment of the argument data in the `delta` property. These deltas are often JSON fragments that, when combined, form the complete arguments object for the tool. Streaming the arguments is particularly valuable for complex tool calls where constructing the full arguments may take time. Frontends can progressively reveal these arguments to users, providing insight into exactly what parameters are being passed to tools. | Property | Description | | - | - | | `toolCallId` | Matches the ID from `ToolCallStart` | | `delta` | Argument data chunk | ### ToolCallEnd Signals the end of a tool call. The `ToolCallEnd` event marks the completion of a tool call. After receiving this event, the frontend knows that all arguments have been transmitted and the tool execution is underway or completed. This allows the UI to finalize the tool call display and prepare for potential results. In systems where tool execution results are returned separately, this event indicates that the agent has finished specifying the tool and its arguments, and is now waiting for or has received the results. | Property | Description | | - | - | | `toolCallId` | Matches the ID from `ToolCallStart` | ### ToolCallResult Provides the result of a tool call execution. The `ToolCallResult` event delivers the output or result from a tool that was previously invoked by the agent. This event is sent after the tool has been executed by the system and contains the actual output generated by the tool. Unlike the streaming pattern of tool call specification (start, args, end), the result is delivered as a complete unit since tool execution typically produces a complete output. Frontends can use this event to display tool results to users, append them to the conversation history, or trigger follow-up actions based on the tool's output. | Property | Description | | - | - | | `messageId` | ID of the conversation message this result belongs to | | `toolCallId` | Matches the ID from the corresponding `ToolCallStart` event | | `content` | The actual result/output content from the tool execution | | `role` | Optional role identifier, typically "tool" for tool results | ### ToolCallChunk Convenience event that expands to Start → Args → End automatically. The `ToolCallChunk` event lets you omit explicit `ToolCallStart` and `ToolCallEnd` events. The client stream transformer expands chunks into the standard tool-call triad: * First chunk for a tool call must include `toolCallId` and `toolCallName` and will emit `ToolCallStart` (propagating any `parentMessageId`). * Each chunk with a `delta` emits a `ToolCallArgs` for the current `toolCallId`. * `ToolCallEnd` is emitted automatically when the stream switches to a new `toolCallId` or when the stream completes. | Property | Description | | - | - | | `toolCallId` | Optional on later chunks; required on the first chunk of a tool call | | `toolCallName` | Optional on later chunks; required on the first chunk of a tool call | | `parentMessageId` | Optional ID of the parent message | | `delta` | Optional argument data chunk (often a JSON fragment) | ## State Management Events These events are used to manage and synchronize the agent's state with the frontend. State management in the protocol follows an efficient snapshot-delta pattern where complete state snapshots are sent initially or infrequently, while incremental updates (deltas) are used for ongoing changes. This approach optimizes for both completeness and efficiency: snapshots ensure the frontend has the full state context, while deltas minimize data transfer for frequent updates. Together, they enable frontends to maintain an accurate representation of agent state without unnecessary data transmission. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Initial state transfer Agent->>Client: StateSnapshot Note over Agent,Client: Incremental updates loop State changes over time Agent->>Client: StateDelta Agent->>Client: StateDelta end Note over Agent,Client: Occasional full refresh Agent->>Client: StateSnapshot loop More incremental updates Agent->>Client: StateDelta end Note over Agent,Client: Message history update Agent->>Client: MessagesSnapshot ``` The combination of snapshots and deltas allows frontends to efficiently track changes to agent state while ensuring consistency. Snapshots serve as synchronization points that reset the state to a known baseline, while deltas provide lightweight updates between snapshots. ### StateSnapshot Provides a complete snapshot of an agent's state. The `StateSnapshot` event delivers a comprehensive representation of the agent's current state. This event is typically sent at the beginning of an interaction or when synchronization is needed. It contains all state variables relevant to the frontend, allowing it to completely rebuild its internal representation. Frontends should replace their existing state model with the contents of this snapshot rather than trying to merge it with previous state. | Property | Description | | - | - | | `snapshot` | Complete state snapshot | ### StateDelta Provides a partial update to an agent's state using JSON Patch. The `StateDelta` event contains incremental updates to the agent's state in the form of JSON Patch operations (as defined in RFC 6902). Each delta represents specific changes to apply to the current state model. This approach is bandwidth-efficient, sending only what has changed rather than the entire state. Frontends should apply these patches in sequence to maintain an accurate state representation. If a frontend detects inconsistencies after applying patches, it may request a fresh `StateSnapshot`. | Property | Description | | - | - | | `delta` | Array of JSON Patch operations (RFC 6902) | ### MessagesSnapshot Provides a snapshot of all messages in a conversation. The `MessagesSnapshot` event delivers a complete history of messages in the current conversation. Unlike the general state snapshot, this focuses specifically on the conversation transcript. This event is useful for initializing the chat history, synchronizing after connection interruptions, or providing a comprehensive view when a user joins an ongoing conversation. Frontends should use this to establish or refresh the conversational context displayed to users. | Property | Description | | - | - | | `messages` | Array of message objects | `activity` and `reasoning` messages are all-or-nothing inside a `MessagesSnapshot`. If the snapshot carries any message of that role, it is the complete set for that role: entries it repeats replace the client's copies, and ones it leaves out are removed. If it carries none, the snapshot says nothing about that role and the client keeps the messages it already has. Both roles are client-side by default, so leaving them out is safe. Activity messages never travel back to the agent — they are stripped from `RunAgentInput` — and reasoning usually exists only as streamed `Reasoning` events. A backend that tracks neither simply omits them from the snapshot, and nothing the client holds is lost. ## Activity Events Activity Events expose structured, in-progress activity updates that occur between chat messages. They follow the same snapshot/delta pattern as the state system so that UIs can render a complete activity view immediately and then incrementally update it as new information arrives. An activity message occupies the same id space as every other message, so its `messageId` must not be reused by a text or reasoning message, and vice versa. Those carry different shapes of `content` — a structured object for activity, a string for text and reasoning — so a shared id has no coherent meaning. A client that receives a text or reasoning message under an id an activity message already holds should leave the activity message untouched rather than overwrite it. ### ActivitySnapshot Delivers a complete snapshot of an activity message. | Property | Description | | - | - | | `messageId` | Identifier for the `ActivityMessage` this event updates | | `activityType` | Activity discriminator (for example `"PLAN"`, `"SEARCH"`) | | `content` | Structured JSON payload representing the full activity state | | `replace` | Optional. Defaults to `true`. When `false`, ignore the snapshot if the message already exists | Frontends should either create a new `ActivityMessage` or replace the existing one with the payload supplied by the snapshot. ### ActivityDelta Applies incremental updates to an existing activity using JSON Patch operations. | Property | Description | | - | - | | `messageId` | Identifier for the target activity message | | `activityType` | Activity discriminator (mirrors the value from the most recent snapshot) | | `patch` | Array of RFC 6902 JSON Patch operations to apply to the activity data | Activity deltas should be applied in order to the previously synchronized activity content. If an application detects divergence, it can request or emit a fresh `ActivitySnapshot` to resynchronize. ## Special Events Special events provide flexibility in the protocol by allowing for system-specific functionality and integration with external systems. These events don't follow the standard lifecycle or streaming patterns of other event types but instead serve specialized purposes. ### Raw Used to pass through events from external systems. The `Raw` event acts as a container for events originating from external systems or sources that don't natively follow the Agent UI Protocol. This event type enables interoperability with other event-based systems by wrapping their events in a standardized format. The enclosed event data is preserved in its original form inside the `event` property, while the optional `source` property identifies the system it came from. Frontends can use this information to handle external events appropriately, either by processing them directly or by delegating them to system-specific handlers. | Property | Description | | - | - | | `event` | Original event data | | `source` | Optional source identifier | ### Custom Used for application-specific custom events. The `Custom` event provides an extension mechanism for implementing features not covered by the standard event types. Unlike `Raw` events which act as passthrough containers, `Custom` events are explicitly part of the protocol but with application-defined semantics. The `name` property identifies the specific custom event type, while the `value` property contains the associated data. This mechanism allows for protocol extensions without requiring formal specification changes. Teams should document their custom events to ensure consistent implementation across frontends and agents. | Property | Description | | - | - | | `name` | Name of the custom event | | `value` | Value associated with the event | ## Reasoning Events Reasoning events support LLM reasoning visibility and continuity, enabling chain-of-thought reasoning while maintaining privacy. These events allow agents to surface reasoning signals (e.g., summaries) and support encrypted reasoning items for state carry-over across turns—especially under `store:false` or zero data retention policies—without exposing raw chain-of-thought. See [OpenAI ZTR documentation](https://developers.openai.com/cookbook/examples/responses_api/reasoning_items/#encrypted-reasoning-items), [OpenAI store parameter documentation](https://platform.openai.com/docs/api-reference/responses/create#responses_create-store), and [Gemini Thought Signatures](https://ai.google.dev/gemini-api/docs/thought-signatures) for the underlying concept of encrypted reasoning items, which inspired this design. See [Reasoning](/concepts/reasoning) for comprehensive documentation including privacy considerations, compliance guidance, and implementation examples. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Reasoning begins Agent->>Client: ReasoningStart Note over Agent,Client: Stream reasoning content Agent->>Client: ReasoningMessageStart Agent->>Client: ReasoningMessageContent Agent->>Client: ReasoningMessageEnd Note over Agent,Client: Reasoning completes Agent->>Client: ReasoningEnd ``` ### ReasoningStart Marks the start of reasoning. The `ReasoningStart` event signals that the agent is beginning a reasoning process. It establishes a reasoning context identified by a unique `messageId`. | Property | Description | | - | - | | `messageId` | Unique identifier of this reasoning | ### ReasoningMessageStart Signals the start of a reasoning message. The `ReasoningMessageStart` event begins a streaming reasoning message. This message will contain the visible portion of the agent's reasoning that should be displayed to users (e.g., a summary or partial chain-of-thought). | Property | Description | | - | - | | `messageId` | Unique identifier of the message | | `role` | Role of the reasoning message (`"reasoning"`) | ### ReasoningMessageContent Represents a chunk of content in a streaming reasoning message. The `ReasoningMessageContent` event delivers incremental reasoning content to the client. Multiple content events with the same `messageId` should be concatenated to form the complete visible reasoning. | Property | Description | | - | - | | `messageId` | Matches ID from ReasoningMessageStart | | `delta` | Reasoning content chunk (non-empty string) | ### ReasoningMessageEnd Signals the end of a reasoning message. The `ReasoningMessageEnd` event indicates that all content for the specified reasoning message has been sent. Clients should finalize any UI representing this reasoning message. | Property | Description | | - | - | | `messageId` | Matches ID from ReasoningMessageStart | ### ReasoningMessageChunk A convenience event to auto start/close reasoning messages. The `ReasoningMessageChunk` event simplifies implementation by automatically managing message lifecycle. The first chunk with a `messageId` implicitly starts the message. An empty `delta` or the next non-reasoning event implicitly closes the message. | Property | Description | | - | - | | `messageId` | Message ID (first event must be non-empty) | | `delta` | Reasoning content chunk (empty string closes the message) | ### ReasoningEnd Marks the end of reasoning. The `ReasoningEnd` event signals that the agent has completed its reasoning process for the given context. No further reasoning events with the same `messageId` should be expected after this event. | Property | Description | | - | - | | `messageId` | Unique identifier of this reasoning | ### ReasoningEncryptedValue Attaches encrypted chain-of-thought reasoning to a message or tool call. The `ReasoningEncryptedValue` event carries encrypted reasoning content that represents the LLM's internal chain-of-thought related to a specific entity. This allows the agent to preserve reasoning state across conversation turns without exposing the raw content to the client. The client stores and forwards these encrypted values opaquely—only the agent (or authorized backend) can decrypt them. | Property | Description | | - | - | | `subtype` | Entity type: `"message"` or `"tool-call"` | | `entityId` | ID of the message or tool call this reasoning belongs to | | `encryptedValue` | Encrypted chain-of-thought content blob | Use cases: * **Message reasoning**: Attach encrypted reasoning to an `AssistantMessage` or `ReasoningMessage` to preserve context for follow-up turns * **Tool call reasoning**: Attach encrypted reasoning to a tool call to capture why the agent chose specific arguments or how it interpreted results ## Subagent Events Subagent events let an agent report that it has delegated work to a child agent, so a frontend can tell which subagent produced which output. Without them, three subagents streaming concurrently arrive as one undifferentiated stream. Attribution is carried by an optional `subagentRunId` on most other events; these three bracket the subagent's activity and give it a name to display. `subagentRunId` identifies **one invocation**, not a reusable subagent definition — running the same subagent twice yields two different values. See [Subagents](/concepts/subagents) for the full model, including nesting, concurrency, and the rules clients enforce. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Subagent begins Agent->>Client: SubagentStarted Note over Agent,Client: Attributed output Agent->>Client: TextMessageStart / Content / End Note over Agent,Client: Subagent concludes alt Success Agent->>Client: SubagentFinished else Failure Agent->>Client: SubagentError end ``` ### SubagentStarted Announces a new subagent invocation. | Property | Description | | - | - | | `subagentRunId` | Opaque identifier for this invocation | | `name` | The subagent's declared name or type, for display | | `description` | Optional human-readable description | | `parentSubagentRunId` | Optional enclosing subagent, when subagents nest | | `parentToolCallId` | Optional tool call that spawned this subagent | | `parentMessageId` | Optional message that held that tool call | ### SubagentFinished Marks a subagent invocation as complete. | Property | Description | | - | - | | `subagentRunId` | Matches ID from SubagentStarted | | `result` | Optional completion payload, mirroring `RunFinished.result` | | `outcome` | Optional discriminated union: `{ type: "success" }` or `{ type: "suspended", interruptIds?: string[] }`. Omitted means success (the legacy reading). `suspended` says the subagent is checkpointed mid-flight awaiting outside input — the run then ends with an interrupt outcome, and `interruptIds` names the run-level interrupts whose answers resume this subagent. | ### SubagentError Marks a subagent invocation as failed. | Property | Description | | - | - | | `subagentRunId` | Matches ID from SubagentStarted | | `message` | Human-readable error message | | `code` | Optional error code | > [!NOTE] > > `StateSnapshot` and `StateDelta` are attributable, but attribution on them is > **provenance, not ownership**: it records which subagent produced the update. > State stays run-scoped and an attributed snapshot or delta is applied to the > run's one state document, exactly as an unattributed one is. There is no > per-subagent state. ## Deprecated Events > [!WARNING] > > The following events are deprecated and will be removed in version 1.0.0. Use > the corresponding Reasoning events instead. ### Thinking Events (Deprecated) The `THINKING_*` events have been replaced by `REASONING_*` events: | Deprecated Event | 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` | See [Reasoning Migration](/concepts/reasoning#migration-from-thinking-events) for detailed migration guidance. ## Draft Events These events are currently in draft status and may change before finalization. They represent proposed extensions to the protocol that are under active development and discussion. ### Meta Events DRAFT [View Proposal](/drafts/meta-events) Meta events provide annotations and signals independent of agent runs, such as user feedback or external system events. #### MetaEvent A side-band annotation event that can occur anywhere in the stream. | Property | Description | | - | - | | `metaType` | Application-defined type (e.g., "thumbs\_up", "tag") | | `payload` | Application-defined payload | ### Modified Lifecycle Events DRAFT [View Specification](/concepts/interrupts) Extensions to existing lifecycle events to support interrupts and branching. #### RunFinished (Extended) The `RunFinished` event gains new fields to support interrupt-aware workflows. | Property | Description | | - | - | | `outcome` | Optional discriminated union: `{ type: "success" }` or `{ type: "interrupt", interrupts: [...] }`. Omitted on legacy producers. | | `result` | Optional. Free-form completion payload. Lives at the event root for back-compat with legacy producers; coexists with any outcome. | See [Serialization](/concepts/serialization) for lineage and input capture. #### RunStarted (Extended) The `RunStarted` event gains new fields to support branching and input tracking. | Property | Description | | - | - | | `parentRunId` | Optional: Parent run ID for branching/time travel | | `input` | Optional: The exact agent input for this run | ## Event Flow Patterns Events in the protocol typically follow specific patterns: 1. **Start-Content-End Pattern**: Used for streaming content (text messages, tool calls) * `Start` event initiates the stream * `Content` events deliver data chunks * `End` event signals completion 2. **Snapshot-Delta Pattern**: Used for state synchronization * `Snapshot` provides complete state * `Delta` events provide incremental updates 3. **Lifecycle Pattern**: Used for monitoring agent runs * `Started` events signal beginnings * `Finished`/`Error` events signal endings ## Implementation Considerations When implementing event handlers: * Events should be processed in the order they are received * Events with the same ID (e.g., `messageId`, `toolCallId`) belong to the same logical stream * Implementations should be resilient to out-of-order delivery * Custom events should follow the established patterns for consistency # Generative UI Source: https://docs.ag-ui.com/concepts/generative-ui-specs Understanding AG-UI's relationship with generative UI specifications ## **AG-UI and Generative UI Specs** Several recently released specs have enabled agents to return generative UI, increasing the power and flexibility of the Agent\<->User conversation. A2UI, MCP-UI, and Open-JSON-UI are all **generative UI specifications.** Generative UIs allow agents to respond to users not only with text but also with dynamic UI components. | **Specification** | **Origin / Maintainer** | **Purpose** | | - | - | - | | **A2UI** | Google | A declarative, LLM-friendly Generative UI spec. JSONL-based and streaming, designed for platform-agnostic rendering. | | **Open-JSON-UI** | OpenAI | An open standardization of OpenAI's internal declarative Generative UI schema. | | **MCP-UI** | Microsoft + Shopify | A fully open, iframe-based Generative UI standard extending MCP for user-facing experiences. | Despite the naming similarities, **AG-UI is not a generative UI specification** — it's a **User Interaction protocol** that provides the **bi-directional runtime connection** between the agent and the application. AG-UI natively supports all of the above generative UI specs and allows developers to define **their own custom generative UI standards** as well. # Interrupts Source: https://docs.ag-ui.com/concepts/interrupts Human-in-the-loop pauses and resumes in the Agent User Interaction Protocol Agents sometimes need to pause: to get human approval before executing a sensitive action, to request structured input, to wait on an out-of-band policy decision. AG-UI exposes this as an **interrupt-aware run lifecycle** — a terminal model where the run ends with an interrupt outcome, and the client starts a new run carrying per-interrupt responses. ## Lifecycle ```mermaid theme={null} sequenceDiagram participant Agent participant Client as Client App Note over Agent,Client: Run 1 begins Agent-->>Client: RunStarted (runId: r1) Agent-->>Client: ...ToolCall* / TextMessage* / StateSnapshot... Note over Agent,Client: Agent needs user input — emit snapshot, then interrupt Agent-->>Client: RunFinished { outcome: { type: "interrupt", interrupts: [...] } } Note over Agent,Client: User resolves interrupts Client-->>Agent: RunAgentInput { threadId, resume: [{interruptId, status, payload?}, ...] } Note over Agent,Client: Run 2 begins; resume[].interruptId links back to run 1's interrupts Agent-->>Client: RunStarted (runId: r2) Agent-->>Client: ...continue / ToolCallResult / ... Agent-->>Client: RunFinished { outcome: { type: "success" }, result } ``` ## Run outcomes `RunFinished` carries an optional `outcome` field — a discriminated union with the variant-specific data nested inside: * **omitted** — legacy/back-compat. Treated as a normal completion. Pre-existing AG-UI clients that did not yet know about interrupts still emit this shape, and new readers should accept it. * `{ type: "success" }` — the run completed normally. The optional `result` stays at the root of the event for back-compat. * `{ type: "interrupt", interrupts: [...] }` — the run paused for user input. `interrupts` is a non-empty array, and it lives inside the outcome so it travels with the variant that needs it. ```typescript theme={null} type RunFinishedOutcome = | { type: "success" } | { type: "interrupt"; interrupts: Interrupt[] } type RunFinishedEvent = { type: "RUN_FINISHED" threadId: string runId: string result?: unknown outcome?: RunFinishedOutcome } ``` Because `outcome` is optional, an old producer that has never heard of interrupts (no `outcome` field) still validates as a `RunFinished` event under the new schema — clients only need to inspect `outcome` when they care about the interrupt-aware variant. ## The Interrupt type ```typescript theme={null} type Interrupt = { id: string reason: string message?: string toolCallId?: string responseSchema?: JsonSchema expiresAt?: string metadata?: Record subagentRunId?: string } ``` | Field | Purpose | | - | - | | `id` | Correlation key across interrupt, resume, idempotency, and audit. | | `reason` | Categorical routing hint — see [Reason taxonomy](#reason-taxonomy). | | `message` | Human-readable prompt. Universal fallback UI content. | | `toolCallId` | Binds the interrupt to a prior `ToolCall*` sequence. | | `responseSchema` | JSON Schema for the expected `resume.payload`. | | `expiresAt` | Optional ISO-8601 TTL. Stale resumes produce `RunError`. | | `metadata` | Free-form framework-specific data. | | `subagentRunId` | The subagent whose work raised this interrupt — absent for a root-raised one. Attribution lives per interrupt because one run can carry interrupts from several subagents. A producer that emits the subagent lifecycle events also closes the subagent with `outcome: { type: "suspended" }` — lifecycle events remain optional, so attribution here is valid on its own (see [Subagents](/concepts/subagents#suspension)). | ## Resuming a run The next `RunAgentInput` on the same thread carries a `resume` array: ```typescript theme={null} type RunAgentInput = { // ... existing fields resume?: Array<{ interruptId: string status: "resolved" | "cancelled" payload?: any metadata?: Record }> } ``` * `resolved` — the user responded. `payload` carries the response, validated against the interrupt's `responseSchema`. Denials are expressed inside the payload (for example, `{ approved: false }`), not as a separate status. * `cancelled` — the user abandoned without providing meaningful input. `payload` should be omitted. * `metadata` — optional envelope data about the response (for example a signature proving the human decision was not tampered with, or routing keys), as opposed to `payload`, which is the answer the agent asked for and will act on. Open by key; any JSON value is allowed under a key, including `null`. The object itself is either absent or an object, never `null`. The `ag-ui` key is reserved for AG-UI's own use. Allowed on either status. ## Contract rules 1. **Same thread.** Resume requests must use the same `threadId` as the interrupted run. 2. **Resume linkage.** `resume[].interruptId` must reference an `id` from the interrupted run's `interrupts[]`. `parentRunId` is orthogonal — it retains its existing AG-UI branching/time-travel semantics. 3. **Cover all open interrupts.** A single `resume` array must address every open interrupt from the interrupted run. Partial resumes are not supported. 4. **Pending interrupts block new input.** If a thread has unresolved interrupts, any `RunAgentInput` on that thread must include a `resume` addressing them. Agents receiving a non-conforming input must emit `RunError`. 5. **Idempotency.** A resume with the same `(threadId, interruptId, status, payload)` must be safe to replay. 6. **Payload validation.** If an interrupt declares a `responseSchema`, the agent may validate the corresponding resume `payload` and emit `RunError` on mismatch. Clients should validate before submitting. 7. **Expiry enforcement.** Clients must not submit a resume past an interrupt's `expiresAt`. Stale resumes produce `RunError`. 8. **Graceful handling.** Agents should handle missing or invalid resume payloads via `RunError`, not silent failures. ## State at the interrupt boundary At the moment of interrupt, the agent must emit any state required for resume via `StateSnapshot` and `MessagesSnapshot` events **before** the `RunFinished` event that carries the interrupt. This rule makes the protocol resume-mode-agnostic: both replay-style continuations (rebuild context from messages + state) and checkpoint-style continuations (restore a suspended coroutine) must produce identical observable behavior on resume. Framework-native checkpointing is an implementation optimization, not a protocol contract. ## Error handling `RunError` is the sole error event. The `outcome` enum does not carry an `"error"` value. Interrupt-specific error conditions that produce `RunError`: * A resume arrives past an interrupt's `expiresAt`. * A resume payload fails validation against its `responseSchema`. * A resume references an `interruptId` the agent cannot correlate. * A resume fails to address every open interrupt (violates rule 3). * A `RunAgentInput` on a thread with pending interrupts omits `resume` (violates rule 4). ## Reason taxonomy `reason` is a required string. A small set of core values is spec-defined; any other string is a valid extension. ### Core values | Value | Semantics | Typical companion fields | | - | - | - | | `tool_call` | Interrupt bound to a specific tool call awaiting decision. | `toolCallId` must be set. | | `input_required` | Agent needs structured input to continue. | `responseSchema` should be set. | | `confirmation` | Free-standing yes/no decision not bound to a tool. | `responseSchema` optional; boolean default. | ### Custom reasons Any other string is valid. Agents should namespace custom reasons as `:` (for example, `langgraph:database_modification`, `mastra:workflow_suspend`). The `core:` prefix is reserved for future spec additions. ### Client routing * Clients should switch on known core values for dedicated UI. * For unknown reasons, clients must not error. Render from `message`, `responseSchema`, and `metadata`. ## Tool-bound interrupts When an interrupt carries `reason: "tool_call"` and a `toolCallId`, the tool call and its resolution span two runs. The full audit trail is: 1. `ToolCallArgs` from the interrupted run (the agent's proposal). 2. `RunAgentInput.resume` payload from the resumed run (user decision and edits). 3. `ToolCallResult` from the resumed run (actual execution outcome). The agent does **not** re-emit `ToolCallStart`/`ToolCallArgs`/`ToolCallEnd` in the resumed run — it emits `ToolCallResult` against the original `toolCallId`. ### Server-owned tool execution A producer can mark a tool-bound interrupt with `metadata: { "ag-ui:tool-execution": "server" }` to explicitly identify a server-owned tool. Clients send the user's approval or rejection in `resume` and must not synthesize a tool-result message from that decision. The server provides the actual execution outcome against the original `toolCallId`. The .NET adapter emits this marker for native tool approval requests. Keep this metadata when persisting or restoring pending interrupts. Its absence does not establish tool ownership; existing frontend completion contracts remain unchanged. The marker describes execution ownership to clients and is not an authorization credential: servers must still validate decisions and reject client-supplied results for server-owned tools. ### Approve with edits The recommended `responseSchema` pattern for tool-bound interrupts that support approve-with-edits: ```json theme={null} { "type": "object", "properties": { "approved": { "type": "boolean" }, "editedArgs": { "type": "object", "description": "Full replacement of the tool args. Not merged." } }, "required": ["approved"] } ``` `editedArgs` is a full replacement, not a partial merge. Its presence in the schema is the **capability signal** that the client may offer edit UI. ## Examples ### Minimal tool approval The agent interrupts after proposing `sendEmail`: ```json theme={null} { "type": "RUN_FINISHED", "threadId": "thread-1", "runId": "run-1", "outcome": { "type": "interrupt", "interrupts": [ { "id": "int-abc123", "reason": "tool_call", "message": "Send email to a@b.com with subject 'Hi'?", "toolCallId": "tc-001", "responseSchema": { "type": "object", "properties": { "approved": { "type": "boolean" } }, "required": ["approved"] } } ] } } ``` The client submits the resume: ```json theme={null} { "threadId": "thread-1", "runId": "run-2", "resume": [ { "interruptId": "int-abc123", "status": "resolved", "payload": { "approved": true } } ] } ``` The agent continues in `run-2`, emits `ToolCallResult` against `tc-001`, then `RunFinished { outcome: { type: "success" } }`. ### Approve with edits (full audit) ```json theme={null} { "type": "RUN_FINISHED", "threadId": "thread-2", "runId": "run-10", "outcome": { "type": "interrupt", "interrupts": [ { "id": "int-email-edit", "reason": "tool_call", "message": "Send email to a@b.com? You can edit the body before approving.", "toolCallId": "tc-42", "responseSchema": { "type": "object", "properties": { "approved": { "type": "boolean" }, "editedArgs": { "type": "object", "properties": { "to": { "type": "string", "format": "email" }, "subject": { "type": "string" }, "body": { "type": "string" } } } }, "required": ["approved"] }, "metadata": { "langgraph": { "checkpointId": "ckpt-xyz", "nodeId": "tool_executor" } } } ] } } ``` Client resume with edits: ```json theme={null} { "threadId": "thread-2", "runId": "run-11", "resume": [ { "interruptId": "int-email-edit", "status": "resolved", "payload": { "approved": true, "editedArgs": { "to": "a@b.com", "subject": "Hi", "body": "Hi (revised per my note)" } } } ] } ``` Audit trail for `tc-42`: * `run-10` `ToolCallArgs` — original proposal. * `run-11` `RunAgentInput.resume[0].payload.editedArgs` — user edits. * `run-11` `ToolCallResult` — actual outcome. ### Parallel interrupts ```json theme={null} { "type": "RUN_FINISHED", "threadId": "thread-3", "runId": "run-20", "outcome": { "type": "interrupt", "interrupts": [ { "id": "i-1", "reason": "tool_call", "toolCallId": "tc-a", "message": "Approve sendEmail to x@y.com?" }, { "id": "i-2", "reason": "tool_call", "toolCallId": "tc-b", "message": "Approve sendEmail to y@z.com?" }, { "id": "i-3", "reason": "tool_call", "toolCallId": "tc-c", "message": "Approve sendEmail to z@w.com?" } ] } } ``` Client approves two, cancels one: ```json theme={null} { "threadId": "thread-3", "runId": "run-21", "resume": [ { "interruptId": "i-1", "status": "resolved", "payload": { "approved": true } }, { "interruptId": "i-2", "status": "resolved", "payload": { "approved": true } }, { "interruptId": "i-3", "status": "cancelled" } ] } ``` In `run-21` the agent emits `ToolCallResult` for `tc-a` and `tc-b` and treats `tc-c` as not-executed. ### Non-tool input request ```json theme={null} { "type": "RUN_FINISHED", "threadId": "thread-4", "runId": "run-30", "outcome": { "type": "interrupt", "interrupts": [ { "id": "int-form", "reason": "input_required", "message": "Please provide the quarterly filing details.", "responseSchema": { "type": "object", "properties": { "quarter": { "type": "string", "enum": ["Q1", "Q2", "Q3", "Q4"] }, "year": { "type": "integer", "minimum": 2000 }, "revenue": { "type": "number" } }, "required": ["quarter", "year", "revenue"] }, "expiresAt": "2026-04-20T17:00:00Z" } ] } } ``` Client response: ```json theme={null} { "threadId": "thread-4", "runId": "run-31", "resume": [ { "interruptId": "int-form", "status": "resolved", "payload": { "quarter": "Q1", "year": 2026, "revenue": 4200000 } } ] } ``` ## Framework integrations | Framework | Package | Interrupt support | | - | - | - | | LangGraph | `@ag-ui/langgraph` / `ag-ui-langgraph` | ✅ Accepts `RunAgentInput.resume[]`. Can emit `RunFinishedEvent.outcome = {type:"interrupt"}` — opt-in via `emitInterruptOutcome` / `emit_interrupt_outcome` (default off; legacy clients that resume via `command.resume` stop resuming once they see the structured outcome). Legacy `CustomEvent(name="on_interrupt")` emitted by default; disable via `enableLegacyOnInterruptEvent: false`. Subclass hooks available for custom HITL translation. | | AWS Strands | `@ag-ui/aws-strands` | ✅ Forwards native Strands interrupts via `RunFinishedEvent.outcome`. | ## Related * [Events](/concepts/events) — how `RunFinished` fits into the broader event stream. * [Capabilities](/concepts/capabilities) — the `humanInTheLoop.interrupts` and `humanInTheLoop.approveWithEdits` flags agents declare. # Messages Source: https://docs.ag-ui.com/concepts/messages Understanding message structure and communication in AG-UI # Messages Messages form the backbone of communication in the AG-UI protocol. They represent the conversation history between users and AI agents, and provide a standardized way to exchange information regardless of the underlying AI service being used. ## Message Structure AG-UI messages follow a vendor-neutral format, ensuring compatibility across different AI providers while maintaining a consistent structure. This allows applications to switch between AI services (like OpenAI, Anthropic, or custom models) without changing the client-side implementation. The basic message structure includes: ```typescript theme={null} interface BaseMessage { id: string // Unique identifier for the message role: string // The role of the sender (user, assistant, system, tool, reasoning) content?: string // Optional text content of the message name?: string // Optional name of the sender encryptedContent?: string // Optional encrypted content for privacy-preserving state continuity metadata?: Record // Optional extra information attached to the message } ``` The `role` discriminator can be `"user"`, `"assistant"`, `"system"`, `"tool"`, `"developer"`, `"activity"`, or `"reasoning"`. Concrete message types extend this shape with the fields they need. Every message type carries `metadata`, an optional open-by-key object that accumulates as the message is built from its events. Tool calls carry one of their own. See [Metadata](/concepts/metadata) for the rules. > The `encryptedContent` field enables privacy-preserving workflows where > sensitive content (such as reasoning chains) can be passed across turns > without exposing the raw content. This is particularly useful for zero data > retention (ZDR) compliance and `store:false` scenarios. ## Message Types AG-UI supports several message types to accommodate different participants in a conversation: ### User Messages Messages from the end user to the agent: ```typescript theme={null} interface UserMessage { id: string role: "user" content: string | ContentPart[] // Text or multimodal input from the user name?: string // Optional user identifier } type ContentPart = | TextPart | ImagePart | AudioPart | VideoPart | DocumentPart interface DataSource { type: "data" value: string mimeType: string } interface UrlSource { type: "url" value: string mimeType?: string } interface FileSource { type: "file" value: string // a handle the provider issued; opaque, never fetched provider?: string // who issued it, e.g. "openai" mimeType?: string } type PartSource = DataSource | UrlSource | FileSource interface TextPart { type: "text" text: string } interface ImagePart { type: "image" source: PartSource metadata?: Record } interface AudioPart { type: "audio" source: PartSource metadata?: Record } interface VideoPart { type: "video" source: PartSource metadata?: Record } interface DocumentPart { type: "document" source: PartSource metadata?: Record } ``` > In Python, the previous `BinaryInputContent` model is deprecated and remains > temporarily available as a compatibility path. This structure keeps traditional plain-text inputs working while enabling richer payloads such as images, audio clips, or uploaded files in the same message. ### Assistant Messages Messages from the AI assistant to the user: ```typescript theme={null} interface AssistantMessage { id: string role: "assistant" content?: string // Text response from the assistant (optional if using tool calls) name?: string // Optional assistant identifier toolCalls?: ToolCall[] // Optional tool calls made by the assistant encryptedContent?: string // Optional encrypted content for state continuity } ``` ### System Messages Instructions or context provided to the agent: ```typescript theme={null} interface SystemMessage { id: string role: "system" content: string // Instructions or context for the agent name?: string // Optional identifier } ``` ### Tool Messages Results from tool executions: ```typescript theme={null} interface ToolMessage { id: string role: "tool" content: string // Result from the tool execution toolCallId: string // ID of the tool call this message responds to error?: string // Optional error message if the tool execution failed encryptedValue?: string // Optional encrypted reasoning for state continuity } ``` Key points: * The `toolCallId` links the result back to the original tool call * Use `error` to indicate tool execution failures * Use `encryptedValue` to attach encrypted chain-of-thought related to how the agent interpreted or processed the tool result ### Activity Messages Structured UI messages that exist only on the frontend.  Used for progress, status, or any custom visual element that shouldn’t be sent to the model: ```typescript theme={null} interface ActivityMessage { id: string role: "activity" activityType: string // e.g. "PLAN", "SEARCH", "SCRAPE" content: Record // Structured payload rendered by the frontend } ``` Key points * Emitted via `ACTIVITY_SNAPSHOT` and `ACTIVITY_DELTA` to support live, updateable UI (checklists, steps, search-in-progress, etc.). * **Frontend-only:** never forwarded to the agent, so no filtering and no LLM confusion. * **Customizable:** define your own `activityType` and `content` and render a matching UI component. * **Streamable:** can be updated over time for long-running operations. * Helps persist/restore custom events by turning them into durable message objects. ### Developer Messages Internal messages used for development or debugging: ```typescript theme={null} interface DeveloperMessage { id: string role: "developer" content: string name?: string } ``` ### Reasoning Messages Messages representing the agent's internal reasoning or chain-of-thought process: ```typescript theme={null} interface ReasoningMessage { id: string role: "reasoning" content: string // Reasoning content (visible to client) encryptedValue?: string // Optional encrypted reasoning for state continuity } ``` > [!TIP] > > Unlike Activity messages, Reasoning messages are intended to represent the > agent's internal thought process and may be encrypted for privacy and are > meant to be sent back to the agent for further processing on subsequent turns. Key points: * Emitted via `REASONING_MESSAGE_START`, `REASONING_MESSAGE_CONTENT`, and `REASONING_MESSAGE_END` events. * **Visibility control:** Content may be visible to users (as a summary) or fully encrypted. * **Encrypted values:** Use `REASONING_ENCRYPTED_VALUE` events to attach encrypted chain-of-thought to messages or tool calls without exposing content. * **State continuity:** Encrypted reasoning items can be passed across conversation turns without exposing raw chain-of-thought. * **Privacy-first:** Supports `store:false` and zero data retention (ZDR) policies while preserving reasoning capabilities. * **Separate from assistant messages:** Reasoning is kept distinct from final responses to avoid polluting the conversation history. See [Reasoning Events](/concepts/events#reasoning-events) for the streaming event lifecycle. ## Vendor Neutrality AG-UI messages are designed to be vendor-neutral, meaning they can be easily mapped to and from proprietary formats used by various AI providers: ```typescript theme={null} // Example: Converting AG-UI messages to OpenAI format const openaiMessages = agUiMessages .filter((msg) => ["user", "system", "assistant"].includes(msg.role)) .map((msg) => ({ role: msg.role as "user" | "system" | "assistant", content: msg.content || "", // Map tool calls if present ...(msg.role === "assistant" && msg.toolCalls ? { tool_calls: msg.toolCalls.map((tc) => ({ id: tc.id, type: tc.type, function: { name: tc.function.name, arguments: tc.function.arguments, }, })), } : {}), })) ``` This abstraction allows AG-UI to serve as a common interface regardless of the underlying AI service. ## Message Synchronization Messages can be synchronized between client and server through two primary mechanisms: ### Complete Snapshots The `MESSAGES_SNAPSHOT` event provides a complete view of all messages in a conversation: ```typescript theme={null} interface MessagesSnapshotEvent { type: EventType.MESSAGES_SNAPSHOT messages: Message[] // Complete array of all messages } ``` This is typically used: * When initializing a conversation * After connection interruptions * When major state changes occur * To ensure client-server synchronization ### Streaming Messages For real-time interactions, new messages can be streamed as they're generated: 1. **Start a message**: Indicate a new message is being created ```typescript theme={null} interface TextMessageStartEvent { type: EventType.TEXT_MESSAGE_START messageId: string role: string } ``` 2. **Stream content**: Send content chunks as they become available ```typescript theme={null} interface TextMessageContentEvent { type: EventType.TEXT_MESSAGE_CONTENT messageId: string delta: string // Text chunk to append } ``` 3. **End a message**: Signal the message is complete ```typescript theme={null} interface TextMessageEndEvent { type: EventType.TEXT_MESSAGE_END messageId: string } ``` This streaming approach provides a responsive user experience with immediate feedback. ## Tool Integration in Messages AG-UI messages elegantly integrate tool usage, allowing agents to perform actions and process their results: ### Tool Calls Tool calls are embedded within assistant messages: ```typescript theme={null} interface ToolCall { id: string // Unique ID for this tool call type: "function" // Type of tool call function: { name: string // Name of the function to call arguments: string // JSON-encoded string of arguments } } ``` Example assistant message with tool calls: ```typescript theme={null} { id: "msg_123", role: "assistant", content: "I'll help you with that calculation.", toolCalls: [ { id: "call_456", type: "function", function: { name: "calculate", arguments: '{"expression": "24 * 7"}' } } ] } ``` ### Tool Results Results from tool executions are represented as tool messages: ```typescript theme={null} { id: "result_789", role: "tool", content: "168", toolCallId: "call_456" // References the original tool call } ``` This creates a clear chain of tool usage: 1. Assistant requests a tool call 2. Tool executes and returns a result 3. Assistant can reference and respond to the result ## Streaming Tool Calls Similar to text messages, tool calls can be streamed to provide real-time visibility into the agent's actions: 1. **Start a tool call**: ```typescript theme={null} interface ToolCallStartEvent { type: EventType.TOOL_CALL_START toolCallId: string toolCallName: string parentMessageId?: string // Optional link to parent message } ``` 2. **Stream arguments**: ```typescript theme={null} interface ToolCallArgsEvent { type: EventType.TOOL_CALL_ARGS toolCallId: string delta: string // JSON fragment to append to arguments } ``` 3. **End a tool call**: ```typescript theme={null} interface ToolCallEndEvent { type: EventType.TOOL_CALL_END toolCallId: string } ``` This allows frontends to show tools being invoked progressively as the agent constructs its reasoning. ## Practical Example Here's a complete example of a conversation with tool usage: ```typescript theme={null} // Conversation history ;[ // User query { id: "msg_1", role: "user", content: "What's the weather in New York?", }, // Assistant response with tool call { id: "msg_2", role: "assistant", content: "Let me check the weather for you.", toolCalls: [ { id: "call_1", type: "function", function: { name: "get_weather", arguments: '{"location": "New York", "unit": "celsius"}', }, }, ], }, // Tool result { id: "result_1", role: "tool", content: '{"temperature": 22, "condition": "Partly Cloudy", "humidity": 65}', toolCallId: "call_1", }, // Assistant's final response using tool results { id: "msg_3", role: "assistant", content: "The weather in New York is partly cloudy with a temperature of 22°C and 65% humidity.", }, ] ``` ## Conclusion The message structure in AG-UI enables sophisticated conversational AI experiences while maintaining vendor neutrality. By standardizing how messages are represented, synchronized, and streamed, AG-UI provides a consistent way to implement interactive human-agent communication regardless of the underlying AI service. This system supports everything from simple text exchanges to complex tool-based workflows, all while optimizing for both real-time responsiveness and efficient data transfer. # Metadata Source: https://docs.ag-ui.com/concepts/metadata Attaching extra information to events, messages and tool calls Metadata is the sanctioned way to attach extra information to the protocol — token usage, a trace id, a finish reason, anything your application needs to carry alongside the conversation. Before it existed, producers hung undeclared properties off events and hoped consumers passed them through. That is why unknown properties reaching subscribers is removed in 1.0: metadata replaces it with something declared and typed that consumers are required to carry. ## Where it lives Four places, all optional: | Carries `metadata` | Notes | | - | - | | Every event | Declared once on the base event, so all event types have it | | Every message | All seven roles: developer, system, assistant, user, tool, activity, reasoning | | Every tool call | A tool call is not a message — see [Tool calls](#tool-calls) | | Every resume entry | A request field, so nothing merges into it — see [Resume entries](#resume-entries) | ## Shape The object is **open by key**. Any JSON value is allowed under a key, including `null`. There is no schema for what you put in it. ```typescript theme={null} { type: EventType.TEXT_MESSAGE_END, messageId: "msg_123", metadata: { "ag-ui": { usage: { input: 1200, output: 340 } }, finishReason: "stop", traceId: "abc-123", retries: 0, labels: ["experimental"] } } ``` The object itself is either **absent or an object — never `null`**. An empty object is valid and means "nothing to say", which is the same as omitting it. > [!NOTE] > > Producers never emit `"metadata": null`: an optional field with no value is > omitted from the JSON entirely, in every official SDK. The TypeScript client > enforces this and rejects a `null` metadata object — unlike some older > optional fields, metadata has no legacy producers to tolerate. Note the > asymmetry: a `null` **value under a key** is meaningful data and is always > preserved. Only a `null` standing in for the whole object is invalid. ## The reserved key The `ag-ui` key is reserved for AG-UI's own use. Every other key is yours. Nothing rejects a write to `ag-ui` at runtime — enforcing it would contradict the open-by-key rule — but treat it as off limits, since AG-UI may put its own values there in future versions. ## Merging into messages A message is assembled from a sequence of events, and the interesting values are only known at the end: a provider does not know its token usage until it has finished generating. So consumers **merge each event's metadata into the message that event builds**, as the sequence arrives. This describes clients that assemble messages from the stream, such as the TypeScript client. The .NET client does not assemble AG-UI messages — see the SDK notes below. The rule is last write wins, key by key: ```typescript theme={null} // TEXT_MESSAGE_START metadata: { source: "openai", stage: "start" } // TEXT_MESSAGE_CONTENT metadata: { stage: "content" } // TEXT_MESSAGE_END metadata: { stage: "end", usage: { output: 340 } } message.metadata // { source: "openai", stage: "end", usage: { output: 340 } } ``` `source` survived because nothing later set it. `stage` ended on the last value written. `usage` arrived only at the end, which is the whole point. ### Values are replaced, never blended Merging never recurses. A key holding an array or an object is replaced whole: ```typescript theme={null} // earlier: { tags: ["a", "b", "c"] } // later: { tags: ["z"] } // result: { tags: ["z"] } not ["a", "b", "c", "z"] ``` This holds under `ag-ui` too. If you need to add to a nested structure, send the complete new value. ### What does not merge Metadata on an event that does not build a message stays on that event and never reaches one: * `RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR` * `STEP_STARTED`, `STEP_FINISHED` * `STATE_SNAPSHOT`, `STATE_DELTA` * `RAW`, `CUSTOM` * `REASONING_START`, `REASONING_END`, `REASONING_ENCRYPTED_VALUE` `MESSAGES_SNAPSHOT` is a special case: the messages inside it carry their own metadata and arrive with it already attached, so the event's own metadata is not merged into any of them. > [!TIP] > > Putting run-level totals on `RUN_FINISHED` is fine and often right — just read > them from the event rather than expecting them on a message. If you want them > on a specific message, send them on that message's `*_END` event. ## Tool calls Tool call events — `TOOL_CALL_START`, `TOOL_CALL_ARGS`, `TOOL_CALL_END` — merge into the **tool call**, not into the assistant message that owns it. ```typescript theme={null} assistantMessage.toolCalls[0].metadata // { provider: "anthropic", latencyMs: 84 } ``` The reason is that several tool calls can share one parent assistant message. Folding all of their metadata into that parent would make the result depend on the order the calls happened to interleave — and stream transforms are allowed to change that order. Giving each tool call its own metadata removes the shared destination, so the outcome is the same however the stream is processed. `TOOL_CALL_RESULT` is different: it creates a tool message, so its metadata merges into that message like any other message-building event. ## Resume entries Each entry in `RunAgentInput.resume` — the per-interrupt response a client sends back after a run finished with an interrupt outcome — carries its own metadata. It holds envelope data about the response, such as a signature proving the human decision was not tampered with, or routing keys; the answer the agent asked for belongs in `payload`. A resume entry is a request field, not something assembled from a stream, so there are **no merge semantics** — nothing accumulates into it. See [Interrupts](/concepts/interrupts#resuming-a-run) for the full resume contract. ## Transports Over JSON — which is what SSE carries, and what every SDK reads and writes — metadata is a plain object, and every value shape round-trips exactly: nulls, arrays, nested objects, strings and numbers. The binary protobuf format carries it as a `google.protobuf.Struct`, whose value type has a real null case, so a `null` under a key is preserved rather than approximated and an absent object stays distinguishable from an empty one. Two caveats apply there, and neither is specific to metadata: * **Not every event has a protobuf representation.** The wire format covers a subset of the event types — the tool result, activity, reasoning and deprecated thinking events have no protobuf message, so they cannot cross that transport at all, with or without metadata. The .NET encoder rejects them outright; the TypeScript encoder produces an empty payload that decoding then rejects. Either way the event does not arrive, so do not rely on the binary transport for those events. * **Numbers are IEEE-754 doubles.** `google.protobuf.Value` models every number as a double, so integers beyond 2^53 lose precision on the round trip. This is a property of the format and applies to `state` and every other dynamic payload equally. Byte-for-byte output is not guaranteed between encoders: `Struct` is a `map`, and protobuf map entry ordering is not canonical. What is guaranteed is that both sides decode to the same value. In practice the TypeScript and .NET encoders do emit identical bytes for every fixture in the cross-language suite, which is asserted there, but do not depend on it. ## SDK reference > [!NOTE] > > Metadata is open by key in every SDK, so the type is deliberately permissive. > It is your responsibility to keep the contents JSON-serializable — a function > or a `bigint` will be accepted at the type level and then fail when encoded. **TypeScript** — `metadata?: Record` on events, messages, tool calls and resume entries. `mergeMetadata(existing, incoming)` is exported from `@ag-ui/core` if you are assembling messages yourself, along with `AGUI_METADATA_KEY` for the reserved key. **Python** — `metadata: Optional[Dict[str, Any]]`. The base model omits any unset optional field on every serialization path, so an absent object is omitted rather than emitted as `null` — no `exclude_none=True` needed. `Metadata` and `AGUI_METADATA_KEY` are exported from `ag_ui.core`. **.NET** — `JsonElement? Metadata` on `BaseEvent`, `AGUIMessage`, `AGUIToolCall` and `AGUIResume`, with `AGUIMetadata.ReservedKey` for the reserved key. It is a **wire-level field**. It round-trips faithfully through JSON and protobuf, so a server reading `RunAgentInput` or a client decoding the stream sees it intact, and `AGUIMessage.Metadata` carries it on messages inside a `MESSAGES_SNAPSHOT`. It is deliberately not surfaced on `Microsoft.Extensions.AI`'s `ChatMessage`, matching how `encryptedValue` and `AGUIToolMessage.Error` already behave. If you consume the stream through `AGUIChatClient`, note what that means in practice: `EventStreamConverter` emits no `ChatResponseUpdate` for `TEXT_MESSAGE_START`, `TEXT_MESSAGE_END`, `TOOL_CALL_START` or `TOOL_CALL_ARGS`, so metadata on those events — including usage placed on `TEXT_MESSAGE_END` as recommended above — does not reach a `RawRepresentation` either. Read it from the raw event stream rather than from the high-level chat client. ## Compaction `compactEvents` in the TypeScript client squashes a run of streaming events into fewer events for storage or replay, deliberately reordering them so each stream's events stay together. Metadata adds no new order sensitivity of its own: every merge destination is unique — each message has its own, and each tool call carries its own rather than folding into a parent it may share — so two events that merge into the same target are never reordered relative to each other. Compaction's reordering is not semantics-preserving in general, though, and that predates metadata. An event that interrupts a stream is emitted after it, so a `MESSAGES_SNAPSHOT` arriving mid-message is replayed after that message's own events and overwrites what they produced — the appended content just as much as the merged metadata. If you rely on exact replay equivalence, avoid interleaving snapshots with an open stream. # Middleware Source: https://docs.ag-ui.com/concepts/middleware Transform and intercept events in AG-UI agents # Middleware Middleware in AG-UI provides a powerful way to transform, filter, and augment the event streams that flow through agents. It enables you to add cross-cutting concerns like logging, authentication, rate limiting, and event filtering without modifying the core agent logic. Examples below assume the relevant RxJS operators/utilities (`map`, `tap`, `catchError`, `switchMap`, `timer`, etc.) are imported. ## What is Middleware? Middleware sits between the agent execution and the event consumer, allowing you to: 1. **Transform events** – Modify or enhance events as they flow through the pipeline 2. **Filter events** – Selectively allow or block certain events 3. **Add metadata** – Inject additional context or tracking information 4. **Handle errors** – Implement custom error recovery strategies 5. **Monitor execution** – Add logging, metrics, or debugging capabilities ## How Middleware Works Middleware forms a chain where each middleware wraps the next, creating layers of functionality. When an agent runs, the event stream flows through each middleware in sequence. ```typescript theme={null} import { AbstractAgent } from "@ag-ui/client" const agent = new MyAgent() // Middleware chain: logging -> auth -> filter -> agent agent.use(loggingMiddleware, authMiddleware, filterMiddleware) // When agent runs, events flow through all middleware await agent.runAgent() ``` Middleware added with `agent.use(...)` is applied in `runAgent()`. `connectAgent()` currently calls `connect()` directly and does not run middleware. ## Function-Based Middleware For simple transformations, you can use function-based middleware. This is the most concise way to add middleware: ```typescript theme={null} import { MiddlewareFunction } from "@ag-ui/client" import { EventType } from "@ag-ui/core" const prefixMiddleware: MiddlewareFunction = (input, next) => { return next.run(input).pipe( map(event => { if ( event.type === EventType.TEXT_MESSAGE_CHUNK || event.type === EventType.TEXT_MESSAGE_CONTENT ) { return { ...event, delta: `[AI]: ${event.delta}` } } return event }) ) } agent.use(prefixMiddleware) ``` ## Class-Based Middleware For more complex scenarios requiring state or configuration, use class-based middleware: ```typescript theme={null} import { Middleware } from "@ag-ui/client" import { Observable } from "rxjs" import { tap } from "rxjs/operators" class MetricsMiddleware extends Middleware { private eventCount = 0 constructor(private metricsService: MetricsService) { super() } run(input: RunAgentInput, next: AbstractAgent): Observable { const startTime = Date.now() return this.runNext(input, next).pipe( tap(event => { this.eventCount++ this.metricsService.recordEvent(event.type) }), finalize(() => { const duration = Date.now() - startTime this.metricsService.recordDuration(duration) this.metricsService.recordEventCount(this.eventCount) }) ) } } agent.use(new MetricsMiddleware(metricsService)) ``` If you are writing class middleware, prefer the helper methods: * `runNext(input, next)` normalizes chunk events into full `TEXT_MESSAGE_*`/`TOOL_CALL_*` sequences. * `runNextWithState(input, next)` also provides accumulated `messages` and `state` after each event. ## Built-in Middleware AG-UI provides several built-in middleware components for common use cases: ### FilterToolCallsMiddleware Filter tool calls based on allowed or disallowed lists: ```typescript theme={null} import { FilterToolCallsMiddleware } from "@ag-ui/client" // Only allow specific tools const allowedFilter = new FilterToolCallsMiddleware({ allowedToolCalls: ["search", "calculate"] }) // Or block specific tools const blockedFilter = new FilterToolCallsMiddleware({ disallowedToolCalls: ["delete", "modify"] }) agent.use(allowedFilter) ``` `FilterToolCallsMiddleware` filters emitted `TOOL_CALL_*` events. It does not block tool execution in the upstream model/runtime. ## Middleware Patterns Common patterns include logging, auth via `forwardedProps`, and rate limiting. See the [JS middleware reference](/sdk/js/client/middleware) for concrete implementations. ## Combining Middleware You can combine multiple middleware to create sophisticated processing pipelines: ```typescript theme={null} const logMiddleware: MiddlewareFunction = (input, next) => next.run(input) const metricsMiddleware = new MetricsMiddleware(metricsService) const filterMiddleware = new FilterToolCallsMiddleware({ allowedToolCalls: ["search"] }) agent.use(logMiddleware, metricsMiddleware, filterMiddleware) ``` ## Execution Order Middleware executes in the order it's added, with each middleware wrapping the next: 1. First middleware receives the original input 2. It can modify the input before passing to the next middleware 3. Each middleware processes events from the next in the chain 4. The final middleware calls the actual agent ```typescript theme={null} agent.use(middleware1, middleware2, middleware3) // Execution flow: // → middleware1 // → middleware2 // → middleware3 // → agent.run() // ← events flow back through middleware3 // ← events flow back through middleware2 // ← events flow back through middleware1 ``` ## Protocol Version on `RUN_STARTED` Each run's `RUN_STARTED.protocolVersion` names the version the run's stream speaks, not who relays it ([Versioning](/spec/1.0/basic/versioning#version-negotiation)). For middleware that means one rule, decided by who produced the run: * **Forwarding a run.** When the `RUN_STARTED` comes from the next agent in the chain, pass it through with its `protocolVersion` untouched. Don't add one, overwrite it, or remove it. If the downstream agent predates the field, the missing declaration is the truthful one. * **Synthesizing a run.** When the middleware answers the request itself and emits its own `RUN_STARTED` (no downstream agent runs), the middleware is the run's producer. Declare the version it was built against with the SDK's `PROTOCOL_VERSION` constant. Don't mirror the client's `RunAgentInput.protocolVersion` and don't omit the field. * **Translating a run.** A middleware that rewrites the stream into a different protocol version declares the version it emits. A synthesized run uses the input's `threadId` and `runId`, like any other producer: ```typescript theme={null} import { EventType, PROTOCOL_VERSION, type RunStartedEvent } from "@ag-ui/client" const runStarted: RunStartedEvent = { type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId, protocolVersion: PROTOCOL_VERSION, } ``` ## Best Practices 1. **Keep middleware focused** – Each middleware should have a single responsibility 2. **Handle errors gracefully** – Use RxJS error handling operators 3. **Avoid blocking operations** – Use async patterns for I/O operations 4. **Document side effects** – Clearly indicate if middleware modifies state 5. **Test middleware independently** – Write unit tests for each middleware 6. **Consider performance** – Be mindful of processing overhead in the event stream ## Advanced Use Cases ### Conditional Middleware Apply middleware based on runtime conditions: ```typescript theme={null} const conditionalMiddleware: MiddlewareFunction = (input, next) => { if (input.forwardedProps?.debug === true) { // Apply debug logging return next.run(input).pipe( tap(event => console.debug(event)) ) } return next.run(input) } ``` For event transformation and stream-control variants, see the [JS middleware reference](/sdk/js/client/middleware). ## Conclusion Middleware provides a flexible and powerful way to extend AG-UI agents without modifying their core logic. Whether you need simple event transformation or complex stateful processing, the middleware system offers the tools to build robust, maintainable agent applications. # Reasoning Source: https://docs.ag-ui.com/concepts/reasoning Support for LLM reasoning visibility and continuity in AG-UI # Reasoning AG-UI provides first-class support for LLM reasoning, enabling chain-of-thought visibility while maintaining privacy and state continuity across conversation turns. ## Overview Modern LLMs increasingly use chain-of-thought reasoning to improve response quality. AG-UI's reasoning support addresses three key challenges: * **Reasoning visibility**: Surface reasoning signals (e.g., summaries) to users without exposing raw chain-of-thought * **State continuity**: Maintain reasoning context across turns using encrypted reasoning items, even under `store:false` or zero data retention (ZDR) policies * **Privacy compliance**: Support enterprise privacy requirements while preserving reasoning capabilities > [!TIP] > > Unlike Activity messages, Reasoning messages are intended to represent the > agent's internal thought process and may be encrypted for privacy and are > meant to be sent back to the agent for further processing on subsequent turns. ## ReasoningMessage The `ReasoningMessage` type represents reasoning content in the message history: ```typescript theme={null} interface ReasoningMessage { id: string role: "reasoning" content: string // Reasoning content (visible to client) encryptedValue?: string // Optional encrypted reasoning for state continuity } ``` | Property | Type | Description | | - | - | - | | `id` | `string` | Unique identifier for the reasoning message | | `role` | `"reasoning"` | Message role discriminator | | `content` | `string` | Reasoning content visible to the client | | `encryptedValue` | `string?` | Encrypted chain-of-thought blob for state continuity | Key characteristics: * **Separate from assistant messages**: Reasoning is kept distinct from final responses to avoid polluting conversation history * **Streamable**: Content arrives via streaming events * **Optional encryption**: When `encryptedValue` is present, it represents encrypted chain-of-thought that the client stores and forwards opaquely ## Reasoning Events Reasoning events manage the lifecycle of reasoning messages. See [Events](/concepts/events#reasoning-events) for the complete event reference. ### Event Flow A typical reasoning flow follows this pattern: ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Reasoning begins Agent->>Client: ReasoningStart Note over Agent,Client: Stream visible reasoning Agent->>Client: ReasoningMessageStart Agent->>Client: ReasoningMessageContent (delta) Agent->>Client: ReasoningMessageContent (delta) Agent->>Client: ReasoningMessageEnd Note over Agent,Client: Attach encrypted chain-of-thought Agent->>Client: ReasoningEncryptedValue Note over Agent,Client: Reasoning completes Agent->>Client: ReasoningEnd ``` ### Event Types | Event | Purpose | | - | - | | `ReasoningStart` | Marks beginning of reasoning phase | | `ReasoningMessageStart` | Begins a streaming reasoning message | | `ReasoningMessageContent` | Delivers reasoning content chunks | | `ReasoningMessageEnd` | Completes a reasoning message | | `ReasoningMessageChunk` | Convenience event that auto-manages message lifecycle | | `ReasoningEnd` | Marks completion of reasoning | | `ReasoningEncryptedValue` | Attaches encrypted chain-of-thought to a message or tool call | ## Privacy and Compliance AG-UI reasoning is designed with privacy-first principles: ### Zero Data Retention (ZDR) For deployments requiring zero data retention: 1. **Encrypted reasoning values** can carry state across turns without storing decryptable content on the client 2. The client receives and forwards `encryptedValue` blobs opaquely via `ReasoningEncryptedValue` events 3. Only the agent (or authorized backend) can decrypt the reasoning content ### Visibility Control Agents control what reasoning is visible to users: * **Full visibility**: Stream the complete chain-of-thought via `ReasoningMessageContent` events * **Summary only**: Emit a condensed summary while attaching detailed reasoning as encrypted values * **Hidden**: Use only `ReasoningEncryptedValue` events with no visible streaming ### Compliance Considerations | Requirement | Solution | | - | - | | GDPR right to erasure | Encrypted content can be discarded without losing reasoning capability | | SOC 2 data handling | Reasoning content never stored in plaintext on client | | HIPAA minimum necessary | Only summaries exposed; detailed reasoning stays encrypted | | Audit logging | `ReasoningStart`/`ReasoningEnd` events provide audit trail without content exposure | ## Example Implementations ### Basic Reasoning Flow A simple implementation showing visible reasoning: ```typescript theme={null} // Agent emits reasoning start yield { type: "REASONING_START", messageId: "reasoning-001", } // Stream visible reasoning content yield { type: "REASONING_MESSAGE_START", messageId: "msg-123", role: "reasoning", } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-123", delta: "Let me ", } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-123", delta: "think through ", } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-123", delta: "this step ", } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-123", delta: "by step...", } yield { type: "REASONING_MESSAGE_END", messageId: "msg-123", } // End reasoning yield { type: "REASONING_END", messageId: "reasoning-001", } ``` ### Encrypted Content for State Continuity When maintaining reasoning state across turns without exposing content, use the `ReasoningEncryptedValue` event to attach encrypted chain-of-thought to messages or tool calls: ```typescript theme={null} // Agent emits reasoning start yield { type: "REASONING_START", messageId: "reasoning-002", } // Stream a visible summary for the user yield { type: "REASONING_MESSAGE_START", messageId: "msg-456", role: "reasoning", } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-456", delta: "Analyzing your request...", } yield { type: "REASONING_MESSAGE_END", messageId: "msg-456", } // Attach encrypted chain-of-thought to the reasoning message yield { type: "REASONING_ENCRYPTED_VALUE", subtype: "message", entityId: "msg-456", encryptedValue: "eyJhbGciOiJBMjU2R0NNIiwiZW5jIjoiQTI1NkdDTSJ9...", } yield { type: "REASONING_END", messageId: "reasoning-002", } // On subsequent turns, client sends back the message with encryptedValue // which the agent can decrypt to restore reasoning context ``` ### Attaching Encrypted Reasoning to Tool Calls You can also attach encrypted reasoning to tool calls to capture why the agent chose specific arguments or how it interpreted results: ```typescript theme={null} // Tool call with encrypted reasoning yield { type: "TOOL_CALL_START", toolCallId: "tool-123", toolCallName: "search_database", parentMessageId: "msg-789", } yield { type: "TOOL_CALL_ARGS", toolCallId: "tool-123", delta: '{"query": "user preferences"}', } yield { type: "TOOL_CALL_END", toolCallId: "tool-123", } // Attach encrypted reasoning explaining why this tool was called yield { type: "REASONING_ENCRYPTED_VALUE", subtype: "tool-call", entityId: "tool-123", encryptedValue: "encrypted-reasoning-about-tool-selection...", } ``` ### ZDR-Compliant Implementation For zero data retention scenarios: ```typescript theme={null} // Server-side: encrypt reasoning before sending const encryptedReasoning = await encrypt(detailedChainOfThought, secretKey) yield { type: "REASONING_START", messageId: "reasoning-003", } // Only emit a high-level summary to the client yield { type: "REASONING_MESSAGE_CHUNK", messageId: "summary-001", delta: "Processing your request securely...", } yield { type: "REASONING_MESSAGE_CHUNK", messageId: "summary-001", delta: "", // Empty delta closes the message } // Attach the encrypted chain-of-thought yield { type: "REASONING_ENCRYPTED_VALUE", subtype: "message", entityId: "summary-001", encryptedValue: encryptedReasoning, } yield { type: "REASONING_END", messageId: "reasoning-003", } // Client stores only: // - The encrypted blob (cannot decrypt) // - The summary text (no sensitive details) // Full reasoning is never persisted in plaintext ``` ### Using the Convenience Chunk Event The `ReasoningMessageChunk` event simplifies implementation by auto-managing message lifecycle: ```typescript theme={null} // First chunk with messageId starts the message automatically yield { type: "REASONING_MESSAGE_CHUNK", messageId: "msg-789", delta: "Analyzing the problem space...", } // Subsequent chunks continue the stream yield { type: "REASONING_MESSAGE_CHUNK", messageId: "msg-789", delta: " Considering multiple approaches...", } // Empty delta (or next non-reasoning event) closes automatically yield { type: "REASONING_MESSAGE_CHUNK", messageId: "msg-789", delta: "", } ``` ## Client Integration ### Handling Reasoning Events ```typescript theme={null} import { EventType, type BaseEvent } from "@ag-ui/core" function handleEvent(event: BaseEvent) { switch (event.type) { case EventType.REASONING_START: // Initialize reasoning UI (e.g., "thinking" indicator) console.log("Agent is reasoning...") break case EventType.REASONING_MESSAGE_CONTENT: // Append visible reasoning to UI appendReasoningText(event.messageId, event.delta) break case EventType.REASONING_ENCRYPTED_VALUE: // Store encrypted value for the referenced entity if (event.subtype === "message") { storeMessageEncryptedValue(event.entityId, event.encryptedValue) } else if (event.subtype === "tool-call") { storeToolCallEncryptedValue(event.entityId, event.encryptedValue) } break case EventType.REASONING_END: // Finalize reasoning UI console.log("Reasoning complete") break } } ``` ### Passing Encrypted Reasoning Back When making subsequent requests, include stored encrypted values: ```typescript theme={null} const response = await agent.run({ threadId: "thread-123", messages: [ ...previousMessages, { id: "reasoning-002", role: "reasoning", content: "Analyzing your request...", // Visible summary encryptedValue: storedEncryptedBlob, // Opaque to client }, { id: "user-msg-001", role: "user", content: "Follow up question...", }, ], }) ``` ## Migration from Thinking Events > [!WARNING] > > The `THINKING_*` events are deprecated and will be removed in version 1.0.0. > New implementations should use `REASONING_*` events. ### Deprecated Events The following events are deprecated: | Deprecated Event | 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` | ### Migration Steps 1. **Update event types**: Replace all `THINKING_*` event types with their `REASONING_*` equivalents 2. **Update message types**: Use `ReasoningMessage` with `role: "reasoning"` instead of any thinking-specific message types 3. **Add encrypted value support**: Consider using `ReasoningEncryptedValue` events for improved privacy compliance 4. **Test thoroughly**: Ensure existing functionality works with the new event types ### Example Migration Before (deprecated): ```typescript theme={null} // ❌ Deprecated - do not use yield { type: "THINKING_START", messageId: "think-001" } yield { type: "THINKING_TEXT_MESSAGE_START", messageId: "msg-001" } yield { type: "THINKING_TEXT_MESSAGE_CONTENT", messageId: "msg-001", delta: "..." } yield { type: "THINKING_TEXT_MESSAGE_END", messageId: "msg-001" } yield { type: "THINKING_END", messageId: "think-001" } ``` After (current): ```typescript theme={null} // ✅ Current implementation yield { type: "REASONING_START", messageId: "reasoning-001" } yield { type: "REASONING_MESSAGE_START", messageId: "msg-001", role: "assistant" } yield { type: "REASONING_MESSAGE_CONTENT", messageId: "msg-001", delta: "..." } yield { type: "REASONING_MESSAGE_END", messageId: "msg-001" } yield { type: "REASONING_END", messageId: "reasoning-001" } ``` ## Best Practices 1. **Always pair start/end events**: Every `ReasoningStart` should have a corresponding `ReasoningEnd` 2. **Use encrypted values for sensitive reasoning**: When chain-of-thought contains sensitive information, use `ReasoningEncryptedValue` to attach encrypted content to messages or tool calls 3. **Provide user feedback**: Even with encrypted reasoning, emit visible summaries so users know the agent is working 4. **Handle missing events gracefully**: Clients should be resilient to incomplete event streams 5. **Consider bandwidth**: For very long reasoning chains, consider emitting only summaries to reduce data transfer ## Related Documentation * [Events](/concepts/events#reasoning-events) - Complete event type reference * [Messages](/concepts/messages#reasoning-messages) - Message type documentation * [Serialization](/concepts/serialization) - State continuity and lineage # Serialization Source: https://docs.ag-ui.com/concepts/serialization Serialize event streams for history restore, branching, and compaction in AG-UI # Serialization Serialization in AG-UI provides a standard way to persist and restore the event stream that drives an agent–UI session. With a serialized stream you can: * Restore chat history and UI state after reloads or reconnects * Attach to running agents and continue receiving events * Create branches (time travel) from any prior run * Compact stored history to reduce size without losing meaning This page explains the model, the updated event fields, and practical usage patterns with examples. ## Core Concepts * Stream serialization – Convert the full event history to and from a portable representation (e.g., JSON) for storage in databases, files, or logs. * Event compaction – Reduce verbose streams to snapshots while preserving semantics (e.g., merge content chunks, collapse deltas into snapshots). * Run lineage – Track branches of conversation using a `parentRunId`, forming a git‑like append‑only log that enables time travel and alternative paths. ## Updated Event Fields The `RunStarted` event includes additional optional fields: ```ts theme={null} type RunStartedEvent = BaseEvent & { type: EventType.RUN_STARTED threadId: string runId: string /** Parent for branching/time travel within the same thread */ parentRunId?: string /** Exact agent input for this run (may omit messages already in history) */ input?: AgentInput } ``` These fields enable lineage tracking and let implementations record precisely what was passed to the agent, independent of previously recorded messages. ## Event Compaction Compaction reduces noise in an event stream while keeping the same observable outcome. A typical implementation provides a utility: ```ts theme={null} declare function compactEvents(events: BaseEvent[]): BaseEvent[] ``` Common compaction rules include: * Message streams – Combine `TEXT_MESSAGE_*` sequences into a single message snapshot; concatenate adjacent `TEXT_MESSAGE_CONTENT` for the same message. * Tool calls – Collapse tool call start/content/end into a compact record. * State – Merge consecutive `STATE_DELTA` events into a single final `STATE_SNAPSHOT` and discard superseded updates. * Run input normalization – Remove from `RunStarted.input.messages` any messages already present earlier in the stream. ## Branching and Time Travel Setting `parentRunId` on a `RunStarted` event creates a git‑like lineage. The stream becomes an immutable append‑only log where each run can branch from any previous run. ```mermaid theme={null} gitGraph commit id: "run1" commit id: "run2" branch alternative checkout alternative commit id: "run3 (parent run2)" commit id: "run4" checkout main commit id: "run5 (parent run2)" commit id: "run6" ``` Benefits: * Multiple branches in the same serialized log * Immutable history (append‑only) * Deterministic time travel to any point ## Examples ### Basic Serialization ```ts theme={null} // Serialize event stream const events: BaseEvent[] = [...]; const serialized = JSON.stringify(events); await storage.save(threadId, serialized); // Restore and compact later const restored = JSON.parse(await storage.load(threadId)); const compacted = compactEvents(restored); ``` ### Event Compaction Before: ```ts theme={null} [ { type: "TEXT_MESSAGE_START", messageId: "msg1", role: "user" }, { type: "TEXT_MESSAGE_CONTENT", messageId: "msg1", delta: "Hello " }, { type: "TEXT_MESSAGE_CONTENT", messageId: "msg1", delta: "world" }, { type: "TEXT_MESSAGE_END", messageId: "msg1" }, { type: "STATE_DELTA", patch: { op: "add", path: "/foo", value: 1 } }, { type: "STATE_DELTA", patch: { op: "replace", path: "/foo", value: 2 } }, ] ``` After: ```ts theme={null} [ { type: "MESSAGES_SNAPSHOT", messages: [{ id: "msg1", role: "user", content: "Hello world" }], }, { type: "STATE_SNAPSHOT", state: { foo: 2 }, }, ] ``` ### Branching With `parentRunId` ```ts theme={null} // Original run { type: "RUN_STARTED", threadId: "thread1", runId: "run1", input: { messages: ["Tell me about Paris"] }, } // Branch from run1 { type: "RUN_STARTED", threadId: "thread1", runId: "run2", parentRunId: "run1", input: { messages: ["Actually, tell me about London instead"] }, } ``` ### Normalized Input ```ts theme={null} // First run includes full message { type: "RUN_STARTED", runId: "run1", input: { messages: [{ id: "msg1", role: "user", content: "Hello" }] }, } // Second run omits already‑present message { type: "RUN_STARTED", runId: "run2", input: { messages: [{ id: "msg2", role: "user", content: "How are you?" }] }, // msg1 omitted; it already exists in history } ``` ## Metadata Events and messages carry an optional [`metadata`](/concepts/metadata) object, and it is part of what you are serializing — persist it alongside everything else or a replayed stream will be missing token usage, trace ids and anything else producers attached. Two things to know when persisting: * **Compaction folds it.** Collapsing deltas merges their metadata, last write wins. See [Event Compaction](#event-compaction) and the [compaction reference](/sdk/js/client/compaction) for the ordering caveats. * **Binary is narrower than JSON.** Over protobuf, metadata is a `google.protobuf.Struct`, so nulls, arrays and nested objects survive — but numbers are IEEE-754 doubles, so integers beyond 2^53 lose precision, and several event types have no protobuf representation at all. JSON round-trips everything exactly. Prefer JSON for archival storage. ## Implementation Notes * Provide SDK helpers for compaction and (de)serialization. * Store streams append‑only; prefer incremental writes when possible. * Consider compression when persisting long histories. * Add indexes by `threadId`, `runId`, and timestamps for fast retrieval. ## See Also * Concepts: [Events](/concepts/events), [Metadata](/concepts/metadata), [State Management](/concepts/state) * SDKs: TypeScript encoder and core event types # State Management Source: https://docs.ag-ui.com/concepts/state Understanding state synchronization between agents and frontends in AG-UI # State Management State management is a core feature of the AG-UI protocol that enables real-time synchronization between agents and frontend applications. By providing efficient mechanisms for sharing and updating state, AG-UI creates a foundation for collaborative experiences where both AI agents and human users can work together seamlessly. ## Shared State Architecture In AG-UI, state is a structured data object that: 1. Persists across interactions with an agent 2. Can be accessed by both the agent and the frontend 3. Updates in real-time as the interaction progresses 4. Provides context for decision-making on both sides This shared state architecture creates a bidirectional communication channel where: * Agents can access the application's current state to make informed decisions * Frontends can observe and react to changes in the agent's internal state * Both sides can modify the state, creating a collaborative workflow ## State Synchronization Methods AG-UI provides two complementary methods for state synchronization: ### State Snapshots The `STATE_SNAPSHOT` event delivers a complete representation of an agent's current state: ```typescript theme={null} interface StateSnapshotEvent { type: EventType.STATE_SNAPSHOT snapshot: any // Complete state object } ``` Snapshots are typically used: * At the beginning of an interaction to establish the initial state * After connection interruptions to ensure synchronization * When major state changes occur that require a complete refresh * To establish a new baseline for future delta updates When a frontend receives a `STATE_SNAPSHOT` event, it should replace its existing state model entirely with the contents of the snapshot. ### State Deltas The `STATE_DELTA` event delivers incremental updates to the state using JSON Patch format (RFC 6902): ```typescript theme={null} interface StateDeltaEvent { type: EventType.STATE_DELTA delta: JsonPatchOperation[] // Array of JSON Patch operations } ``` Deltas are bandwidth-efficient, sending only what has changed rather than the entire state. This approach is particularly valuable for: * Frequent small updates during streaming interactions * Large state objects where most properties remain unchanged * High-frequency updates that would be inefficient to send as full snapshots ## JSON Patch Format AG-UI uses the JSON Patch format (RFC 6902) for state deltas, which defines a standardized way to express changes to a JSON document: ```typescript theme={null} interface JsonPatchOperation { op: "add" | "remove" | "replace" | "move" | "copy" | "test" path: string // JSON Pointer (RFC 6901) to the target location value?: any // The value to apply (for add, replace) from?: string // Source path (for move, copy) } ``` Common operations include: 1. **add**: Adds a value to an object or array ```json theme={null} { "op": "add", "path": "/user/preferences", "value": { "theme": "dark" } } ``` 2. **replace**: Replaces a value ```json theme={null} { "op": "replace", "path": "/conversation_state", "value": "paused" } ``` 3. **remove**: Removes a value ```json theme={null} { "op": "remove", "path": "/temporary_data" } ``` 4. **move**: Moves a value from one location to another ```json theme={null} { "op": "move", "path": "/completed_items", "from": "/pending_items/0" } ``` Frontends should apply these patches in sequence to maintain an accurate state representation. If inconsistencies are detected after applying patches, the frontend can request a fresh `STATE_SNAPSHOT`. ## State Processing in AG-UI In the AG-UI implementation, state deltas are applied using the `fast-json-patch` library: ```typescript theme={null} case EventType.STATE_DELTA: { const { delta } = event as StateDeltaEvent; try { // Apply the JSON Patch operations to the current state without mutating the original const result = applyPatch(state, delta, true, false); state = result.newDocument; return emitUpdate({ state }); } catch (error: unknown) { console.warn( `Failed to apply state patch:\n` + `Current state: ${JSON.stringify(state, null, 2)}\n` + `Patch operations: ${JSON.stringify(delta, null, 2)}\n` + `Error: ${errorMessage}` ); return emitNoUpdate(); } } ``` This implementation ensures that: * Patches are applied atomically (all or none) * The original state is not mutated during the application process * Errors are caught and handled gracefully ## Human-in-the-Loop Collaboration The shared state system is fundamental to human-in-the-loop workflows in AG-UI. It enables: 1. **Real-time visibility**: Users can observe the agent's thought process and current status 2. **Contextual awareness**: The agent can access user actions, preferences, and application state 3. **Collaborative decision-making**: Both human and AI can contribute to the evolving state 4. **Feedback loops**: Humans can correct or guide the agent by modifying state properties For example, an agent might update its state with a proposed action: ```json theme={null} { "proposal": { "action": "send_email", "recipient": "client@example.com", "content": "Draft email content..." } } ``` The frontend can display this proposal to the user, who can then approve, reject, or modify it before execution. ## CopilotKit Implementation [CopilotKit](https://docs.copilotkit.ai), a popular framework for building AI assistants, leverages AG-UI's state management system through its "shared state" feature. This implementation enables bidirectional state synchronization between agents (particularly LangGraph agents) and frontend applications. CopilotKit's shared state system is implemented through: ```jsx theme={null} // In the frontend React application const { state: agentState, setState: setAgentState } = useCoAgent({ name: "agent", initialState: { someProperty: "initialValue" }, }) ``` This hook creates a real-time connection to the agent's state, allowing: 1. Reading the agent's current state in the frontend 2. Updating the agent's state from the frontend 3. Rendering UI components based on the agent's state On the backend, LangGraph agents can emit state updates using: ```python theme={null} # In the LangGraph agent async def tool_node(self, state: ResearchState, config: RunnableConfig): # Update state with new information tool_state = { "title": new_state.get("title", ""), "outline": new_state.get("outline", {}), "sections": new_state.get("sections", []), # Other state properties... } # Emit updated state to frontend await copilotkit_emit_state(config, tool_state) return tool_state ``` These state updates are transmitted using AG-UI's state snapshot and delta mechanisms, creating a seamless shared context between agent and frontend. ## Best Practices When implementing state management in AG-UI: 1. **Use snapshots judiciously**: Full snapshots should be sent only when necessary to establish a baseline. 2. **Prefer deltas for incremental changes**: Small state updates should use deltas to minimize data transfer. 3. **Structure state thoughtfully**: Design state objects to support partial updates and minimize patch complexity. 4. **Handle state conflicts**: Implement strategies for resolving conflicting updates from agent and frontend. 5. **Include error recovery**: Provide mechanisms to resynchronize state if inconsistencies are detected. 6. **Consider security implications**: Avoid storing sensitive information in shared state. ## Conclusion AG-UI's state management system provides a powerful foundation for building collaborative applications where humans and AI agents work together. By efficiently synchronizing state between frontend and backend through snapshots and JSON Patch deltas, AG-UI enables sophisticated human-in-the-loop workflows that combine the strengths of both human intuition and AI capabilities. The implementation in frameworks like CopilotKit demonstrates how this shared state approach can create collaborative experiences that are more effective than either fully autonomous systems or traditional user interfaces. # Subagents Source: https://docs.ag-ui.com/concepts/subagents Attributing a run's output to the subagent that produced it # Subagents Many agent frameworks let an agent delegate work to child agents — a supervisor dispatching research tasks, an agents-as-tools pattern where a tool call *is* a nested agent, or a planner farming out subtasks in parallel. To a frontend, all of that arrives as one event stream. Without extra information there is no way to tell which subagent produced a given message, so three concurrent researchers render as one undifferentiated wall of text. AG-UI's subagent support solves exactly that problem and nothing more: it **attributes** each event to the subagent that produced it, and reports when subagents start and stop. It does not orchestrate, schedule, or define subagents — that stays entirely with the framework. ## `subagentRunId` identifies an invocation, not a definition This is the single most important thing to understand, and the easiest thing to get wrong. `subagentRunId` is an **opaque handle for one invocation** of a subagent. Run the same subagent twice and you get two different values. It is not a stable identifier for a reusable subagent definition, and it is not a name. The symmetry with the top-level run makes the distinction clearer: | Top level | Subagent | | - | - | | `agentId` — a configured, reusable agent | the subagent's `name` — a reusable subagent type | | `runId` — one invocation's lifecycle | `subagentRunId` — one nested invocation's lifecycle | So: * **Do** key transient UI state — a collapsible group, a spinner, a progress row — by `subagentRunId`. * **Do not** persist anything by `subagentRunId` expecting it to be meaningful on a later run, and do not treat two invocations of one subagent as sharing a value. Use `name` when you mean "which kind of subagent this is". * **One exception**: a subagent that finished with `outcome: { type: "suspended" }` **may** reuse its id on a resuming run. Producers that can correlate the resumed work with the suspended invocation should do so, and a client must treat that later `SubagentStarted` as a continuation (waiting → running), never as a duplicate. A producer that cannot correlate mints a new id — the client's waiting state then resolves through the interrupt ids it answered, not through id reuse (see [Suspension](#suspension)). > [!NOTE] > > This field was called `subagentId` in prerelease builds. It was renamed because > the old name implied a reusable definition. If you are on a `canary` build that > still uses `subagentId`, the value has the same meaning — only the name changed. ## Lifecycle events Three events bracket a subagent's activity. ```mermaid theme={null} sequenceDiagram participant Agent participant Client Note over Agent,Client: Subagent begins Agent->>Client: SubagentStarted Note over Agent,Client: Attributed output Agent->>Client: TextMessageStart / Content / End Agent->>Client: ToolCallStart / Args / End Note over Agent,Client: Subagent concludes alt Success Agent->>Client: SubagentFinished else Failure Agent->>Client: SubagentError end ``` ### SubagentStarted Announces a new subagent invocation and gives it a name a UI can display. | Property | Description | | - | - | | `subagentRunId` | Opaque id for this invocation. Required | | `name` | The subagent's declared type or name, for display. Required | | `description` | Optional human-readable description | | `parentSubagentRunId` | Optional — the enclosing subagent, when subagents nest | | `parentToolCallId` | Optional — the tool call that spawned this subagent | | `parentMessageId` | Optional — the message that held that tool call | `parentToolCallId` and `parentMessageId` exist for the agents-as-tools pattern. They let a client correlate a subagent to the call that created it — to render the subagent's output inside the tool-call card, for instance — without having to inspect `rawEvent`. ### SubagentFinished Closes a subagent invocation's stream segment for this run. | Property | Description | | - | - | | `subagentRunId` | Matches the id from `SubagentStarted`. Required | | `result` | Optional completion payload, mirroring `RunFinished.result` | | `outcome` | Optional typed outcome, mirroring `RunFinished.outcome`: `{ type: "success" }` or `{ type: "suspended", interruptIds?: [...] }`. Omitted means success | #### Suspension A subagent can pause mid-task waiting for outside input — a human approval raised inside it, say. The run then ends with an interrupt outcome (see [Interrupts](/concepts/interrupts)), and because every started subagent closes before `RunFinished`, the paused subagent still emits `SubagentFinished` — but with `outcome: { type: "suspended" }`, so a UI can render "waiting" instead of "done". `interruptIds` names the run-level interrupts this subagent directly owns (each such `Interrupt` also carries `subagentRunId` back-reference); it may be empty or omitted for an ancestor suspended because a *descendant* interrupted. Suspension is the one case where a `subagentRunId` may deliberately span runs — with different obligations on each side: * **Clients must accept** a later run's `SubagentStarted` that reuses a suspended invocation's id, and treat it as a *continuation* — transition the existing group from waiting back to running, never render a duplicate. * **Producers should reuse the id when they can** correlate the resumed work with the suspended invocation. The LangGraph integration does: its ids derive from checkpoint task identity, which survives the pause. Reuse is what lets a client continue the group seamlessly. * **Producers that cannot correlate mint a new id**, and that is valid. The client's waiting state does not dangle: the suspended outcome's `interruptIds` and the resume entries the client itself sent identify which approvals were answered, so the waiting badge resolves on that basis even if no continuation arrives under the old id. ### SubagentError Marks a subagent invocation as failed. | Property | Description | | - | - | | `subagentRunId` | Matches the id from `SubagentStarted`. Required | | `message` | Human-readable error message. Required | | `code` | Optional error code | ## Attribution Beyond the lifecycle events, most events can carry an optional `subagentRunId` saying who produced them. An event with no `subagentRunId` belongs to the parent agent. Attribution is additive: a stream that never sets the field behaves exactly as it did before subagents existed. Attribution also stands on its own. A producer may tag events without ever emitting `SubagentStarted` / `SubagentFinished` — enough for a UI to group output by producer, without committing to reporting lifecycle. Clients must accept an identifier they have never seen announced; see [Rules clients enforce](#rules-clients-enforce). **Events that can carry attribution:** the text message family, the tool call family, activity events, the reasoning family, step events, state events, and `Raw`/`Custom`. **Events that cannot:** `RunStarted`, `RunFinished`, `RunError` — these describe the run as a whole — and `MessagesSnapshot`, which carries attribution per-message instead, since one snapshot mixes messages from several producers. ### Attribution is provenance, not ownership This distinction matters most on state events, so it is worth stating plainly. `StateSnapshot` and `StateDelta` are attributable. Attribution on them records **which subagent produced the update** — it does not mean the subagent has state of its own. AG-UI state is run-scoped: there is one state document for the run, and an attributed snapshot or delta is still applied to that one document. The tag is provenance you can surface ("the researcher updated the shared scratchpad"), not a separate scope. That is exactly the meaning attribution carries on the other standalone events. Nobody reads an attributed `Custom` event as the subagent having private custom events, and state is no different. > [!WARNING] > > There is no such thing as per-subagent state. If you attribute a snapshot > expecting the parent's state to be left alone, it will not be — the snapshot > replaces the run's state as any snapshot does. Use a distinct key inside the > run state if you need to keep subagents' data apart. Note that a producer is never *obliged* to attribute state. Some do not: the LangGraph integration, for instance, does not emit state while a subagent is active, because its state is one shared document and a mid-delegation snapshot would carry a partial view. That is a reasonable producer-side choice, not a protocol requirement. ### Attribution transfers to messages When an attributed event creates a message, the `subagentRunId` transfers onto that message. This is what lets a rendering layer group a conversation by subagent without replaying the event stream — the messages themselves carry their origin, and they keep it across turns and snapshots. ### Tool results carry their own attribution `ToolCallResult` is attributed independently of the call it answers, and that is intentional rather than an oversight: the party that *executes* a tool call can differ from the subagent that *requested* it. A frontend-executed tool, or a supervisor running a call on a subagent's behalf, both produce a result whose owner is not the caller. Inheriting the caller's attribution would misreport those cases, so each result states its own. ## Nesting and concurrency Subagents nest. `parentSubagentRunId` on `SubagentStarted` links a child to its enclosing subagent; a subagent with no parent link belongs directly to the run. Subagents also run **concurrently**, and this is the case that separates a working implementation from a plausible one. When three subagents stream at once, their events interleave, and attribution is the only thing that disambiguates them. In particular: * Two subagents may have open text messages simultaneously. A client must track each independently rather than assuming one open message at a time. * A subagent's `SubagentFinished` closes only that subagent's own open streams, never a sibling's or the parent's. * A parent may finish before its child. `parentSubagentRunId` may therefore name a subagent that has already finished, which is valid. ### Concurrency and the chunk shorthand The `TextMessageChunk` / `ToolCallChunk` / `ReasoningMessageChunk` events are a shorthand: a client synthesizes the START/CONTENT/END boundaries, and a chunk that omits its id means "the same as the previous one". Under concurrency, "previous" is only meaningful **per subagent** — so the shorthand resolves it within the sending subagent's own stream, not across the run. The practical consequence is one rule for producers: a chunk that carries neither an id nor a `subagentRunId` is resolved to the parent's open stream of that kind if there is one — untagged means the parent — and otherwise to the sole open stream of that kind. When several subagents' streams could all claim it, there is nothing to resolve it against, and the client rejects it rather than guessing. **When streaming concurrently, attribute every chunk**, or repeat the id. ## Rules clients enforce Every rule below is *conditional on the events being present*. Attribution alone is a complete, valid use of subagent support, so nothing here requires a stream to send lifecycle events at all. * `SubagentFinished` and `SubagentError` name a subagent that is currently active, and a subagent is not started twice within a run. The lifecycle events carry their schema-required fields (`subagentRunId` on all three, `name` on `SubagentStarted`, `message` on `SubagentError`) — clients enforce this even for in-process producers that bypass wire-level schema validation. * Continuation and close events agree with the owner their entity was created under. A text message opened by one subagent cannot be continued by another. * Ownership is also established by replayed history: a message in a `MessagesSnapshot` (and each tool call it carries) is owned by the `subagentRunId` it carries — absent means the parent — and a later event reopening that id under a different owner is rejected, exactly as a conflicting second opener is. * A tool call belongs to the assistant message its `parentMessageId` names. `ToolCall` itself carries no attribution field, so a `ToolCallStart` whose explicit `subagentRunId` disagrees with that message's owner cannot be represented faithfully and is rejected; an untagged tool call inherits the parent message's owner. * Steps are scoped to the agent that opened them: a subagent cannot close the parent's step, or a sibling's. * Every started subagent is closed before `RunFinished`. Several things are deliberately **not** enforced, because the protocol does not require them: * **A `subagentRunId` used for attribution need not have been started.** Attribution without lifecycle events is a supported mode, so a client must not treat an unannounced identifier as an error. UIs should group by whatever ids they see and fall back to the id when they have no `name`. * `parentSubagentRunId` need only name a subagent that has been *started*, not one still active — a parent legitimately finishes before its child. * Events attributed to an already-finished subagent are accepted. Continuation events carry the tag of the subagent they belong to even after it finishes. * Closure is required before `RunFinished` only, not before `RunError`. An unclosed subagent is the expected shape of an aborted run. * Attributed state events are accepted. A producer choosing not to emit them mid-delegation is a producer-side decision, not a rule — see [Attribution is provenance, not ownership](#attribution-is-provenance-not-ownership). ## Compatibility > [!WARNING] > > **Older clients reject the lifecycle events outright.** Attribution is additive > and safe — `subagentRunId` is an unknown *field*, which clients tolerate — but > `SubagentStarted`, `SubagentFinished`, and `SubagentError` are unknown *event > types*, and a client older than subagent support fails on them while decoding, > before any application code runs. There is no way to filter them out > client-side. > > If some of your consumers predate subagent support, the producer must not emit > the lifecycle events to them. The two directions are not symmetric, so they are worth separating. ### A new agent talking to an older client The lifecycle events break it, so emitting them has to be **opt-in on the producer**. Integrations that support subagents therefore expose a flag to enable them, off by default — in the LangGraph integration it is `subagent_visibility`: ```python theme={null} agent = LangGraphAgent( name="my-agent", graph=graph, subagent_visibility="attributed", ) ``` The value names what the client sees. `"inline"` (the default) emits no lifecycle events and no `subagentRunId`, and nothing subagent-related reaches `MessagesSnapshot` — the stream is exactly what it was before subagent support existed, including the subagent's own text arriving as the parent's work. `"attributed"` emits the full surface described in this document. `"hidden"` suppresses the subagent's internal stream entirely: the client sees only the parent's spawning tool call, its result, and the parent's own reply. Turn on `"attributed"` once every consumer is new enough. Attribution alone is the safer intermediate step, since the field is ignored by clients that do not know it. A producer that wants grouping without a compatibility break can attribute events and skip the lifecycle entirely. ### A new client talking to an older agent Handled automatically. The TypeScript client inserts a compatibility shim based on the agent's reported version, and it acts in **both** directions. * **Client → agent.** The shim strips `subagentRunId` from the outgoing input messages, so an older agent never receives attribution it cannot interpret. This is the load-bearing half: a replayed message history, or a stored thread written by a newer client, really can carry the field. * **Agent → client.** The shim also drops `SubagentStarted`, `SubagentFinished` and `SubagentError`, and strips `subagentRunId` from every remaining event (including `MessagesSnapshot` messages and the `RunStarted` input echo). This is defensive normalization rather than translation — an agent that reports a pre-subagent version should not be emitting either of those in the first place, so what it really guards is a mixed or proxied pipeline. The consequence of the second direction is worth knowing: a consumer sitting behind this shim sees a **flattened, unattributed stream**, even if something upstream did attribute it. If you want attribution to reach your UI, the agent has to report a version that supports subagents. ## Support | SDK | Status | | - | - | | TypeScript | Events, attribution, verification, subscriber hooks, protobuf | | Python | Events and attribution | | .NET | Events, attribution, verification, protobuf | The binary protobuf transport carries subagent attribution on both TypeScript and .NET, generated from the same schema, so a subagent-attributed stream survives a round trip across languages. ### Known limitations * **Protobuf covers a subset of event types.** 19 of the 36 event types have a protobuf mapping: all three subagent events, plus the text message, tool call start/args/end, state, step and run families, and `MessagesSnapshot`, `Raw` and `Custom`. The rest cannot be encoded at all — and several of those carry attribution: `ToolCallResult`, the reasoning family, and the activity events. The chunk shorthand is unencodable too: `TextMessageChunk` and `ToolCallChunk` have message definitions carrying `subagent_run_id`, but no `EventType` enum entry to select them, and `ReasoningMessageChunk` has no proto message at all. All of this predates subagent support. * **.NET loses the second owner on parallel tool calls from different subagents.** Converting a run to `Microsoft.Extensions.AI` messages merges consecutive tool calls into one `ChatMessage`, and AG-UI attributes per message, so only the first owner survives. It is a current limitation rather than an inherent conflict — the provider constraint is adjacency, which interleaving would also satisfy while preserving attribution. ## See also * [Events](/concepts/events) — the full event catalogue, including the subagent events * [Messages](/concepts/messages) — how attribution appears on messages * [State](/concepts/state) — why state is run-scoped # Tools Source: https://docs.ag-ui.com/concepts/tools Understanding tools and how they enable human-in-the-loop AI workflows # Tools Tools are a fundamental concept in the AG-UI protocol that enable AI agents to interact with external systems and incorporate human judgment into their workflows. AG-UI distinguishes between tools that are defined by the agent backend and tools that are provided by the client at runtime. Backend-defined tools stay in the backend agent or framework configuration. Client-defined tools are passed in `RunAgentInput.tools` so the agent can call back into application-specific frontend behavior, such as UI actions, approvals, or user-mediated workflows. ## What Are Tools? In AG-UI, tools are functions that agents can call to: 1. Request specific information 2. Perform actions in external systems 3. Ask for human input or confirmation 4. Access specialized capabilities Tools bridge the gap between AI reasoning and real-world actions, allowing agents to accomplish tasks that would be impossible through conversation alone. ## Tool Structure Tools follow a consistent structure that defines their name, purpose, and expected parameters: ```typescript theme={null} interface Tool { name: string // Unique identifier for the tool description: string // Human-readable explanation of what the tool does parameters: { // JSON Schema defining the tool's parameters type: "object" properties: { // Tool-specific parameters } required: string[] // Array of required parameter names } } ``` The `parameters` field uses [JSON Schema](https://json-schema.org/) to define the structure of arguments that the tool accepts. This schema is used by both the agent (to generate valid tool calls) and the frontend (to validate and parse tool arguments). ## Frontend-Defined Tools A key aspect of AG-UI's tool system is that tools are defined in the frontend and passed to the agent during execution: ```typescript theme={null} // Define tools in the frontend const userConfirmationTool = { name: "confirmAction", description: "Ask the user to confirm a specific action before proceeding", parameters: { type: "object", properties: { action: { type: "string", description: "The action that needs user confirmation", }, importance: { type: "string", enum: ["low", "medium", "high", "critical"], description: "The importance level of the action", }, }, required: ["action"], }, } // Pass tools to the agent during execution agent.runAgent({ tools: [userConfirmationTool], // Other parameters... }) ``` This approach has several advantages: 1. **Frontend control**: The frontend determines what capabilities are available to the agent 2. **Dynamic capabilities**: Tools can be added or removed based on user permissions, context, or application state 3. **Separation of concerns**: Agents focus on reasoning while frontends handle tool implementation 4. **Security**: Sensitive operations are controlled by the application, not the agent `RunAgentInput.tools` is only for these client-provided tools. It is not intended to contain every tool available to the backend agent. If your integration has backend tools, define them in the backend framework or advertise them through agent capabilities instead of sending their schemas from the client. ## Tool Call Lifecycle When an agent needs to use a tool, it follows a standardized sequence of events: 1. **ToolCallStart**: Indicates the beginning of a tool call with a unique ID and tool name ```typescript theme={null} { type: EventType.TOOL_CALL_START, toolCallId: "tool-123", toolCallName: "confirmAction", parentMessageId: "msg-456" // Optional reference to a message } ``` 2. **ToolCallArgs**: Streams the tool arguments as they're generated ```typescript theme={null} { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool-123", delta: '{"act' // Partial JSON being streamed } ``` ```typescript theme={null} { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool-123", delta: 'ion":"Depl' // More JSON being streamed } ``` ```typescript theme={null} { type: EventType.TOOL_CALL_ARGS, toolCallId: "tool-123", delta: 'oy the application to production"}' // Final JSON fragment } ``` 3. **ToolCallEnd**: Marks the completion of the tool call ```typescript theme={null} { type: EventType.TOOL_CALL_END, toolCallId: "tool-123" } ``` The frontend accumulates these deltas to construct the complete tool call arguments. Once the tool call is complete, the frontend can execute the tool and provide results back to the agent. ### Tool call metadata All three of these events can carry a `metadata` object, and it accumulates onto the **tool call itself** rather than onto the assistant message that owns it: ```typescript theme={null} assistantMessage.toolCalls[0].metadata // { provider: "anthropic", latencyMs: 84 } ``` That is deliberate. One assistant message can own several tool calls, so folding their metadata into the parent would make the result depend on the order the calls happened to interleave. Giving each tool call its own keeps it unambiguous. `ToolCallResult` is different — it creates a tool message, so its metadata merges into that message like any other message-building event. See [Metadata](/concepts/metadata). ## Tool Results After a tool has been executed, the result is sent back to the agent as a "tool message": ```typescript theme={null} { id: "result-789", role: "tool", content: "true", // Tool result as a string toolCallId: "tool-123" // References the original tool call } ``` This message becomes part of the conversation history, allowing the agent to reference and incorporate the tool's result in subsequent responses. If the tool did not succeed, set `error` to the failure message: ```typescript theme={null} { id: "result-789", role: "tool", content: "Deployment blocked: the production environment is locked", toolCallId: "tool-123", error: "the production environment is locked" // Marks this result as a failure } ``` `error` is how the protocol expresses a client-side tool failure. Without it, a tool that failed is indistinguishable from one that succeeded — whatever the frontend happened to put in `content` is all the agent has to go on. See [Messages](/concepts/messages) for the full `ToolMessage` shape. ## Human-in-the-Loop Workflows The AG-UI tool system is especially powerful for implementing human-in-the-loop workflows. By defining tools that request human input or confirmation, developers can create AI experiences that seamlessly blend autonomous operation with human judgment. For example: 1. Agent needs to make an important decision 2. Agent calls the `confirmAction` tool with details about the decision 3. Frontend displays a confirmation dialog to the user 4. User provides their input 5. Frontend sends the user's decision back to the agent 6. Agent continues processing with awareness of the user's choice This pattern enables use cases like: * **Approval workflows**: AI suggests actions that require human approval * **Data verification**: Humans verify or correct AI-generated data * **Collaborative decision-making**: AI and humans jointly solve complex problems * **Supervised learning**: Human feedback improves future AI decisions ## CopilotKit Integration [CopilotKit](https://docs.copilotkit.ai/) provides a simplified way to work with AG-UI tools in React applications through its [`useCopilotAction`](https://docs.copilotkit.ai/guides/frontend-actions) hook: ```tsx theme={null} import { useCopilotAction } from "@copilotkit/react-core" // Define a tool for user confirmation useCopilotAction({ name: "confirmAction", description: "Ask the user to confirm an action", parameters: { type: "object", properties: { action: { type: "string", description: "The action to confirm", }, }, required: ["action"], }, handler: async ({ action }) => { // Show a confirmation dialog const confirmed = await showConfirmDialog(action) return confirmed ? "approved" : "rejected" }, }) ``` This approach makes it easy to define tools that integrate with your React components and handle the tool execution logic in a clean, declarative way. ## Tool Examples Here are some common types of tools used in AG-UI applications: ### User Confirmation ```typescript theme={null} { name: "confirmAction", description: "Ask the user to confirm an action", parameters: { type: "object", properties: { action: { type: "string", description: "The action to confirm" }, importance: { type: "string", enum: ["low", "medium", "high", "critical"], description: "The importance level" } }, required: ["action"] } } ``` ### Data Retrieval ```typescript theme={null} { name: "fetchUserData", description: "Retrieve data about a specific user", parameters: { type: "object", properties: { userId: { type: "string", description: "ID of the user" }, fields: { type: "array", items: { type: "string" }, description: "Fields to retrieve" } }, required: ["userId"] } } ``` ### User Interface Control ```typescript theme={null} { name: "navigateTo", description: "Navigate to a different page or view", parameters: { type: "object", properties: { destination: { type: "string", description: "Destination page or view" }, params: { type: "object", description: "Optional parameters for the navigation" } }, required: ["destination"] } } ``` ### Content Generation ```typescript theme={null} { name: "generateImage", description: "Generate an image based on a description", parameters: { type: "object", properties: { prompt: { type: "string", description: "Description of the image to generate" }, style: { type: "string", description: "Visual style for the image" }, dimensions: { type: "object", properties: { width: { type: "number" }, height: { type: "number" } }, description: "Dimensions of the image" } }, required: ["prompt"] } } ``` ## Best Practices When designing tools for AG-UI: 1. **Clear naming**: Use descriptive, action-oriented names 2. **Detailed descriptions**: Include thorough descriptions to help the agent understand when and how to use the tool 3. **Structured parameters**: Define precise parameter schemas with descriptive field names and constraints 4. **Required fields**: Only mark parameters as required if they're truly necessary 5. **Error handling**: Implement robust error handling in tool execution code 6. **User experience**: Design tool UIs that provide appropriate context for human decision-making ## Conclusion Tools in AG-UI bridge the gap between AI reasoning and real-world actions, enabling sophisticated workflows that combine the strengths of AI and human intelligence. By defining tools in the frontend and passing them to agents, developers can create interactive experiences where AI and humans collaborate efficiently. The tool system is particularly powerful for implementing human-in-the-loop workflows, where AI can suggest actions but defer critical decisions to humans. This balances automation with human judgment, creating AI experiences that are both powerful and trustworthy. # Contributing Source: https://docs.ag-ui.com/development/contributing How to participate in Agent User Interaction Protocol development # Naming conventions Add your package under `integrations/` with docs and tests. If your integration is work in progress, you can still add it to main branch. You can prefix it with `wip-`, i.e. (`integrations/wip-your-integration`) or if you're a third party contributor use the `community` prefix, i.e. (`integrations/community/your-integration`). For questions and discussions, please use [GitHub Discussions](https://github.com/ag-ui-protocol/ag-ui/discussions). # Roadmap Source: https://docs.ag-ui.com/development/roadmap Our plans for evolving Agent User Interaction Protocol You can follow the progress of the AG-UI Protocol on our [public roadmap](https://github.com/orgs/ag-ui-protocol/projects/1). ## Get Involved If you’d like to contribute ideas, feature requests, or bug reports to the roadmap, please see the [Contributing Guide](https://github.com/ag-ui-protocol/ag-ui/blob/main/CONTRIBUTING.md) for details on how to get involved. # What's New Source: https://docs.ag-ui.com/development/updates The latest updates and improvements to AG-UI **2025-04-09** AG-UI repositories are now public * Initial release of the Agent User Interaction Protocol # Generative User Interfaces Source: https://docs.ag-ui.com/drafts/generative-ui AI-generated interfaces without custom tool renderers # Generative User Interfaces ## Summary ### Problem Statement Currently, creating custom user interfaces for agent interactions requires programmers to define specific tool renderers. This limits the flexibility and adaptability of agent-driven applications. ### Motivation This draft describes an AG-UI extension that addresses **generative user interfaces**—interfaces produced directly by artificial intelligence without requiring a programmer to define custom tool renderers. The key idea is to leverage our ability to send client-side tools to the agent, thereby enabling this capability across all agent frameworks supported by AG-UI. ## Status * **Status**: Draft * **Author(s)**: Markus Ecker ([mail@mme.xyz](mailto:mail@mme.xyz)) ## Challenges and Limitations ### Tool Description Length OpenAI enforces a limit of 1024 characters for tool descriptions. Gemini and Anthropic impose no such limit. ### Arguments JSON Schema Constraints Classes, nesting, `$ref`, and `oneOf` are not reliably supported across LLM providers. ### Context Window Considerations Injecting a large UI description language into an agent may reduce its performance. Agents dedicated solely to UI generation perform better than agents combining UI generation with other tasks. ## Detailed Specification ### Two-Step Generation Process ```mermaid theme={null} flowchart TD A[Agent needs UI] --> B["Step 1: What?
Agent calls generateUserInterface
(description, data, output)"] B --> C["Step 2: How?
Secondary generator builds actual UI
(JSON Schema, React, etc.)"] C --> D[Rendered UI shown to user] D --> E[Validated user input returned to Agent] ``` ### Step 1: What to Generate? Inject a lightweight tool into the agent: **Tool Definition:** * **Name:** `generateUserInterface` * **Arguments:** * **description**: A high-level description of the UI (e.g., *"A form for entering the user's address"*) * **data**: Arbitrary pre-populated data for the generated UI * **output**: A description or schema of the data the agent expects the user to submit back (fields, required/optional, types, constraints) **Example Tool Call:** ```json theme={null} { "tool": "generateUserInterface", "arguments": { "description": "A form that collects a user's shipping address.", "data": { "firstName": "Ada", "lastName": "Lovelace", "city": "London" }, "output": { "type": "object", "required": [ "firstName", "lastName", "street", "city", "postalCode", "country" ], "properties": { "firstName": { "type": "string", "title": "First Name" }, "lastName": { "type": "string", "title": "Last Name" }, "street": { "type": "string", "title": "Street Address" }, "city": { "type": "string", "title": "City" }, "postalCode": { "type": "string", "title": "Postal Code" }, "country": { "type": "string", "title": "Country", "enum": ["GB", "US", "DE", "AT"] } } } } } ``` ### Step 2: How to Generate? Delegate UI generation to a secondary LLM or agent: * The CopilotKit user stays in control: Can make their own generators, add custom libraries, include additional prompts etc. * On tool invocation, the secondary model consumes `description`, `data`, and `output` to generate the user interface * This model is focused solely on UI generation, ensuring maximum fidelity and consistency * The generation method can be swapped as needed (e.g., JSON, HTML, or other renderable formats) * The UI format description is not subject to structural or length constraints, allowing arbitrarily complex specifications ## Implementation Examples ### Example Output: UISchemaGenerator ```json theme={null} { "jsonSchema": { "title": "Shipping Address", "type": "object", "required": [ "firstName", "lastName", "street", "city", "postalCode", "country" ], "properties": { "firstName": { "type": "string", "title": "First name" }, "lastName": { "type": "string", "title": "Last name" }, "street": { "type": "string", "title": "Street address" }, "city": { "type": "string", "title": "City" }, "postalCode": { "type": "string", "title": "Postal code" }, "country": { "type": "string", "title": "Country", "enum": ["GB", "US", "DE", "AT"] } } }, "uiSchema": { "type": "VerticalLayout", "elements": [ { "type": "Group", "label": "Personal Information", "elements": [ { "type": "Control", "scope": "#/properties/firstName" }, { "type": "Control", "scope": "#/properties/lastName" } ] }, { "type": "Group", "label": "Address", "elements": [ { "type": "Control", "scope": "#/properties/street" }, { "type": "Control", "scope": "#/properties/city" }, { "type": "Control", "scope": "#/properties/postalCode" }, { "type": "Control", "scope": "#/properties/country" } ] } ] }, "initialData": { "firstName": "Ada", "lastName": "Lovelace", "city": "London", "country": "GB" } } ``` ### Example Output: ReactFormHookGenerator ```tsx theme={null} import React from "react" import { useForm } from "react-hook-form" import { z } from "zod" import { zodResolver } from "@hookform/resolvers/zod" // ----- Schema (contract) ----- const AddressSchema = z.object({ firstName: z.string().min(1, "Required"), lastName: z.string().min(1, "Required"), street: z.string().min(1, "Required"), city: z.string().min(1, "Required"), postalCode: z.string().regex(/^[A-Za-z0-9\\-\\s]{3,10}$/, "3–10 chars"), country: z.enum(["GB", "US", "DE", "AT", "FR", "IT", "ES"]), }) export type Address = z.infer type Props = { initialData?: Partial
meta?: { title?: string; submitLabel?: string } respond: (data: Address) => void // <-- called on successful submit } const COUNTRIES: Address["country"][] = [ "GB", "US", "DE", "AT", "FR", "IT", "ES", ] export default function AddressForm({ initialData, meta, respond }: Props) { const { register, handleSubmit, formState: { errors }, } = useForm
({ resolver: zodResolver(AddressSchema), defaultValues: { firstName: "", lastName: "", street: "", city: "", postalCode: "", country: "GB", ...initialData, }, }) const onSubmit = (data: Address) => { // Guaranteed to match AddressSchema respond(data) } return (
{meta?.title &&

{meta.title}

} {/* Section: Personal Information */}
Personal Information
{errors.firstName && {errors.firstName.message}}
{errors.lastName && {errors.lastName.message}}
{/* Section: Address */}
Address
{errors.street && {errors.street.message}}
{errors.city && {errors.city.message}}
{errors.postalCode && {errors.postalCode.message}}
{errors.country && {errors.country.message}}
) } ``` ## Implementation Considerations ### Client SDK Changes TypeScript SDK additions: * New `generateUserInterface` tool type * UI generator registry for pluggable generators * Validation layer for generated UI schemas * Response handler for user-submitted data Python SDK additions: * Support for UI generation tool invocation * Schema validation utilities * Serialization for UI definitions ### Integration Impact * All AG-UI integrations can leverage this capability without modification * Frameworks emit standard tool calls; client handles UI generation * Backward compatible with existing tool-based UI approaches ## Use Cases ### Dynamic Forms Agents can generate forms on-the-fly based on conversation context without pre-defined schemas. ### Data Visualization Generate charts, graphs, or tables appropriate to the data being discussed. ### Interactive Workflows Create multi-step wizards or guided processes tailored to user needs. ### Adaptive Interfaces Generate different UI layouts based on user preferences or device capabilities. ## Testing Strategy * Unit tests for tool injection and invocation * Integration tests with multiple UI generators * E2E tests demonstrating various UI types * Performance benchmarks comparing single vs. two-step generation * Cross-provider compatibility testing ## References * [AG-UI Tools Documentation](/concepts/tools) * [JSON Schema](https://json-schema.org/) * [React Hook Form](https://react-hook-form.com/) * [JSON Forms](https://jsonforms.io/) # Meta Events Source: https://docs.ag-ui.com/drafts/meta-events Annotations and signals independent of agent runs # Meta Events Proposal ## Summary ### Problem Statement Currently, AG-UI events are tightly coupled to agent runs. There's no standardized way to attach user feedback, annotations, or external signals to the event stream that are independent of the agent's execution lifecycle. ### Motivation AG-UI is extended with **MetaEvents**, a new class of events that can occur at any point in the event stream, independent of agent runs. MetaEvents provide a way to attach annotations, signals, or feedback to a serialized stream. They may originate from users, clients, or external systems rather than from agents. Examples include reactions such as thumbs up/down on a message. ## Status * **Status**: Draft * **Author(s)**: Markus Ecker ([mail@mme.xyz](mailto:mail@mme.xyz)) ## Detailed Specification ### Overview This proposal introduces: * A new **MetaEvent** type for side-band annotations * Events that can appear anywhere in the stream * Support for user feedback, tags, and external annotations * Extensible payload structure for application-specific data ## New Type: MetaEvent ```typescript theme={null} type MetaEvent = BaseEvent & { type: EventType.META /** * Application-defined type of the meta event. * Examples: "thumbs_up", "thumbs_down", "tag", "note" */ metaType: string /** * Application-defined payload. * May reference other entities (e.g., messageId) or contain freeform data. */ payload: Record } ``` ### Key Characteristics * **Run-independent**: MetaEvents are not tied to any specific run lifecycle * **Position-flexible**: Can appear before, between, or after runs * **Origin-diverse**: May come from users, clients, or external systems * **Extensible**: Applications define their own metaType values and payload schemas ## Implementation Examples ### User Feedback **Thumbs Up:** ```json theme={null} { "id": "evt_123", "ts": 1714063982000, "type": "META", "metaType": "thumbs_up", "payload": { "messageId": "msg_456", "userId": "user_789" } } ``` **Thumbs Down with Reason:** ```json theme={null} { "id": "evt_124", "ts": 1714063985000, "type": "META", "metaType": "thumbs_down", "payload": { "messageId": "msg_456", "userId": "user_789", "reason": "inaccurate", "comment": "The calculation seems incorrect" } } ``` ### Annotations **User Note:** ```json theme={null} { "id": "evt_789", "ts": 1714064001000, "type": "META", "metaType": "note", "payload": { "text": "Important question to revisit", "relatedRunId": "run_001", "author": "user_123" } } ``` **Tag Assignment:** ```json theme={null} { "id": "evt_890", "ts": 1714064100000, "type": "META", "metaType": "tag", "payload": { "tags": ["important", "follow-up"], "threadId": "thread_001" } } ``` ### External System Events **Analytics Event:** ```json theme={null} { "id": "evt_901", "ts": 1714064200000, "type": "META", "metaType": "analytics", "payload": { "event": "conversation_shared", "properties": { "shareMethod": "link", "recipientCount": 3 } } } ``` **Moderation Flag:** ```json theme={null} { "id": "evt_902", "ts": 1714064300000, "type": "META", "metaType": "moderation", "payload": { "action": "flag", "messageId": "msg_999", "category": "inappropriate_content", "confidence": 0.95 } } ``` ## Common Meta Event Types While applications can define their own types, these are commonly used: | MetaType | Description | Typical Payload | | - | - | - | | `thumbs_up` | Positive feedback | `{ messageId, userId }` | | `thumbs_down` | Negative feedback | `{ messageId, userId, reason? }` | | `note` | User annotation | `{ text, relatedId?, author }` | | `tag` | Categorization | `{ tags[], targetId }` | | `bookmark` | Save for later | `{ messageId, userId }` | | `copy` | Content copied | `{ messageId, content }` | | `share` | Content shared | `{ messageId, method }` | | `rating` | Numeric rating | `{ messageId, rating, maxRating }` | ## Use Cases ### User Feedback Collection Capture user reactions to agent responses for quality improvement. ### Conversation Annotation Allow users to add notes, tags, or bookmarks to important parts of conversations. ### Analytics and Tracking Record user interactions and behaviors without affecting agent execution. ### Content Moderation Flag or mark content for review by external moderation systems. ### Collaborative Features Enable multiple users to annotate or comment on shared conversations. ### Audit Trail Create a complete record of all interactions, not just agent responses. ## Implementation Considerations ### Client SDK Changes TypeScript SDK: * New `MetaEvent` type in `@ag-ui/core` * Helper functions for common meta event types * MetaEvent filtering and querying utilities Python SDK: * `MetaEvent` class implementation * Meta event builders for common types * Event stream filtering capabilities ## Testing Strategy * Unit tests for MetaEvent creation and validation * Integration tests with mixed event streams * Performance tests with high-volume meta events * Security tests for payload validation ## References * [AG-UI Events Documentation](/concepts/events) * [Event Sourcing](https://martinfowler.com/eaaDev/EventSourcing.html) * [CQRS Pattern](https://martinfowler.com/bliki/CQRS.html) # Overview Source: https://docs.ag-ui.com/drafts/overview Draft changes being considered for the AG-UI protocol # Overview This section contains draft changes being considered for the AG-UI protocol. These proposals are under internal review and may be modified or withdrawn before implementation. ## Current Drafts > [card] [Reasoning](/drafts/reasoning) > > Support for LLM reasoning visibility and continuity with encrypted content > [card] [Interrupt-Aware Run Lifecycle](/concepts/interrupts) > > Native support for agent pauses requiring human approval or input > [card] [Generative User Interfaces](/drafts/generative-ui) > > AI-generated interfaces without requiring custom tool renderers > [card] [Meta Events](/drafts/meta-events) > > Annotations and signals independent of agent runs ## Status Definitions * **Draft** - Initial proposal under consideration * **Under Review** - Active development and testing * **Accepted** - Approved for implementation * **Implemented** - Merged into the main protocol specification * **Withdrawn** - Proposal has been withdrawn or superseded # Integrations Source: https://docs.ag-ui.com/integrations A list of AG-UI integrations This page showcases various Agent User Interaction Protocol (AG-UI) integrations that demonstrate the protocol's capabilities and versatility. ## Frontend Integrations * **[CopilotKit](https://copilotkit.ai)** - AI Copilots for your product. ## Agent Frameworks * **[LangChain](https://www.langchain.com/langgraph)** - Balance agent control with agency * **[Microsoft Agent Framework](https://github.com/microsoft/agent-framework)** - A framework for building, orchestrating, and deploying AI agents and multi-agent workflows with support for Python and .NET. * **[Google ADK](https://google.github.io/adk-docs/)** - Google's Agent Development Kit for building AI agents with Gemini models. AG-UI supports both the [Python middleware](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/adk-middleware/python) and the [JavaScript runtime integration](/integrations/google-adk-js), with a [live ADK JavaScript demo](https://dojo.ag-ui.com/adk-js/feature/agentic_chat?openCopilot=true). * **[Google Antigravity](https://github.com/google-antigravity/antigravity-sdk-python)** - Google's agent harness SDK, which runs the model, its tools and its sessions in a bundled local process * **[AWS Strands Agents](https://github.com/strands-agents/sdk-python)** - A model-driven approach to building AI agents * **[Mastra](https://mastra.ai)** - The TypeScript Agent Framework * **[Pydantic AI](https://github.com/pydantic/pydantic-ai)** - Production-ready AI agents in Python * **[Agno](https://agno.com)** - A full-stack framework for building Multi-Agent Systems * **[LlamaIndex](https://www.llamaindex.ai)** - Data framework for LLM applications with RAG capabilities and workflow orchestration * **[CrewAI](https://crewai.com)** - Streamline workflows across industries with powerful AI agents. * **[AG2](https://ag2.ai)** - The Open-Source AgentOS * **[Claude Managed Agents](https://platform.claude.com/docs/en/managed-agents/overview)** - Anthropic's hosted agent sessions, bridged to AG-UI in TypeScript, Python, and .NET * **[Claude Agent SDK](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/claude-agent-sdk)** - Anthropic's local agent SDK, served over AG-UI * **[Langroid](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/langroid)** - Lightweight Python framework for building multi-agent LLM applications * **[OpenAI Agents SDK](https://openai.github.io/openai-agents-python/)** - OpenAI’s Python framework for building agentic applications * **[Cloudflare Agents](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/community/cloudflare-agents)** - Agents on Cloudflare Workers and Durable Objects, served over AG-UI * **[Omnara](https://omnara.com)** - Open-source control plane for durable, hosted agents, bridged to AG-UI in TypeScript ([quickstart](/integrations/omnara)) ## Infrastructure / Deployment * **[Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/)** - Fully managed infrastructure for deploying AG-UI agents to production with native protocol support, managed authentication, session isolation, and auto-scaling. See the [deployment guide](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html) and the [announcement](https://aws.amazon.com/about-aws/whats-new/2026/03/amazon-bedrock-agentcore-runtime-ag-ui-protocol/). ## Specification (standard) * **[Oracle Open Agent Spec](http://oracle.github.io/agent-spec/)** - A portable language for defining agentic systems * **[A2A Middleware](https://a2a-protocol.org/)** - Provides the definitive common language for agent interoperability ## Generative UI * **[A2UI](https://github.com/google/A2UI)** - Declarative generative UI for generating rich, interactive interfaces * **[MCP Apps](https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/)** - Render interactive UI components from MCP servers directly in your chat interface ## SDKs \| [Kotlin](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/kotlin/overview.mdx) \| [Golang](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/go/overview.mdx) \| [Dart](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/dart) \| [Java](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/java/overview.mdx) \| [Rust](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/rust/crates/ag-ui-client) \| [Ruby](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/ruby) \| [C++](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/c%2B%2B) \| [.NET](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/dotnet/samples/GettingStarted) #### (coming soon) \| [Nim](https://github.com/ag-ui-protocol/ag-ui/pull/29) \| [Flowise](https://github.com/ag-ui-protocol/ag-ui/issues/367) \| [Langflow](https://github.com/ag-ui-protocol/ag-ui/issues/366) ### Clients \| [CopilotKit](https://docs.copilotkit.ai/direct-to-llm/guides/quickstart) \| [Terminal + Agent](https://docs.ag-ui.com/quickstart/clients) \| [Chat platforms — Slack, Microsoft Teams](https://github.com/CopilotKit/channels-sdk) \| [React Native](https://github.com/CopilotKit/CopilotKit/tree/main/examples/v2/react-native/demo) \| [OpenBot](https://open.bot/builders) Join [Discord](https://discord.gg/Jd3FzfdJa8) to engage with the AG-UI community. # Google ADK JavaScript quickstart Source: https://docs.ag-ui.com/integrations/google-adk-js Run a native Google ADK JavaScript agent behind an AG-UI endpoint. `@ag-ui/adk-js` adapts a server-side Google ADK JavaScript `Runner` to an AG-UI `AbstractAgent`. Use it when the agent and web runtime run in Node.js. The existing `@ag-ui/adk` package remains the HTTP client for the Python ADK middleware. ## Prerequisites * Node.js 20.19 or newer * `@ag-ui/client` and `@ag-ui/core` 1.x (use a CopilotKit release that resolves the 1.x line) * A Gemini API key from [Google AI Studio](https://aistudio.google.com/apikey) ## Add ADK JavaScript to an application Install the server dependencies: ```bash theme={null} npm install @ag-ui/adk-js @google/adk @copilotkit/runtime hono ``` `@ag-ui/adk-js` requires `@google/adk` 2.x. Set your Gemini key in the environment the server reads: ```dotenv theme={null} GOOGLE_GENAI_API_KEY=your-api-key GOOGLE_GENAI_USE_VERTEXAI=0 ``` Create `app/api/copilotkit/route.ts`: ```typescript theme={null} import { ADKJSAgent } from "@ag-ui/adk-js" import { CopilotRuntime, InMemoryAgentRunner, createCopilotEndpointSingleRoute, } from "@copilotkit/runtime/v2" import { Agent, InMemorySessionService } from "@google/adk" import { handle } from "hono/vercel" export const runtime = "nodejs" const rootAgent = new Agent({ name: "assistant", model: "gemini-2.5-flash", instruction: "Be concise and use frontend tools when appropriate.", }) const copilotRuntime = new CopilotRuntime({ agents: { assistant: new ADKJSAgent({ appName: "assistant_app", sessionService: new InMemorySessionService(), agent: rootAgent, userId: "user-123", // resolve from server-side auth }), }, runner: new InMemoryAgentRunner(), }) const endpoint = createCopilotEndpointSingleRoute({ runtime: copilotRuntime, basePath: "/api/copilotkit", }) export const POST = handle(endpoint) ``` The frontend can now select the `assistant` agent through any AG-UI client. A CopilotKit React application uses `runtimeUrl="/api/copilotkit"` and `agent="assistant"` on its `CopilotKit` provider. ## Runs and concurrency Different threads run concurrently on one shared agent instance; a second run on the same user and thread is refused with `THREAD_BUSY`. ADK closes every toolset after each run, so give roots that share a closable toolset (or keep per-run state) per-run instances with a factory (`agent: () => new Workflow(...)`), or pass `runner:` to bring a fully configured Runner. Failures after the client disconnected go to `logger` (default `console`). ## Supported integration behavior * Streaming assistant messages and lifecycle events * Backend and frontend tool calls * AG-UI state snapshots and RFC 6902 state deltas * ADK input, confirmation, and credential requests as resumable interrupts. A tool that raises its own confirmation at runtime must also answer `true` from `checkRequireConfirmation` (or declare `requireConfirmation` on a `FunctionTool`), because ADK 2.x binds an approval to the tool it names. * Restored message history with stable message IDs * Provider/model token usage when ADK reports it * A bare ADK `Workflow` as the `Runner` root (its workflow nodes are reported as sub-agents and their frontend tools are discovered) * The AG-UI subagent protocol for ADK multi-agent trees (see below) The integration is server-only. Never import `@ag-ui/adk-js` or `@google/adk` from a browser component. See the [live Dojo examples](https://dojo.ag-ui.com/adk-js/feature/agentic_chat?openCopilot=true) for tools, shared state, generative UI, and interrupts. ## Sub-agents `subagents: "steps"` emits `STEP_STARTED` / `STEP_FINISHED` (`agent:`) and `CUSTOM` `MultiAgentHandoff` events around ADK sub-agent work — safe for every client and what the Dojo multi-agent page reads. `subagents: "attributed"` adds the AG-UI subagent protocol (`SUBAGENT_STARTED/FINISHED/ERROR`, `subagentRunId` on events, messages, and interrupts, continuation of a suspended sub-agent under the same id on resume); the consuming client needs `@ag-ui/client` 1.x, because older clients reject those event types before any application code runs. The default is `off`. # AG-UI Overview Source: https://docs.ag-ui.com/introduction # The Agent–User Interaction (AG-UI) Protocol AG-UI is an **open**, **lightweight**, **event-based** protocol that standardizes how AI agents connect to user-facing applications. AG-UI is designed to be the general-purpose, bi-directional connection between a user-facing application and any agentic backend. Built for simplicity and flexibility, it standardizes how agent state, UI intents, and user interactions flow between your model/agent runtime and user-facing frontend applications—to allow application developers to ship reliable, debuggable, user‑friendly agentic features fast while focusing on application needs and avoiding complex ad-hoc wiring. ![AG-UI Overview](https://mintcdn.com/tawkitai/p1KIIAcVU0goF39y/images/ag-ui-overview-with-partners.png?fit=max\&auto=format\&n=p1KIIAcVU0goF39y\&q=85\&s=62e8ee85a2e1623d1bfbbea76227ca99) *** ## Agentic Protocols > [!NOTE] > > **Confused about "A2UI" and "AG-UI"?** That's understandable! Despite the naming similarities, they are quite different and work well together. A2UI is a [generative UI specification](./concepts/generative-ui-specs) - allowing agents to deliver UI widgets, where AG-UI is the Agent↔User Interaction protocol - which connects an agentic frontend to any agentic backend. [Learn more](https://copilotkit.ai/ag-ui-and-a2ui) AG-UI is one of three prominent open [agentic protocols](./agentic-protocols). | **Layer** | **Protocol / Example** | **Purpose** | | - | - | - | | **Agent ↔ User Interaction** | **AG-UI (Agent–User Interaction Protocol)** | The open, event-based standard that connects agents to user-facing applications — enabling real-time, multimodal, interactive experiences. | | **Agent ↔ Tools & Data** | **MCP (Model Context Protocol)** | Open standard (originated by Anthropic) that lets agents securely connect to external systems — tools, workflows, and data sources. | | **Agent ↔ Agent** | **A2A (Agent to Agent)** | Open standard (originated by Google) which defines how agents coordinate and share work across distributed agentic systems. | *** ## Building blocks (today & upcoming) Streaming chat Live token and event streaming for responsive multi turn sessions, with cancel and resume. Multimodality Typed attachments and real time media (files, images, audio, transcripts); supports voice, previews, annotations, provenance. Generative UI, static Render model output as stable, typed components under app control. Generative UI, declarative Small declarative language for constrained yet open-ended agent UIs; agents propose trees and constraints, the app validates and mounts. Shared state (Read-only & read-write). Typed store shared between agent and app, with streamed event-sourced diffs and conflict resolution for snappy collaboration. Thinking steps Visualize intermediate reasoning from traces and tool events; no raw chain of thought. Frontend tool calls Typed handoffs from agent to frontend-executed actions, and back. Backend tool rendering Visualize backend tool outputs in app and chat, emit side effects as first-class events. Interrupts (human in the loop) Pause, approve, edit, retry, or escalate mid flow without losing state. Sub-agents and composition Nested delegation with scoped state, tracing, and cancellation. Agent steering Dynamically redirect agent execution with real-time user input to guide behavior and outcomes. Tool output streaming Stream tool results and logs so UIs can render long-running effects in real time. Custom events Open-ended data exchange for needs not covered by the protocol. *** ## Why Agentic Apps need AG-UI Agentic applications break the simple request/response model that dominated frontend-backend development in the pre-agentic era: a client makes a request, the server returns data, the client renders it, and the interaction ends. #### The requirements of user‑facing agents While agents are just software, they exhibit characteristics that make them challenging to serve behind traditional REST/GraphQL APIs: * Agents are **long‑running** and **stream** intermediate work—often across multi‑turn sessions. * Agents are **nondeterministic** and can **control application UI nondeterministically**. * Agents simultanously mix **structured + unstructured IO** (e.g. text & voice, alongside tool calls and state updates). * Agents need user-interactive **composition**: e.g. they may call sub‑agents, often recursively. * And more... AG-UI is an event-based protocol that enables dynamic communication between agentic frontends and backends. It builds on top of the foundational protocols of the web (HTTP, WebSockets) as an abstraction layer designed for the agentic age—bridging the gap between traditional client-server architectures and the dynamic, stateful nature of AI agents. *** ## AG-UI in Action You can see demo apps of the AG-UI features with the framework of your choice, with preview, code, and walkthrough docs in the [AG-UI Dojo](https://dojo.ag-ui.com/) *** ## Supported Integrations AG-UI was born from CopilotKit's initial **partnership** with LangChain and CrewAI - and brings the incredibly popular agent-user-interactivity infrastructure to the wider agentic ecosystem. **1st party** = the platforms that have AG‑UI built in and provide documentation for guidance. ### Direct to LLM | Framework | Status | AG-UI Resources | | :- | - | - | | Direct to LLM | Supported | [Docs](https://docs.copilotkit.ai/direct-to-llm) | ### Agent Framework - Partnerships | Framework | Status | AG-UI Resources | | :- | - | - | | [LangChain](https://www.langchain.com/langgraph) | Supported | [Docs](https://docs.copilotkit.ai/langgraph/), [Demos](https://dojo.ag-ui.com/langgraph-fastapi/feature/shared_state) | | [CrewAI](https://crewai.com/) | Supported | [Docs](https://docs.copilotkit.ai/crewai-flows), [Demos](https://dojo.ag-ui.com/crewai/feature/shared_state) | ### Agent Framework - 1st Party | Framework | Status | AG-UI Resources | | :- | - | - | | [Microsoft Agent Framework](https://azure.microsoft.com/en-us/blog/introducing-microsoft-agent-framework/) | Supported | [Docs](https://docs.copilotkit.ai/microsoft-agent-framework), [Demos](https://dojo.ag-ui.com/microsoft-agent-framework-dotnet/feature/shared_state) | | [Google ADK](https://google.github.io/adk-docs/get-started/) | Supported | [Docs](https://docs.copilotkit.ai/adk), [Demos](https://dojo.ag-ui.com/adk-middleware/feature/shared_state?openCopilot=true) | | [Google ADK (JavaScript)](https://adk.dev/get-started/typescript/) | Supported | [Quickstart](/integrations/google-adk-js), [Demos](https://dojo.ag-ui.com/adk-js/feature/agentic_chat?openCopilot=true) | | [Google Antigravity](https://github.com/google-antigravity/antigravity-sdk-python) | Supported | [Docs](https://docs.copilotkit.ai/google-antigravity), [Demos](https://dojo.ag-ui.com/antigravity/feature/shared_state) | | [AWS Strands Agents](https://github.com/strands-agents/sdk-python) | Supported | [Docs](https://docs.copilotkit.ai/aws-strands), [Demos](https://dojo.ag-ui.com/aws-strands/feature/shared_state) | | [AWS Bedrock AgentCore](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui-protocol-contract.html) | Supported | [Docs](https://github.com/awslabs/fullstack-solution-template-for-agentcore) | | [Mastra](https://mastra.ai/) | Supported | [Docs](https://docs.copilotkit.ai/mastra/), [Demos](https://dojo.ag-ui.com/mastra/feature/tool_based_generative_ui) | | [Pydantic AI](https://github.com/pydantic/pydantic-ai) | Supported | [Docs](https://docs.copilotkit.ai/pydantic-ai/), [Demos](https://dojo.ag-ui.com/pydantic-ai/feature/shared_state) | | [Agno](https://github.com/agno-agi/agno) | Supported | [Docs](https://docs.copilotkit.ai/agno/), [Demos](https://dojo.ag-ui.com/agno/feature/tool_based_generative_ui) | | [LlamaIndex](https://github.com/run-llama/llama_index) | Supported | [Docs](https://docs.copilotkit.ai/llamaindex/), [Demos](https://dojo.ag-ui.com/llamaindex/feature/shared_state) | | [AG2](https://ag2.ai/) | Supported | [Docs](https://docs.copilotkit.ai/ag2/) [Demos](https://dojo.ag-ui.com/ag2/feature/shared_state) | | [AWS Bedrock Agents](https://aws.amazon.com/bedrock/agents/) | In Progress | – | ### Agent Framework - Community | Framework | Status | AG-UI Resources | | :- | - | - | | [Claude Agent SDK](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/claude-agent-sdk) | Supported | [Demos](https://dojo.ag-ui.com/claude-agent-sdk-python/feature/shared_state) | | [Claude Managed Agents SDK](https://platform.claude.com/docs/en/managed-agents/overview) | Supported | [Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/claude-managed-agents), [Demos](https://dojo.ag-ui.com/claude-managed-agents-python/feature/tool_based_generative_ui) | | [Langroid](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/langroid) | Supported | [Demos](https://dojo.ag-ui.com/langroid/feature/shared_state) | | [Omnara](https://omnara.com) | Supported | [Quickstart](/integrations/omnara), [Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/community/omnara/typescript), [Demos](https://dojo.ag-ui.com/omnara/feature/tool_based_generative_ui) | | [OpenAI Agents SDK](https://openai.github.io/openai-agents-python/) | Supported | [Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/openai-agents), [Demos](https://dojo.ag-ui.com/openai-agents-python/feature/tool_based_generative_ui) | | [Cloudflare Agents](https://developers.cloudflare.com/agents/) | Supported | [Integration](https://github.com/ag-ui-protocol/ag-ui/tree/main/integrations/community/cloudflare-agents) | ### Agent Interaction Protocols | Protocol | Status | AG-UI Resources | Integrations | | :- | - | - | - | | [A2A Middleware](https://a2a-protocol.org/) | Supported | [Docs](https://docs.copilotkit.ai/a2a-protocol) | Partnership | ### Infrastructure / Deployment | Platform | Status | AG-UI Resources | Integrations | | :- | - | - | - | | [Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/) | Supported | [Docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-agui.html) | 1st Party | ### Specification (standard) | Framework | Status | AG-UI Resources | | :- | - | - | | [Oracle Agent Spec](http://oracle.github.io/agent-spec/) | Supported | [Docs](https://go.copilotkit.ai/copilotkit-oracle-docs), [Demos](https://dojo.ag-ui.com/agent-spec-langgraph/feature/tool_based_generative_ui) | ### Generative UI | Framework | Status | AG-UI Resources | | :- | - | - | | [A2UI](https://github.com/google/A2UI) | Supported | [Docs](./concepts/generative-ui-specs) | | [MCP Apps](https://blog.modelcontextprotocol.io/posts/2025-11-21-mcp-apps/) | Supported | [Docs](https://docs.copilotkit.ai/generative-ui-specs/mcp-apps) | ### SDKs | SDK | Status | AG-UI Resources | Integrations | | :- | - | - | - | | [Kotlin]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/kotlin/overview.mdx) | Community | | [Golang]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/go/overview.mdx) | Community | | [Dart]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/dart) | Community | | [Java]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/blob/main/docs/sdk/java/overview.mdx) | Community | | [Rust]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/rust/crates/ag-ui-client) | Community | | [Ruby]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/ruby) | Community | | [C++]() | Supported | [GitHub Source](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/community/c%2B%2B) | Community | | [.NET]() | Supported | [Getting Started](https://github.com/ag-ui-protocol/ag-ui/tree/main/sdks/dotnet/samples/GettingStarted) | Community | | [Nim]() | In Progress | [PR](https://github.com/ag-ui-protocol/ag-ui/pull/29) | Community | | [Flowise]() | In Progress | [GitHub Source](https://github.com/ag-ui-protocol/ag-ui/issues/367) | Community | | [Langflow]() | In Progress | [GitHub Source](https://github.com/ag-ui-protocol/ag-ui/issues/366) | Community | ### Clients | Client | Status | AG-UI Resources | Integrations | | :- | - | - | - | | [CopilotKit](https://github.com/CopilotKit/CopilotKit) | Supported | [Getting Started](https://docs.copilotkit.ai/direct-to-llm/guides/quickstart) | 1st Party | | [Terminal + Agent]() | Supported | [Getting Started](https://docs.ag-ui.com/quickstart/clients) | Community | | Chat platforms (Slack, Microsoft Teams) | Supported | [Channels SDK](https://github.com/CopilotKit/channels-sdk), [OpenTag example](https://github.com/CopilotKit/OpenTag) | 1st Party | | [React Native](https://reactnative.dev/) | Supported | [Example](https://github.com/CopilotKit/CopilotKit/tree/main/examples/v2/react-native/demo) | 1st Party | | [OpenBot](https://open.bot) | Supported | [Getting Started](https://open.bot/builders), [Source](https://github.com/CopilotKit/OpenBot) | 1st Party | An AG-UI client does not have to be a web application. The protocol describes an event stream rather than a rendering target, so a terminal, a mobile app, or a chat platform can each act as a client. A chat-platform client is something anyone can build against AG-UI. The [Channels SDK](https://github.com/CopilotKit/channels-sdk) is one implementation of it, and [OpenTag](https://github.com/CopilotKit/OpenTag) is a complete application built on it: a Python LangGraph agent served over AG-UI into Slack and Microsoft Teams. In that application the agent and its runtime are self-hosted, while platform credentials and message delivery are handled by CopilotKit Intelligence — a hosted service, not part of the protocol. [OpenBot](https://open.bot) is a complete application on the client side of the protocol: an open-source workspace for AI coworkers that connects to any AG-UI agent endpoint. Each coworker can get a computer of its own, with a browser, files and tools, and every action it takes is checked against the deployment's policy before it runs. You host the application yourself. It keeps threads and memory in CopilotKit Intelligence, a separate service that is not part of the protocol, which CopilotKit can host for you or you can run yourself. *** ## Quick Start Choose the path that fits your needs: > [card] [Build agentic applications](/quickstart/applications) > > Build agentic applications powered by AG-UI compatible agents. > [card] [Build new AG-UI integrations](/quickstart/introduction) > > Build integrations for new agent frameworks, custom in-house solutions, or use AG-UI without any agent framework. > [card] [Build AG-UI compatible clients](/quickstart/clients) > > Build new clients for AG-UI-compatible agents (web, mobile, slack, messaging, etc.) ## Explore AG-UI Dive deeper into AG-UI's core concepts and capabilities: > [card] [Core architecture](/concepts/architecture) > > Understand how AG-UI connects agents, protocols, and front-ends > [card] [Events](/concepts/events) > > Learn about AG-UI's event-driven protocol ## Resources Explore guides, tools, and integrations to help you build, optimize, and extend your AG-UI implementation. These resources cover everything from practical development workflows to debugging techniques. > [card] [Developing with Cursor](/tutorials/cursor) > > Use Cursor to build AG-UI implementations faster > [card] [Troubleshooting AG-UI](/tutorials/debugging) > > Fix common issues when working with AG-UI servers and clients ## Contributing Want to contribute? Check out our [Contributing Guide](/development/contributing) to learn how you can help improve AG-UI. ## Support and Feedback Here's how to get help or provide feedback: * For bug reports and feature requests related to the AG-UI specification, SDKs, or documentation (open source), please [create a GitHub issue](https://github.com/ag-ui-protocol/ag-ui/issues) * For discussions or Q\&A about AG-UI, please join the [Discord community](https://discord.gg/Jd3FzfdJa8) * For production architecture or implementation help, see [Production support](/talk-to-us) # Migrating to 1.0 Source: https://docs.ag-ui.com/migrating-to-1-0 What changes for an application or integration moving to AG-UI 1.0, per SDK — and what does not AG-UI 1.0 is the first release with a specification behind it: the [schema](/spec/1.0/schema) is the single source of truth, every SDK's protocol types are generated from it, and the behavioural rules that used to live only in the TypeScript client are [written down](/spec/1.0). Most of what changed is additive. This page is the part that is not — what you have to touch, per SDK, and what keeps working on its own. The protocol-level changes are listed in the specification's [Key Changes](/spec/1.0/changelog); this page does not repeat them, it says what each one means for code you already have. ## What keeps working **A 0.x agent keeps working against a 1.0 client.** The TypeScript client carries an always-on compatibility boundary that translates the retired shapes it meets on an incoming stream — the five `THINKING_*` events become their `REASONING_*` equivalents, a legacy `binary` content part becomes the media part its media type names, and historically accepted whole optional `null` fields become absent before validation. Every inbound translation warns. These shims are recorded, with their replacements and their expiry, in [`DEPRECATIONS.md`](https://github.com/ag-ui-protocol/ag-ui/blob/main/DEPRECATIONS.md). Their expiry dates are provisional — twelve months from when each shim was written, re-dated at the 1.0 release if it slips — after which the retired shapes stop working entirely. **A 1.0 agent keeps working against a 0.x client.** Everything 1.0 adds — `RUN_FINISHED.outcome`, the subagent events, `protocolVersion`, activity events — is optional or is a new event type, and a client from before the version field ignores what it does not know. **The wire is the same.** JSON over SSE and the protobuf binding are unchanged in framing. The field names you send today are the field names 1.0 reads, with the exceptions on this page. *** ## Producers: declare your version A 1.0 producer MUST declare the protocol version it speaks on `RUN_STARTED` ([Versioning](/spec/1.0/basic/versioning)). The field is optional in the schema, because its absence is how a pre-1.0 producer is recognised. That means no SDK adds it for you when you build the event yourself. Use the SDK's constant, never a hand-typed string, and never echo the version from `RunAgentInput.protocolVersion`: the declaration states the producer's own version. **Python:** pass `protocol_version` when you construct the event. ```python theme={null} from ag_ui.core import PROTOCOL_VERSION, RunStartedEvent RunStartedEvent( thread_id=input.thread_id, run_id=input.run_id, protocol_version=PROTOCOL_VERSION, ) ``` **TypeScript:** set `protocolVersion` on the `RUN_STARTED` object. ```ts theme={null} import { EventType, PROTOCOL_VERSION, type RunStartedEvent } from "@ag-ui/core"; const runStarted: RunStartedEvent = { type: EventType.RUN_STARTED, threadId: input.threadId, runId: input.runId, protocolVersion: PROTOCOL_VERSION, }; ``` **.NET:** `AGUI.Server` builds `RUN_STARTED` for you when it converts a `ChatResponseUpdate` stream, and that event carries `AGUIProtocol.Version`. There is nothing to add. If you emit your own `RunStartedEvent` (as an update's `RawRepresentation`), it passes through unchanged, so set the field yourself: ```csharp theme={null} new RunStartedEvent { ThreadId = threadId, RunId = runId, ProtocolVersion = AGUIProtocol.Version, }; ``` **Middlewares and proxies** that pass a producer's `RUN_STARTED` through must leave its `protocolVersion` untouched. Don't add one, overwrite it, or remove it. The declaration names what the stream speaks, so a forwarder reports the original producer's version. Only a layer that translates the stream into a different version declares the version it emits. *** ## TypeScript The TypeScript SDK takes the most change of the three, because it is where the protocol's behaviour was previously defined. ### Omit whole optional null fields Optional protocol fields must be absent instead of `null`, including `rawEvent`, `RUN_FINISHED.result`, and `forwardedProps`. `EventEncoder` and outgoing HTTP inputs omit these fields before transmission. Emit the canonical shape from in-memory agents too. Older producers remain compatible for a fixed set of whole optional `null` fields. Before validation, the client translates each to an absent field and warns once per field per run for `rawEvent` and `subagentRunId` on any event, `RUN_STARTED.parentRunId`, `RUN_STARTED.input`, `RUN_FINISHED.result`, `RUN_FINISHED.outcome.pendingToolCallIds`, `provider`, `reasoningTokens`, `cachedInputTokens` and `cacheWriteInputTokens` in `RUN_FINISHED.usage[]`, `RUN_ERROR.usage`, `SUBAGENT_FINISHED.result`, `TOOL_CALL_RESULT.role`, resume-entry `payload`, media-part `metadata` (`image`, `audio`, `video`, `document`), tool `parameters`, and `RunAgentInput.forwardedProps`. This applies to in-memory runs, reconnects, SSE, and protobuf, including messages and input echoes embedded in events. Subscribers receive the normalized event: for example, an old `RUN_FINISHED.result: null` becomes an absent result. This is client tolerance, not a protocol change: producers must still omit these fields. Direct schema validation remains strict and does not invoke the client's event compatibility boundary. Request handlers accepting older `RunAgentInput` values must locally omit historically accepted optional nulls before calling `RunAgentInputSchema.parse`: `forwardedProps`, `tools[].parameters`, `resume[].payload`, and `metadata` on image/audio/video/document parts in `messages[].content[]`. Do not recursively strip nulls from application data or remove nulls from fields that already rejected them. CopilotKit's shared run/connect request parser handles this as a local compatibility exception; AG-UI does not expose a public request-normalization helper. The existing `RunAgentInput.state: null` parser tolerance still yields `undefined`. This compatibility is limited to the fields listed above. Event or message `metadata: null` remains invalid, as do other optional nulls such as `TEXT_MESSAGE_START.name`, `RUN_ERROR.code`, `usage[].model`, `RunAgentInput.parentRunId`, and a `subagentRunId: null` nested in a message. Required JSON payloads such as `CUSTOM.value`, and null values inside state, metadata, or other application data, remain valid and are preserved. ### zod is an optional peer dependency of `@ag-ui/core` `@ag-ui/core`'s main entry is types and constants only and never loads zod. The validators live on a subpath, and importing them requires zod (`3.25.18` or newer, or any `4.x`): ```ts theme={null} // before import { EventSchemas, UserMessageSchema, EventType } from "@ag-ui/core"; // after import { EventType } from "@ag-ui/core"; import { EventSchemas, UserMessageSchema } from "@ag-ui/core/schemas"; ``` ```bash theme={null} npm install @ag-ui/core # types only — nothing else to install npm install @ag-ui/core zod # if you use the validators ``` Every validator moves, including the historic aliases (`EventSchemas`, the `*InputPartSchema` names, `OptionalMetadataSchema`) and the capability schemas. `@ag-ui/client` no longer re-exports them either — it re-exports core's main entry, and they are not on it. `@ag-ui/client` itself still needs zod, because its enforcement stage validates with it. It takes zod as a peer dependency (as do `@ag-ui/encoder` and `@ag-ui/proto`), so install zod alongside it: ```bash theme={null} npm install @ag-ui/client zod ``` ### Unknown material no longer reaches your code The client has one enforcement stage, after middleware, on every path. An event whose type nothing recognises is dropped with a warning. An unknown property on a known event is stripped with a warning naming its path. A *malformed* value on a field the protocol defines — a number where a string belongs — fails the run, as it always did. If you were reading a non-standard property off an event in a subscriber, it is gone before the subscriber sees it. The sanctioned channel for extra data is `metadata`, which is open by key on every event and message and is never stripped. The same applies to what you send: unknown keys on `RunAgentInput` are stripped before transmission. The field that exists for exactly that purpose is `forwardedProps`, which carries any JSON to the agent untouched. ### Behaviour that is stricter in 1.0 Two rules the client used to apply loosely, or on one path only, now apply everywhere. Each of them turns something that quietly succeeded into a run that fails. **Reasoning open/close discipline is verified.** A `REASONING_MESSAGE_CONTENT` or `REASONING_MESSAGE_END` naming a message nothing opened, a reasoning span closed without being opened, and a span or message still open when the run finishes all fail the run. Orphaned fragments and reopens were rejected for `THINKING_*` before 1.0; failing the run for a span or message left open at `RUN_FINISHED` is new, and matches what text messages and tool calls already did. Spans and reasoning messages are separate namespaces — unlike `THINKING_*`, a reasoning message does not have to sit inside a span, and a `REASONING_START` id does not open a reasoning message. **The enforcement guarantee holds on every path.** Unknown material is stripped *with* a warning and a malformed known value is fatal — and that is now true with middleware installed and over the protobuf binding, not only on the plain SSE path. Material that used to vanish silently on those paths — an unrecognised property on a chunk expanded inside a middleware chain, an unknown message role or patch operation crossing the protobuf decoder — now rides through to enforcement and is reported where it is dropped. **`RUN_ERROR` is unchanged, and that is deliberate.** A producer-sent `RUN_ERROR` is a well-formed stream — an agent honestly reporting that its run failed is not a protocol violation — so the client accepts it: the event is delivered to `onRunErrorEvent`, the stream continues, and `runAgent()` resolves, exactly as in 0.x. A `RUN_STARTED` after a `RUN_ERROR` begins a new run in the same stream, and everything that run produces is applied. If you need a rejected promise when a run reports its own failure, raise it from your own `onRunErrorEvent` subscriber; the client will not do it for you. ### Retired from the public API * The five `THINKING_*` events — enum members, types, schemas, factories. Emit `REASONING_*`; incoming `THINKING_*` streams are still translated by the boundary above. * `BinaryInputContent` (`{ type: "binary" }`). Use the media parts — `image`, `audio`, `video`, `document` — with a `source`. Incoming legacy parts are still translated. * `stripUnknown` and `StripResult` from `@ag-ui/client`. They were the enforcement stage's internals. ### Renames and type changes * `SubAgentInfo` → `SubagentInfo`, and `multiAgent.subAgents` → `subagents` on `AgentCapabilities`. The wire key changes with it and there is no alias: a declaration still carrying `subAgents` has that key treated as unknown. The protocol spells the word as one everywhere (`subagentRunId`). * `RunAgentInput.tools` and `.context` are non-optional on the TypeScript type. Absent on the wire still means empty; the SDK presents it as `[]` so nothing downstream has to narrow. * `AbstractAgent.maxVersion` is deprecated in favour of `maxProtocolVersion`. An override of the old name still works and warns once per process. * The content parts are named by what they are, not by direction: `InputContent` → `ContentPart`, `TextInputContent` → `TextPart`, `ImageInputContent` → `ImagePart` (audio, video, document alike), `InputContentSource` → `PartSource`, `InputContentDataSource` → `DataSource`, `InputContentUrlSource` → `UrlSource`, with the matching `...Schema` validators. The old names are exported as deprecated aliases of the same types and validators, so nothing breaks; the wire `type` values are unchanged. * `PartSource` has a third arm, `FileSource` (`type: "file"`): a handle the model provider issued for bytes already uploaded to it. Code that switches exhaustively on `source.type` needs a case for it. Never treat its `value` as a URL; if your agent cannot hand the handle to its provider, skip the part with a warning, as for any part the model cannot take. * `ToolMessage.content` and `ToolCallResultEvent.content` are `string | ContentPart[]`, as `UserMessage.content` already was. Code that passed a tool result straight into a string slot narrows it first; `contentToText()` from `@ag-ui/core` gives the text parts concatenated and `contentHasMedia()` says whether that flattening would lose anything. The legacy `ActionExecutionResult` bridge flattens this way and warns. * Every content part has an optional `id`, and `TextPart` has the optional `metadata` the media parts already had. * `execution.maxIterations` and `maxExecutionTime` on capabilities validate as non-negative integers within JavaScript's safe range. ### New, and worth knowing The client declares `protocolVersion: "1.0"` on every `RunAgentInput` and reads the producer's answer on `RUN_STARTED`, warning when a producer speaks a newer or uninterpretable version. Pinning `maxProtocolVersion` below this client package's own version omits the field, for a peer whose parser would reject an unknown member. Protobuf decoding merges repeated occurrences of a singular message field, including nested messages, metadata, and run inputs. Later scalar values win, repeated list entries append in order, and an omitted field preserves its earlier value. TypeScript and .NET follow the same protobuf merging rules. *** ## Python The Python SDK is producer-side: it defines the shapes an agent emits. The changes are narrower, and one of them affects anyone parsing its output. ### Absent fields are omitted, never `null` A field with no value is left out of the JSON rather than written as `null`. This shipped ahead of 1.0, in `ag-ui-protocol` 0.1.20, so it is only a migration step if you are coming from earlier than that — but it is the change most likely to affect anything that parses an agent's output. A reader doing `if payload["parentMessageId"] is None` or the equivalent needs `"parentMessageId" not in payload`. A `null` that *is* a value — under a metadata key, inside a state snapshot, a JSON Patch `add` of `null` — is preserved exactly; only a `null` standing in for a whole absent field is gone. ### Generated models replace the hand-written ones `ag_ui.core` keeps every name it exported except the ones retired and renamed below, now re-exported from the generated source, and construction is unchanged: snake\_case keyword arguments, aliases on the wire. Three behaviours differ: * Single-value literals (`type`, a fixed `role`) default to their only legal value; you no longer spell them. * Fields with schema defaults (`role` on a text message, `replace` on a patch) parse to `None` when absent rather than to a materialised default. * JSON Patch operations parse to typed operation models. The wire form is identical. Validation stays as tolerant as it was: unknown keys are kept, pydantic's default coercion applies. The strict contract is the specification's [fixture corpus](/spec/1.0/schema-files), not the SDK models. ### Read JSON Patch entries as objects Entries in `StateDeltaEvent.delta` and `ActivityDeltaEvent.patch` are typed operation models, even when you construct the event with dictionaries. Replace dictionary reads such as `patch["path"]` and `patch.get("op")` with `patch.path` and `patch.op`. The JSON sent over the wire is unchanged. If a patch library or other existing code needs dictionaries, convert each operation with `model_dump(by_alias=True)`. Keeping aliases enabled preserves wire keys such as `from` on move and copy operations. ```python theme={null} from ag_ui.core import StateDeltaEvent event = StateDeltaEvent(delta=[{"op": "replace", "path": "/count", "value": 1}]) operation = event.delta[0] assert operation.path == "/count" # Previously: event.delta[0]["path"] assert operation.op == "replace" # Previously: event.delta[0].get("op") patches = [operation.model_dump(by_alias=True) for operation in event.delta] assert patches == [{"op": "replace", "path": "/count", "value": 1}] ``` An adapter that supports both older SDK dictionaries and 1.0 models can keep a dictionary entry as-is and call `model_dump(by_alias=True)` only when the entry is a model. ### Retired * `MetadataMixin`, from `ag_ui.core`, `ag_ui.core.types`, and `ag_ui.core.events`. Generated protocol models already carry their metadata fields, so the separate helper is no longer exported. Remove the import; for a custom model that inherited it, inherit `ConfiguredBaseModel` from `ag_ui.core.types` and declare `metadata: Optional[Metadata] = None`, importing `Optional` from `typing` and `Metadata` from `ag_ui.core`. * The `THINKING_*` events. Emit `REASONING_*`. There is no producer-side shim; the TypeScript client translates old streams on its side. * `BinaryInputContent` as a *part*: `ContentPart` has no `binary` member, and a message carrying one is rejected at `RunAgentInput` validation. The class itself stays importable from `ag_ui.core` for one release (see `DEPRECATIONS.md`), so an adapter written against 0.x still imports and its legacy branch simply never runs; the SDK never constructs one. Use the media parts. ### Renamed * `SubAgentInfo` → `SubagentInfo`; `MultiAgentCapabilities.sub_agents` → `subagents` (wire key `subagents`). `SubAgentInfo` remains exported as an alias of the same class for one release; the old field and wire key do not. * `ExecutionCapabilities.max_iterations` and `max_execution_time` now reject negative values. * The content parts are named by what they are, not by direction: `InputContent` → `ContentPart`, `TextInputContent` → `TextPart`, `ImageInputContent` → `ImagePart` (audio, video, document alike), `InputContentSource` → `PartSource`, `InputContentDataSource` → `DataSource`, `InputContentUrlSource` → `UrlSource`. The old names remain exported from `ag_ui.core` as aliases of the same classes. * `PartSource` has a third arm, `FileSource` (`type="file"`): a handle the model provider issued for bytes already uploaded to it. Code that branches on the source class needs a case for it. Never treat its `value` as a URL; if your agent cannot hand the handle to its provider, skip the part with a warning, as for any part the model cannot take. * `ToolMessage.content` and `ToolCallResultEvent.content` are `Union[str, List[ContentPart]]`, as `UserMessage.content` already was. Code that treated a tool result as `str` narrows it first. Every part has an optional `id`, and `TextPart` has the optional `metadata` the media parts already had. *** ## .NET The .NET SDK jumps furthest, because it was the last still hand-writing its protocol types and because it gains a feature the other two already had. ### Generated models replace \~60 hand-written classes The surface a consumer sees is the one it saw before, minus what the schema retired: the same sealed classes, the same `[JsonPropertyName]` wire names, absent members omitted on write. .NET's tolerance of unrecognised material is unchanged — an unknown property is skipped, as `System.Text.Json` has always done — so nobody should expect a strictness that is not there. One behaviour is more tolerant than before: an event *type* the SDK has no model for is now skipped with a trace warning by both stream readers, rather than ending the stream. Decoding a single unknown event on its own still throws `AGUIUnknownEventTypeException`. ### Subagents `SUBAGENT_STARTED`, `SUBAGENT_FINISHED` and `SUBAGENT_ERROR` have models, JSON handling and protobuf mappers. `SubagentRunId` is a property on the events that can carry attribution — not on the base event — so a consumer that switches on event type reads it from the concrete type. The [subagent rules](/spec/1.0/events/subagents) say what a consumer must do with it. ### Retired * `AGUIBinaryInputContent`, its type constant, and its converter arms. A `DataContent` or `UriContent` crossing the Microsoft.Extensions.AI boundary becomes the media part its media type names, filename in the part's metadata. .NET has no middleware layer and nothing translates an incoming legacy part: a `{ "type": "binary" }` part is an unknown discriminator, and the converter throws a `JsonException` that fails deserialisation of the whole message — and of the snapshot or run input carrying it. A producer still emitting the legacy part must be upgraded before a 1.0 .NET consumer can read it. ### Renames and type changes * `SubAgentInfo` → `SubagentInfo`; `MultiAgentCapabilities.SubAgents` → `Subagents` (wire key `subagents`). No alias; the old key is skipped. * `AgentCapabilities.Custom` and `IdentityCapabilities.Metadata` are `JsonElement?` rather than `IDictionary?`. Build them with `JsonSerializer.SerializeToElement(...)`. * `ExecutionCapabilities.MaxIterations` and `MaxExecutionTime` are `long?` rather than `int?`. * `Tool.Parameters` and `RunAgentInput.ForwardedProperties` (wire key `forwardedProps`) are nullable `JsonElement?`, as the schema says; a message's `Id` is a non-nullable string that is always written. * `AGUIUserContent` → `AGUIContent`: the string-or-parts union is no longer the user message's alone. `AGUIToolMessage.Content` and `ToolCallResultEvent.Content` are `AGUIContent` rather than `string`; a string still assigns implicitly, and reading one back is `Content.Value as string` or a walk over its parts. The part classes keep their `AGUI...InputContent` names even though the schema renamed them to `...Part`. Every part has an optional `Id`, and `AGUITextInputContent` has the optional `Metadata` the media parts already had. * `AGUIInputContentSource` has a third subclass, `AGUIInputContentFileSource` (`type: "file"`, constant `AGUIInputContentSourceTypes.File`): a handle the model provider issued for bytes already uploaded to it, with optional `Provider` and `MimeType`. A `switch` over the source hierarchy needs a case for it. Never treat its `Value` as a URL; if your agent cannot hand the handle to its provider, skip the part with a warning, as for any part the model cannot take. ### AOT The source-generated serializer context is the only resolver under ahead-of-time compilation. Tool results are serialized by their runtime type, and a result of a type the context does not register throws `NotSupportedException`. `int` is registered explicitly in 1.0 — it had previously been present only by accident. *** ## Where to look next * The [Key Changes](/spec/1.0/changelog) page for the protocol-level differences, with links into the specification. * [Capabilities](/spec/1.0/basic/capabilities) for what a declaration obliges now that it is specified. * [Versioning and compatibility](/spec/1.0/basic/versioning) for how a 1.0 peer talks to an older one. # Build applications Source: https://docs.ag-ui.com/quickstart/applications Build agentic applications utilizing compatible event AG-UI event streams # Introduction AG-UI provides a concise, event-driven protocol that lets any agent stream rich, structured output to any client. It can be used to connect any agentic system to any client. A client is defined as any system that can receieve, display, and respond to AG-UI events. For more information on existing clients and integrations, see the [integrations](/integrations) page. # Automatic Setup AG-UI provides a CLI tool to automatically create or scaffold a new application with any client and server. ```sh theme={null} npx create-ag-ui-app@latest ``` ![](https://copilotkit-public-assets.s3.us-east-1.amazonaws.com/docs/ag-ui/quickstart.gif) Once the setup is done, start the server with ```sh theme={null} npm run dev ``` For the CopilotKit example, open [http://localhost:3000](http://localhost:3000) to see the app in action. # Build clients Source: https://docs.ag-ui.com/quickstart/clients Showcase: build a conversational CLI agent from scratch using AG-UI and Mastra # Introduction A client implementation allows you to **build conversational applications that leverage AG-UI's event-driven protocol**. This approach creates a direct interface between your users and AI agents, demonstrating direct access to the AG-UI protocol. A client does not have to be a web application. AG-UI describes an event stream rather than a rendering target, so anything that can consume those events and present them to a user is a client: a web app, the terminal client built below, a mobile app, or a chat platform such as Slack or Microsoft Teams. The [Channels SDK](https://github.com/CopilotKit/channels-sdk) is one chat-platform implementation, with [OpenTag](https://github.com/CopilotKit/OpenTag) as a worked example. ## When to use a client implementation Building your own client is useful if you want to explore/hack on the AG-UI protocol. For production use, use a full-featured client like [CopilotKit](https://copilotkit.ai). ## What you'll build In this guide, we'll create a CLI client that: 1. Uses the `MastraAgent` from `@ag-ui/mastra` 2. Connects to OpenAI's GPT-4o model 3. Implements a weather tool for real-world functionality 4. Provides an interactive chat interface in the terminal Let's get started! ## Prerequisites Before we begin, make sure you have: * [Node.js](https://nodejs.org/) **22.13.0 or later** * An **OpenAI API key** * [pnpm](https://pnpm.io/) package manager ### 1. Provide your OpenAI API key First, let's set up your API key: ```bash theme={null} # Set your OpenAI API key export OPENAI_API_KEY=your-api-key-here ``` ### 2. Install pnpm If you don't have pnpm installed: ```bash theme={null} # Install pnpm npm install -g pnpm ``` ## Step 1 – Initialize your project Create a new directory for your AG-UI client: ```bash theme={null} mkdir my-ag-ui-client cd my-ag-ui-client ``` Initialize a new Node.js project: ```bash theme={null} pnpm init ``` ### Set up TypeScript and basic configuration Install TypeScript and essential development dependencies: ```bash theme={null} pnpm add -D typescript @types/node tsx ``` Create a `tsconfig.json` file: ```json theme={null} { "compilerOptions": { "target": "ES2022", "module": "commonjs", "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` Update your `package.json` scripts: ```json theme={null} { "scripts": { "start": "tsx src/index.ts", "dev": "tsx --watch src/index.ts", "build": "tsc", "clean": "rm -rf dist" } } ``` ## Step 2 – Install AG-UI and dependencies Install the core AG-UI packages and dependencies: ```bash theme={null} # Core AG-UI packages pnpm add @ag-ui/client @ag-ui/core @ag-ui/mastra # Mastra ecosystem packages pnpm add @mastra/core @mastra/client-js @mastra/memory @mastra/libsql # Mastra peer dependencies pnpm add zod ``` ## Step 3 – Create your agent Let's create a basic conversational agent. Create `src/agent.ts`: ```typescript theme={null} import { Agent } from "@mastra/core/agent" import { MastraAgent } from "@ag-ui/mastra" import { Memory } from "@mastra/memory" import { LibSQLStore } from "@mastra/libsql" export const agent = new MastraAgent({ resourceId: "cliExample", agent: new Agent({ id: "ag-ui-assistant", name: "AG-UI Assistant", instructions: ` You are a helpful AI assistant. Be friendly, conversational, and helpful. Answer questions to the best of your ability and engage in natural conversation. `, model: "openai/gpt-4o", memory: new Memory({ storage: new LibSQLStore({ id: "storage-memory", url: "file:./assistant.db", }), }), }), threadId: "main-conversation", }) ``` ### What's happening in the agent? 1. **MastraAgent** – We wrap a Mastra Agent with the AG-UI protocol adapter 2. **Model Configuration** – We use OpenAI's GPT-4o for high-quality responses 3. **Memory Setup** – We configure persistent memory using LibSQL for conversation context 4. **Instructions** – We give the agent basic guidelines for helpful conversation ## Step 4 – Create the CLI interface Now let's create the interactive chat interface. Create `src/index.ts`: ```typescript theme={null} import * as readline from "readline" import { agent } from "./agent" import { randomUUID } from "@ag-ui/client" const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }) async function chatLoop() { console.log("🤖 AG-UI Assistant started!") console.log("Type your messages and press Enter. Press Ctrl+D to quit.\n") return new Promise((resolve) => { const promptUser = () => { rl.question("> ", async (input) => { if (input.trim() === "") { promptUser() return } console.log("") // Pause input while processing rl.pause() // Add user message to conversation agent.messages.push({ id: randomUUID(), role: "user", content: input.trim(), }) try { // Run the agent with event handlers await agent.runAgent( {}, // No additional configuration needed { onTextMessageStartEvent() { process.stdout.write("🤖 Assistant: ") }, onTextMessageContentEvent({ event }) { process.stdout.write(event.delta) }, onTextMessageEndEvent() { console.log("\n") }, } ) } catch (error) { console.error("❌ Error:", error) } // Resume input rl.resume() promptUser() }) } // Handle Ctrl+D to quit rl.on("close", () => { console.log("\n👋 Thanks for using AG-UI Assistant!") resolve() }) promptUser() }) } async function main() { await chatLoop() } main().catch(console.error) ``` ### What's happening in the CLI interface? 1. **Readline Interface** – We create an interactive prompt for user input 2. **Message Management** – We add each user input to the agent's conversation history 3. **Event Handling** – We listen to AG-UI events to provide real-time feedback 4. **Streaming Display** – We show the agent's response as it's being generated ## Step 5 – Test your assistant Let's run your new AG-UI client: ```bash theme={null} pnpm dev ``` You should see: ``` 🤖 AG-UI Assistant started! Type your messages and press Enter. Press Ctrl+D to quit. > ``` Try asking questions like: * "Hello! How are you?" * "What can you help me with?" * "Tell me a joke" * "Explain quantum computing in simple terms" You'll see the agent respond with streaming text in real-time! ## Step 6 – Understanding the AG-UI event flow Let's break down what happens when you send a message: 1. **User Input** – You type a question and press Enter 2. **Message Added** – Your input is added to the conversation history 3. **Agent Processing** – The agent analyzes your request and formulates a response 4. **Response Generation** – The agent streams its response back 5. **Streaming Output** – You see the response appear word by word ### Event types you're handling: * `onTextMessageStartEvent` – Agent starts responding * `onTextMessageContentEvent` – Each chunk of the response * `onTextMessageEndEvent` – Response is complete ## Step 7 – Add tool functionality Now that you have a working chat interface, let's add some real-world capabilities by creating tools. We'll start with a weather tool. ### Create your first tool Let's create a weather tool that your agent can use. Create the directory structure: ```bash theme={null} mkdir -p src/tools ``` Create `src/tools/weather.tool.ts`: ```typescript theme={null} import { createTool } from "@mastra/core/tools" import { z } from "zod" interface GeocodingResponse { results: { latitude: number longitude: number name: string }[] } interface WeatherResponse { current: { time: string temperature_2m: number apparent_temperature: number relative_humidity_2m: number wind_speed_10m: number wind_gusts_10m: number weather_code: number } } export const weatherTool = createTool({ id: "get-weather", description: "Get current weather for a location", inputSchema: z.object({ location: z.string().describe("City name"), }), outputSchema: z.object({ temperature: z.number(), feelsLike: z.number(), humidity: z.number(), windSpeed: z.number(), windGust: z.number(), conditions: z.string(), location: z.string(), }), execute: async (inputData) => { return await getWeather(inputData.location) }, }) const getWeather = async (location: string) => { const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent( location )}&count=1` const geocodingResponse = await fetch(geocodingUrl) const geocodingData = (await geocodingResponse.json()) as GeocodingResponse if (!geocodingData.results?.[0]) { throw new Error(`Location '${location}' not found`) } const { latitude, longitude, name } = geocodingData.results[0] const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code` const response = await fetch(weatherUrl) const data = (await response.json()) as WeatherResponse return { temperature: data.current.temperature_2m, feelsLike: data.current.apparent_temperature, humidity: data.current.relative_humidity_2m, windSpeed: data.current.wind_speed_10m, windGust: data.current.wind_gusts_10m, conditions: getWeatherCondition(data.current.weather_code), location: name, } } function getWeatherCondition(code: number): string { const conditions: Record = { 0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", 45: "Foggy", 48: "Depositing rime fog", 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle", 56: "Light freezing drizzle", 57: "Dense freezing drizzle", 61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", 66: "Light freezing rain", 67: "Heavy freezing rain", 71: "Slight snow fall", 73: "Moderate snow fall", 75: "Heavy snow fall", 77: "Snow grains", 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers", 85: "Slight snow showers", 86: "Heavy snow showers", 95: "Thunderstorm", 96: "Thunderstorm with slight hail", 99: "Thunderstorm with heavy hail", } return conditions[code] || "Unknown" } ``` ### What's happening in the weather tool? 1. **Tool Definition** – We use `createTool` from Mastra to define the tool's interface 2. **Input Schema** – We specify that the tool accepts a location string 3. **Output Schema** – We define the structure of the weather data returned 4. **API Integration** – We fetch data from Open-Meteo's free weather API 5. **Data Processing** – We convert weather codes to human-readable conditions ### Update your agent Now let's update our agent to use the weather tool. Update `src/agent.ts`: ```typescript theme={null} import { weatherTool } from "./tools/weather.tool" // <--- Import the tool export const agent = new MastraAgent({ agent: new Agent({ // ... tools: { weatherTool }, // <--- Add the tool to the agent // ... }), threadId: "main-conversation", }) ``` ### Update your CLI to handle tools Update your CLI interface in `src/index.ts` to handle tool events: ```typescript theme={null} // Add these new event handlers to your agent.runAgent call: await agent.runAgent( {}, // No additional configuration needed { // ... existing event handlers ... onToolCallStartEvent({ event }) { console.log("🔧 Tool call:", event.toolCallName) }, onToolCallArgsEvent({ event }) { process.stdout.write(event.delta) }, onToolCallEndEvent() { console.log("") }, onToolCallResultEvent({ event }) { if (event.content) { console.log("🔍 Tool call result:", event.content) } }, } ) ``` ### Test your weather tool Now restart your application and try asking about weather: ```bash theme={null} pnpm dev ``` Try questions like: * "What's the weather like in London?" * "How's the weather in Tokyo today?" * "Is it raining in Seattle?" You'll see the agent use the weather tool to fetch real data and provide detailed responses! ## Step 8 – Add more functionality ### Create a browser tool Let's add a web browsing capability. First install the `open` package: ```bash theme={null} pnpm add open ``` Create `src/tools/browser.tool.ts`: ```typescript theme={null} import { createTool } from "@mastra/core/tools" import { z } from "zod" import { open } from "open" export const browserTool = createTool({ id: "open-browser", description: "Open a URL in the default web browser", inputSchema: z.object({ url: z.url().describe("The URL to open"), }), outputSchema: z.object({ success: z.boolean(), message: z.string(), }), execute: async (inputData) => { try { await open(inputData.url) return { success: true, message: `Opened ${inputData.url} in your default browser`, } } catch (error) { return { success: false, message: `Failed to open browser: ${error}`, } } }, }) ``` ### Update your agent with both tools Update `src/agent.ts` to include both tools: ```typescript theme={null} import { Agent } from "@mastra/core/agent" import { MastraAgent } from "@ag-ui/mastra" import { Memory } from "@mastra/memory" import { LibSQLStore } from "@mastra/libsql" import { weatherTool } from "./tools/weather.tool" import { browserTool } from "./tools/browser.tool" export const agent = new MastraAgent({ resourceId: "cliExample", agent: new Agent({ id: "ag-ui-assistant", name: "AG-UI Assistant", instructions: ` You are a helpful assistant with weather and web browsing capabilities. For weather queries: - Always ask for a location if none is provided - Use the weatherTool to fetch current weather data For web browsing: - Always use full URLs (e.g., "https://www.google.com") - Use the browserTool to open web pages Be friendly and helpful in all interactions! `, model: "openai/gpt-4o", tools: { weatherTool, browserTool }, // Add both tools memory: new Memory({ storage: new LibSQLStore({ id: "storage-memory", url: "file:./assistant.db", }), }), }), threadId: "main-conversation", }) ``` Now you can ask your assistant to open websites: "Open Google for me" or "Show me the weather website". ## Step 9 – Deploy your client ### Building your client Create a production build: ```bash theme={null} pnpm build ``` ### Create a startup script Add to your `package.json`: ```json theme={null} { "bin": { "weather-assistant": "./dist/index.js" } } ``` Add a shebang to your built `dist/index.js`: ```javascript theme={null} #!/usr/bin/env node // ... rest of your compiled code ``` Make it executable: ```bash theme={null} chmod +x dist/index.js ``` ### Link globally Install your CLI globally: ```bash theme={null} pnpm link --global ``` Now you can run `weather-assistant` from anywhere! ## Extending your client Your AG-UI client is now a solid foundation. Here are some ideas for enhancement: ### Add more tools * **Calculator tool** – For mathematical operations * **File system tool** – For reading/writing files * **API tools** – For connecting to other services * **Database tools** – For querying data ### Improve the interface * **Rich formatting** – Use libraries like `chalk` for colored output * **Progress indicators** – Show loading states for long operations * **Configuration files** – Allow users to customize settings * **Command-line arguments** – Support different modes and options ### Add persistence * **Conversation history** – Save and restore chat sessions * **User preferences** – Remember user settings * **Tool results caching** – Cache expensive API calls ## Share your client Built something useful? Consider sharing it with the community: 1. **Open source it** – Publish your code on GitHub 2. **Publish to npm** – Make it installable via `npm install` 3. **Create documentation** – Help others understand and extend your work 4. **Join discussions** – Share your experience in the [AG-UI GitHub Discussions](https://github.com/orgs/ag-ui-protocol/discussions) ## Conclusion You've built a complete AG-UI client from scratch! Your weather assistant demonstrates the core concepts: * **Event-driven architecture** with real-time streaming * **Tool integration** for real-world functionality * **Conversation memory** for context retention * **Interactive CLI interface** for user engagement From here, you can extend your client to support any use case – from simple CLI tools to complex conversational applications. The AG-UI protocol provides the foundation, and your creativity provides the possibilities. Happy building! 🚀 # Introduction Source: https://docs.ag-ui.com/quickstart/introduction Learn how to get started building an AG-UI integration