bb Plugin API 2.0 design overview
The 2.0 API gives app, server, and host plugins one contract model, one winner model, and one authoring flow.
Start here
This page gives the model and the page map. It does not define final method signatures. The settled design record defines the decisions behind this site.
The research starts at the 2026-08-26 research index. The allocation brief defines the page roster and the old API ownership. The frontend synthesis and backend synthesis provide the evidence.
The model in brief
A plugin is a package with static declarations and separate artifacts for the app, server, and host tiers. The manifest declares claims, new contracts, dependencies, exports, lineage, and artifact paths. The build adds contract.json with full method and type schemas. The kernel validates both files before it loads code.
The headless rule
This rule is a first-party design principle.
A domain service ships headless whenever it has consumers beyond its own UI. Its default presentation is a separate -ui plugin. Presentation-only affordances are child surfaces declared by the default implementation that renders them, never by the domain.
The first-party roster applies this rule to threads, workspace, files, version control, and commands. A data plugin declares no surfaces. Its UI plugin requires the data service and owns the default presentation.
The bb.vcs plugin owns the generic version-control contract. The bb.vcs-ui plugin renders only that contract.
The bb.git plugin claims bb.vcs as its declared default provider. It also exports bb.git.* services for Git-only work.
Presentation-only surfaces follow the implementation that renders them. For example, the default thread view declares bb.thread-ui.sidePanels. A replacement view can omit that child surface without changing bb.threads.
Contracts
A contract is a named, typed, versioned capability. A UI region is a surface contract. A server capability is a service contract. A private server-to-host capability is a host role. All contracts use owner-namespaced IDs:
type FirstPartyContractId = `bb.${string}.${string}`;
type PluginContractId = `${string}.${string}`;
interface ContractDeclaration {
id: string;
kind: "single" | "list" | "keyed" | "chain";
version: string;
stability: "stable" | "experimental";
replaceable?: boolean;
}The kernel seeds bb.app.root as a single surface. A winning surface can declare typed child surfaces. A service owner declares its default provider when the service can fall back after a failure.
Use a contract when the user can replace the capability or when another plugin needs the current winner. Use a module import when the consumer needs one exact implementation.
import { Surface } from "@get-bb/plugin/app";
import { Composer } from "@bb/thread-ui/components";
// The current winner follows the user's choice.
<Surface id="bb.thread-ui.composer" props={{ threadId }} />;
// This import always uses this concrete module.
<Composer threadId={threadId} />;Service tokens use the same rule on the server. A service consumer gets a live in-process object, and a host call uses an explicit typed host role.
Four kinds
The kind states how a contract combines claims. The same four kinds apply to app surfaces and backend services.
| Kind | Active result | User choice | Typical examples |
|---|---|---|---|
single |
One implementation | Pick one winner when replaceable |
Composer, drafts service |
list |
All claims, in deterministic order | Hide or reorder items | Side panels, event listeners |
keyed |
One implementation for each key | Pick a winner for each key when replaceable |
Timeline item type, tool name, provider ID |
chain |
Ordered wrappers around a base | Inspect and reorder the chain | Send interception, command interception |
A non-replaceable collision fails at load with a clear error. A list does not have a winner conflict. A chain has visible order. A keyed contract has one winner per key.
Winners and defaults
The kernel stores one global winner for each replaceable single contract and each replaceable keyed key. A fresh install selects the first-party claimant or the declared default provider. A second claimant does not change the active result without a user choice.
The frontend winner of a single surface receives the typed Original component. Original always means the built-in default. It never means another claimant. The backend has no Original handle. A replaceable backend contract declares a default provider instead.
A surface failure reaches a per-surface error boundary. The host shows a notice and uses Original or the default claimant. A service failure uses the declared default provider. Safe mode disables overrides and keeps kernel recovery, defaults, and ports available.
Forks
A fork is a new plugin ID. Its manifest records the parent ID and parent revision:
{
"id": "acme.github-fork",
"fork": {
"parent": "bb.github",
"parentRevision": "1.4.2"
},
"claims": [
{ "surface": "bb.thread-ui.sidePanels" },
{ "service": "bb.github.issueCache", "version": "^1" }
]
}The fork claims each parent contract that it still implements. The install checks those claims against the parent's contract.json. The fork receives fresh service storage. Consumers keep the same contract ID across a winner change.
The normal fork path is short:
- Resolve the active contract and its source.
- Copy the parent into a new plugin package.
- Record the parent ID and revision.
- Edit the package and build its artifacts.
- Install the fork and choose it in the winner picker.
- Merge parent updates from the recorded revision.
Three tiers
The app, server, and host tiers have separate runtime planes. App code shares the browser page, React, and the DOM. Server code runs in the bb server process. Host code runs in the machine daemon. Server code reaches host code only through an explicit typed host role.
| Tier | Main work | Author entry | Shared capability |
|---|---|---|---|
| App | Surfaces, commands, composer integration, and app state | One app factory with api |
Surface contracts and direct module exports |
| Server | Domain services, tools, RPC, settings, and events | One server factory with api |
Service tokens and kernel ports |
| Host | Machine-local provider, AI, environment, and native work | One host factory with api |
Private host roles |
One package can provide one or more tiers. The loader stages each factory and commits registrations as one atomic plugin unit. A failed candidate does not affect the active generation.
Required, optional, and watched edges define service dependencies:
| Edge | Activation rule | Provider change |
|---|---|---|
required |
The consumer needs the provider before activation | Restart required dependents in graph order |
optional |
The consumer can work without the provider | Stay live and observe availability |
watched |
The consumer needs provider changes but not provider startup | Stay live and observe the new generation |
On a winner change, the new provider becomes ready before cutover. Old calls drain to a deadline. Surfaces remount. Required dependents restart with fresh handles. Optional and watched consumers stay live.
First-party roster
The kernel is not a plugin. Seventeen core plugins ship on the ordinary path.
| Unit | ID | Owns |
|---|---|---|
| Kernel | (none) | Boot, loader, manifest + contract.json, claim/winner registry, arbitration, safe mode, error surfaces, install/rollback, bb plugin CLI core verbs, marketplace install sources, artifact verification, host daemon runtime and host-role transport, and the ports: bb.storage, bb.secrets, bb.realtime, bb.http, bb.rpc, bb.plugins (introspection), diagnostics/cost attribution. |
| Layout | bb.layout |
The app shell. Claims bb.app.root. Declares bb.layout.sidebar (single), bb.layout.main (single), bb.layout.statusbar (list), bb.layout.panels (list), bb.layout.modals (list). Owns themes (bb.layout.theme, keyed) and shell chrome. |
| Threads (data) | bb.threads |
The headless thread domain. Service bb.threads owns records, execution, timeline data, thread events, subscriptions, drafts, and selection. It declares no surfaces. Its data model has no presentation assumptions. |
| Thread UI | bb.thread-ui |
The thread presentation. It consumes bb.threads. Surfaces: bb.thread-ui.list (single), bb.thread-ui.view (single), bb.thread-ui.composer (single), bb.thread-ui.composer.actions (list), bb.thread-ui.composer.send (chain), and bb.thread-ui.timeline.item (keyed by item type). The default view declares the bb.thread-ui.sidePanels child surface. It exports ThreadChat, Composer, Markdown, and timeline components. |
| Workspace (data) | bb.workspace |
Headless service bb.workspace owns projects, environments, and routing. It declares bb.workspace.envProvider as a keyed environment provider contract. It declares no surfaces. |
| Workspace UI | bb.workspace-ui |
The project switcher and environment pages. It requires the bb.workspace service. |
| Files (data) | bb.files |
Headless service bb.files provides read, write, watch, and upload operations. It declares no surfaces. |
| Files UI | bb.files-ui |
Surfaces: bb.files-ui.opener (keyed), bb.files-ui.source (single), bb.files-ui.diff (single), and bb.files-ui.preview (keyed by extension). It requires the bb.files service. |
| Version control (domain) | bb.vcs |
Headless service bb.vcs provides generic status, changed files, commits, heads, history, and diff data. It declares bb.git as its default provider. |
| Version control UI | bb.vcs-ui |
The commit button, changes panel, head indicator, and history views. It requires bb.vcs and uses bb.files-ui.diff. |
| Git (default provider) | bb.git |
The default bb.vcs provider. It claims bb.vcs through a private Git host role. It also exports bb.git.staging, bb.git.worktrees, and bb.git.stash. |
| Providers | bb.providers |
Provider registry service. Provider contract (bb.providers.provider, keyed by provider ID, replaceable). The whole provider bridge protocol (requests, notifications, deltas, items, interactions, maintenance, recovery) as its contract surface. Provider host machinery as declared host roles. Usage, health, maintenance. |
| Agents | bb.agents |
Agent tools (bb.agents.tool, keyed by tool name, replaceable). AI services (bb.agents.ai, keyed by service ID, replaceable). Skills. Agent configuration. Tool-call interception (bb.agents.toolCalls, chain). |
| Commands (data) | bb.commands |
Headless app command registry, keybindings, and command interception chain. It declares bb.commands.cli as a keyed plugin CLI verb contract. The kernel keeps core verbs. It declares no surfaces. |
| Palette | bb.commands-ui |
The single, replaceable command palette surface. It requires the bb.commands service. |
| Settings | bb.settings |
Presentation-only settings UI pages, settings schema rendering, and secrets UI. The kernel port bb.preferences owns the preferences service. |
| Mentions | bb.mentions |
Headless mention provider contract, search service, and resolve service. The default bb.thread-ui.composer implementation declares the mention popover child surface. |
| UI kit | bb.ui |
The shared component kit. Pure module exports; no claims. Buttons, lists, forms, markdown, virtualized lists — the current shared/core UI component surface. |
The shipping provider plugins are ordinary built-in plugins. They are bb.claude-code, bb.codex, bb.pi, and bb.acp.
Each provider claims bb.providers.provider and ships its own host role and bridge artifacts. Marketplace providers claim the same contract.
The built-in github plugin requires bb.vcs. It uses optional bb.git.* edges only for Git-specific features.
How the pages divide the work
The pages use the same order as the new API. Read pages 03–14 for first-party contracts.
Page 15 contains coverage. Page 16 contains complete rebuild stories.
| Page | Topic | Read it for |
|---|---|---|
| 01 — Kernel | Boot, loader, manifest, claims, ports, and recovery | The runtime boundary that stays outside plugins |
| 02 — Authoring | Package shape, factories, api, kinds, forks, tiers, testing, and exports |
How to write and test a plugin |
03 — bb.layout |
Shell, root claim, chrome, themes, and layout surfaces | How the app shell becomes replaceable |
04 — bb.threads |
Records, execution, timeline data, events, drafts, and selection | The headless thread domain service |
05 — bb.thread-ui |
Thread list, view, composer, timeline, and shared components | Thread presentation and its hierarchical surface graph |
06 — bb.workspace and bb.workspace-ui |
Projects, environments, routing, environment providers, and workspace UI | The headless workspace service and its default presentation |
07 — bb.files and bb.files-ui |
File access, openers, source, diffs, and previews | The headless file service and its default presentation |
08 — bb.vcs, bb.vcs-ui, and bb.git |
Generic version-control data, presentation, and the default Git provider | The replaceable VCS contract and the two-lane provider rule |
09 — bb.providers |
Provider registry, bridge protocol, host roles, and health | Provider replacement and recovery |
10 — bb.agents |
Tools, AI services, skills, configuration, and tool-call chains | Agent capability contracts |
11 — bb.commands and bb.commands-ui |
Commands, palette, keybindings, CLI verbs, and chains | The headless command service and its default palette |
12 — bb.settings |
Settings pages, preferences, schemas, and secrets UI | User configuration contracts |
13 — bb.mentions |
Mention providers, search, resolve, and composer use | Mention integration |
14 — bb.ui |
Buttons, lists, forms, markdown, and virtualized lists | Shared exact implementations with no claims |
| 15 — Coverage | The generated old-to-new matrix | Every inventory item and its destination |
| 16 — Plugin rebuilds | Twenty-two shipping plugin rebuild stories | The practical effect on existing plugins |