bb · provider plugins

Provider plugins

A provider plugin is a bb plugin that registers one or more providers: the agent backends a thread can run on. Codex, Claude Code, Pi and every ACP agent bb ships are provider plugins, and they use exactly the API described here.

What changed recently #

2026-08-23: mobile compatibility windows dropped
2026-08-23: adversarial review fixes

Two review rounds over the pass below. Each line is something a plugin author, a bridge, or an SDK consumer can observe; the linked page carries the detail.

2026-08-22: the simplification pass, in plugin-author terms

What a provider plugin is #

bb runs every conversation on a provider: a coding agent such as Codex, Claude Code, Pi, or any agent that speaks the Agent Client Protocol (Cursor, opencode, Amp, your own). A provider is contributed by a plugin, and nothing else: there is no built-in provider table in bb, so disabling a provider plugin removes its provider from the picker.

A provider plugin is an ordinary bb plugin that does two extra things:

  1. Its server entry (server.ts, which runs inside the bb server) calls bb.providers.register({ ... }) with a declaration: the provider's id, display name, copy, icon, capabilities, permission modes, reasoning levels, service tiers, fallback models, maintenance support, native skill roots, extension kinds, and a hook that derives per-command options from the plugin's own settings.
  2. Its host entry (bb.host in the manifest, bundled by bb plugin build) exports a bridge named experimental_providerBridge. The host daemon on an enrolled machine runs that bridge as its own process, and the bridge translates the agent's native protocol into bb's Provider Bridge Protocol.

One plugin may own several providers. The first-party ACP plugin registers one provider per agent it knows about, plus every agent the user configures in the plugin's settings, and all of them run on one shared bridge.

The smallest complete example

examples/plugins/echo-provider in the bb repository is a full provider plugin in a few files: a declaration, a bridge that answers every prompt by echoing it back, a host RPC entry, a bb tool, plugin settings, two extension kinds, and the tests every provider should ship. The Quickstart walks through it.

Zero first-party privilege #

The bundled provider plugins have no private hooks. They import the same published package a marketplace plugin installs, @get-bb/plugin-sdk and its subpaths, and a test in each of them fails the moment a private @bb/* workspace package is imported. The echo example exists to prove the rule from the outside: it reaches every provider capability through the public SDK alone, and its own public-sdk-only.test.ts guards that.

Two consequences follow. Core never branches on a provider id (no tool-name tables, no per-provider rendering, no special-cased recovery). And anything a first-party provider can do, yours can do too.

The mental model #

declaration bridge process thread/delta items with presentation delta assembler ThreadEvent, persisted timeline rows on web, mobile, CLI
Declaration
Static facts the server needs before a session exists: what to show in the picker, which permission modes the agent supports, whether it can fork, which sessionless maintenance requests its bridge answers, where it keeps its own skills. The server turns the declaration into one client shape, ProviderInfo, and never reads anything else about the provider.
Bridge
A process per provider artifact, spawned by the daemon from your plugin's host bundle. It speaks line-delimited JSON-RPC 2.0 on stdin/stdout: the runtime sends initialize, thread/start, turn/start, thread/stop and friends; the bridge answers and streams thread/delta notifications. The bridge supervises any child processes (the agent's CLI, an SDK session) itself.
Items with presentation
A bridge never builds timeline events. It emits semantic deltas: a turn opened, a command item opened with its presentation, streamed text, the item closed with its terminal shape, usage, the turn settled. Every item.open and item.close carries a declarative presentation (label, glyph, title, detail, tint) that is persisted with the row.
Assembly and rendering
The runtime's delta assembler (the same code the testing kit ships) mints every turn and item id and constructs canonical ThreadEvents. The server validates extension payloads against the schemas you declared, persists the events, and projects rows. Every client renders every row from the declarative base; a plugin's web bundle may upgrade the body of rows it owns.
Interactions
When the agent needs the user, the bridge raises an interaction/request: a closed approval (a command, a file change, a tool use, a permission grant) that the thread's permission mode may auto-decide, or an open request (a user question, a plan review, or a plugin-defined <pluginId>/<name> form) the user answers.
Recovery
A bridge never makes the runtime match error text. It attaches a typed hint (sessionArchived, authRequired, restartRecommended, staleTurn, rateLimited) to a rejected request, or sends a provider/recovery notification when there is no request to reject.
Skills and native roots
bb injects its own skills into a session through skills/configure when the bridge's handshake declares support. The declaration also names the directories the agent reads its own skills and slash commands from, relative to the host's home or the workspace, so bb can list them beside its own; a directory only one host can name is the answer of the plugin's resolveNativeRoots host RPC.
Maintenance
Sessionless requests the server sends through the bridge when the declaration turns them on: provider/health, provider/usage, provider/installation/status and provider/installation/run.
AI services
A plugin's host entry can also serve bb's helper inference (thread titles, commit messages) and voice transcription, registered with bb.experimental_aiServices.register.

Who runs what #

The split is deliberate. The server decides what bb does; the daemon provides where it runs; the bridge knows how the agent talks. The daemon never interprets a provider's native protocol and the bridge never mints a bb id. The Concepts page lists which side owns each decision.

Vocabulary #

TermMeaning
providerAn agent backend a thread runs on. Identified by a flat, permanent id (codex, acp-cursor, echo-agent).
declarationThe object passed to bb.providers.register. Validated at call time, committed when the plugin load commits, replaced wholesale on reload.
ProviderInfoThe one client shape a declaration projects to. Read by the app's pickers, experimental_useProviders() and bb.sdk.providers.list().
bridgeThe module your host artifact exports as experimental_providerBridge; runs as its own process and speaks the Provider Bridge Protocol.
host artifactThe self-contained bundle bb plugin build produces from the manifest's bb.host entry (dist/host.js). Carries the bridge export and, optionally, a default-exported host RPC entry.
host entryThe default export of bb.host, built with experimental_defineHostEntry: RPC methods the server calls on a specific host (probe an agent, resolve native roots, serve AI services).
agent runtimeThe daemon-side component that spawns bridges, holds the delta assembler, and talks to the server.
delta assemblerRuntime code that turns thread/delta batches into canonical ThreadEvents, minting ids and enforcing the turn and item lifecycle. Shipped to plugins for tests as experimental_createDeltaAssembler.
grammar v3The current thread/delta vocabulary: core item kinds, presentation, extension items and state, one streaming dialect, one usage dialect. Negotiated at initialize as grammarVersions: [3, 3].
itemOne row-producing unit of work inside a turn: a command, a file read, a search, a delegation, a plan snapshot, a tool call, a message, an extension item.
presentationThe declarative rendering hint on an item: label { pending, completed }, icon { glyph } (a host glyph, or a plugin-declared icon as <pluginId>/<name>), title?, detail?, suppress?, tint?.
extension kindA plugin-defined item or thread-state kind named <pluginId>/<name>, declared with a schema the server enforces at ingest.
interactionAn approval or a request the agent raises mid-turn through interaction/request; every status change appends one system/interaction/lifecycle event.
recovery hint{ kind, message, retryable }: the typed reason a request failed or a session needs attention. Carried in error.data.recovery or by provider/recovery.
native rootsThe directories an agent reads its own skills (experimental_nativeSkillRoots) and slash commands (experimental_nativeCommandRoots) from, declared as { user, project } relative roots; host-absolute ones are resolved per host through resolveNativeRoots. bb lists them beside its own.
maintenanceThe sessionless requests a declaration turns on: health, usage, installation.
AI serviceHelper inference or voice transcription a plugin serves from its host entry.
conformance kitThe published scenarios that drive a bridge through JSON-RPC hygiene, the handshake and a session lifecycle and report per-rule pass/fail.
recording, cell, parityA capture of both sides of a bridge's wire for one session (a cell such as turn-tools), replayed through the bridge and diffed against itself.
experimental_The prefix every new public plugin API member ships with until it is audited and renamed. See Compatibility.

Four principles #

These come from the design document the implementation follows, and they explain most of the API's shape.

  1. Zero first-party privilege. First-party providers use only the public API. Every special case is a public primitive or is deleted.
  2. Each fact lives in one place. A capability is declared or reported, never both. Pre-session facts live on the declaration; session-behavior facts are reported by the bridge at initialize. Presentation comes from the bridge, never from core tables.
  3. Core understands a small semantic vocabulary. Everything else is an extension kind with mandatory declarative presentation.
  4. Every client renders everything without plugin code. Plugin renderers are a web upgrade; mobile renders the declarative base.

Where to go next #