Skip to main content

AG-UI Event Stream

Frontends shouldn't need bespoke glue to render a Chronos run. The AG-UI event stream translates Chronos's native execution events into the standard AG-UI protocol, so any AG-UI-compatible frontend can render a run — steps, tool calls, plan updates, state snapshots, and completion — out of the box. It is served alongside the native /api/events/stream, which is unchanged.

Endpoint

GET /api/agui/stream?session=<id>&run=<id>

Server-Sent Events, one AG-UI event per data: frame. Scope the stream to a session with ?session=<id> (the AG-UI thread); omit it to subscribe to the firehose (all sessions — useful for a dashboard). Per-session isolation, the subscriber cap, and heartbeats are inherited from the event broker.

Event mapping

Each native broker event maps to zero or more AG-UI events:

Chronos eventAG-UI event(s)
(on connect)RUN_STARTED
node_start / node_endSTEP_STARTED / STEP_FINISHED
model_delta (streamed token)TEXT_MESSAGE_START (first) → TEXT_MESSAGE_CONTENT
model_responseTEXT_MESSAGE_END (closes a streamed message), or a full STARTCONTENTEND when non-streaming
tool_callTOOL_CALL_STARTTOOL_CALL_ARGSTOOL_CALL_END
tool_resultTOOL_CALL_RESULT (a TOOL_CALL_START is synthesized first if this connection never saw the call)
plan_update (planning tool)CUSTOM (name: "plan")
checkpointSTATE_SNAPSHOT
interrupt (HITL)CUSTOM (name: "interrupt")
completedRUN_FINISHED
errorRUN_ERROR
customCUSTOM

RUN_STARTED is emitted immediately on connect (not lazily), so a client sees a live, well-formed lifecycle even before the run produces its first event. Assistant text streams as TEXT_MESSAGE_* (token by token on the streaming path, or as one message on the blocking path). TOOL_CALL_* events correlate by the model's tool-call id when present (falling back to a per-run synthetic id).

The agent routes its model/tool events to the session topic (via ChatWithSession or a graph run), so a ?session=<id> subscriber is genuinely isolated from other sessions' events; the plain Chat method (no session) broadcasts to the firehose.

Embedding the handler

The mapping layer (os/interop/agui) is a thin translator over the stream.Broker; you can mount it on your own server:

import "github.com/spawn08/chronos/os/interop/agui"

http.Handle("/agui", agui.Handler(broker)) // broker is your *stream.Broker

The agui.Translator is also usable directly if you deliver events over a transport other than SSE — NewTranslator(thread, run), then Start() once and Translate(evt) per broker event.

Example

A complete, runnable example (no API key) is in examples/agui_stream. It replays a scripted run through the broker and prints the AG-UI events a frontend would receive.