BB - это открытая программа под лицензией MIT, в которой Claude Code, Codex, Pi и ACP-агенты работают в одном окне. За 7 месяцев репозиторий собрал около 4 тысяч звёзд. Главное в BB не окно, а то, что его интерфейс достраивается плагинами и новый плагин подхватывается без перезапуска программы.
Недавно я снимал про BB ролик. Там агент за 20 минут и два промпта собрал плагин, который расшифровывает запись созвона, выделяет договорённости со сроками и пишет письмо клиенту. Ролик был про то, что это даёт пользователю, а в этой статье я разберу код и покажу, за счёт чего это работает. Всё ниже проверено по репозиторию get-bb/bb по состоянию на конец сентября 2026.

Идея BB держится на двух слоях. Снизу стоят агенты, которые у вас уже есть, и каждый работает в своей обвязке, со своими инструментами и настройками. Сверху одно окно, которое с ними разговаривает. Чтобы понять, как одно окно управляет разными агентами, начнём с того, какие процессы вообще запускаются.
Сервер хранит, демон исполняет
Команда npx bb-app запускает лаунчер, а он поднимает два Node-процесса: server и host-daemon. Сервер написан на Hono, по умолчанию слушает 127.0.0.1:38886 и хранит всё состояние в SQLite, в файле ~/.bb/bb.db. Демон держит примитивы машины, то есть файлы, git, терминалы и запуск агентов. Десктопное приложение на Electron здесь только оболочка, которая запускает тот же bb-app и показывает веб-интерфейс сервера.
Примечательно в какую сторону идёт связь. Не сервер ходит к демону, а демон сам открывает к серверу WebSocket и получает по нему команды вроде turn.submit. Поэтому вторая машина подключается к BB без открытых портов: в настройках генерируется однострочник curl .../install.sh, он ставит демон как сервис launchd или systemd, и демон получает свой ключ машины. Сервер при этом это роль, а не отдельный компьютер, и на серверной машине работает такой же демон, как на остальных.
Демон и сервер не всегда обновляются одновременно, поэтому протокол между ними версионирован. Сейчас в контракте стоит HOST_DAEMON_PROTOCOL_VERSION = 220, и если версии разошлись, демон сам скачивает подходящую сборку, сверяет её SHA-256 и перезапускается. Для второй машины это важно, потому что обновлять её руками после каждого релиза сервера не нужно.

Клиентов у сервера может быть несколько: веб-интерфейс на React 19, окно Electron, CLI bb, SDK и мобильное приложение на Expo. Все они ходят в один и тот же /api/v1, поэтому тред, начатый в браузере, продолжается с телефона или из терминала. Давайте разберемся как до всех них доходят ответы агента.
Тред хранится как журнал: таблица events только дописывается, и у каждого события есть номер sequence внутри треда. Когда демон присылает новую пачку событий, сервер вставляет их и рассылает по WebSocket одно уведомление changed. Самих данных в WebSocket нет, поэтому клиент помечает свое состояние интерфейса устаревшим и дочитывает таймлайн по HTTP. Схема «уведомил, потом дочитал» проще потоковой, потому что у клиента нет своей копии треда, которая могла бы разойтись с сервером.
Свой протокол, а ACP только диалект
Чтобы события в журнале выглядели одинаково, BB приходится разговаривать с агентами, у которых совсем разные интерфейсы. Для этого у него есть собственный provider bridge protocol. Демон запускает для каждого агента отдельный процесс-мост и общается с ним по JSON-RPC 2.0 через стандартный ввод и вывод. Мост входит в плагин провайдера, демон скачивает его с сервера и перед запуском сверяет хэш.
Я ожидал, что BB построен поверх ACP, то есть Agent Client Protocol от Zed, но это не так. ACP в BB один из диалектов, которые мосты переводят в собственный протокол. Ниже в таблице указано как запускается каждый агент:
Агент | Как запускается | Как разговаривает |
|---|---|---|
Claude Code | Claude Agent SDK, функция | SDK сам поднимает CLI |
Codex | отдельный | JSON-RPC по stdio |
Pi |
| JSON по stdio, инструменты BB по fd 3 и 4 |
ACP-агенты |
|
|
Нормализация идёт в два шага. Сначала мост переводит родной поток агента в небольшой набор событий: открыть ход, открыть элемент, дописать текст, закрыть элемент, сообщить расход токенов. Потом ассемблер внутри демона сам выдаёт идентификаторы ходов и элементов, следит за жизненным циклом и склеивает поток токенов окнами по 100 мс. Документация протокола описывает это разделение одной фразой: «the bridge knows the dialect, the runtime knows the timeline».

Подпись и иконку элемента присылает мост, поэтому в ядре нет таблиц с именами инструментов Claude или Codex. Нового агента можно добавить, не трогая ни ядро, ни интерфейс.
С разрешениями BB тоже не навязывает одну модель. Мост при рукопожатии сообщает поле approvalEnforcedBy, и у Claude Code там стоит provider. Это значит, что решения принимает собственный движок разрешений Claude, а BB подключается к нему через колбэк canUseTool. У Codex, Pi и ACP-агентов стоит runtime, и политику применяет сам BB. Всё, что политика не решила, приходит пользователю одинаковым запросом, у какого бы агента он ни возник.
Похоже устроены и навыки. Демон собирает все навыки в один каталог и передаёт его мосту командой skills/configure, а мост раскладывает их на язык своего агента. Для Claude Code на лету собирается Claude-плагин с симлинком на каталог навыков, Codex получает skills/extraRoots/set, Pi флаг --skill, а ACP-агенту список навыков с путями дописывается в инструкции. Поэтому навык, положенный в BB один раз, видит любой агент.
Где именно агент работает, решают environment-плагины. Встроенный git-worktree создаёт для треда отдельный worktree, а project-checkout работает прямо в чекауте проекта и не даёт сменить ветку, пока в нём есть незакоммиченные правки. Самый интересный вариант это modal-sandbox, который регистрирует не каталог, а целую машину. BB поднимает песочницу Modal, ставит туда свой демон и дальше обращается с ней как с любым другим компьютером.
Отдельно мне понравилось, как такой слой тестируют. В репозитории лежат записанные сессии настоящих агентов по сценариям вроде одобрения, прерывания и компакции. Команда pnpm parity проигрывает их через мосты старой и новой версии кода и сравнивает строки таймлайна. Раз мосты это плагины, пора посмотреть, как устроен сам плагин.
Из чего состоит плагин
Плагин в BB это обычная папка, а его манифестом служит package.json с блоком bb. В этом блоке перечислены части плагина. Вот фрагмент package.json встроенного плагина задач:
"bb": { "name": "Tasks", "branding": { "icon": "ListTodo" }, "server": "./server.ts", "app": "./app.tsx", "skills": ["skills"] }
Основных частей две. Серверная часть server.ts работает внутри процесса сервера BB и через объект bb получает всё, что нужно бэкенду: хранилище, свою базу SQLite, настройки, HTTP-маршруты и фоновые задачи. Клиентская часть app.tsx встраивается прямо в окно BB, например в боковое меню или в панель треда. У мостов провайдеров есть и третья часть, которую демон запускает на машине отдельным процессом.

Провайдер агента устроен как обычный плагин, и это видно на подключении своего агента. У встроенного provider-acp есть настройка customAgents, через которую я подключал DeepSeek Harness для ролика:
bb plugin config provider-acp set customAgents '[{ "id": "dsh", "displayName": "DeepSeek Harness", "command": "npx", "args": ["-y", "@deepseek-ai/dsh@latest", "--profile", "acp"] }]'
Перезапуск после этого не нужен: плагин замечает новую настройку и регистрирует провайдера acp-dsh на лету. Учтите, что id потом не переименовать, а запись с id opencode или grok молча заменит встроенного агента. Так меняются настройки плагина, а как BB подменяет весь код плагина, разберём дальше.
Как плагин обновляется без перезапуска
Сложность здесь в самом Node. Загруженный через import модуль кэшируется, и повторный import того же файла вернёт старый код. BB обходит это хуком загрузчика registerHooks из node:module, который дописывает к адресу каждого файла внутри папки плагина метку с номером загрузки:
// apps/server/src/services/plugins/plugin-runtime.ts const separator = resolved.url.includes("?") ? "&" : "?"; return { ...resolved, url: `${resolved.url}${separator}bbPluginLoad=${match.id}.${epoch}`, shortCircuit: true, };
С новой эпохой у каждого файла новый адрес, поэтому Node загружает свежий граф модулей, а старый остаётся нетронутым. Для CommonJS-сборки то же самое делается проще: модуль грузится через require и сразу удаляется из require.cache.

Весь цикл запускает bb plugin dev. Он следит за папкой плагина, через 300 мс после правки пересобирает клиентскую часть и часть для машины и просит сервер перезагрузить плагин. Сервер собирает серверную часть в server.cjs и кэширует результат по хэшу исходников и версий SDK, BB и Node. Затем новая версия запускается на кандидатном наборе регистраций, то есть рядом со старой. Если фабрика плагина упала, сервер пишет в лог «reload failed (kept previous instance)», и старая версия продолжает работать.
Если запуск удался, старое поколение гасится в строгом порядке. Закрываются его WebSocket, останавливается процесс на машине, прерываются вызовы инструментов, а фоновые сервисы получают сигнал отмены и 5 секунд на остановку. Затем в обратном порядке выполняются хуки onDispose и закрываются базы. Старый объект bb после этого бросает PluginContextStaleError при любом обращении, поэтому забытый в старой версии таймер получит ошибку, а не запишет данные молча.
Остаётся интерфейс. Сервер рассылает по WebSocket событие plugins-changed, а адрес бандла содержит хэш содержимого, вроде /api/v1/plugin-app-assets/<hash>/app.js. Если хэш не изменился, интерфейс ничего не делает. Если изменился, он загружает модуль через import(), выгружает скрипты старого поколения и меняет регистрации, после чего React перемонтирует блоки плагина. Module federation здесь нет, хватает хэша в адресе и динамического импорта. После перезагрузки сохраняются хранилище, база, настройки и секреты, а всё, что плагин держал в памяти, пропадает.
Почему агент умеет писать плагины
Горячая перезагрузка решает только половину задачи, потому что кто-то должен ещё написать сам плагин. Эту часть закрывает встроенный плагин bb-guide. Через agents.contributeInstructions он добавляет каждому агенту введение в BB, а через agents.configure включает навык bb-plugin-authoring. Навык ведёт примерно по 19 справочникам о бэкенде, интерфейсе и жизненном цикле плагина, а главы руководства агент открывает командой bb guide.
Получается замкнутый цикл. Агент внутри BB знает SDK и правит плагин, bb plugin dev пересобирает его, и изменение появляется в том же окне, где агент работает. Так и появился плагин из ролика: агенту я описывал только экран и процесс, а устройство платформы он узнал из навыка.
Безопасность и минусы
У такой открытости есть своя цена:
На публичном API
/api/v1проверки пользователя нет, есть только защита от CSRF и DNS rebinding. Документация пишет об этом прямо: «The public API is unauthenticated and permits command execution and file reads». Защищает только то, что сервер по умолчанию слушает127.0.0.1, поэтому открывать его порт в интернет нельзя.Снаружи BB открывается через BB Connect или Tailscale. BB Connect устроен как исходящий туннель: плагин внутри сервера держит WebSocket к Cloudflare Worker на
*.getbb.app, и через него идут все HTTP-запросы и WebSocket-соединения. Вход там через GitHub, и шлюз пускает только владельца сервера, а остальным отвечает 403. Машины подключаются с отдельным ключом, и менять список машин им запрещено.Плагины без песочницы: серверная часть работает в процессе BB, а интерфейс в том же окне. Модель доверия такая же, как у npm-пакета, и из защиты есть только запрет нативных зависимостей, установка с
--ignore-scriptsи аварийный режимbb plugin safe-mode. Поэтому сторонний плагин стоит читать так же внимательно, как незнакомый npm-пакет. Остальные минусы связаны с молодостью проекта. Многие точки расширения носят префиксexperimental_, пользователь в BB один, ролей и совместной работы нет. Стабильная десктопная версия есть только для Мака на Apple Silicon, на других системах BB работает через сервер и браузер.
Что стоит утащить к себе
Даже если BB вам не нужен, в его коде есть решения, которые пригодятся в своих проектах:
Узкая грамматика событий с ассемблером отделяет диалекты агентов от интерфейса.
Схема «уведомил, потом дочитал» избавляет клиентов от собственной копии состояния.
Запуск нового поколения модулей рядом со старым и откат при падении делают горячую перезагрузку безопасной для работающего сервера.
Код лежит в репозитории get-bb/bb, а как всё это выглядит в работе, я показывал в ролике. Мне кажется, самое ценное в BB не окно для агентов, а то, что программу можно менять тем же агентом, который в ней работает.

