How bb, the programmable IDE for coding agents, is built
By Flavio Copes
I read the bb source to follow one coding-agent task through its server, SQLite database, host daemon, provider runtime, event stream, and app.
I’ve been trying out bb, an open source IDE built around coding agents.
The tagline is “the IDE for loop-driven development”.
You give tasks to agents. They run in separate threads. You check their work, steer them, or hand a thread to another agent.
But the interesting part is not the interface.
bb treats the interface itself as something agents can use.
The app, command-line tool, SDK, and HTTP API control the same system. A person can click a button to start a thread. An agent can run a command to start another thread. A script can do the same thing on a schedule.
This changes the role of the IDE.
It is not only a place where we watch agents. It becomes the runtime that coordinates them.
I wanted to understand how that works, so I read the bb source and followed one task through the system.
The problem is no longer running one agent
Running one coding agent is easy.
You open a terminal, describe the work, and wait.
The problems start when you run five:
- which agent owns each task?
- which directory is each agent changing?
- which one is waiting for approval?
- which one finished?
- how does a reviewer see another agent’s work?
- who merges, cleans up, and closes the session?
The agents can do the implementation. The coordination around them is still manual.
This is the bottleneck bb tries to remove.
It gives every unit of work a durable identity, an execution environment, a lifecycle, and an event history.
The word bb uses for that unit is thread.
Everything starts with a thread
A thread is more than a chat transcript.
It records:
- the project
- the agent provider
- the environment where work runs
- the current lifecycle state
- its parent or source thread
- an ordered stream of messages, tool calls, approvals, and file changes
Threads can be standard or manager threads.
A standard thread does work directly. A manager thread coordinates other threads.
The relationship is stored explicitly. A child thread points at its parent, so delegation becomes part of the data model instead of something hidden inside a prompt.
That matters when a manager creates five workers. The app can show the tree. The CLI can wait for one child. The parent can receive results from all of them.
The system has four runtime pieces
bb is split into four main pieces:
flowchart LR
U["Person or agent"] --> A["App, CLI, or SDK"]
A --> S["bb server"]
S --> D["Host daemon"]
D --> P["Codex, Claude Code, Pi, or ACP agent"]
S --> DB[("SQLite")]
P --> D
D --> S
S --> A
The server owns product state and policy. It exposes the HTTP API, stores data in SQLite, and sends live notifications over WebSocket.
The host daemon runs on every machine that can execute work. It provisions workspaces, starts provider processes, and sends their events back to the server.
The app is the web interface.
The CLI and SDK expose the same operations to people, agents, and scripts.
Two contract packages define the important boundaries:
@bb/server-contractdescribes the API between clients and the server@bb/host-daemon-contractdescribes commands and events between the server and daemons
The server does not know how a provider process works. The daemon does not make product decisions about projects and threads.
This separation is the foundation of the system.
Following one task through bb
Suppose I create this task:
Find why the tests fail, fix the problem, and report what changed.
Let’s follow it.
1. The client creates the thread
The app, CLI, and SDK all call the same server API.
The CLI version looks like this:
bb thread spawn \
--project proj_flaviocopes \
--prompt "Find why the tests fail, fix the problem, and report what changed"
The server validates the project, provider, parent thread, environment choice, visibility, and permission mode.
It then creates a thread row in SQLite.
The initial status is commonly starting. The work has an identity before an agent process exists.
This order is important. If environment provisioning fails, bb still has a thread that can explain the failure.
2. bb resolves the execution environment
A thread needs a directory on a particular machine.
bb calls this an environment.
An environment can point at an existing directory, or bb can manage it. A managed environment can create an isolated Git worktree and later remove it when no active thread uses it.
The environment also owns the host boundary.
This means the server does not say “run in /Users/flavio/project” and hope that path exists everywhere. It identifies both the host and the workspace belonging to that host.
The environment has its own lifecycle:
provisioning → ready → retiring → destroying → destroyed
Provisioning can also fail. Destruction can fail. A retirement can be cancelled when a new thread starts using the environment.
bb stores these as explicit transitions instead of scattered Boolean flags.
3. The server sends a command to the host daemon
Once the environment is ready, the server sends a thread.start command to the daemon connected to that host.
The connection is a WebSocket, but the messages form an RPC protocol.
The server sends a command with a thread ID, environment ID, provider, prompt, execution settings, and skills. The daemon returns a command result separately from the stream of agent events.
This distinction is useful:
- the command result says whether starting the work succeeded
- thread events describe what happened while the work ran
Starting a process and receiving its first output are not the same operation.
4. The daemon starts the provider runtime
The daemon resolves the workspace path and retains the environment while the command runs.
Then it passes the thread to @bb/agent-runtime.
This package contains adapters for Codex, Claude Code, Pi, and agents implementing the Agent Client Protocol.
Each provider speaks a different protocol.
Codex uses its app-server protocol. Claude Code has its own process and message format. ACP agents expose a shared protocol. Pi can run through its SDK bridge.
bb does not leak those differences into the rest of the application.
Each adapter converts provider-specific commands and responses into a shared set of thread events.
This is one of the most important parts of the architecture.
Without the adapter layer, every screen and automation would need to understand every provider.
5. Provider output becomes thread events
The runtime reads the provider process output and translates it.
Events include things such as:
turn/started- a message item starting
- a command execution waiting for approval
- a file change completing
turn/completed- a provider error
The daemon does not write directly to SQLite.
It puts events into an in-memory queue. Ordinary events are batched for up to 100 milliseconds. Important events flush immediately, including approvals, completed items, completed turns, interruptions, and non-retrying errors.
This reduces small network writes without making the interface feel delayed.
The trade-off is explicit in the source: if the daemon crashes before queued events reach the server, those pending events are lost.
The queue retries after connection failures, but it is not a durable log.
That is reasonable for progress telemetry. It would not be reasonable for a payment ledger.
6. The server assigns the final event order
When the daemon posts a batch, the server validates that every thread belongs to the daemon’s host.
It then writes the accepted events in one SQLite transaction.
The server assigns a monotonically increasing sequence number for each thread.
The daemon supplies the content. The server owns the order.
This prevents two reconnecting or overlapping producers from inventing conflicting positions in the timeline.
The event table stores the thread, environment, turn, provider thread, event type, item identity, data, timestamp, and server sequence.
The sequence is what lets the app ask for everything after event 47 and receive a stable continuation.
7. The app receives a notification
After committing the events, the server notifies connected clients.
The notification does not need to contain the complete reconstructed thread. It tells the client that the thread changed.
The app can then read the new timeline data from the server.
This keeps SQLite as the source of truth. A missed WebSocket notification does not destroy the thread history.
The app can reconnect and rebuild its view from stored events.
Thread state is a state machine
A thread does not have a loose collection of flags such as running, failed, and stopped.
It moves through a small state machine:
idle → starting → active → stopping → idle
↓
error
The exact transition depends on events such as:
run.preparingrun.startedrun.succeededrun.failedstop.requestedstop.settled
The database applies transitions through compare-and-swap writers.
An event that no longer applies becomes a recorded no-op instead of silently overwriting newer state.
For example, an old run.started callback should not reactivate a thread that was archived or deleted while the daemon was responding.
This is the kind of race that appears as soon as a local app controls several asynchronous processes.
Why the event log matters
An append-only event stream gives bb more than a chat transcript.
It can reconstruct:
- which turn is active
- which tool is waiting for approval
- which files changed
- whether the provider is retrying
- what a child thread reported
- what happened before a crash
It also makes different interfaces possible.
The app renders the events visually. The CLI can print the latest output. A plugin can react to thread.completed. A manager agent can inspect a child’s result.
All of them read the same underlying history.
Manager threads are ordinary threads with more tools
There is no separate manager service running a special kind of artificial intelligence.
A manager is a thread that can create, inspect, steer, and wait for other threads.
The bb CLI is exposed to agents through a built-in skill. A manager can run commands such as:
bb thread spawn \
--project proj_flaviocopes \
--parent-thread thr_manager \
--prompt "Inspect the failing API tests and report the cause"
It can then wait:
bb thread wait thr_worker
And read the result:
bb thread output thr_worker
Agent orchestration becomes normal application behavior built from threads, relationships, commands, and events.
The manager is still an agent. It can make bad decisions, create overlapping tasks, or misunderstand a result.
bb provides the coordination primitives. It does not make coordination automatically correct.
The same project can run on several machines
The server and daemon separation allows remote execution.
One bb server can enroll several machines. A project can map to a different path on each host.
For example:
MacBook /Users/flavio/project
Mac Studio /Users/flavio/www/project
Linux server /srv/project
The browser is only a control surface. Opening bb from a laptop does not mean work runs on that laptop.
When creating a thread, you choose the execution machine. The server dispatches the command to that machine’s daemon.
This is a strong boundary because it keeps filesystem access local to the host that owns the files.
It is also a serious security boundary.
A connected daemon can run commands and change code. The server API can create work. bb’s documentation warns that binding the server directly to a public interface exposes an unauthenticated command and file-reading API.
Use the managed bb connection or a private network such as Tailscale. Do not put the raw local server on the public internet.
The IDE can extend itself
bb plugins can add server behavior, CLI commands, skills, automations, and interface panels.
The same APIs used by the built-in app are available to plugins.
This is why an agent can receive a request such as “add a task tracker to bb” and implement more than a code change. It can add the server records, CLI surface, sidebar panel, and instructions that teach other agents how to use it.
The interesting idea is not that an AI generated a panel.
The interesting idea is that the application exposes enough of itself as stable contracts for the panel to become a real product feature.
Why SQLite fits this system
bb uses one SQLite database as the source of truth.
That keeps the local installation small:
- no external database server
- transactions around event ordering
- straightforward backups
- one file containing the durable coordination state
The server can still control remote machines because the code and processes stay on those hosts. Only coordination state returns to the central database.
The limit is also clear.
One bb server owns one coordination domain. This is not a globally distributed scheduler. If the server machine disappears, remote daemons cannot independently continue coordinating through another server.
For a personal or small-team agent IDE, that simplicity is a feature.
What I like about the architecture
The system uses ordinary pieces:
- HTTP
- WebSocket
- SQLite
- child processes
- explicit state machines
- append-only events
- typed contracts
The novelty comes from how those pieces are arranged.
bb treats an agent as another operator of the application. It also treats the agent process as unreliable: processes disconnect, events race, commands finish late, and providers speak different protocols.
The architecture does not hide those facts.
It gives each one a boundary.
What can go wrong
bb is young software. The repository says the core architecture is stable while workflows and surfaces are still evolving.
I would pay attention to these limits:
Queued daemon events are not durable. A daemon crash can lose progress events that have not reached the server.
The server is central. SQLite makes operation easy, but the server remains the coordination point.
Remote execution increases the trust surface. An enrolled host runs commands. A compromised server can dispatch work to connected machines.
Providers behave differently. Normalizing events does not make Codex, Claude Code, Pi, and ACP agents identical. Resume behavior, approvals, models, and tool semantics still vary.
Parallel agents can still conflict. Separate environments help, but two agents can make incompatible architectural decisions or edit shared external systems.
Managers are not magic. A manager thread needs a good plan, clear ownership, and useful completion criteria.
How I would use bb
I would start with work that already divides cleanly:
- inspect separate parts of a codebase
- fix unrelated test failures
- audit several pages
- review a completed implementation
- monitor CI after a change
- update independent documentation files
I would keep one clear owner for shared abstractions.
I would also separate implementation from review. One thread makes the change. Another reads the diff and runs the checks.
The most useful automations come after that workflow is already reliable.
For example:
- Create a worker for one issue.
- Wait for it to finish.
- Create a reviewer using the same project but a clean context.
- Send actionable findings back to the worker.
- Wait for CI.
- Archive the threads when the work is complete.
That removes the babysitting without removing the review.
The deeper idea
Most coding-agent tools focus on making one agent more capable.
bb focuses on the system around the agents.
T3 Code is another local control plane for the same problem: one interface over several provider CLIs, with threads, approvals, and diffs.
bb gives work an identity. It gives execution a host and environment. It gives output an ordered history. It gives people, agents, scripts, and plugins the same controls.
That is the part I find important.
If agents become normal participants in software development, our tools cannot keep treating them as text boxes attached to an editor.
They need APIs, lifecycle state, permissions, isolation, and observable work.
bb is an early implementation of that idea.
It is MIT-licensed, local-first, and available in the get-bb/bb repository. The quickest way to try it is:
npx bb-app@latest
Then open http://localhost:38886.
If some foundations here are new to you, I have free courses on Git and the command line. Parallel agents, review loops, and agent-managed workflows are also part of the AI Workshop and Ship Factory.
Want me to talk about your product? You can sponsor this site.