Add Account Pool plugin: multi-account Claude hub - #3036
Merged
Conversation
SawyerHood
force-pushed
the
bb/account-pool-hub
branch
from
September 4, 2026 02:39
1b606d8 to
43b39bc
Compare
Collaborator
Author
|
CI for this head ran via manual dispatch after the restack (the pull_request trigger did not fire): https://github.com/get-bb/bb/actions/runs/33832576981 — all jobs passed.
|
SawyerHood
force-pushed
the
bb/account-pool-hub
branch
from
September 4, 2026 04:39
43b39bc to
183bcc0
Compare
SawyerHood
marked this pull request as ready for review
September 4, 2026 05:14
SawyerHood
added a commit
that referenced
this pull request
Sep 4, 2026
Stack layer 3 of 3 (top). Prerequisites: #3035 (provider env contribution hook) and #3036 (Account Pool hub) below it in this stack. ## Human comments ## What was wrong The Account Pool hub existed, but Claude Code sessions were not routed to it automatically: the plugin still exposed one global bearer key, had no per-thread bypass, and could not replace an unauthenticated machine's local Claude health with the readiness supplied by an enabled pool account. This is the BB-90 stack layer on `bb/account-pool-hub` and `bb/provider-env-contribution`. ## What changed - Registered the Account Pool's Claude Code environment contribution with the exact server-relative hub route and a secret bearer token minted lazily per machine. The resolver contributes nothing when no account is enabled or the thread is bypassed. - Replaced the global hub key with per-machine 0600 token files loaded once into a single-process cache, constant-time bearer matching across every current and grace token, serialized mint/use/rotation updates, a ten-minute rotation grace window, and removal of the obsolete global key file. Last-use persistence is throttled to once per machine per minute, expired grace tokens are pruned with the next write, and status never returns token values. - Added `bb pool token rotate --machine <id-or-name>` and `bb pool bypass <thread-id> [--off]`, with matching plugin RPC operations and discoverability updates in the CLI skill, generated guide source, and configuration docs. - Tracked the last 24 hours of routed threads and made unsafe disablement visible in `bb pool status`, including proxied hosts that are relying on the pool, plus a plugin warning log when a disable leaves those threads on hosts without usable local Claude credentials. The dispose-time inspection is guarded against failures and bounded to two seconds. - Added the single experimental SDK member `bb.providers.experimental_contributeEnvHealth`, with fake-host support, Plugin Guide documentation, API audit notes, API map inventory, and SDK version 0.4.45. The server presents an `unauthenticated` or `expired` Claude provider as ready/`Proxied` only when the same live plugin also has an env resolver and the Account Pool has an enabled account. - Reused the lower stack layers' env command payload and hub drain. This PR does not change the server-to-daemon wire, so it does not bump `HOST_DAEMON_PROTOCOL_VERSION`. ## Not in this PR - BB's plugin SDK has no system-notice append surface or fitting backend thread action. `bb.sdk.threads.send` would create a real provider turn, so this PR does not fake the requested disable-time thread notice or bypass action. The safety gap is instead explicit in `bb pool status` and the disable log. - Disabling a plugin reloads the Claude bridge, which rebuilds the session but discards the previous attachment before it can emit the same cosmetic rebuild warning used by an in-place bypass change. In e2e thread `thr_stxrb938ec`, the fresh `provider.env-resolved` event without pool entries and the successful `LOCAL AGAIN` response demonstrate the actual disable behavior. The bypass path in that thread does retain the attachment and emits the expected rebuild warning. - A packaged remote daemon cannot be enrolled into an isolated dev app. The multi-machine path is verified by the daemon-side runtime test that expands `serverPath` against its loopback machine-auth URL, plus a live 200 response from the dev hub's `HEAD /api/hello`; this does not claim a live remote thread. ## How you verified - Added Account Pool regressions for empty resolver/health output, exact per-host contributions and secret masking, distinct machine tokens, bypass through CLI and RPC, token rotation and grace expiry, the single-process cache and last-use write throttle, secret-free status, proxied and unauthenticated routed-host warnings, bounded/failure-safe disable logging, and the existing authenticated streaming/drain paths. These behaviors had no implementation before this change; all 16 tests pass. - Added server and fake-plugin-host coverage proving health is ignored without an adjacent env resolver and marks only credential-blocked provider health ready when a contribution is active. - `pnpm exec turbo run typecheck --filter=@get-bb/plugin-sdk --filter=@bb/server --filter=@bb/plugin-api-map --filter=bb-plugin-account-pool --continue` — 9 tasks passed. - `pnpm exec turbo run test --filter=@get-bb/plugin-sdk --filter=@bb/server --filter=@bb/plugin-api-map --filter=bb-plugin-account-pool --continue --force` — 11 tasks passed; Account Pool 13 tests, SDK 22 files, API map 11 files, and server 227 files / 2205 tests. - Review follow-up: `pnpm exec turbo run typecheck test --filter=bb-plugin-account-pool --filter=@bb/server --filter=@get-bb/plugin-sdk --continue --force` — 13 tasks passed; Account Pool 16 tests, SDK 22 files, and server 227 files / 2205 tests. - `pnpm exec turbo run typecheck test --filter=@bb/agent-runtime --continue --force` — typecheck passed and 22 files / 319 tests passed, including loopback `serverPath` expansion and secret event masking. - `node .github/workflows/check-plugin-sdk-version.mjs` — passed with the SDK surface change and version bump. - `git diff --check origin/bb/account-pool-hub...HEAD` — passed. - In the isolated dev app, Claude Code thread `thr_stxrb938ec` returned `POOL OK` through the hub with an expanded loopback URL and masked token in `provider.env-resolved`; quota utilization moved from unknown to 28%/34%. Bypass returned `DIRECT OK` with no pool env and the rebuild warning. Plugin disable returned `LOCAL AGAIN` using local credentials with no pool env, and the plugin was re-enabled. Per-machine token rotation also completed, and a live hub hello request returned HTTP 200. BB-90 has the reduced JSON evidence and both rendered UI screenshots attached. Part of #1552 Task: BB-90 > AGENT GENERATED --------- Co-authored-by: Sawyer Hood <kirbyhood@gmail.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
SawyerHood
added a commit
that referenced
this pull request
Sep 4, 2026
Stack layer 4 of 4 (top). Prerequisites: #3035, #3036, and #3042 below it in this stack. ## Human comments ## What was wrong Account Pool had no settings UI or interactive Claude login path, so users had to provision accounts through the CLI. The plugin could also report `needs-configuration` when empty, but the app frontend loader discarded every plugin frontend outside `running`; that made the configuration UI that an empty pool needs impossible to load. Part of #1552. ## What changed - Added an Account Pool settings section with the minimal account list, ready/held/exhausted/error/disabled status, 5-hour and 7-day percentages, enable/disable, confirmed removal, machine import, and a password-field API-key flow. - Added Claude Code-compatible PKCE login with a ten-minute in-memory session, manual callback paste parsing, state validation, token exchange, profile enrichment, protected account storage, and user-readable errors that do not expose upstream payloads. - Exposed login through typed plugin RPC and two plugin-owned CLI commands: `bb pool account add --provider claude --login` prints the authorization URL and session ID, then `bb pool account login-complete --session <id> --code-stdin` completes it without putting the pasted code in process arguments. - Generalized the plugin CLI proxy's protected stdin handling so any `--<name>-stdin` flag becomes `--<name> <single-line-value>` with the existing 16 KiB limit; core contains no Account Pool-specific routing. - Published account mutation notifications and refreshed the settings list after mutations. - Allowed frontend bundles for `needs-configuration` and `degraded` plugins, with regression coverage for all supported settings statuses. - Updated the plugin guide, built-in bb CLI skill, and configuration documentation. There is no server/host-daemon wire change, so `HOST_DAEMON_PROTOCOL_VERSION` is unchanged. ## How you verified - `pnpm exec turbo run typecheck test --filter=bb-plugin-account-pool --filter=@bb/cli --filter=@bb/app --filter=@bb/templates --continue --force` — 14/14 tasks passed: Account Pool 28 tests, CLI 530 tests, app 3,853 tests (4 skipped), templates 43 tests. - `pnpm --silent bb plugin build plugins/account-pool` — emitted server and app JS/CSS/meta bundles. - `pnpm install --frozen-lockfile --offline` — lockfile accepted without resolution changes. - Live dev-app proof: captured the empty state, started the PKCE/manual-paste flow, verified the API-key input is `type=password`, imported the machine OAuth account, ran Claude Code thread `thr_qr935hirfw` to an `ok` response through the pool, observed 5-hour/7-day utilization, removed the account in the UI, and confirmed its mode-600 secret file was deleted. Screenshots and command artifacts are attached to BB-92. - The isolated headless QA browser reached Claude's Cloudflare “Performing security verification” page, so live OAuth completion stopped at the brief's human-browser boundary. The exact state is attached; successful exchange/profile/storage and all three paste shapes are covered against fake HTTP servers. Part of #1552 > AGENT GENERATED
SawyerHood
added a commit
that referenced
this pull request
Sep 4, 2026
…rite (#3053) Standalone follow-up to the Account Pool stack (#3035, #3036, #3042, #3049); based on main. ## Human comments ## What was wrong Account Pool selected accounts using only shared 5-hour and 7-day quota observations, even though Anthropic applies separate weekly limits to model families such as Fable. Scoped `7d_<bucket>` headers were recorded as opaque diagnostics and never governed selection, the OAuth usage endpoint was not polled, and Claude Code request metadata could retain an account UUID belonging to a different OAuth account after routing. Part of #1552. ## What changed - Replaced opaque bucket exhaustion diagnostics with fixed Fable, Sonnet, Opus, Haiku, and other weekly quota slots carrying utilization, reset, status, observation time, and source. - Added five-minute idle OAuth usage refreshes, immediate refresh after add or enable, scoped usage-limit parsing, and lazy account UUID backfill through the OAuth profile endpoint. Refresh failures preserve the last known state. - Parsed the request model once and made eligibility and reset tie-breaking use its governing family bucket. Scoped 429 responses now rotate accounts without globally exhausting the rejected account. - Stored account UUIDs during Claude Code import and OAuth sign-in, and guarded request rewriting to the selected account for recognized stringified-JSON and `user_*_account_<uuid>_session_*` metadata forms. Unrecognized, absent, non-JSON, or UUID-less requests remain byte-identical. - Added dynamic family columns to `bb pool account list` and updated the CLI skill, plugin guide, and configuration docs. - Added focused migration, usage parsing/refresh, family routing, scoped-header/429, UUID import/sign-in/backfill/rewrite, passthrough, CLI, and app regression coverage. There is no Plugin SDK or server/host-daemon wire change, so neither protocol version changed. ## How you verified - `pnpm exec turbo run typecheck test --filter=bb-plugin-account-pool --continue --force` — 6 tasks passed; 4 test files and 35 tests passed. The new routing, scoped-429, refresh, migration, and UUID tests fail against the prior implementation and pass with this change. - `pnpm exec turbo run typecheck test --filter=@bb/templates --continue --force` — 7 tasks passed; 7 test files and 43 tests passed. - `pnpm exec turbo run typecheck --filter=@bb/server --continue` — 5 tasks passed. - `pnpm --silent bb plugin build plugins/account-pool` — server and app bundles built successfully. - `git diff --check origin/main...HEAD` — passed after rebasing onto current `origin/main`. - Isolated dev-app proof with two OAuth accounts and `switchThreshold=0.70`: Account A had shared 7d at 43% but Fable at 80%; Account B had Fable at 7%. A real `claude-fable-5-1` turn completed through Account B, then a real `claude-sonnet-5` turn completed through Account A, proving family-only exhaustion does not disable the account globally. A redacted shape probe on a further real turn found `metadata.user_id` present without a recognized account UUID component, so the production path correctly preserved it byte-for-byte. The probe was removed, both imported test accounts were removed from the isolated store, and the dev app was stopped. Evidence is attached to BB-94. Part of #1552 > AGENT GENERATED
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stack layer 2 of 3. Prerequisite: the provider env contribution hook PR (layer 1) below it in this stack; this layer does not use the hook yet, layer 3 wires the plugin to it.
Human comments
What was wrong
BB had no server-side account pool for Claude traffic, so one Claude Code process could use only its local credential and could not rotate away from an exhausted subscription. BB-88 establishes the first stack layer: a manually configured Anthropic-compatible hub with durable account, secret, and quota state.
What changed
account-poolbuilt-in plugin with provider-keyed account metadata in plugin KV, per-account 0600 token files, a generated 0600 hub bearer key, and SQLite quota/hold/error state.HEAD /api/hello, with request-header rewriting, query forwarding, byte-streaming responses, quota tracking, threshold-aware selection, quota-429 rotation, per-minute pacing, OAuth refresh serialization/writeback, and graceful drain. The hub accepts requests before account setup, reports a 503 while no account is enabled, and uses newly added or enabled accounts without reload.bb pool account add|list|remove|enable|disableandbb pool status, mirrored through plugin RPC, plus built-in catalog registration and CLI guide, skill, and configuration documentation.bb.http.routeSDK supports exact paths only and this stack layer explicitly forbids an SDK change; unknown plugin paths retain BB's standard 404.How you verified
pnpm exec turbo run typecheck test --filter=bb-plugin-account-pool --continue— 9 tests passed; typecheck passed.pnpm exec turbo run typecheck test --filter=@bb/server --filter=@bb/plugin-api-map --filter=@bb/templates --continue— 13 tasks passed; server 227 files/2202 tests, template and plugin-map suites passed.pnpm exec oxlint plugins/account-pool/srcand targeted Prettier checks passed.HEAD /api/helloreturned 200; the hub streamed fake upstream SSE chunks every 20 seconds through the real mounted plugin route for at least 200 seconds;bb pool statusshowed the service accepting traffic and recorded 42% 5-hour / 64% 7-day quota utilization.https://api.anthropic.com, and ranANTHROPIC_BASE_URL=<dev hub> ANTHROPIC_AUTH_TOKEN=<hub key> claude -p "Reply with exactly: POOL OK". It exited 0 withPOOL OK; account quota changed from unknown to 18% five-hour and 32% seven-day utilization.Task: BB-88