Skip to content

Concepts — the data model

How kaboo-react turns an AG-UI stream into the chat, the agent cards and the canvas you see.

One stream

Everything rides the same AG-UI run stream as the chat — there is no second endpoint. The entry agent's text and tool calls are the chat. Each agent it delegates to arrives as AG-UI's own sub-agent events: SUBAGENT_STARTED, its text, reasoning and tool events tagged with its subagentRunId, and SUBAGENT_FINISHED. kaboo-react folds those into a tree of StreamGroups that the chat, the agent cards and the drill-in view all read from. The same fold runs live and on replay, so a reload draws the same cards.

flowchart LR
    Stream["AG-UI run stream"] -->|SUBAGENT_* and tagged events| Fold["kaboo-react fold"]
    Fold --> State["ActivityState<br/>(groups keyed by subagentRunId)"]
    State -->|useActivity| Chat["KabooChat (cards in the transcript)"]
    State -->|useActivity| Panel["drill-in view"]

StreamGroup — one sub-agent invocation

Each invocation is a StreamGroup, keyed by its subagentRunId. The tree is formed by parentGroup (a group id or null for a top-level group), and each top-level group sits in the transcript at the call that started it (toolCallId). The fields you'll touch most:

Field Meaning
title / agentName Display title and machine name of the agent.
parentGroup Parent group id, or null for a top-level group.
toolCallId The delegating call the card sits at.
status active | completed | interrupted | error | stopped.
task What this agent was asked to do (shown atop its card).
timeline Interleaved text and tool entries, in order.
tools Every tool call in the group, in call order, with toolLabel when the workflow names it.
structuredOutput / outputSchemaName Schema-shaped output, and which renderer draws it.
usage The agent's token usage.
runIds Every run that streamed into the group: a paused agent resumes in a new run.
traceId The agent's trace, for feedback scores.

ActivityState also holds usageByRun and traceByRun, from each run's RUN_FINISHED.

What the transcript shows

  • A delegate is a card at the call that started it, not a tool row. Its own tool calls render inside it, with the same renderers as the chat.
  • What needs the person is lifted into the transcript. A delegate's question, request or approval shows in the main chat, labelled with the agent, while it waits; once answered it stays in the agent's card.
  • Tool calls render as they stream. Arguments arrive as the model writes them, so a renderer can draw a query or a widget before the call runs (Custom tool cards).

Reading the tree

useActivity() returns the groups; helpers navigate them:

import { useActivity, topLevelGroups, directChildren } from "@pgege/kaboo-react";

export function RootCount() {
  const { groups } = useActivity();
  const roots = topLevelGroups(groups);
  return <span>{roots.length} root agents</span>;
}

See Activity panel & drill-down for the components that render this tree, and Human-in-the-loop for questions and approvals.

Shared state and the canvas

The agents' shared state arrives as STATE_SNAPSHOT and STATE_DELTA events. Three hooks let a canvas follow the agent:

Hook Returns
useAgentState(select) A slice of the shared state, read again whenever the state changes.
useLiveToolCalls(match) The calls in flight whose tool name matches, with their arguments so far — for ghost widgets while a plot call streams.
useAgentPresence() Which sub-agents are working now, and on which tool.
import { useAgentPresence, useAgentState, useLiveToolCalls } from "@pgege/kaboo-react";

export function CanvasStatus() {
  const dashboards = useAgentState((state) => Object.keys((state.kaboo_workspace as { dashboards?: object })?.dashboards ?? {}));
  const plotting = useLiveToolCalls(/___plot_widgets$/);
  const working = useAgentPresence();
  return (
    <p>
      {dashboards.length} dashboards · {plotting.length} plotting · {working.map((agent) => agent.title).join(", ")}
    </p>
  );
}

Where it sits in the stack

kaboo-react is the UI layer of the kaboo stack: kaboo-workflows produces the stream, kaboo-runtime persists and replays it, and kaboo-react renders it.