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:

src/app/agent-status.component.ts
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:

EventCallback
Run lifecycleonRunStartedEvent, onRunFinishedEvent, onRunErrorEvent
StepsonStepStartedEvent, onStepFinishedEvent
Text messagesonTextMessageStartEvent, onTextMessageContentEvent, onTextMessageEndEvent
Tool callsonToolCallStartEvent, onToolCallArgsEvent, onToolCallEndEvent, onToolCallResultEvent
StateonStateSnapshotEvent, onStateDeltaEvent
MessagesonMessagesSnapshotEvent
CustomonCustomEvent, onRawEvent
High-level changesonMessagesChanged, 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.

What your component sees
const store = injectAgentStore("default");
const agent = store().agent;
store().messages();
store().state();
agent.subscribe({ /* … */ });
What happens underneath
// injectAgentStore() → registry checks /info → resolves a proxy agent
// core.runAgent({ agent }) → runtime POST → agent execution → SSE events

This 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:

  1. The runtime resolves the target agent by ID.
  2. It clones the agent for request isolation and supplies messages, state, and thread context.
  3. AgentRunner executes the agent and receives AG-UI events.
  4. 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.