AG-UI
The AG-UI protocol connects your frontend to your Agno agents via event-based Server-Sent Events (SSE).
CopilotKit is built on the AG-UI protocol, a lightweight, event-based standard that defines how AI agents communicate with user-facing applications over Server-Sent Events (SSE).
Messages, state updates, tool calls, and agent lifecycle events all flow through AG-UI. Understanding this layer helps you debug and extend any CopilotKit integration.
Accessing your agent with injectAgentStore#
injectAgentStore exposes the AG-UI agent and projects its messages, state,
and run status into Angular signals:
import { Component, computed } from "@angular/core";
import { injectAgentStore } from "@copilotkit/angular";
@Component({
selector: "app-agent-status",
template: `
<p>{{ messageCount() }} messages</p>
@if (store().isRunning()) {
<p>Agent is running…</p>
}
`,
})
export class AgentStatusComponent {
readonly store = injectAgentStore("research-agent");
readonly messageCount = computed(() => this.store().messages().length);
}The resolved agent is a standard AG-UI AbstractAgent. You can read its
state, invoke protocol methods, and subscribe to its event stream.
Subscribing to AG-UI events#
Subscribe to store().agent and release the subscription with the owning
injector:
private readonly destroyRef = inject(DestroyRef);
readonly store = injectAgentStore("research-agent");
constructor() {
const subscription = this.store().agent.subscribe({
onTextMessageContentEvent({ textMessageBuffer }) {
console.log("Streaming text:", textMessageBuffer);
},
onToolCallEndEvent({ toolCallName, toolCallArgs }) {
console.log("Tool called:", toolCallName, toolCallArgs);
},
onStateChanged({ agent }) {
console.log("State changed:", agent.state);
},
});
this.destroyRef.onDestroy(() => subscription.unsubscribe());
}The callback names map directly to the AG-UI event types:
| Event | Callback |
|---|---|
| Run lifecycle | onRunStartedEvent, onRunFinishedEvent, onRunErrorEvent |
| Steps | onStepStartedEvent, onStepFinishedEvent |
| Text messages | onTextMessageStartEvent, onTextMessageContentEvent, onTextMessageEndEvent |
| Tool calls | onToolCallStartEvent, onToolCallArgsEvent, onToolCallEndEvent, onToolCallResultEvent |
| State | onStateSnapshotEvent, onStateDeltaEvent |
| Messages | onMessagesSnapshotEvent |
| Custom | onCustomEvent, onRawEvent |
| High-level changes | onMessagesChanged, onStateChanged |
The proxy pattern#
When you use CopilotKit with a runtime, your frontend does not talk directly
to the backend agent. CopilotKit discovers agents through the runtime's
/info endpoint and represents each one with a proxy that implements the
same AbstractAgent interface.
const store = injectAgentStore("default");
const agent = store().agent;
store().messages();
store().state();
agent.subscribe({ /* … */ });// injectAgentStore() → registry checks /info → resolves a proxy agent
// core.runAgent({ agent }) → runtime POST → agent execution → SSE eventsThis indirection lets the runtime provide authentication, middleware, agent routing, and CopilotKit Enterprise Intelligence without changing how the frontend interacts with agents.
How agents slot into the runtime#
On the server, CopilotRuntime accepts a map of AG-UI AbstractAgent
instances. A framework adapter, an HttpAgent pointing at a remote server,
and a custom implementation all use the same request path:
- The runtime resolves the target agent by ID.
- It clones the agent for request isolation and supplies messages, state, and thread context.
AgentRunnerexecutes the agent and receives AG-UI events.- The runtime encodes those events as SSE and streams them to the frontend proxy.
The backend framework can change without forcing a corresponding change to the frontend AG-UI contract.