Guides
AG-UI bridge
One mapper function. SCREAMING_SNAKE in, PascalCase out.
TL;DR
import "@gemmapod/embed/runtime"; // side-effects window.GemmaPod
// or load the IIFE via <script>
runtime.events.on("ui.event", ({ event }) => {
const agui = GemmaPod.mapDartcUiEventToAgUi(event);
// agui.type is PascalCase now. Payload fields unchanged.
});That's the whole bridge.
Why a mapper instead of native support
Two decisions baked into GemmaPod that AG-UI hosts care about:
-
Field names match AG-UI on purpose.
threadId,runId,messageId,toolCallId,delta,snapshot,patch— every payload field is the same name on the wire. Hosts that already destructure these field names work without changes. -
The discriminator differs by design. DARTC frames are visible in network panels and CI logs; SCREAMING_SNAKE jumps out from surrounding JSON. AG-UI uses PascalCase to fit TypeScript convention. Neither is wrong — they serve different audiences. The mapper bridges the two.
What the mapper does
function mapDartcUiEventToAgUi(event: DartcUiEvent): AgUiEvent;- Takes a
DartcUiEvent(the typed event you get onruntime.events.on("ui.event", …)). - Returns a new object with the
typerewritten to PascalCase and all other fields preserved verbatim. - Pure: no allocation beyond the new top-level object, no runtime state, no async.
- Defensive: unknown DARTC
typevalues map toRaw— your AG-UI host receives a typedRawevent with the original payload nested underdartc, rather than a missing field.
Where to call it
Anywhere. Three useful spots:
In the event handler
runtime.events.on("ui.event", ({ event }) => {
host.dispatch(GemmaPod.mapDartcUiEventToAgUi(event));
});In a generic adapter
function toAgUi(runtime, host) {
return runtime.events.on("ui.event", ({ event }) =>
host.dispatch(GemmaPod.mapDartcUiEventToAgUi(event)),
);
}In a worker for logging / replay
self.onmessage = (e) => {
postMessage(GemmaPod.mapDartcUiEventToAgUi(e.data));
};What it doesn't do
- It doesn't filter or batch events. If your host expects rate-limited events, you batch.
- It doesn't validate. Send any object with a
typeand you get back an object with atype. Validation belongs in the consumer. - It doesn't introspect payloads.
STATE_DELTA.deltastays a JSON-Patch operation array; field names match RFC 6902.