Renders events as AG-UI events, for SuperPi.Gateway.AGUI.
Each output item is a map with a "type" (RUN_STARTED, TEXT_MESSAGE_CONTENT, ...) and the
fields of that event in camelCase, ready for Jason.encode!/1 and an SSE data: line.
Mapping
| Event | AG-UI |
|---|---|
turn_started | RUN_STARTED |
text_delta | TEXT_MESSAGE_START (once per message), TEXT_MESSAGE_CONTENT |
thinking_delta | REASONING_START, REASONING_MESSAGE_START, REASONING_MESSAGE_CONTENT |
message_completed | TEXT_MESSAGE_END (and the reasoning end events) |
toolcall_delta | TOOL_CALL_START (once per call), TOOL_CALL_ARGS |
tool_started | TOOL_CALL_END - AG-UI means arguments complete by it, not tool complete |
tool_completed, tool_blocked | TOOL_CALL_RESULT |
turn_completed | RUN_FINISHED |
turn_error | RUN_ERROR |
turn_cancelled | RUN_ERROR with code "cancelled" (AG-UI has no cancel event) |
compaction_completed | MESSAGES_SNAPSHOT, because the context really changed |
state_changed, mode_changed, usage_updated, plan_updated, permission_request, ui_resolved | STATE_DELTA |
permission_request, ui_request, ui_resolved, notice, stream_reset, subagent_* | CUSTOM |
A bare run of deltas gets its TEXT_MESSAGE_START and TEXT_MESSAGE_END here, because the machine
only emits deltas. A tool call that a provider did not stream gets TOOL_CALL_START,
TOOL_CALL_ARGS and TOOL_CALL_END together when tool_started arrives.
State
The renderer keeps a small projection of the conversation (status, mode, model, usage,
plan, pendingPermission). snapshot/1 gives STATE_SNAPSHOT for an opening or a reconnect.
After a state event it recomputes the projection and emits STATE_DELTA with
Jsonpatch.diff(previous, next). The durable document layer offers no patch stream for this: only
pi.agent keeps ops, pi.live and pi.usage do not, and no write broadcasts. Doing it here is
correct by construction and needs no change to document retention.
Options
:thread_id,:run_id- ids of the run.run_iddefaults to the turn id.:projection- initial projection (initial_projection/1builds one from the documents).:messages_fun- zero-arity function that returns the messages forMESSAGES_SNAPSHOT.
Coalescing
coalesce/1 merges consecutive TEXT_MESSAGE_CONTENT and TOOL_CALL_ARGS items of the same
message or call. The transport calls it on the batch of events that arrived within about 16 ms.
There is no ack window as in the RPC server: SSE is one-way and TCP is the backpressure.
Summary
Functions
Merges consecutive content items of the same message or tool call into one.
The projection of a conversation, read from its documents.
STATE_SNAPSHOT of the current projection. Marks it as sent, so the next delta is relative to it.
Functions
Merges consecutive content items of the same message or tool call into one.
The projection of a conversation, read from its documents.
@spec snapshot(%SuperPi.Event.Renderer.AGUI{ call_order: term(), calls: term(), message: term(), messages_fun: term(), projection: term(), reasoning: term(), run_id: term(), sent: term(), seq: term(), thread_id: term() }) :: {[map()], %SuperPi.Event.Renderer.AGUI{ call_order: term(), calls: term(), message: term(), messages_fun: term(), projection: term(), reasoning: term(), run_id: term(), sent: term(), seq: term(), thread_id: term() }}
STATE_SNAPSHOT of the current projection. Marks it as sent, so the next delta is relative to it.