Concepts¶
A mental model for how kaboo-workflows turns a YAML file into a running, streaming multi-agent system.
load() returns plain strands objects¶
kaboo-workflows is a resolver, not a runtime wrapper. load(config) reads
your YAML, resolves every reference, and returns live
strands-agents objects —
strands.Agent, Swarm, Graph — with nothing wrapping them. Whatever you can
do with a hand-built strands object, you can do with the one kaboo hands back.
flowchart LR
YAML["config.yaml"] -->|load| Resolved["ResolvedConfig"]
Resolved --> Entry["resolved.entry<br/>(strands Agent / Swarm / Graph)"]
Entry -->|"kaboo-serve"| SSE["AG-UI SSE endpoint"]
SSE -->|"Server-Sent Events"| CK["CopilotKit frontend"]
The pieces¶
A config wires together a handful of resolvable sections (each has its own chapter in the Configuration reference):
| Section | What it is |
|---|---|
models |
Named model providers (bedrock, openai, ollama, gemini). |
agents |
Named agents: a model, a system prompt, tools, hooks and MCP clients. |
mcp_servers |
MCP servers the library starts and stops for you. |
mcp_clients |
Connections to MCP servers, which agents attach by name. |
orchestrations |
How agents compose into a multi-agent system. |
attachments |
How references (files + custom entities cited via @) reach agents. |
session_manager |
Where conversation state persists between runs. |
entry |
The root agent or orchestration a run starts from. The only required key. |
Tools and hooks are not top-level sections — they are declared per agent, as
agents.<name>.tools and agents.<name>.hooks. Tools are Python @tool
functions loaded from files or modules; tools the browser runs, such as
ask_user, arrive with each run and reach the agents whose client_tools name
them. Hooks are lifecycle callbacks for event publishing, interrupts and
guards. state_effects write tool results into the shared state. The remaining
root keys are settings rather than wiring: history, telemetry, runtime,
log_level and version, plus vars for interpolation.
Orchestration shapes¶
Agents compose through orchestrations, which nest arbitrarily:
- delegate — an entry agent calls other agents as tools (see deep nesting).
- swarm — agents hand off to one another until one produces the answer.
- graph — an explicit DAG of agents with edges and an entry point.
Those three are the whole list — mode: accepts nothing else. Two patterns
people look for as modes are properties of a graph rather than shapes of their
own:
- parallel — a graph node with several outgoing edges fans out, and the branches run concurrently (see parallel).
- nested — any orchestration can be a node inside another (see swarm + graph).
Serving: the AG-UI event stream¶
kaboo-serve (or create_agui_app in kaboo_workflows.adapters) wraps the
resolved entry with ag-ui-strands and
exposes it as an AG-UI 1.0 SSE endpoint. A run uses the protocol's own events
throughout:
RUN_STARTED → STATE_SNAPSHOT → TEXT_MESSAGE_* / TOOL_CALL_* → SUBAGENT_STARTED → (tagged events) → SUBAGENT_FINISHED → STATE_DELTA → RUN_FINISHED
- Sub-agents are
SUBAGENT_*events, with their text, reasoning and tool calls tagged bysubagentRunId, which kaboo-react folds into cards (Sub-agents in a chat UI). - Pauses end the run with
RUN_FINISHED: waiting on a tool the browser runs (pendingToolCallIds), or on approvals (aninterruptoutcome) — see human-in-the-loop. - State changes — the conversation's sessions and the
shared state — leave as
STATE_DELTAs. - Usage on
RUN_FINISHEDcounts every agent of the run.
kaboo-runtime persists the stream
and replays it on reconnect. GET /invocations/capabilities describes what the
service supports as AG-UI AgentCapabilities.
Response size¶
A run streams only what it adds: its text, its tool calls and their results,
its sub-agents' events and its state changes. It never re-sends the
conversation. ag-ui-strands' MESSAGES_SNAPSHOT, which repeats the whole
transcript after every step, is turned off, because with it a response grows
with the length of the thread times the steps of the turn, and a long thread
eventually reaches a host's response cap (AWS AgentCore closes the socket at
100 MiB). Clients build the transcript from the events, as CopilotKit does, and
send it back with the next turn.
Measured in the lab, a deliberately long turn (twenty SQL results of up to 200 rows, a 63-component A2UI surface, a 40 KB generated visual and a dashboard of twelve widgets) sends about 230 KB, and every turn of a twelve-turn thread sends the same 28 KB whatever came before it.
References & attachments¶
Anything a user cites from the frontend with @ — an uploaded file or a pointer
to a custom entity (a table, a dashboard) — is a reference. The AG-UI layer
gives inline media to the entry agent automatically; the attachments: config
extends that to any agent in a pipeline via a lightweight manifest, an
on-demand tool (fetch_attachment / list_references), and opt-in
inline ContentBlocks for vision/doc-capable models. File attachments are
resolved by the built-in tool; custom object kinds are resolved by your own MCP
tool. See Attachments & Multimodal.
Attachment URL fetches funnel through a pluggable fetcher: configure
attachments.base_url / authorization for files behind your API's auth, or
register a ReferenceFetcher in code for multi-store routing.
The host side channel: forwardedProps¶
The AG-UI protocol carries a free-form forwardedProps object on every run —
context the host backend sends the runtime that is not part of the
conversation and never reaches the model as text: run-scoped credentials,
tenant ids, per-run agent configuration. kaboo binds it per request; your
tools and hooks read it back with get_forwarded_props(), and every agent
also receives a copy in its state under forwarded_props.
Two built-in features consume it:
- Authorized attachment fetching —
attachments.authorization: forwarded_props:<key>reads a run-scoped bearer token for own-origin fetches. - Runtime-submitted workflow configs — with
create_agui_app(session_config_key="workflow_config"), the run's own YAML arrives under that key and is merged over the service's base config. This is how one process serves many host-defined agent types: each run gets its own agents, orchestration, entry and MCP client sessions, and editing an agent type takes effect on the next turn without a restart. See Chapter 13.
A submitted config supersedes forwardedProps.agent_config, which applied only
system_prompt and model_id behind runtime.allow_invocation_overrides. That
flag is deprecated: an overlay expresses both, plus the structure they could
not.
Everything on this channel is only as trustworthy as whoever set it, so a host should stamp these server-side rather than accept them from a browser — see server-side props in kaboo-runtime for where that goes on a Node host.
Statelessness: what a run keeps and what it carries¶
A run keeps nothing. Everything a conversation accumulates arrives with the turn
on the AG-UI state channel, under kaboo_sessions: every agent's transcript
(history), the paused checkpoint of a run waiting on the person (paused),
and the approvals it waits on (approvals). The run writes it back as one
STATE_DELTA, and the host persists it. kaboo-runtime keeps the key on the
server, so a browser never sees or sends it.
That is what makes the rest safe: a service can rebuild its agents every run, scale to a second replica, or restart mid-approval, and behave identically, because the objects being rebuilt hold behaviour rather than memory.
Where to go next¶
- Getting started if you haven't run it yet.
- Configuration reference for the exhaustive option list.
- Troubleshooting when something doesn't resolve.