Skip to content

fix(browser-control): keep external CDP control in the background - #3445

Merged
bobleer merged 2 commits into
GCWing:mainfrom
bobleer:openbitfun/background-browser-cdp
Oct 18, 2026
Merged

bobleer merged 2 commits into
GCWing:mainfrom
bobleer:openbitfun/background-browser-cdp

Conversation

@bobleer

@bobleer bobleer commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

External CDP browser control brought the user's browser to the front on practically every attach. Four independent causes:

  • connect, switch_page and tab_new defaulted activate to true and sent Page.bringToFront.
  • New tabs were created by Target.createTarget without background: true, and the protocol default is a foreground tab that raises the window.
  • The macOS launch path used open -na <App>, which activates the application.
  • The legacy PUT /json/new endpoint has no background option at all.

Browser control is now background by default; the window and focus stay where the user left them.

Type and Areas

Type: bug fix

Areas: Rust core (agent tools / browser control), services (browser launch), desktop/Tauri (Settings > Browser Control), docs (architecture)

Motivation / Impact

A user driving an existing, logged-in browser only wanted the page read or clicked. Each connect/navigate instead pulled the browser over their editor and stole keyboard focus, so background automation was not usable at all.

What changes:

  • Tabs are created with background: true; Page.bringToFront runs only for an explicit activate: true request. Results keep activated and add activation: background|foreground.
  • The legacy fixed-port path (Brave/Arc/Chromium, explicit user_data_dir) creates its background tab through the browser-level WebSocket advertised by /json/version, then resolves the page WebSocket from /json; it falls back to the HTTP endpoint when that WebSocket is unavailable.
  • macOS launches with open -g (new BrowserLaunchOptions.activate, default false). Launched automation instances also pass --disable-backgrounding-occluded-windows, --disable-renderer-backgrounding and --disable-background-timer-throttling so a background page still loads, renders and captures.
  • Foreground is kept only where it is genuinely required: the browser-owned approval prompt before a guarded real-profile connection (BrowserLauncher::activate_browser before the request), and a user-initiated browser restart from Settings.
  • Tool description and input schema document activate and when it is allowed, so the model does not raise the window to make an action "look" like it ran.
  • The contract is recorded in the browser orchestration section of docs/architecture/computer-use-control.md and in the nearest module guide, so a later change does not silently reintroduce window raising.

Windows and Linux have no supported non-activating launch through process_manager (CREATE_NO_WINDOW hides consoles, not GUI windows), so only the initial launch of a not-yet-running browser can take focus there. Every CDP operation afterwards stays in the background on all three platforms. activate_browser reports false on platforms without a supported activation entry point instead of pretending.

Verification

  • cargo test -p openbitfun-core --no-default-features --features agent-runtime,tools-browser-web,tools-computer-use,git --lib browser_control:: — passed (41 tests; includes new page_creation_only_asks_for_a_background_tab_when_requested)
  • cargo test -p openbitfun-core --no-default-features --features agent-runtime,tools-browser-web,tools-computer-use,git --lib control_hub_tool:: — passed (55 tests; includes new browser_activation_is_opt_in, description_documents_background_browser_control)
  • cargo test -p openbitfun-services-integrations --features browser-control browser_control:: — passed (11 tests; includes new macos_background_launch_keeps_the_browser_out_of_the_foreground, browser_launch_does_not_activate_by_default)
  • cargo clippy -p openbitfun-services-integrations --features browser-control --lib --all-targets and cargo clippy -p openbitfun-core --no-default-features --features agent-runtime,tools-browser-web,tools-computer-use,git --lib — no new warnings
  • cargo fmt --all -- --check — passed
  • node scripts/check-core-boundaries.mjs — passed
  • Windows/Linux cross-compilation was not run on the authoring machine (the local C toolchain for ring is missing); the CI matrix is the evidence for those targets.

Reviewer Notes

  • Platform difference is deliberate and documented in the code: macOS can launch without activation, Windows/Linux cannot. The fallback is behavioral, not a silent local success — the activation helper returns false and callers degrade.
  • Target.createTarget background is a Chrome-only protocol parameter. Chromium-based browsers that ignore it degrade to the previous foreground behavior; the call does not fail.
  • No persisted shape, config field, or Tauri command changed, so no upgrade migration and no new remote-surface registry row. This is host-local OS automation: remote workspaces and CLI hosts are unaffected.
  • No new setting is added: background is the default, and activate: true is the only opt-in.

Generated with OpenBitFun

bobleer and others added 2 commits October 18, 2026 15:31
External browser control raised the user's browser on every attach: connect,
switch_page and tab_new defaulted to activate=true and sent
Page.bringToFront, new tabs used Target.createTarget's foreground default,
and the macOS launch path used `open -na`, which activates the app.

Browser control is background work now. Tabs are created with
background: true, Page.bringToFront only runs for an explicit `activate`
request, and the macOS launch path uses `open -g`. The legacy /json/new
endpoint cannot create a background tab, so background creation goes through
the browser-level WebSocket advertised by /json/version and falls back to the
HTTP endpoint when that is unavailable. Launched automation instances also
disable occluded-window backgrounding so a background page still loads,
renders and captures.

The two flows that genuinely need a visible window keep their activation: the
browser-owned approval prompt before a real-profile connection, and a
user-initiated restart. Windows and Linux have no supported non-activating
launch, so only that first launch can take focus; every CDP operation
afterwards stays in the background.

Co-authored-by: OpenBitFun <318544290+bitfun-ai@users.noreply.github.com>
… work

Record the browser activation contract next to the existing browser tool
orchestration section so a future change does not reintroduce window raising:
tabs are created in the background, page selection never surfaces the window,
and only an explicit `activate` request, a browser-owned approval prompt, or a
user-initiated restart may bring the browser forward.

Co-authored-by: OpenBitFun <318544290+bitfun-ai@users.noreply.github.com>
@bobleer
bobleer merged commit 0545f2a into GCWing:main Oct 18, 2026
13 checks passed
@bobleer
bobleer deleted the openbitfun/background-browser-cdp branch October 18, 2026 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant