Run itTemplatesWhen a site needs you (robot checks, sign-ins, codes)Your Chrome β€” the BuddyBots extension (best answer to robot checks)Formatting of bot repliesLiveness and long stepsWorkspacesHosted mode β€” accounts and organisations (BB_MULTI=1)Plans, credits and billing (hosted mode)Onboarding, crews, built-in connectors, email, sharingLearning β€” it gets better the more you use itTrust and safety (hosted mode)Connectors β€” MCP and databasesThe bots' computerWhat is builtRead this before trusting itLayout

BuddyBots

Always-on AI teammates with their own computer β€” a parity-plus build of xAI's Grok Bot, self-hosted. Phase 1 + Phase 2 of docs/parity-spec.md.

Run it

Needs Node 22.13+ (uses the built-in SQLite).

npm run setup     # installs server + web deps and Playwright's Chromium
npm run dev       # API on :8787, UI on http://localhost:5173

Production style (one port): npm run build && npm start β†’ http://localhost:8787 Docker (recommended once bots run terminal commands you haven't reviewed): docker build -t buddybots . && docker run -p 8787:8787 -v bb-data:/data buddybots

With no keys the app runs on a built-in simulator so every flow works offline (it is scripted, not smart, and every reply is labelled). Add real keys in Settings β†’ API keys:

Try: create a bot, open its computer, and send Open http://localhost:8787/testsite/ search flights from "Dubai" to "Lahore" then click Book now on a flight (the bundled SkyBench page is a safe target; "Book now" triggers an approval).

BB_HEADED=1 npm run dev shows the real Chromium window on your desktop.

Templates

When a site needs you (robot checks, sign-ins, codes)

Bots never try to get past a robot check, never type passwords and never retry a blocked page. When the navigator meets one it stops the task, marks the bot "Needs you" and asks in chat:

Installed Google Chrome is used for the bots when it is found (BB_BROWSER=chromium forces the bundled Chromium, BB_CHROME_PATH points at another install); both windows must be the same program because they share one profile. The hand sign-in window needs a screen: your own computer, or the Docker desktop. With the shared profile it is refused while another bot is mid-task in that browser. Not tested against Google itself β€” only against the bundled sign-in test page (/testsite/login.html) and robot-check page (/testsite/captcha.html).

Your Chrome β€” the BuddyBots extension (best answer to robot checks)

A fresh automated browser gets challenged because it *looks* fresh: no history, no cookies, launch switches, unusual timing; solving one check by hand does not change that, so the next page challenges again. The way out is not hiding the automation (BuddyBots never does that) but not using a fresh browser at all: with the BuddyBots for Chrome extension, bots set to Browser β†’ My Chrome work in tabs of *your* real Chrome β€” your logins, cookies, history and home network. Sites treat those tabs like you, and when one does show a check you solve it in your own tab and it sticks, because it is your session.

In the app, a bot set to My Chrome shows its computer panel as a mirror of its tab; *Go to <bot>’s tab* switches your Chrome to the exact window and tab the bot is using (it keeps working; the window is raised even if minimised). *Take over in my Chrome* pauses the bot and brings the tab to the front β€” you act in Chrome itself and press *Hand back*. Options that only make sense for the bot's own browser (normal-window sign-in, on-screen driving) are not shown in that mode, and a banner says so when the extension is not connected and the bot has fallen back to its own browser.

Setup (one minute): Settings β†’ Your Chrome shows the steps and a pairing code. In Chrome open chrome://extensions, turn on *Developer mode*, *Load unpacked* β†’ the chrome-extension folder of this repo; click the BuddyBots toolbar icon, paste the pairing code, *Connect*. Then in a bot's Settings tab choose My Chrome. Chat says "Browsing in your Chrome" when it is in use; the computer panel shows the tab live, and *Take over* / "Needs you" bring the tab to the front so you act in Chrome itself and press *Hand back*.

How it works: the extension opens one BuddyBots window with a "BuddyBots" tab group β€” one tab per bot, always inside that group, never your own tabs β€” and runs small page helpers (chrome-extension/content.js, the same file the server evaluates in its own Playwright browser) in them via chrome.scripting. No DevTools debugger is attached, so Chrome shows no "is debugging this browser" bar. The server does all the thinking (server/src/chrome.ts wraps a tab in the same page surface the navigator already uses, so Jev, vision, take-over, teach mode and screenshots work unchanged); clicks and typing are synthesized DOM events, screenshots come from captureVisibleTab, which is why a bot's tab is kept active in that window while it works (the window is never focused unless you press *Show the tab*). Chrome asks for "read and change all your data on all websites" at install β€” that is what acting in a tab needs; nothing is sent anywhere but 127.0.0.1. The pairing token lives in data/chrome-extension.token. Limits: Chrome must be open on the computer running BuddyBots (a bot falls back to its own browser and says so when it isn't); if the BuddyBots window is fully covered or minimised, Chrome may pause the page β€” keep it visible somewhere; press-and-hold captchas are for you to do in that tab (synthetic events cannot hold a button); on the Docker/desktop image the extension is not available. Honest expectation: far fewer challenges, not zero β€” aggressive sites can still challenge *you*; the difference is that your answer holds.

Updating the extension. Chrome keeps running whatever files it loaded until you reload it. After updating BuddyBots, open chrome://extensions and press the reload (↻) button on BuddyBots (or remove it and Load unpacked from the current chrome-extension folder). An old extension shows as "out of date" in Settings β†’ Your Chrome and in the bot's computer panel, and bots use their own browser until it is reloaded; the server requires extension protocol version 1.2.0 (GET /api/chrome β†’ minVersion).

Formatting of bot replies

Replies render as markdown: headings, lists, GitHub-style tables (| a | b | with a |---|---| line; numeric columns right-align), and charts β€” a fenced block ``` `chart `` holding {"type":"bar|line|pie","title":"…","labels":[…],"series":[{"name":"…","values":[…]}],"unit":"$"} renders as an SVG chart with hover values; a malformed block shows as code. Charts and code fences survive the end of streaming (the reply parser no longer strips `` inside the final text). Bots are told to use a table for tabular data and add a chart for numbers over time / by category / as shares. Messages with a table or chart take the full chat width.

Liveness and long steps

Workspaces

The workspace switcher sits under the BuddyBots name in the sidebar. Each workspace has its own crew and rooms; switching swaps them everywhere (sidebar, home, rooms). A bot only sees teammates in its own workspace. Settings, API keys, connectors, skills and the /workspace files folder are shared across workspaces (of one organisation β€” see Hosted mode). Deleting a workspace deletes its bots and rooms; the first workspace cannot be deleted.

Hosted mode β€” accounts and organisations (BB_MULTI=1)

By default BuddyBots is a single-owner app: no accounts, one implicit organisation, everything in data/. Start it with BB_MULTI=1 and it becomes multi-tenant:

Tests: cd server && npx tsx --test test/multi.test.ts boots a BB_MULTI=1 server (with fake Stripe, Resend and app APIs) and checks sign-up/in/out, invites and roles, isolation between two organisations, metering and limits, Stripe checkout and webhooks, sharing, emails, password reset, audit, export, deletion, the sandbox and the public site.

Plans, credits and billing (hosted mode)

Everything a bot consumes is metered per organisation into usage_events and priced in credits (1 credit = one US cent of model/tool cost at list price; a browser step ΒΌ, a terminal command Β½, a connector call 1, a media generation 5 β€” model tokens at list price, free with the organisation's own key). Plans: Free $0 (300 credits, 3 bots, cheap models), Pro $29 (3,000 credits, 10 bots, every model, overage $12/1k up to a budget you set), Team $99 (12,000 credits, 50 bots, 5 seats + $15/seat, overage $10/1k), Enterprise. When credits run out the bots pause with a clear reason (banner, Usage page, email) instead of failing silently. Stripe Checkout starts a subscription, the customer portal manages it, webhooks (/api/billing/webhook, signed) drive plan, period and status, and overage is pushed as invoice items every 6 hours. Env: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_PRICE_PRO|TEAM|SEAT. Platform admins (BB_ADMIN_EMAILS) get a Platform admin screen: every organisation, plan overrides, credit grants, suspend/restore, metrics and funnel.

Onboarding, crews, built-in connectors, email, sharing

Learning β€” it gets better the more you use it

Show me (teaching by demonstration). The fastest way to make a bot better is to do the thing yourself once. Press πŸŽ“ Show me β€” next to any step of a finished task, on the task card (*Show me how you'd do it*), in the chat composer, or in the computer panel β€” and the bot pauses, brings its tab to the front in your Chrome (or on its own screen) and watches what you do: clicks, typing, navigation, across as many sites as you like. Press Done and it writes down what it understood as *procedures* β€” site-scoped how-tos with a title, when to use them, the steps, and the values that vary (dates, cities, counts) β€” and posts a review card in the chat. Nothing is bound to the task you were on: if you set flight filters on Google Flights and then send an email in Gmail, you get two procedures, each used later when a task on that site fits. You rename, drop or confirm them; from then on every bot in the team follows them (the planner sees them as knowledge, the navigator replays their steps first and Jev gets them as hints), and they are listed and editable under Learning β†’ *What you showed it*. The understanding step uses the bot's model (or the cheapest available one); with no model key the steps are split by site without a written summary.

Show me how (teach one step). When a bot gets a single step wrong on a site (it keeps clicking a button that does nothing, picks the wrong control, misses a passenger count), open its computer panel, find that step under Activity and press πŸŽ“ Show me how. The bot pauses, brings its tab to the front (in your Chrome, or on its own screen), and records exactly what *you* do β€” clicks, typing, Enter β€” until you press Done. That becomes a lesson for that site (visible and editable under Learning) and a replayable shortcut, and every bot in the team follows it from then on. It is the fast alternative to *Teach a task*, which records a whole workflow as a skill.

Real clicks in your Chrome. Some sites (Google Flights among them) react to hover but ignore the synthetic click events an extension can send from a page script β€” the button shows its tooltip and nothing happens. The extension now checks whether a click changed anything; when it did not, it repeats it as a *real* click through Chrome's input path and remembers that the site needs real clicks. Chrome shows its "BuddyBots is debugging this browser" bar while that path is attached (about 20 s after the last real click); this is Chrome's own notice, not a hidden mode. The extension needs its debugger permission for this β€” reload it after updating (chrome://extensions β†’ BuddyBots β†’ ↻, then accept the new permission).

No model is fine-tuned; the crew learns the way a good team does β€” by writing things down and reusing what worked. After every browser goal (server/src/learn.ts) the run is analysed and four kinds of knowledge are kept per organisation, shared by every bot in it:

Also in this change: the navigator bans scrolling after three scrolls that revealed nothing and bans a control that has been opened three times on the same page β€” the two loops that turned a 4-minute flight search into a 24-minute one.

GET /api/learning returns it all; notes are exported with the organisation's data and purged with it. Tested end to end: the second run of the same goal replays the learned path with fewer Jev decisions, a failed deep link produces a note, notes can be added/disabled/deleted, paths deleted.

Trust and safety (hosted mode)

Per-organisation sandbox for shell commands (bubblewrap: read-only system, only the organisation's workspace writable at /workspace, no server secrets in the environment, CPU/memory/process caps; /api/status reports sandbox), audit log (sign-ins, invites, key and plan changes, exports, deletions), data export (zip of JSON + workspace files), organisation deletion with a 7-day grace period, rate limiting (failed sign-ins per IP, API per IP), security headers (CSP, HSTS over https, frame-ancestors none), password reset and email confirmation by one-time links, vault key from BB_VAULT_KEY in production. Legal drafts in docs/terms.md, docs/privacy.md, docs/security.md; deployment in docs/deploy.md (Fly.io reference config in fly.toml); launch plan in docs/launch.md.

Connectors β€” MCP and databases

App catalogue (Composio). Connectors β†’ *App catalogue*: paste a Composio API key once (free tier: 100,000 calls/month) and BuddyBots shows Composio's catalogue β€” Salesforce (CRM + Service Cloud objects), Shopify, Google Analytics, Jira, Notion, Google Drive, Slack, HubSpot, Tableau, Databricks, Power BI and ~1,000 more, with logos and tool counts, listed in full (most used first, connected and featured apps at the top) with search and "Show more" paging. *Connect* on an app: OAuth apps open Composio's hosted sign-in (your own account), the browser returns to http://127.0.0.1:PORT/api/composio/callback, and the app becomes an ordinary connector (transport composio) whose tools β€” real schemas, "important" tools first β€” go to the bots like any MCP server; calls run through Composio's execute endpoint. No-auth apps connect at once. One connector per app per workspace: Composio's user id is buddybots-<workspaceId>, so each business links its own accounts. Bots can do it from chat too ("connect Salesforce" β†’ the app name is looked up in the catalogue). The sample connectors older builds seeded (GitHub/Slack/Notion/Stripe/Filesystem with placeholder tokens) are removed on start-up; the paste box, MCP servers, OAuth MCP servers and databases still work exactly as before. Set COMPOSIO_BASE_URL to point at a stand-in (the tests do). Apps Composio has no managed credentials for (Tableau PAT, Databricks token, Shopify and Power BI with your own OAuth app…) show a small form on their card with exactly the fields Composio declares (server URL, token, client id/secret…); BuddyBots creates a custom auth config from them and connects β€” or, for own-OAuth-app modes, opens the sign-in. Verified live against Composio on 22 Sep 2026: catalogue search, no-auth app (Hacker News) connect + tool call, Salesforce hosted sign-in link, Tableau/Shopify field discovery. Not covered: triggers/webhooks and the tool-router.

Generated media shows in the chat, live. When a bot calls a connector's generation tool (Kling, Higgsfield, any image / video / voice / music MCP), the chat gets a card at once β€” "Generating video with Klingai Β· 45%" with a progress bar (percentage when the server reports one, an indeterminate bar otherwise), the prompt, and elapsed time. Async servers (a task id first): the bot calls the status tool once and the app keeps polling it every 15 s itself (BB_POLL_S), updating the card, for up to 10 minutes β€” no steps or tokens are spent waiting, and the bot only sees the finished file or the failure. A run with a generation in flight also gets extra step room, so a long render cannot end in "ran out of steps". When the file arrives it is shown inline: images open in the preview, videos and audio play in place, with download links. MCP image/audio content items (base64) are saved to workspace/generated/; URLs in text or JSON results are picked up by extension or field name. The bot is told to reply in one or two lines instead of pasting URLs or JSON; bare media URLs and !… in any reply render inline too. Generation calls get a 10-minute budget (other connector calls 90 s). Bots see each connector tool's exact argument shape ({model:string, arguments?:[{name:string,value:string}]}), and a tool error comes back with that shape attached, so a wrong shape (an object where Kling wants an array of name/value pairs β€” the cause of "service error") is corrected on the next call instead of being reported as an outage; a corrected retry updates the same card. The honesty audit also rejects "I filed a report/ticket" claims that no tool made. Tool search. Above 40 connected tools in total (BB_TOOL_SEARCH_MIN), the bot prompt lists each connector with its tool count and first few names instead of every schema, and bots call find_tools {"query":"update a Jira ticket"} to get the matching tools with exact argument shapes (lexical ranking over names and descriptions with verb synonyms; no model call). A call to a tool name that does not exist comes back with the closest real ones. Keeps prompts small and accurate with Salesforce (185), HubSpot (262) and GitHub (874) connected together. Tool descriptions go to the bots at full length (600 chars), and when a server has an argument-free "call this first" tool (who_am_i, describe, get_instructions, capabilities, list_models…) BuddyBots calls it once at connect time and puts the answer in the catalogue as that connector's conventions, so the first real call already follows the vendor's format.

OAuth MCP servers (Kling, Notion, Linear, Atlassian, …) work like in Claude or Cursor. Paste the URL, the docs' JSON (even a fragment like "klingai": {"url": "https://kling.ai/mcp"}), or the vendor's "help me set up … https://…/mcp" sentence into Connectors β†’ Add β€” or ask a bot in chat ("connect mcp https://kling.ai/mcp", the connect_mcp tool). BuddyBots follows the MCP authorization spec: protected-resource discovery, authorization-server metadata, dynamic client registration (no client id to configure), PKCE, then opens the vendor's sign-in page in your browser (also a Sign in button on the card). Approve; the vendor sends the browser back to http://127.0.0.1:PORT/api/connectors/oauth/callback, BuddyBots exchanges the code, stores the tokens in the vault and connects β€” refreshes happen by themselves. Verified against kling.ai's real endpoints (registration + sign-in URL) and end to end against a stand-in OAuth server in the tests. The sign-in tab must land on the computer running BuddyBots (the callback is on 127.0.0.1) β€” for a cloud install, do the sign-in from a browser on that box or tunnel the port.

Connectors β†’ Add a connector is one box. Paste any of: an MCP server URL, the JSON block from a server's docs ({"mcpServers": {…}}, with command/args/env or url/headers), a database address (postgresql://, mysql://, mongodb+srv://, sqlserver://, sqlite:file.db β€” Supabase, Neon, pgvector, PlanetScale and Atlas all use these), or a command line. BuddyBots recognises which it is, connects, and shows the tools or the reason it failed. Passwords, tokens and env values go into the encrypted vault and are masked everywhere. Remote MCP servers try Streamable HTTP then SSE; servers that need a browser (OAuth) login are not supported yet β€” use a header token. Databases connect read-only (list_tables, describe_table, query; SQL runs in a read-only transaction, one statement at a time, 200 rows max); a per-connector switch adds execute, and every data change goes through the approval gate. MongoDB is read-only in this version; when the address names no database, list_tables shows every database on the cluster and the other tools take a database argument. Database cards have Try: list tables to check a connection without a bot. Connector names are kept short and dot-free (e.g. MongoDBAtlas-gmg-uae) because bots type them, and the lookup forgives case, spaces and dashes. Tested live against Postgres and SQLite; MySQL, SQL Server and MongoDB drivers are wired but untested.

The bots' computer

Same model as Grok Bot: one persistent computer that keeps working when your laptop is closed β€” browser with saved logins, a terminal, and the shared /workspace β€” and you can watch it and take over.

What is built

AreaStatus
Bots: create (8 templates, generate-from-description), edit, pin, hide, duplicate (optional memory copy), export/import template, deletedone
Per-bot model (any provider) + weekly cost cap that actually stops the botdone
Editable memory (preferences / role / work summaries), auto work summariesdone
Chat: tasks with live steps, approvals, handoff cards, file cards, threads, reactions, attachments, /skill, @mention, redirect while working, Stop nowdone
Computer: persistent Chromium profile, live screen, takeover (click/type/scroll), masked secret entry, screenshots, activity timelinedone
Jev-first navigation (shadow DOM + iframes), small-LLM typing, vision fallback incl. coordinate clicks for canvas, per-step method/cost/success loggingdone β€” real Jev untested, see below
Approvals: Allow once / Deny / Always allow, ask-first & allow rules (ask wins), Jev risk class, audit log + CSV exportdone
Rooms: bots decide who answers, claim order, value gate (holds echo/agreement), turn cap, per-room budget, warnings bypass the cap, talkativeness per botdone
Tool calls that come back as broken JSON (almost always cut off by the output limit) are never shown as messages: the bot is told to redo the call in smaller parts (write_file has append), and a final reply that still looks like a tool call is replaced with a plain warningdone
Bot-to-bot handoff (depth-limited, deadlock-safe). The delegating bot decides whether to wait; background handoffs report back on their own β€” the delegator gets a new turn with the result (or it is fed into its running turn) and carries the plan forward; capped at 12 callbacks per chain. Writing-only work orders are not forced to use tools (Jev decides)done β€” verified live with gpt-5: plain request β†’ researcher β†’ file β†’ copywriter β†’ summary, and a background job that reported back unprompted
Your Chrome β€” the BuddyBots Chrome extension: bots browse in a BuddyBots tab group of the owner's real Chrome (content-script relay, no debugger bar, no stealth)done β€” tested end to end with a stand-in extension (Your Chrome test) and with the real extension loaded in Chromium 141 (window + tab group + task); UI: Settings β†’ Your Chrome, bot Settings β†’ Browser β†’ My Chrome, chat/computer-panel "Show the tab in my Chrome"
App catalogue β€” Composio-backed: key β†’ search β†’ Connect β†’ hosted sign-in β†’ tools; per-workspace accounts; app names in the paste box and connect_mcpdone β€” tested end to end against a stand-in Composio API (app catalogue test); and live against Composio: catalogue, no-auth connect + execute, Salesforce link, credential-field discovery
Honest-progress audit on every final reply: Jev checks (1) does the reply claim work is underway / coming later, (2) is each done item backed by a tool call; a handed-off bot that used no tool is sent back to do the job. Up to 2 corrections per run, ~$0.00003 each. Handoff cards show the teammate's live step and a Watch linkdone β€” verified live 20 Sep 2026 with gpt-5 + Jev: a Chief of Staff created two specialists, handed research over, the specialist's fabricated "file saved" reply was caught (file did not exist), she then browsed Amazon Best Sellers for real and saved research/pets.md; the card showed her live steps. Open dialogs / popovers (filters, date pickers, cookie walls) own the page: the navigator lists only the dialog's controls, its text comes first, and scrolling moves the list *inside* the dialog (it reports when the list is at its end) instead of the page behind. Bots are told to apply a site's filters (airlines, stops, price, category) before reading results. Checks are: claimed files must exist, "briefed X" needs a real handoff to X, a teammate the plan or reply says was created must exist (no create_bot β†’ sent back to create it or say so), no quitting after one blocked site, save partial findings, Jev for "work is underway" claims. Task cards only tick plan steps a tool actually ran; a step skipped in the middle shows not run and the Done line says how many
Bots staff their own team: create_bot / create_room tools (new bot inherits the creator's model, cap ≀ creator's, crew limit 24, logged in Activity); side-thread replies run the full agent with tools; a reply that announces work without having called a tool is bounced back once ("act or say you haven't")done β€” verified live with gpt-5
Skills as markdown files; Teach-a-task recording β†’ deterministic replay with Jev repairdone
Routines: cron + timezone, webhook trigger, dry-run, test run, full history, auto-pause after 2 weeks awaydone
MCP connectors (stdio + HTTP), tools exposed to every bot, writes gateddone
Usage: real cost per bot / task / model; Jev savings (estimate)done
Local-only mode, isolated browser profile per bot, encrypted key vault, browser notificationsdone
Light/dark, responsive, command palette, onboardingdone
look tool: bots check their own work visually through a vision modeldone
Template library: save/share/import (file, share code, public link), Grok Bot template import with conversiondone
Replies stream in as they are written (all providers; rooms wait for the value gate)done
Audio and video attachments are transcribed (OpenAI key; ffmpeg for video)done
Email trigger: one read-only IMAP mailbox, same match rules as webhooksdone β€” not tested against a live mailbox
desktop tool: a vision model drives the real mouse/keyboard on the whole screen, with zoom-refined clicksworks by keyboard; small click targets are unreliable with general models
Navigator hardening: anti-loop (dead actions are withdrawn), Enter key, autocomplete hints, blank-page and robot-wall detection, deep-link guidancedone
PDF / Word / Excel / PowerPoint attachments read inline; previews for themdone
Structured results (facts / assumptions / done / pending / questions) on tasks where the bot acteddone
Computer maintenance: daily auto-snapshot, manual snapshot, recover, reset browser profilesdone
Proactive follow-ups: unanswered approvals (30 min), unfinished tasks, routines failing twicedone
Webhook routines understand Slack Events + GitHub webhooks, with a narrow match ruledone
Handoffs can carry files; delete bot can wipe its files; mark as unread; error toasts with request IDdone

Not built (Phase 3): native desktop/mobile apps, teams/admin policy, DLP/proxy, egress routing, hardware keys. Also not built: multi-user accounts (there is one shared access token), a separate screen per bot while logins are shared.

Read this before trusting it

Layout

server/ Fastify + node:sqlite + Playwright + MCP client Β· web/ React + Vite + zustand Β· shared/types.ts the contract Β· API.md endpoints Β· design/ the design handoff Β· docs/parity-spec.md feature checklist.

Β© 2026 BuddyBots Β· TermsPrivacySecurityStatusContact