Skip to content

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, plus built-ins like ask_user; hooks are lifecycle callbacks for event publishing, interrupts and guards. 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 SSE endpoint. A single run emits a well-formed event stream:

RUN_STARTED → TEXT_MESSAGE_* → TOOL_CALL_* → ACTIVITY_SNAPSHOT → RUN_FINISHED

ACTIVITY_SNAPSHOT events carry the hierarchical activity tree that kaboo-react renders, while kaboo-runtime can persist and replay the whole stream. Human-in-the-loop pauses surface as interrupts on RUN_FINISHED and resume cleanly — see human-in-the-loop.

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 fetchingattachments.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 channelkaboo_history for sub-agent transcripts, kaboo_session for a pending human-in-the-loop gate — and leaves in the outgoing STATE_SNAPSHOT the host persists.

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