TanStack AI implements the AG-UI (Agent-User Interaction) Protocol, an open, lightweight, event-based protocol that standardizes how AI agents connect to user-facing applications.
All streaming responses in TanStack AI consist of a series of AG-UI Events - discrete JSON objects representing different stages of the conversation lifecycle. These events enable real-time updates for content generation, tool calls, thinking/reasoning, and completion signals.
All AG-UI events share a common base structure:
interface BaseAGUIEvent {
type: AGUIEventType;
timestamp: number; // Unix timestamp in milliseconds
model?: string; // Model identifier (TanStack AI addition)
rawEvent?: unknown; // Original provider event for debugging
}
type AGUIEventType =
| 'RUN_STARTED' // Run lifecycle begins
| 'RUN_FINISHED' // Run completed successfully
| 'RUN_ERROR' // Error occurred
| 'TEXT_MESSAGE_START' // Text message begins
| 'TEXT_MESSAGE_CONTENT' // Text content streaming
| 'TEXT_MESSAGE_END' // Text message completes
| 'TOOL_CALL_START' // Tool invocation begins
| 'TOOL_CALL_ARGS' // Tool arguments streaming
| 'TOOL_CALL_END' // Tool call completes (with result)
| 'STEP_STARTED' // Thinking/reasoning step begins
| 'STEP_FINISHED' // Thinking/reasoning step completes
| 'STATE_SNAPSHOT' // Full state synchronization
| 'STATE_DELTA' // Incremental state update
| 'CUSTOM'; // Custom extensibility events
Only AG-UI event types are supported; previous legacy chunk formats are no longer accepted.
Emitted when a run begins. This is the first event in any streaming response.
interface RunStartedEvent extends BaseAGUIEvent {
type: 'RUN_STARTED';
runId: string; // Unique identifier for this run
threadId?: string; // Optional thread/conversation ID
}
Example:
{
"type": "RUN_STARTED",
"runId": "run_abc123",
"model": "gpt-4o",
"timestamp": 1701234567890
}
Emitted when a run completes successfully.
interface RunFinishedEvent extends BaseAGUIEvent {
type: 'RUN_FINISHED';
runId: string;
finishReason: 'stop' | 'length' | 'content_filter' | 'tool_calls' | null;
usage?: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
}
Example:
{
"type": "RUN_FINISHED",
"runId": "run_abc123",
"model": "gpt-4o",
"timestamp": 1701234567900,
"finishReason": "stop",
"usage": {
"promptTokens": 100,
"completionTokens": 50,
"totalTokens": 150
}
}
Emitted when an error occurs during a run.
interface RunErrorEvent extends BaseAGUIEvent {
type: 'RUN_ERROR';
runId?: string;
error: {
message: string;
code?: string;
};
}
Example:
{
"type": "RUN_ERROR",
"runId": "run_abc123",
"model": "gpt-4o",
"timestamp": 1701234567890,
"error": {
"message": "Rate limit exceeded",
"code": "rate_limit"
}
}
Emitted when a text message starts.
interface TextMessageStartEvent extends BaseAGUIEvent {
type: 'TEXT_MESSAGE_START';
messageId: string;
role: 'assistant';
}
Emitted when text content is generated (streaming tokens).
interface TextMessageContentEvent extends BaseAGUIEvent {
type: 'TEXT_MESSAGE_CONTENT';
messageId: string;
delta: string; // The incremental content token
content?: string; // Full accumulated content so far
}
Example:
{
"type": "TEXT_MESSAGE_CONTENT",
"messageId": "msg_abc123",
"model": "gpt-4o",
"timestamp": 1701234567890,
"delta": "Hello",
"content": "Hello"
}
Emitted when a text message completes.
interface TextMessageEndEvent extends BaseAGUIEvent {
type: 'TEXT_MESSAGE_END';
messageId: string;
}
Emitted when a tool call starts.
interface ToolCallStartEvent extends BaseAGUIEvent {
type: 'TOOL_CALL_START';
toolCallId: string;
toolName: string;
index?: number; // Index for parallel tool calls
}
Emitted when tool call arguments are streaming.
interface ToolCallArgsEvent extends BaseAGUIEvent {
type: 'TOOL_CALL_ARGS';
toolCallId: string;
delta: string; // Incremental JSON arguments delta
args?: string; // Full accumulated arguments so far
}
Emitted when a tool call completes.
interface ToolCallEndEvent extends BaseAGUIEvent {
type: 'TOOL_CALL_END';
toolCallId: string;
toolName: string;
input?: unknown; // Final parsed input arguments
result?: string; // Tool execution result (if executed)
}
Emitted when a thinking/reasoning step starts.
interface StepStartedEvent extends BaseAGUIEvent {
type: 'STEP_STARTED';
stepId: string;
stepType?: string; // e.g., 'thinking', 'planning'
}
Emitted when a thinking/reasoning step finishes.
interface StepFinishedEvent extends BaseAGUIEvent {
type: 'STEP_FINISHED';
stepId: string;
delta?: string; // Incremental thinking content
content?: string; // Full accumulated thinking content
}
Content Generation:
RUN_STARTED
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT (delta: "Hello")
TEXT_MESSAGE_CONTENT (delta: " world")
TEXT_MESSAGE_CONTENT (delta: "!")
TEXT_MESSAGE_END
RUN_FINISHED (finishReason: "stop")
With Thinking:
RUN_STARTED
STEP_STARTED (stepType: "thinking")
STEP_FINISHED (delta: "I need to...")
STEP_FINISHED (delta: " check the weather")
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT (delta: "Let me check")
TEXT_MESSAGE_END
RUN_FINISHED (finishReason: "stop")
Tool Usage:
RUN_STARTED
TOOL_CALL_START (name: "get_weather")
TOOL_CALL_ARGS / TOOL_CALL_END (result: "{...}")
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT (delta: "The weather is...")
TEXT_MESSAGE_END
RUN_FINISHED (finishReason: "stop")
Client Tool with Approval:
RUN_STARTED
TOOL_CALL_START (name: "send_email")
TOOL_CALL_ARGS / TOOL_CALL_END
CUSTOM (name: "approval-requested")
[User approves]
[Client executes]
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT (delta: "Email sent successfully")
TEXT_MESSAGE_END
RUN_FINISHED (finishReason: "stop")
When the model calls multiple tools in parallel:
RUN_STARTED
TOOL_CALL_START (index: 0, name: "get_weather")
TOOL_CALL_START (index: 1, name: "get_time")
TOOL_CALL_END (toolCallId: "call_1", result: "...")
TOOL_CALL_END (toolCallId: "call_2", result: "...")
TEXT_MESSAGE_START
TEXT_MESSAGE_CONTENT (delta: "Based on the data...")
TEXT_MESSAGE_END
RUN_FINISHED (finishReason: "stop")
All chunks are represented as the AG-UI event union (StreamChunk = AGUIEvent):
type StreamChunk =
| RunStartedEvent
| RunFinishedEvent
| RunErrorEvent
| TextMessageStartEvent
| TextMessageContentEvent
| TextMessageEndEvent
| ToolCallStartEvent
| ToolCallArgsEvent
| ToolCallEndEvent
| StepStartedEvent
| StepFinishedEvent
| StateSnapshotEvent
| StateDeltaEvent
| CustomEvent;
This enables type-safe handling in TypeScript:
function handleChunk(chunk: StreamChunk) {
switch (chunk.type) {
case 'TEXT_MESSAGE_CONTENT':
console.log(chunk.delta);
break;
case 'STEP_FINISHED':
console.log(chunk.content);
break;
case 'TOOL_CALL_START':
console.log(chunk.toolName);
break;
// ... other cases
}
}