[STG-1940] feat: add browser-swarm extension bridge POC#100
Conversation
d8c3cb7 to
86a3aa3
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit edad4bb. Configure here.
| waitingForDebugger: false | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Synthetic event references wrong target for attachToTarget
Low Severity
In emitSyntheticEvents, the Target.attachToTarget path looks up the target via this.findTarget(params.targetId, client), but handleCdpCommand resolves the target via sessionTarget || this.findTarget(null, client) when params.targetId is absent. For a root client with multiple targets, sessionTarget (from the provided sessionId) may differ from the first target returned by findTarget(null, ...). The synthetic Target.attachedToTarget event would then carry targetInfo for the wrong target compared to the sessionId in the response.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit edad4bb. Configure here.
| }, 30000); | ||
| this.extensionRequests.set(id, { resolve, reject, timer }); | ||
| this.extension.send(JSON.stringify(payload)); | ||
| }); |
There was a problem hiding this comment.
Relay timeout too short for multi-tab ensureTabs
Medium Severity
sendToExtension uses a fixed 30-second timeout for all extension requests. The extension's ensureTabs calls waitForTabLoad with a 15-second timeout per tab sequentially, plus attachTab overhead. Creating 3+ tabs pointing to slow-loading URLs can exceed 30 seconds total, causing the relay to time out and return an error to the caller even though the extension is still successfully creating tabs. This leaves relay and extension state out of sync.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit edad4bb. Configure here.
| params, | ||
| targetId: firstTarget?.targetId | ||
| }).catch(() => {}); | ||
| return {}; |
There was a problem hiding this comment.
AutoAttach silently lost when no targets exist yet
Low Severity
When Target.setAutoAttach is sent before any targets exist, the relay forwards it to the extension with targetId: undefined. The extension's forwardCDPCommand calls findTarget, which returns null and throws — but the relay swallows this with .catch(() => {}) and returns {} to the client. The extension's autoAttachParams is never set, so subsequently created tabs skip auto-attach setup in attachTab, even though the client believes auto-attach is active.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit edad4bb. Configure here.


Summary
browser-swarmskill with a Chrome extension bridge and localhost relaybrowse --session+--cdpcontracts and structured worker reportschrome.tabGroups.*orchrome.tabs.groupInput.*CDP commands/healthso stale Arc extension reloads are visibleskills/browser-swarm/RUNNING_TEST_NOTES.mdLinear: https://linear.app/browserbase/issue/STG-1940/add-browser-swarm-extension-bridge-poc
E2E Test Matrix
node --checkfor browser-swarm relay, e2e, setup, launcher, and extension service workerextension/manifest.jsonparsed as JSONBROWSER_SWARM_PORT=19997 BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2efromskills/browser-swarm0.1.1, three labeled tabs created in a Chrome tab group, each worker session read title/url,tab listreturned exactly one tab, snapshots and screenshots were captured, and relay CLIscreenshot --pathwrote a1509844byte PNG--session/--cdpbrowse workflow works end to end in a disposable browser, even while Arc has an installed browser-swarm extension on the default port. Also proves the relay screenshot CLI honors--path.BROWSER_SWARM_PORT=20000 BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2efromskills/browser-swarm0.1.1; session-scopedTarget.getTargetssaw exactly one worker target; session-scoped sibling attach/info errored; rootTarget.createTargetincreased target count3 -> 4; rootTarget.closeTargetreduced it4 -> 3; relay screenshot wrote a1510778byte PNG; three same-page workers submittedalpha-worker/beta-worker/gamma-workerin parallelBROWSER_SWARM_PORT=20005 BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2efromskills/browser-swarm0.1.1; rootTarget.createTargetwith a rootsessionIdincreased target count3 -> 4; rootTarget.closeTargetwith the samesessionIdreduced it4 -> 3; the root CDP client observed exactly oneTarget.detachedFromTargetevent for the closed target; relay screenshot wrote a1510792byte PNG; same-page workers again submittedalpha-worker/beta-worker/gamma-workerin parallelsessionIdstill go through relay bookkeeping instead of raw forwarding.BROWSER_SWARM_PORT=20007 BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2efromskills/browser-swarm0.1.1; worker endpoint isolation and session-scoped sibling/lifecycle rejection passed; rootTarget.createTarget/Target.closeTargetwith a rootsessionIdmoved target count3 -> 4 -> 3; exactly one detach event was observed; relay screenshot wrote a1509933byte PNG; same-page workers filled and clicked submit foralpha-worker/beta-worker/gamma-workerin paralleld455b35only update browser-swarm docs andRUNNING_TEST_NOTES.md.BROWSER_SWARM_PORT=20018 BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2efromskills/browser-swarm112ecf8with extension idbljijffbkipealmmglkacdmfjjpdjphm, extension version0.1.1, and service workerservice-worker-v0-1-1.js; worker endpoint isolation and session-scoped sibling/lifecycle rejection passed; rootTarget.createTarget/Target.closeTargetwith a rootsessionIdmoved target count3 -> 4 -> 3; exactly one detach event was observed; relay screenshot wrote a1515911byte PNG; same-page workers filled and clicked submit foralpha-worker/beta-worker/gamma-workerin parallel, and each finaltab listexposed exactly one targetnpm run e2ebrowse fillwrotealpha-worker/beta-worker/gamma-worker, parallelclick #submitsubmitted each form, and final title/text/value/tab-list assertions all passednode scripts/setup-real-browser.mjs --browser arc --no-open --no-start-relay --timeout 2 --json,node scripts/setup-real-browser.mjs --browser chrome --no-open --no-start-relay --port 19995 --timeout 10 --json, andnode scripts/setup-real-browser.mjs --browser not-a-browser --no-open --no-start-relay --no-wait3with connected version0.1.0, expected manifest version0.1.1, andversionMatches: false; disposable Chrome exited0with connected version0.1.1andversionMatches: true; unknown browser exited1with the supported browser listextensionConnected: trueas sufficient when a stale MV3 service worker is active, and validates browser names before relay/browser side effects. It detects the current Arc blocker and passes on a fresh Chrome happy path.npm run e2eTarget.attachToTargetandTarget.getTargetInforeturned errors; workerTarget.createTargetandTarget.closeTargetreturned explicit browser-swarm harness errors; the same lifecycle commands with an attachedsessionIdalso returned errors; an unknownsessionIdfailed closed instead of falling back to the worker targetworkeragents against three target-bound Chrome endpoints; each worker reported one visible target, operated only on its own assigned page, and captured a screenshot (/tmp/browser-swarm-subagent-alpha.png,/tmp/browser-swarm-subagent-beta.png,/tmp/browser-swarm-subagent-gamma.png)112ecf8/ recorded inb172bf8: spawned three real Codexworkeragents (Franklin,Raman,Mencius) against three target-bound endpoints in one disposable Chrome profile on identicalhttp://127.0.0.1:18105/sametabs; workers wroteproper-codex-alpha/proper-codex-beta/proper-codex-gamma, returned structured JSON, and the main harness independently verified each endpoint exposed exactly one target with its own title/text/value stateclaude -p --permission-mode bypassPermissions --allowedTools Bash --output-format jsonagent ran concurrently in the same disposable Chrome profile on identical same-page tabs; each reported distinct title/text/value/url/tabCount/targetId/screenshot; the main harness independently verified one visible target per endpointa3fe8ad: one real Codexworkersubagent and oneclaude -p --permission-mode bypassPermissions --allowedTools Bash --output-format jsonworker ran concurrently in the same disposable Chrome profile on identicalhttp://127.0.0.1:18087/sametabs; Codex reportedlatest-codex-worker, Claude reportedlatest-claude-worker, and the main harness independently verified each target-bound endpoint saw exactly one tab with its own title/text/value state87267b6: one real Codexworkersubagent and oneclaude -p --permission-mode bypassPermissions --allowedTools Bash --output-format jsonworker ran concurrently in a disposable Chrome profile on identical same-page tabs. Codex reportedcurrent-head-codex-worker-87267b6; Claude reportedcurrent-head-claude-worker-87267b6; the main harness independently verified each target-bound endpoint exposed exactly one target with distinct title/text/value state. Follow-up strict browse-only probing showed long session names can block browse daemon startup, while short sessionbs-claude-9039passedget title,fill,click,tab list,get text,get value, andscreenshot --pathwith one visible tab.d455b35: one real Codexworkersubagent and oneclaude -p --permission-mode bypassPermissions --allowedTools Bash --output-format jsonworker ran concurrently in a disposable Chrome profile on identicalhttp://127.0.0.1:18090/sametabs. Codex reportedcurrent-head-codex-d455b35; Claude reportedcurrent-head-claude-d455b35; screenshots were written to/tmp/browser-swarm-current-codex-d455b35.pngand/tmp/browser-swarm-current-claude-d455b35.png; the main harness independently verified each target-bound endpoint exposed exactly one target with distinct title/text/value state.d455b35only update browser-swarm docs andRUNNING_TEST_NOTES.md.aba8036: one real Codexworkersubagent and oneclaude -p --permission-mode bypassPermissions --allowedTools Bash --output-format jsonworker ran concurrently in a disposable Chrome profile with extension version0.1.1on identicalhttp://127.0.0.1:55459/sametabs. Codex reportedcodex-latest-a726f6c; Claude reportedclaude-latest-a726f6c; screenshots were written to/tmp/browser-swarm-mixed-codex-a726f6c.png(29743bytes) and/tmp/browser-swarm-mixed-claude-a726f6c.png(29104bytes); the main harness independently verified each target-bound endpoint exposed exactly one tab with distinct title/text/value state.arc-alpha,arc-beta, andarc-gamma0.1.0service worker.Target.createTargetcreated a fourth target, relay navigation worked on that target, target-bound endpoint for the created tab saw only itself, worker endpoints blockedTarget.createTarget/Target.closeTarget, and rootTarget.closeTargetcleaned the target back to the original threehttp://127.0.0.1:18080/sameand identical titlesame-page-action-test; issued parallel actions; final DOM/title states were distinct (alpha-worker,beta-worker,gamma-worker) and each workertab liststill returned exactly one targetfill --press-enter/parallel input flake that led to theInput.*queue fix.chrome.tabGroups.query/chrome.tabs.group; Arc 1.146.0 crashed shortly after--no-groupand documents Arc mode as no visual grouping, with isolation still provided by target-bound endpoints./healthreturnedextensionConnected: true;ensure --no-group --count 1returnedgroupId: null,groupDisabled: true, and one target;browse get title/url/tab list --session <arc-smoke> --cdp <target-ws>returnedExample Domain,https://example.com/, and exactly one visible target; relay emitted no extension warnings; Arc process stayed runningbrowse --session+--cdpendpoint loaded public Google result pages, extracted titles/snapshots, and captured screenshots;tab listreturned exactly one visible target per worker after session initialization; raw CDPTarget.getTargetsreturned only the assigned target for each endpoint, and siblingTarget.attachToTargetfailed; relay emitted no extension warnings; Arc stayed runningBROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2e:arc-serialized-clickfromskills/browser-swarm0.1.0while the unpacked manifest expected0.1.1, created two no-group target-bound tabs on the same local URL, filledarc-serialized-alpha/arc-serialized-betain parallel, clicked#submitsequentially through the top-level harness, verified each tab title/result/input matched its assigned value, verified each endpoint reported exactly one tab, then closed its tabs and daemons./healthreports version0.1.1, then rerun parallel pointer-click submission against the latest service worker input queue.npm run diagnose:arc-worker -- --jsonfromskills/browser-swarm3withstatus: "STALE_ARC_SERVICE_WORKER_REGISTRATION", connected version0.1.0, expected version0.1.1; Arc Secure Preferences points at the current unpacked extension path but still records service-worker registration version0.1.0; exact old worker URL hits18, exact expected worker URL hits0/healthreports extension version0.1.1.BROWSER_SWARM_BROWSE_BIN=<local oclif browse> npm run e2e:arc-parallel-clickfromskills/browser-swarm3withstatus: "BLOCKED_STALE_EXTENSION", expected extension version0.1.1, connected Arc worker version0.1.0, andtargetCount: 0./healthreports extension version0.1.1.Artifacts from the real e2e runs:
<temp>/browser-swarm-e2e/report.json<temp>/browser-swarm-e2e/flights.png<temp>/browser-swarm-e2e/rentals.png<temp>/browser-swarm-e2e/dinner.png/tmp/browser-swarm-subagent-alpha.png/tmp/browser-swarm-subagent-beta.png/tmp/browser-swarm-subagent-gamma.png/tmp/browser-swarm-mixed-codex.png/tmp/browser-swarm-mixed-claude.png<temp>/browser-swarm-realworld-flights.png<temp>/browser-swarm-realworld-rentals.png<temp>/browser-swarm-realworld-dinner.pngskills/browser-swarm/RUNNING_TEST_NOTES.mdNote
High Risk
Adds a new local Chrome extension + relay that forwards and scopes CDP traffic; mistakes could expose cross-tab access or unsafe lifecycle control despite added guardrails.
Overview
Introduces a new
browser-swarmskill that coordinates multiple agents against a single real Chromium profile via a localhost relay (scripts/swarm-relay.mjs) and a Manifest V3 extension bridge, producing target-bound CDP endpoints per tab.Adds hardening to the relay/extension boundary: worker endpoints are scoped to one target, reject sibling target inspection/attach, and block
Target.createTarget/Target.closeTarget; the relay also exposes extension metadata in/healthand makes detach handling/idempotency more robust.Adds operational tooling and docs:
setup-real-browser.mjs(real-profile setup with version-staleness detection),launch-chrome.mjs(disposable Chrome with--relay-portby patching the extension), Arc-specific--no-groupmode plus Arc diagnostics (diagnose-arc-worker.mjs), and e2e harnesses for same-page parallel input/click behavior. Updates plugin marketplace + README to list the new skill and addsRUNNING_TEST_NOTES.mdwith stress-test evidence.Reviewed by Cursor Bugbot for commit edad4bb. Bugbot is set up for automated code reviews on this repo. Configure here.