1.0 archive. This section describes the previous 1.0 API. The current model is the 2.0 design.
Reference
Draft 1, 2026-08-21. Assembled from @get-bb/plugin-sdk/api.lock.json (the locked surface), the SDK README, the spec sections under next/docs/sections/, the as-built notes in next/docs/stage-2/04-kernel-as-built.md, and the reference plugin next/examples/plugins/hello-slot. Source tree: branch bb/rewrite-bb-app-from-scratch-thr_e2n9n5n7i6 at dc07292bf plus the accepted kernel delta e1efe72a3 (its direct child; next/packages only; local only). The sections were written against 6f3d4592f, updated for the dc07292bf delta listed in §0.7, and updated again for the e1efe72a3 delta listed in §0.8.
0. How to read this document
0.1 Scope
This document describes what a plugin author can import, declare, and call. It does not describe the kernel's internals. Where the spec and the built kernel differ, the built kernel wins; the "As built" column names the deviation (D1–D20, see Appendix A) or the source file.
0.2 Entry points
A plugin imports one package, @get-bb/plugin-sdk, through seven entry points. Each entry runs in one place.
| Entry | Runs in | Section |
|---|---|---|
@get-bb/plugin-sdk (root) |
the core process; same as /server |
§3 |
@get-bb/plugin-sdk/server |
the core process (in-process, or a plugin:<id> worker for marketplace installs) |
§3 |
@get-bb/plugin-sdk/app |
the browser, through the import map | §4, §5 |
@get-bb/plugin-sdk/host |
a host-role child process on a machine | §6 |
@get-bb/plugin-sdk/bridge |
the provider bridge child process; the declaration also loads in core | §7 |
@get-bb/plugin-sdk/contracts |
every tier and the browser; schemas only, no runtime side effects | §2 |
@get-bb/plugin-sdk/testing |
tests only | §9 |
A plugin may also import @bb/ui (the component kit, on the import map) and other plugins' published contracts modules (@get-bb/plugin-<id>/contracts). It imports nothing else from bb.
0.3 Tiers at a glance
(The import and reducers tiers, and the build outputs per tier, are in §1.6.)
| Tier | Manifest key | Default export | Receives |
|---|---|---|---|
| server | bb.server |
definePlugin({ activate(ctx, config), background? }) |
ctx: PluginContext (§3) |
| app | bb.app |
definePluginApp({ setup(app, config) }) |
app: PluginAppApi (§4) |
| host | bb.host |
defineHostEntry({ roles, signals, dispose }) |
RoleContext per role (§6) |
| contracts | bb.contracts |
named exports built with defineService, defineEvent, … |
— (§2) |
| cli | bb.cli |
defineOfflineHandlers({...}) — not built; the entry is spec-only (03 §5.9; Appendix B) |
local files only |
At least one of server, app, host is required. A package with a non-empty provides must set contracts.
0.4 One definition, five surfaces
A service method is defined once with defineService/method. The kernel derives the HTTP route (/api/v1/<pluginId>/<service>/<kebab-method>), the typed SDK call, the bb CLI word, the agent tool (<plugin>_<service>_<method>), and the reference docs from that one definition (§8). Every mutation method is also a command on the bus with an actor, before interceptors, and after listeners (§3.4).
0.5 Conventions
- Signatures are abridged: optional fields carry
?, defaults are shown as= value. - Spec cites are
NN §x.y(section fileNN-*.md). Built cites areD#or a path undernext/packages/.README deviation Nis the numbered entry under "Deviations from the spec" inpackages/plugin-sdk/README.md;kernel-host README deviationlikewise. - Identifiers: plugin ids match
^[a-z0-9][a-z0-9-]{0,63}$(PLUGIN_ID_RE, §1.1); service ids are<pluginId>/<serviceName>; event names are<pluginId>/<name>(kernel events arekernel/<name>); command names are<noun>.<verb>; extension kinds are<pluginId>/<kind>; core kinds are bare snake_case (file_change,web_search). - No member carries an
experimental_prefix. The surface is reviewed throughapi.lock.json; adding or renaming an export edits that file in the same change. - Sentences use present tense and active voice.
0.6 Known open questions for authors
Product decisions this document does not make. Each line names the gap, the fact as built, and what is undecided.
engines.bbandpeer["@bb/ui"]for a 1.0 plugin (§1.2): the core and@bb/uiare0.0.0in the tree (apps/bb-app/package.json,packages/ui/package.json) and hello-slot omits both, so the defaults*apply. The version strings a 1.0 plugin should pin are not decided.- Published dependency ranges (§1.5):
@get-bb/plugin-sdkis1.0.0-next.0(packages/plugin-sdk/package.json),@bb/uihas no published version, and the example usesworkspace:*. The ranges an out-of-tree plugin writes are not decided. target: "host"(§2.2, D5): answers 503service_unavailable. Which release flips D5 is not decided.bb plugin new(§1.7, 01 §3.1): not in@bb/plugin-build. Whether it ships, and with which skeleton, is not decided; §1.5 has the hand-written starter.ctx.hostClientin the product (§3.9, D15): resolved atdc07292bf— core passescreateHostClients(...).clientForto the loader (packages/core/src/core.ts), so a server tier reaches itsrpcrole. The stage-2/04 D15 note predates this commit.
0.7 Changes at dc07292bf (the Stage 2 domain integration)
@get-bb/plugin-sdk/serverexportswithDefaults(def, handlers)(§3.3).contributes.commands.nounsadmits dotted nouns:^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)*$(§1.3).- New kernel services:
kernel/store.eventsAppend,kernel/host-runtime,kernel/bridge-driver;kernel/storegainsqueueGet,projectsGet,environmentsGet,attachmentsGet,threadsByEnvironment(§3.11). EnvironmentProvider.provisionreceivessource: ProjectSource | null; the role answersenv/describe;EnvironmentsPortgainssummarizeanddescribe; provision progress ridesenv.progressnotifications, not a stored event (§6.5).HostView.homeDir: string | nullonkernel/hosts.list|get(§3.11).ctx.hostClientis wired in core (§3.9; supersedes D15).- CLI: a variadic positional of structured items keeps a non-JSON word as a string (§8.4);
bb plugin install <source>has its positional (closes D9's gap). - A
usesre-run is one observable step: teardown and re-activation in one registry batch; a handle follows a same-generation re-provide instead ofstale_handle(§3.3).
0.8 Changes at e1efe72a3 (the Stage 2 UI workstream requests)
Every plugin-visible change in the delta, one line each. Source: git show e1efe72a3 -- next/packages; api.lock.json gains 26 entries (17 distinct names) and drops none. "Before" means dc07292bf.
@get-bb/plugin-sdk/contracts(re-listed on/server) exports kernel-store's row schemasrowSchema,storedEventSchema,threadHeadSchema,threadRowSchema,threadListItemSchema,interactionRowSchemaand the typesRow,StoredEvent,ThreadHead(§2.11;plugin-sdk/src/contracts/index.tsover the new@bb/kernel-store/schemassubpath, which imports onlyzodand@bb/kernel-core, so the entry stays browser-safe).@get-bb/plugin-sdk/serverexports the typesHubApi,HubCommandInput,HubCommandResult;ctx.hub.command({ target, name, input, timeoutMs })sends a client command over the realtime hub with the plugin as actor and answers{ delivered }(§3.2, §3.5, §3.13; in-process rows only).@get-bb/plugin-sdk/appexportsLayoutRootand the typesLayoutRootProps,DragSession(§4.5): a plugin's ownrootoccupant can now mount the pane tree. §4.5's "not exported" sentence is withdrawn.@get-bb/plugin-sdk/appexportsuseCommands(): ClientCommands(run(id, input?),has(id)) and the typeClientCommands(§4.6): the caller side of the client command bus.RowState.exhausted: booleanis a required member:trueonce a page came back shorter thanpageSize. A pluginapplythat builds aRowStateliteral must set it (§4.8;kernel-ui/src/data/define-row-store.ts).defineRowStore'sapply(state, ops, { threadId })gains a third argument; a two-argumentapplystill type-checks (§4.8).- A pane kind's
childrenare slot-tree declarations while the kind's plugin wins the kind; before, a registration into a pane-kind child (thread.timeline) failed the load withslot … is not declared(§4.2, §4.5;slot-tree.tskindReg,create-ui-kernel.tskindLive). app.contributions.jsondeclarescarries pane-kind children (owner: "pane:<kind>") and the children of registrations aninjectthunk makes; the thunk runs atbb plugin buildagainst a recorder (§4.1, §4.12).contributions_mismatchcompares the whole file, so an artifact built before this delta fails to load after it, and vice versa, when it has either.- The slot instance key gains
|<registration id>, so twolistregistrations of one plugin into one slot keep separate React keys, stores and crash latches (§4.3;slot-tree.tsinstanceKey). - A bootstrap or wire preference row for a key no plugin has defined yet is parked and applied at that key's
define, so the first paint shows the stored value instead of the default (§4.7;services/preferences.ts). - Plugin stylesheets load: core keys
cssUrlonmeta.digests["app.css"](before:"dist/app.css", a key the build never writes, socssUrlwas alwaysnulland no plugin sheet ever loaded) (§4.11, §4.12;core/src/app.ts). kernel-ui/dockOpenDefaultdefaults tofalse(before:true), and a new pane's dock opens only when the kind'sdock.default(state).openistrueand the preference istrue; a pane kind whose dock opened by default now starts closed until the user flips the preference (§4.5;layout/layout-store.ts).threadListItemSchema.displayStatusmirrors today'sresolveThreadRuntimeState: only a running thread (active | starting | stopping) on a remote-host environment waits on a host session (host-reconnectingafter a closed session, elsewaiting-for-host); the primary host is always connected; an idle thread isidlewhatever its host does. Before, an idle thread on any environment with no session row showedwaiting-for-host(§2.11;kernel-store/src/threads.ts).- Item rows carry
payload.startedAt(the open event'stime) andpayload.completedAt(the close event'stime;nullwhile open); rows in astore.dbwritten before the delta lack both until rebuilt (§2.11;kernel-store/src/reducers.ts). - A manifest
requiresid every provider of which is app-only (noservertier) is satisfied by the provider's presence in the plan and still orders the load; before, such a row satwaitingforever (§1.3;kernel-loader/src/loader/graph.tsappTierServices,load.ts). - Smaller: the
overlay: truechain keeps the owner fallback in one stable wrapper across a takeover, so its DOM state survives (§4.3); browser back/forward reportscause: "pop"(before:replace, §4.5); a keydown during IME composition (isComposingor keyCode 229) never dispatches a chord (§4.6);dist/contracts.mjsis built without the Node ESM banner so a sibling app tier bundles it (plugin-build/src/bundle.ts);createAppHarness({ shell: { panes: true } })mountsLayoutRootand registers no/route (§9.3,plugin-sdk/src/testing/app.ts).
0.9 The kernel-domain tier (decision, 2026-08-26)
Three tiers, not two. The kernel is mechanism: container, store, loader, UI kernel, host runtime. The kernel domain is the set of first-class services for the concepts the kernel already hard-codes in its schema and scoping: threads (threads/*, the thread-head projection, the timeline row model, pending interactions, lifecycle events), projects (projects/*), environments (environments/*: entity, lifecycle, provision), files (files/files), terminals (terminals/sessions), and the provider framework (providers/registry|models|usage, the bridge protocol). Plugins are everything else.
Kernel-domain rules: core provides these services; they are always present and cannot be disabled; their contracts version with the SDK major, not with any plugin; a consumer injects them like kernel/* services and declares no requires edge. The extension points stay pluggable: provider drivers and environment providers are ordinary plugins behind the framework, and every rendered surface over the domain is a slot occupant. Earlier sections of this reference describe threads, projects, and environments as domain plugins — that was the rewrite's packaging; read those contract shapes as the kernel-domain surface. Rationale and migration sequencing: plan-papi-to-idealized-1.0.md; empirical motivation (36 of 82 marketplace plugins depend on these contracts): marketplace-feasibility-2026-08-26.md.