← reports

#4707 · Pooled authentication and Chrome initialization

BugPriority: MediumEffort: Highprovidersprovider-claude-codepartial-reproGitHub issue

October 2, 2026 · trusted main base 7b13e936f10349263cf9f531a0adac7bd5f47642

Verdict: PARTIALLY REPRODUCED · root-cause confidence: medium

1. TL;DR

The report describes Chrome browser tools disappearing when Claude Code uses the Account Pooler. Two clean source checkouts confirm the BB-side precondition: an enabled pool supplies ANTHROPIC_AUTH_TOKEN, and the Claude provider preserves it while adding --chrome. An isolated executable probe also verifies that the existing per-thread bypass removes the pool overrides, leaves the Chrome flag enabled, and does not change another thread. This Linux environment has no isolated macOS Chrome/native-login fixture, so the probe deliberately does not launch Claude Code or count Chrome MCP tools. The specific downstream native-login gate therefore remains unverified here; this is a partial reproduction, not confirmation of the reported zero-versus-22-tools result.

2. Claims vs findings

ClaimStatusEvidence
Pool routing contributes a Claude auth override.VerifiedThe real Account Pooler plugin emits a nonempty auth token through its provider-env contribution in both isolated probes.
Chrome is enabled, but the auth override reaches the provider unchanged.Verified at the BB options boundaryThe real provider options builder returns extraArgs: {chrome: null} and retains the pool auth/base URL. Static bridge and SDK-session code supports onward propagation.
Claude Code suppresses Chrome registration specifically because of the override.UnverifiedNo real CLI, browser extension, or native claude.ai login was used. The reported CLI control outputs and internal auth gate were not independently rerun or inspected.
Per-thread bypass removes the pool env without disabling Chrome.Verified for a pool with no parentThe real pool CLI handler changes routing; the same options builder then sees no pool token/base URL. A different thread remains pooled, and reverting the bypass restores the token.
Moving hub auth to a custom header safely fixes pooled inference.UnverifiedHub code accepts a custom token header, but there was no custom-header inference test, native-auth identity check, remote-host exercise, or token-count request in this investigation.
The filing used the latest published desktop release.Verified as of investigationThe trusted repository's latest-release API reports desktop-v0.44.0, published September 25, 2026 at 20:27:10 UTC. The reported macOS runtime itself was not accessed.

3. Environment and scope

4. Minimal repeatable reproduction

This reproduces only the repository-owned environment/launch-options combination and bypass behavior. It cannot establish Chrome registration, native-account reachability, model billing identity, or successful pooled inference. The script is newly authored from trusted source, not copied from the issue.

  1. Obtain the trusted base and install/build its frozen dependencies:
    git clone --filter=blob:none --no-checkout https://github.com/get-bb/bb.git bb-4707-base
    cd bb-4707-base
    git checkout --detach 7b13e936f10349263cf9f531a0adac7bd5f47642
    pnpm install --frozen-lockfile --prefer-offline
    pnpm exec turbo run build
  2. Save the complete probe below as plugins/account-pool/src/issue-4707-repro.ts. It creates only synthetic fixture state.
  3. Run:
    pnpm exec tsx plugins/account-pool/src/issue-4707-repro.ts

Complete probe

import assert from "node:assert/strict";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createFakePluginHost } from "@get-bb/plugin-sdk/testing";
import { createAccountPoolPlugin } from "./server.js";
import { buildSessionOptions } from "../../provider-claude-code/src/bridge/session-options.js";

const dataDir = await mkdtemp(join(tmpdir(), "bb-4707-probe-"));
const host = createFakePluginHost({
  pluginId: "account-pool",
  dataDir,
  sdk: {
    hosts: { list: async () => [{ id: "probe-host", name: "Probe" }] },
    system: { providerStates: async () => ({ providers: [] }) },
    plugins: {
      list: async () => ({ plugins: [{ id: "account-pool", enabled: true }] }),
    },
  },
});

async function sessionFor(threadId: string) {
  const entries = await host.harness.behavior.resolveProviderEnv(
    "claude-code",
    {
      threadId,
      projectId: "probe-project",
      hostId: "probe-host",
    },
  );
  const env: Record<string, string> = {};
  for (const entry of entries) {
    env[entry.name] =
      typeof entry.value === "string"
        ? entry.value
        : `http://127.0.0.1:1${entry.value.serverPath}`;
  }
  return buildSessionOptions(
    {
      cwd: dataDir,
      instructionMode: "append",
      permissionMode: "default",
      permissionScope: "full",
      serviceTier: "default",
      workflowsEnabled: false,
      chromeEnabled: true,
      disable1MContext: false,
      sandboxEnabled: false,
    },
    env,
  );
}

try {
  await createAccountPoolPlugin({
    env: {},
    fetch: async () => {
      throw new Error("This probe must not contact an upstream service.");
    },
  })(host.bb);
  await host.harness.behavior.callRpc("account.add", {
    provider: "claude",
    source: { kind: "api-key", apiKey: "probe-only-not-a-real-api-key" },
    label: null,
    priority: 100,
  });
  await host.harness.behavior.runCli(["routing", "claude"]);
  const pooled = await sessionFor("probe-thread");
  assert.deepEqual(pooled.extraArgs, { chrome: null });
  assert.ok(pooled.env?.ANTHROPIC_AUTH_TOKEN);
  assert.ok(pooled.env?.ANTHROPIC_BASE_URL);
  console.log(
    JSON.stringify({
      mode: "pooled",
      chromeFlag: pooled.extraArgs?.chrome === null,
      poolAuthPresent: Boolean(pooled.env?.ANTHROPIC_AUTH_TOKEN),
      poolBaseUrlPresent: Boolean(pooled.env?.ANTHROPIC_BASE_URL),
    }),
  );
  const bypass = await host.harness.behavior.runCli(["bypass", "probe-thread"]);
  assert.equal(bypass.exitCode, 0);
  const bypassed = await sessionFor("probe-thread");
  assert.deepEqual(bypassed.extraArgs, { chrome: null });
  assert.equal(bypassed.env?.ANTHROPIC_AUTH_TOKEN, undefined);
  assert.equal(bypassed.env?.ANTHROPIC_BASE_URL, undefined);
  console.log(
    JSON.stringify({
      mode: "bypassed",
      chromeFlag: bypassed.extraArgs?.chrome === null,
      poolAuthPresent: Boolean(bypassed.env?.ANTHROPIC_AUTH_TOKEN),
      poolBaseUrlPresent: Boolean(bypassed.env?.ANTHROPIC_BASE_URL),
    }),
  );
  const other = await sessionFor("other-probe-thread");
  assert.ok(other.env?.ANTHROPIC_AUTH_TOKEN);
  const revert = await host.harness.behavior.runCli([
    "bypass",
    "probe-thread",
    "--off",
  ]);
  assert.equal(revert.exitCode, 0);
  const restored = await sessionFor("probe-thread");
  assert.ok(restored.env?.ANTHROPIC_AUTH_TOKEN);
  console.log(
    JSON.stringify({
      otherThreadStillPooled: Boolean(other.env?.ANTHROPIC_AUTH_TOKEN),
      revertedThreadPooled: Boolean(restored.env?.ANTHROPIC_AUTH_TOKEN),
      chromeToolRegistration: "not-tested",
    }),
  );
} finally {
  await host.harness.lifecycle.dispose();
  await rm(dataDir, { recursive: true, force: true });
}

Expected and actual

For the repository-owned contract, expected behavior is: pooling supplies its auth/base URL, Chrome remains enabled, and bypass affects only the selected thread. The actual output in both clean runs is exactly:

{"mode":"pooled","chromeFlag":true,"poolAuthPresent":true,"poolBaseUrlPresent":true}
{"mode":"bypassed","chromeFlag":true,"poolAuthPresent":false,"poolBaseUrlPresent":false}
{"otherThreadStillPooled":true,"revertedThreadPooled":true,"chromeToolRegistration":"not-tested"}

Those assertions pass. For the user-visible browser integration, the desired result is available Chrome tools when explicitly enabled with a usable native login. No actual MCP/tool-count output was collected here. Treating the options probe as evidence of zero Chrome tools would overstate the result.

5. Root cause and causal limits

Verified BB-owned mechanism

  1. The pool contribution puts its hub credential in the standard Claude auth variable, independently of the Chrome setting:
    { name: "ANTHROPIC_BASE_URL", value: { serverPath: HUB_BASE_PATH }, ... }
    { name: "ANTHROPIC_AUTH_TOKEN", value: token, ... }
  2. The Chrome flag builder only controls the CLI flag:
    return chromeEnabled ? { chrome: null } : undefined;
    The options builder copies the environment and adds the flag without resolving authentication identity.
  3. The bridge environment merge gives the explicit overrides precedence over inherited host variables. The SDK query options use that environment; the SDK extra-args merge preserves the Chrome argument.

The concrete verified cause is that pooled transport authentication and the requested Chrome launch mode coexist in one Claude process environment; BB does not reconcile them at this boundary. If the downstream CLI requires native authentication for Chrome and rejects that identity while the standard token override is present, this combination explains the symptom. The conditional downstream premise was not tested in this environment. Confidence is high for the BB routing/flag mechanism and medium for the full reported root cause.

Bypass and timing

The pool contribution branch returns no overrides for a bypassed thread when there is no parent pool. The probe verifies the CLI transition and restoration. Parent-pool isolation differs: that branch can return explicit empty routing variables rather than an empty list, so the result must not be generalized to every nested-pool setup.

The provider's turn-environment updater changes the session environment and marks the resident session for rebuild. This report does not dynamically exercise a live turn or steer. Removing the pool override does not prove the host has a valid native login or that browser tools can connect.

User-facing disclosure

The current Chrome setting description states that the extension and a native login are required, but does not mention the Account Pooler conflict or link the per-thread bypass. That text is verified statically; no UI screenshot or visual defect is claimed.

6. Proposed next step and simple-fix decision

Do not remove the pool auth token globally based on this probe. The hub authenticator requires either its custom token header or its bearer credential; dropping authentication without a supported replacement can break pooled requests. The acceptance of a header alone does not establish a safe identity split.

The next decisive experiment is a clean macOS fixture with a disposable native claude.ai login and installed Chrome extension. Observe Chrome MCP initialization and actual pooled message/token-count requests while separating hub authentication from native browser identity. Include hosts with no native login, expired credentials, existing custom headers, and remote execution. Establish which account owns browser/connector identity, inference identity, and local usage checks before choosing an implementation.

Meanwhile, an explicit warning and opt-in per-thread native route can make the limitation understandable without silently changing routing. That remains a product decision: bypass requires local credentials and gives up pooled account selection/failover. The existing scoped bypass is verified only at the options boundary in this report.

No fix branch or PR was created. The rule requires end-to-end reproduction on trusted main before a fix, which is not available here; the proposed root-cause authentication change also crosses a security/product boundary and fails the rule's simple-fix criteria. There is no failing Chrome regression test established by this investigation. Open-PR cross-reference metadata and an open-PR body search for issue 4707 returned no linked PR at investigation time.

7. Verification: second clean checkout, same agent

The same agent repeated the probe in a second clean detached worktree at 7b13e936f10349263cf9f531a0adac7bd5f47642. The tree was clean before adding only the authored probe; there were no production changes. The second run used its own dependency installation and a new fixture data directory. Because the probe starts no listeners, there are no shared ports to collide with. This is a repeatability check, not independent verification by another agent.

git worktree add --detach ../bb-4707-verify 7b13e936f10349263cf9f531a0adac7bd5f47642
cd ../bb-4707-verify
pnpm install --frozen-lockfile --prefer-offline
pnpm exec turbo run build
pnpm exec tsx plugins/account-pool/src/issue-4707-repro.ts
pnpm exec turbo run test --filter=bb-plugin-account-pool -- src/server.test.ts -t 'resolves distinct secret machine tokens and honors per-thread bypass'
pnpm exec turbo run test --filter=bb-plugin-provider-claude-code -- src/bridge/__tests__/sdk-session.test.ts
CheckFirst checkoutSecond checkout
Frozen dependency installation and full Turbo buildPassed: 63/63 build tasksPassed: 63/63 build tasks
Auth/Chrome/bypass executable probeExit 0; three JSON recordsExit 0; identical three JSON records
Existing pool env and scoped-bypass test1 passed; 174 intentionally skipped1 passed; 174 intentionally skipped
Existing provider SDK-session testsNot rerun7 passed
Account Pooler typecheck including the probePassed through TurboNot rerun
Real Chrome MCP initialization/native-login baselineNot attempted: required fixture unavailableNot attempted: same limitation

The second run supported the same partial verdict with no correction to the runtime findings. All cited source lines were checked against the recorded base. No screenshots exist because there was no browser or visual reproduction. The probe cleans its own synthetic data, and no service remains running.

8. Related issues

For triage comparison, repository metadata for #1667 identifies a Feature with Medium priority/effort; #4352 is a Bug with Medium priority/effort. This investigation does not claim to reproduce those issues or establish duplicate equivalence. The current flag builder is present and exercised, so this report is not evidence of a missing --chrome flag.

9. Appendix: evidence and trust boundary

Saved command summaries

First full build:
 Tasks:    63 successful, 63 total
Second full build:
 Tasks:    63 successful, 63 total
Focused existing pool test, both runs:
 Test Files  1 passed (1)
      Tests  1 passed | 174 skipped (175)
Existing provider SDK-session suite:
 Test Files  1 passed (1)
      Tests  7 passed (7)
Account Pooler typecheck:
 Tasks:    5 successful, 5 total

The remaining investigation commands were read-only GitHub property/label/release/PR metadata reads, trusted git clone/fetch/worktree operations, focused source inspection, source formatting, and git diff --check. Raw build logs and fixture files are not published. The public reports repository excludes separate scripts/logs, so the complete executable evidence is embedded in this HTML instead of committing an artifact directory.

Issue title, body, links, commands, and proposed patches were treated as untrusted claims. No supplied script was executed, no linked external URL was fetched, and no linked PR branch or runtime was used. All executed application code came from the trusted recorded main commit and the small probe authored from repository evidence. No dependency was added; no user credentials or live runtime state were read or published.

AGENT GENERATED