The AG-UI protocol add-on mounted alongside the native contract.
AG-UI is a standardized streaming protocol for connecting front-end applications to AI agents. It defines a set of event types that the agent emits over a Server-Sent Events (SSE) stream as a run progresses. Enabling the AG-UI add-on gives you a path-compatible endpoint that any AG-UI client can connect to, including CopilotKit and other frameworks built on the protocol.
Enable it in your manifest:
http_server {
protocol {
ag_ui {}
}
}The default base path is /ag-ui, and the run endpoint is served at /ag-ui/run. You can change
the base path:
protocol {
ag_ui {
path = "/custom-path"
}
}With a custom base path, the run endpoint becomes <path>/run.
/ag-ui/runSend a POST request to the run endpoint under the configured base path (default /ag-ui/run).
The request body follows the AG-UI RunAgentInput shape.
Optional session ID to continue. If omitted, a new session is created.
Optional identifier for this run. Generated automatically if not provided.
Conversation messages. A run accepts system, user and tool messages. Messages with any
other role are ignored.
Tool definitions supplied in the run request.
Context variables supplied in the run request.
Optional initial or updated state. Applied to the context field of the agent state.
Additional properties forwarded to the agent.
Optional interrupt to resume. Supply at most one entry.
The role of the message sender.
userOptional message ID. Generated automatically if not provided.
The content of the message, as text or as a list of content blocks.
Optional name of the message sender.
The role of the message sender.
assistantOptional message ID. Generated automatically if not provided.
Optional text content of the message.
Optional name of the message sender.
Optional tool calls the assistant requested.
The role of the message sender.
systemOptional message ID. Generated automatically if not provided.
The text content of the message.
Optional name of the message sender.
The role of the message sender.
toolOptional message ID. Generated automatically if not provided.
The result of the tool call.
The ID of the tool call this message answers.
Optional error from the tool call.
The role of the message sender.
reasoningOptional message ID. Generated automatically if not provided.
The reasoning text.
Optional encrypted reasoning, passed through unchanged.
The block type.
textThe text.
The block type.
imageWhere the image's bytes come from.
The block type.
audioWhere the audio's bytes come from.
The block type.
videoWhere the video's bytes come from.
The block type.
documentWhere the document's bytes come from.
The source type.
dataThe base64-encoded bytes.
The MIME type of the bytes.
The source type.
urlThe URL of the bytes.
The MIME type of the bytes.
The tool call ID.
The tool call type.
functionThe function to call.
The tool name.
The tool arguments, as a JSON-encoded string.
The tool name.
A description of the tool.
Optional JSON Schema object describing the tool's parameters. Defaults to an empty object schema.
The interrupted run's ID.
Set to resolved.
Optional value returned to the interrupted run.
What the variable represents.
The variable value.
curl -X POST http://localhost:8080/ag-ui/run \
-H "Content-Type: application/json" \
-d '{
"messages": [
{ "role": "user", "content": "What is 42 plus 7?" }
]
}'To continue a previous conversation, pass the threadId from a prior run. The agent loads the
checkpoint for that thread and resumes the conversation from where it left off. To resume an
interrupted run, use the same threadId with a new runId and a resume entry:
{
"threadId": "<thread ID>",
"runId": "<new run ID>",
"messages": [],
"resume": [
{
"interruptId": "<interrupted run ID>",
"status": "resolved",
"payload": { "approved": true }
}
]
}Client-supplied thread, run, and message IDs may be any strings. Events use server-generated UUIDs. Event timestamps are integer milliseconds since the Unix epoch.
RUN_FINISHED.outcome has one of these shapes:
{ "type": "success" }{ "type": "cancelled" }{
"type": "interrupt",
"interrupts": [
{
"id": "<run ID>",
"reason": "interrupt",
"metadata": { "payload": { "question": "continue?" } }
}
]
}For an interrupted run, the interrupt ID is that run's ID. If the run provides a payload, it is
available at metadata.payload.
Invalid request input receives an HTTP 400 response with the standard error body before streaming.
Once streaming has started, a failure is sent as a RUN_ERROR event.
The response is a Server-Sent Events stream. Each event has an event line and a JSON data
payload.
event: RUN_STARTED
data: {"type":"RUN_STARTED","timestamp":1710000000000,"threadId":"...","runId":"..."}
event: TEXT_MESSAGE_START
data: {"type":"TEXT_MESSAGE_START","timestamp":1710000000100,"messageId":"..."}
event: TEXT_MESSAGE_CONTENT
data: {"type":"TEXT_MESSAGE_CONTENT","timestamp":1710000000200,"messageId":"...","delta":"Hello"}
event: TEXT_MESSAGE_END
data: {"type":"TEXT_MESSAGE_END","timestamp":1710000000300,"messageId":"..."}
event: RUN_FINISHED
data: {"type":"RUN_FINISHED","timestamp":1710000000400,"threadId":"...","runId":"...","outcome":{"type":"success"}}JSON patches are used to update the agent state, often found in events like STATE_DELTA.
They follow the RFC 6902 JSON Patch standard. Each
patch operation has an op (operation), a path (location in the JSON document), and a value (the new value to apply).
The operation to perform.
addremovereplacemovecopytestThe location in the JSON document to apply the operation.
The new value to apply.
The response is a Server-Sent Events stream. Events follow the AG-UI event schema. All events
have a type field and an optional timestamp.
RUN_STARTED emitted when the run begins
RUN_FINISHED emitted when the run concludes
RUN_ERROR emitted if the run encounters an unrecoverable error
STEP_STARTED emitted at the start of each reasoning step
STEP_FINISHED emitted at the end of each reasoning stepText output from the model is streamed using a start-content-end pattern:
TEXT_MESSAGE_START begins a new message, includes the message ID and role
TEXT_MESSAGE_CONTENT a chunk of content for the current message
TEXT_MESSAGE_END marks the end of the current messageTool invocations are streamed in the same pattern:
TOOL_CALL_START begins a tool call, includes the call ID and tool name
TOOL_CALL_ARGS a chunk of the tool call arguments (streamed JSON)
TOOL_CALL_END marks the end of the tool callMESSAGES_SNAPSHOT the full conversation history at a point in time
STATE_SNAPSHOT the complete agent state (context field) at a point in time
STATE_DELTA incremental state update as RFC 6902 JSON Patch operationsSTATE_DELTA events are emitted when tools return state updates. Only the context field of the
agent state is visible to clients via these events.
CUSTOM used to deliver real-time updates from tools to the clientWhen a TypeScript tool calls input.emit?.(...), or a Python tool calls input.emit(...), during
execution, the update is delivered to the client as a CUSTOM event. Note that AG-UI defines its
own ACTIVITY_DELTA event type; agentc tool emissions are not that. They arrive as CUSTOM events
because agentc's concept of an activity update does not map to AG-UI's ACTIVITY_DELTA.
These updates are stateless and not persisted. They are intended for generative UI, such as showing progress or intermediate results while a tool is working.
For more information about the AG-UI protocol, see the AG-UI specification.
AG-UI is the protocol that CopilotKit uses to connect to backend agents. Enabling the AG-UI add-on makes your agent directly compatible with CopilotKit without any additional adapter work. See Connect a CopilotKit frontend for the connection steps.
© 2026 pogue.dev. All rights reserved.
CC BY 4.0