Architecture
bread turns a directory of agent folders into a running, observable HTTP service. This page maps the moving parts.
Packages
| Package | Responsibility |
|---|---|
@breadai/core | SDK: defineAgent/defineTool/…, the runner, sessions, KG/documents, checkpoints, plugins, types |
@breadai/server | File-system loader + Hono server (importable library) |
@breadai/cli | The bread binary |
@breadai/runtime-bun, @breadai/runtime-node | Listen adapters (Bun.serve / @hono/node-server); CLI selects via --runtime / config.server.runtime |
@breadai/store-postgres, @breadai/store-sqlite, @breadai/store-memory | BreadStore backends — see store.md |
@breadai/provider-llm | The 21 language-model providers as a ProviderRegistry. @breadai/provider-catalog re-exports it — see providers.md |
@breadai/provider-decisions | System One hosts as a DecisionRegistry for config.decisions — see decisions.md |
@breadai/otel, @breadai/protocol-ag-ui, @breadai/protocol-a2a-server, @breadai/a2ui, @breadai/protocol-mcp-client, @breadai/protocol-mcp-server | Plugins |
@breadai/auth-api-key, @breadai/auth-jwt, @breadai/auth-oauth2, @breadai/auth-trusted | Standalone BreadAuthStrategy/BreadSigner factories — not plugins themselves; wrap with @breadai/server's authPlugin() to attach — see auth.md |
@breadai/transport-http-chunked, @breadai/transport-http-sse | HTTP ingress BreadTransports — mount() the four streaming routes + remoteAgent() for config.remoteAgents (transports/http-chunked, transports/http-sse) — see transports.md |
@breadai/transport-redis | Redis Streams BreadTransport — cross-replica crumb fan-out, no mount (transports/redis) |
@breadai/transport-stdout | Terminal-rendering BreadTransport (sink) for bread chat/bread invoke (transports/stdout) |
Package families
Beyond packages/* (core/server/cli), everything installable lives in one of five workspace folders, each with its own @breadai/* npm prefix:
| Folder | npm packages | Covers |
|---|---|---|
stores/ | @breadai/store-postgres, @breadai/store-sqlite, @breadai/store-memory | BreadStore backends — see store.md |
providers/ | @breadai/provider-llm, @breadai/provider-catalog, @breadai/provider-decisions | Language-model registries, plus the System One DecisionRegistry. provider-catalog re-exports provider-llm — see providers.md and decisions.md |
protocols/ | @breadai/protocol-ag-ui, @breadai/protocol-a2a-server, @breadai/protocol-mcp-client, @breadai/protocol-mcp-server | Wire-protocol adapters (BreadPlugins) |
extensions/ | @breadai/otel, @breadai/a2ui (attach as BreadPlugins); @breadai/auth-api-key, @breadai/auth-jwt, @breadai/auth-oauth2, @breadai/auth-trusted (standalone auth factories, see auth.md) | Observability, UI generation, auth strategies/signers |
transports/ | @breadai/transport-http-chunked, @breadai/transport-http-sse, @breadai/transport-redis, @breadai/transport-stdout | BreadTransport implementations — see transports.md |
Protocols and most extensions plug in through the same BreadPlugin mechanism (see plugins.md) — the folder split is a naming/discovery convention, not a second plugin system. The four @breadai/auth-* extensions are the one exception: they're standalone BreadAuthStrategy/BreadSigner factories, not BreadPlugins — see auth.md.
From folder to running service
The loader reads each agents/<id>/ folder, attaches prompt.md, tools/, and skills/ metadata onto the agent's config as private fields (_systemPrompt, _tools, _humanTools, _skills, _agentDir), and builds an AgentRegistry (Map<string, AgentDefinition>). createBread(config, agents, tasks) returns a BreadInstance; the server wraps it.
One crumb stream: the choke point
Every public stream (run, resume, runPipeline, sync mode) passes through one per-run choke point inside the instance. As each crumb is yielded it gets its per-run monotonic seq, is appended to the durable crumb log (text:deltas aggregated into windows), reaches local bread.on('crumb'/'human:required') listeners, and is published to the transport (config.transport — the embedded Stream by default, @breadai/transport-redis for multi-replica deployments). Nothing below the choke point emits anywhere, so the plugin view, the transport view, the log, and the NDJSON stream are the same well-defined stream. The store is truth (replay/Last-Event-ID catch-up); the transport is liveness. See transports.md.
Crumbs
A run is a stream of typed crumbs (the project lexicon — Crumb, Plugin — is defined in the glossary):
agent:run:start · text:delta · reasoning:delta · file:generated · tool:call · tool:input:start · tool:input:delta · tool:input:end · tool:result · tool:result:partial · tool:error · human:required · human:resumed · subagent:run:start · subagent:run:end · pipeline:step:start · pipeline:step:end · loop:start · loop:iteration:start · loop:iteration:end · loop:end · task:start · task:end · agent:run:end · agent:error.
A run that calls a human tool ends its stream at human:required; resume replays it from the store and returns the continuation starting with human:resumed.
The public instance surface
Ingresses (the HTTP server, @breadai/protocol-mcp-server, or your own) consume only the public BreadInstance API: run / resume / runPipeline / runTask, the bus and store getters, and the agents / tasks / pluginTools / credentials registries. The internal RunnerContext is exactly that — internal (@internal export for the CLI loader and tests only). See the "bring your own ingress" guide in transports.md.
Plugin lifecycle
See plugins.md for the BreadPlugin interface.
Storage
All state — sessions, HITL checkpoints, the knowledge graph, documents, and task-run audit records — persists through a single BreadStore interface, set as config.store. PostgreSQL is the recommended backend (@breadai/store-postgres's store() reads DATABASE_URL itself); SQLite (@breadai/store-sqlite, bun:sqlite) and in-memory implementations ship as alternatives, and any custom backend can be passed as store. Neither core nor the CLI bundles a driver, and nothing is wired implicitly — an unset store throws STORE_NOT_CONFIGURED. See store.md.

