Raw capture โ raw/bb-architecture-research-notes.md
url: https://gist.github.com/galligan/73beb12a47851dc0b3ce34aef4d8d529 date_fetched: 2026-08-07
Research notes from reading get-bb/bb (clone: research/repos/get-bb/bb in The Grid) and the locally installed bb 0.35.1 app.
3e3e1934f22afd38a82ccf770cd423c6c2d1c4a2(2026-08-06)/Applications/bb.appโ host-daemon atโฆ/bb-app/host-daemon/dist/packages/agent-runtime,apps/host-daemon,apps/serverbb does not call Anthropic/OpenAI HTTP APIs itself for coding threads. It shells out to existing agent harnesses (Codex CLI app-server, Claude Agent SDK โ Claude Code CLI, Pi SDK, ACP agents like Cursor's agent acp) and normalizes their traffic into a shared bb thread-event model.
Two sanctioned adapter shapes:
app-server).bb-*-bridge.mjs) that hosts an SDK / ACP client and exposes bb's common JSON-RPC surface on stdio.Architecture path for a turn:
UI / CLI / HTTP API
โ bb Server (SQLite state, product policy)
โ Host daemon WebSocket RPC (thread.start / turn.submit)
โ AgentRuntime (process lifecycle, JSON-RPC framing)
โ ProviderAdapter (buildCommandPlan + translateEvent)
โ Provider process (codex app-server | bridge โ SDK/ACP)
โ events stream back โ daemon โ server โ clients
| Piece | Role |
|---|---|
Server(apps/server) |
Source of truth. Threads, environments, providers policy. Sends host RPC commands over the daemon WebSocket. |
Host daemon(apps/host-daemon) |
Provisions workspaces/worktrees, owns createAgentRuntime(), spawns provider processes, posts event batches. |
Agent runtime(packages/agent-runtime) |
Multiplexes providers/threads; never interprets provider-specific wire content itself โ adapters own that. |
Agent providers catalog(packages/agent-providers) |
Built-in provider IDs, capabilities, composer actions, reasoning ladders. |
| CLI / App | First-class clients of the same server contract. |
Environment variables injected into agent shells include BB_PROJECT_ID, BB_THREAD_ID, BB_ENVIRONMENT_ID, BB_CLI.
From a live bb provider list on this machine (and the repo catalog):
| Provider ID | Display | Shape | What actually runs |
|---|---|---|---|
codex |
Codex | Direct JSON-RPC | codex app-server |
claude-code |
Claude Code | Bridge | node bb-claude-code-bridge.mjsโ@anthropic-ai/claude-agent-sdkโ hostclaudeCLI |
pi |
Pi | Bridge | node bb-pi-bridge.mjsโ Pi coding-agent SDK |
acp-cursor |
Cursor | Bridge + ACP child | node bb-acp-bridge.mjsโ spawnsagent acp |
acp-opencode |
opencode | Known ACP (if CLI on PATH) | opencode acp |
acp-omp |
omp | Known ACP | omp acp |
acp-grok |
Grok Build | Known ACP | grok agent stdio |
acp-hermes-agent |
Hermes Agent | Known ACP | hermes acp |
acp-<slug> |
Custom | Configured ACP | ~/.bb/config.jsonโcustomAcpAgents |
Built-in ACP profile in agent-runtime today is Cursor (ACP_AGENT_PROFILES). Other known ACP agents are registered server-side in apps/server/src/services/system/known-acp-agents.ts and become available when their executable is detected on the host.
AgentRuntime keys provider processes via resolveProviderProcessKey:
codex\0thread:<bbThreadId>โ one- codex app-serverper bb thread.Packaged bridge bundles live next to the host-daemon binary:
bb-claude-code-bridge.mjsbb-pi-bridge.mjsbb-acp-bridge.mjscodex app-server
Adapter: packages/agent-runtime/src/codex/adapter.ts
codex app-server generate-ts) intopackages/agent-runtime/src/codex/generated/codex-app-server/.docs/codex-app-server.md.| bb command | Codex method |
|---|---|
| initialize | initialize(clientInfo name"bb",experimentalApi: true) |
| model/list | model/list |
| skills | skills/extraRoots/set |
| thread/start | thread/start |
| thread/resume | thread/resume |
| thread/fork | thread/fork |
| turn/start | turn/start(orthread/compact/startfor standalone compact) |
| turn/steer | turn/steer |
| rename/archive | thread/name/set,thread/archive, โฆ |
Thread start enables experimentalRawEvents: true so bb can translate richer item streams. Events are normalized in codex/event-translation.ts.
Via Codex config on start/resume/turn:
shell_environment_policy.set.BB_THREAD_IDmodel_reasoning_effortfrom bb reasoning levelmemories.use_memories/- memories.generate_memories(provider Settings preference)features.multi_agent=falseand V2 concurrent threads capped to1accept-edits/auto/full)Auth: Codex's own credentials (CLI login / env). bb does not proxy OpenAI tokens for coding turns; it may reuse Codex credentials for helper inference routes separately.
node bb-claude-code-bridge.mjs
Bridge source: packages/agent-runtime/src/claude-code/bridge/
The bridge is a thin JSON-RPC shell. It does not translate to bb thread events; it forwards raw Claude Agent SDK messages as:
{ "method": "sdk/message", "params": { "threadId": "โฆ", "message": /* SDKMessage */ } }The parent adapter (claude-code/adapter.ts) translates SDKMessage โ ThreadEvent[].
SdkSession (bridge/sdk-session.ts) calls:
query({
prompt: asyncIterableOfUserMessages,
options: { cwd, systemPrompt, model, permissionMode, sandbox, hooks, mcpServers, โฆ }
})
from @anthropic-ai/claude-agent-sdk.
User turns are pushed into that async iterable (pushInput), not sent as one-shot CLI argv prompts.
The SDK is pointed at the host Claude Code executable via:
BB_CLAUDE_CODE_EXECUTABLEif set and executable, elseclaudefound on- PATH(resolveClaudeCodeExecutable in session-options.ts)
So the request path is:
bb Server โ daemon โ AgentRuntime JSON-RPC
โ Claude bridge (thread/start, turn/start, โฆ)
โ Claude Agent SDK query()
โ Claude Code CLI process (pathToClaudeCodeExecutable)
โ Anthropic (whatever the CLI/SDK uses for the selected model)
claude_codewith optional append, or full replace for manager/instructionModesettingSources: ["user", "project", "local"]โ loads- ~/.claude+ project settingspersistSession: true, resume via SDK- resume/- forkSessiondefault,acceptEdits,bypassPermissions, โฆ)sandboxfor accept-edits/auto + workspace scopeeffort+ adaptive thinking;ultracode= effortxhigh+ settings flagultracodesettings.autoMemoryEnabledsettings.enableWorkflowsbb-bridge(tool-proxy-mcp.ts), tool namesmcp__bb-bridge__<name>disallowedToolswhen provider-native subagents are disabled in SettingsInteractive permissions/user questions are bridged back to bb as pending interactions, then answered via JSON-RPC responses into the SDK canUseTool / question handlers.
Same bridge pattern as Claude Code:
node bb-pi-bridge.mjs โ @earendil-works/pi-coding-agent (and pi-ai)
Adapter owns event translation from Pi AgentSessionEvents. Process is provider-scoped like Claude. Capabilities are narrower (e.g. only full permission mode in the live provider list).
node bb-acp-bridge.mjs
โโ per thread: spawn(<agentCommand>) speaking Agent Client Protocol over NDJSON stdio
Bridge source: packages/agent-runtime/src/acp/bridge/
bb implements a minimal ACP client (no external ACP SDK). Frames are newline-delimited JSON-RPC. Schemas live in acp/wire.ts.
Built-in profile (acp/profiles.ts):
agentCommand: { command: "agent", args: ["acp"] }
modelCli: { listArgs: ["--list-models"], selectFlag: "--model", primaryModels: [โฆ] }
Important comment in-repo: the Cursor editor binary (cursor) is not the ACP agent; the CLI agent binary is agent.
Model/reasoning for Cursor-style agents is often encoded in model id variants selected at launch (--model โฆ), not only via ACP config options. Hermes uses ACP-native reasoning_effort config instead.
agent acp,opencode acp, โฆ)initialize(protocolVersion, clientInfo- bb, fs capabilities)session/new(or- session/loadon resume when supported)session/set_config_optionor CLI flagssession/promptwith content blockssession/updatenotifications (message chunks, thoughts, tool calls, plans)session/request_permissionโ bridge policy / bb UIfs/read_text_file/fs/write_text_file; bridge enforces workspace write rootssession/cancelAdapter translates ACP updates into bb ThreadEvents (acp/adapter.ts).
bb thread tell, or spawn prompt.thread.start(cold) vsturn.submit(warm), builds aHostDaemonCommandwith providerId, model, permissionMode, instructions, dynamicTools, optionalacpLaunchSpec.command-handlers/thread.tsstages attachments, resolves workspace, callsruntime.startThread/runtime.runTurn/runtime.steerTurn.threadId+providerThreadId.Separate from provider-native files (CLAUDE.md, repo-root AGENTS.md for Codex):
~/.bb/AGENTS.mdโ global bb instructions appended at provider session start<workspace>/.bb/AGENTS.mdโ workspace instructions.bb/skills/โ injected per threaddynamicToolson start/submitProvider-native memory (Codex memories, Claude auto-memory) is controlled on Settings โ Providers pages and applied at start/resume/fork โ not mid-turn.
| Topic | Path |
|---|---|
| Runtime public API / architecture | packages/agent-runtime/README.md |
| Provider registry | packages/agent-runtime/src/provider-registry.ts |
| Codex adapter | packages/agent-runtime/src/codex/adapter.ts |
| Claude adapter + bridge | packages/agent-runtime/src/claude-code/ |
| ACP adapter + bridge | packages/agent-runtime/src/acp/ |
| Known ACP agents | apps/server/src/services/system/known-acp-agents.ts |
| Daemon thread handlers | apps/host-daemon/src/command-handlers/thread.ts |
| Runtime manager | apps/host-daemon/src/runtime-manager.ts |
| System overview | docs/system-overview.md |
| Config / custom ACP | docs/configuration.md |
Think of bb as an orchestrator + normalizer:
If you ask "how does bb send a request to Claude Code?", the accurate answer is: JSON-RPC into a Node bridge that drives the Claude Agent SDK's query() against the local claude binary โ not a direct Anthropic Messages API call from bb itself.
If you ask the same about Codex: JSON-RPC into a per-thread codex app-server process, using Codex's native
thread/* and turn/* methods.If you ask about Cursor: JSON-RPC into the ACP bridge, which spawns agent acp and calls ACP session/prompt.