Skip to main content
The pkg/agui package serves your agents over the AG-UI protocol — the same wire format CopilotKit and @ag-ui/client speak. You get streaming assistant text, reasoning, tool calls, conversation threads, and human-in-the-loop approvals over a single SSE endpoint, plus an optional embedded chat UI so you can talk to an agent in the browser without building a frontend. There are three layers, from highest to lowest level: *hastekit.AgentRegistry satisfies the agui.Registry interface (Agent(name) + AgentNames()), so you pass one to any of these. Agents register into a package-global registry when you create them with hastekit.NewAgent, so a zero-value &hastekit.AgentRegistry{} already sees every agent.

Embedded chat UI

The fastest way to interact with an agent. web.Serve mounts a ready-made CopilotKit chat client and the AG-UI protocol endpoints on the same address:
Open http://localhost:8080. The default UI lists registered agents, shows a sidebar of prior conversations to resume, streams assistant text / reasoning / tool calls live, and renders approval cards inline for human-in-the-loop pauses. web.Handler(registry, opts...) returns the same surface as an http.Handler for mounting into an existing server. It serves: The protocol endpoints are mounted under /api/agui (exported as web.APIPrefix), so external AG-UI clients can target them too — the embedded UIs are just two more consumers.

Protocol handler

If you have your own frontend (CopilotKit’s HttpAgent, raw @ag-ui/client, or a custom SSE consumer), serve just the protocol with agui.NewHandler:

Routes

The threads endpoints power conversation pickers. Listing requires the agent’s persistence adapter to implement history.ThreadLister (the SDK’s in-memory and file adapters do); when it doesn’t, the listing endpoint answers 501 so clients can hide the picker. See Conversation History for persistence setup.

Single agent

To mount one agent’s run endpoint on an existing mux, use agui.AgentHandler, which runs the agent for every POST regardless of path:

Request body

The run endpoint accepts the canonical AG-UI RunAgentInput body (camelCase field names):
Each Message follows the AG-UI message shape:
A request must have a threadId and at least one of messages or an approval decision (see Human-in-the-loop below).
By default the handler extracts only the new trailing turn from messages and relies on the SDK’s conversation persistence for prior history. Use WithFullHistory if the client owns history and the agent has no persistence.

Grounding context

context entries are flattened into the run’s RunContext so prompt templates can reference them. A {description, value} pair becomes {{Context.<description>}}, while state and forwardedProps are available as {{State.x}} and {{ForwardedProps.y}}.

Event stream

The run endpoint responds with Content-Type: text/event-stream and emits canonical AG-UI events. It always starts with RUN_STARTED and always ends with RUN_FINISHED (or RUN_ERROR), synthesising the terminal event if the stream closes early so clients never hang. The main event types: Right after RUN_STARTED, the handler also emits a CUSTOM event carrying the broker streamId, runId, and threadId so a client can correlate the AG-UI run with the SDK’s streaming surface.

Correlation headers

The response sets these headers before the first event:
  • X-Stream-Id — the broker stream id (matches handle.StreamID)
  • X-Agui-Run-Id — the AG-UI run id (echoed from runId, or generated)
  • X-Agui-Thread-Id — the thread id from the request
An idle connection receives a keep-alive SSE comment every 15 seconds (configurable) so reverse proxies don’t reap it.

Options

All three entry points (web.Serve, web.Handler, agui.NewHandler, agui.AgentHandler) accept the same options:
  • agui.WithNamespace(ns) — Conversation namespace (default "default").
  • agui.WithSenderID(id) — Sender attribution for messages POSTed by AG-UI clients (default "user").
  • agui.WithFullHistory() — Forward the client’s complete message list into the run instead of only the trailing turn. Use this when the agent has no conversation persistence and the client owns history; with persistence enabled (the SDK default) it would duplicate prior turns in the thread on every POST.
  • agui.WithKeepalive(d) — SSE keep-alive comment interval (default 15s).

Human-in-the-loop

When an agent pauses on an approval-gated tool, the run ends with a RUN_FINISHED in a paused state and the client renders an approval card. To resume, the client POSTs back to the same threadId with the decision under forwardedProps.command.resume (CopilotKit’s useInterrupt shape) — no new messages are required:
A flat forwardedProps.hastekitApprovals array with the same {toolCallId, approved} entries is also accepted for simpler clients. The handler maps these decisions onto the SDK’s approval resume flow and continues the paused run.