Agent Browser Relay lets your agent control and read from tabs in your real Chrome session, including multiple attached tabs in parallel, without forcing a separate browser profile.
Chrome remains the documented target. Chromium-based browsers that can load the extension may also work, and relay status now reports the detected host browser and profile id, but that is visibility support rather than a guaranteed cross-browser orchestration feature.
Release history lives in CHANGELOG.md.
- Run agent reads on multiple attached tabs concurrently with per-tab lease scoping (
--tab-id) inside one shared extension instance. - Execute on background/non-focused tabs while you continue using your browser.
- Keep your real logged-in session (cookies, auth state, extensions) instead of re-automating login in a disposable browser.
- Extract structured page data, full DOM, screenshots, and chat/message presets via one CLI.
- Pass complex custom expressions safely via
--expression-fileor--expression-stdininstead of fragile shell quoting. - Recover from tab/context changes with reconnect + attach checks designed for long-running agent workflows.
- Stop safely when CAPTCHA/human verification appears, and alert the user before continuing.
- Chrome extension bridge for attach/detach from the toolbar popup.
- Local relay service with global always-on mode (
launchd/systemd --user). - Scriptable read/check interface (
node scripts/read-active-tab.js) for agent workflows. - One-command readiness preflight via
npm run relay:doctor. - Explicit tab leasing model for multi-agent safety on shared relay infrastructure.
- Detected host-browser identity in relay status so you can distinguish Chrome, Edge, Brave-style hosts and separate browser profiles.
- Multiple tabs can be attached at the same time.
- A given attached tab can only be actively leased by one relay session at a time.
--tab-idscopes an agent run to one attached tab lease. It does not create a private browser or extension instance.- Use
npm run relay:status -- --all --status-timeout-ms 3000to inspect:attachedTabs[].leasedSessionIdtabLeasesleaseSummary.availableAttachedTabIds
- If a tab is already leased, the supported recovery path is to wait for that session to finish, or choose another attached tab without an active lease. Do not force takeover.
- Documented target: Google Chrome.
- Possible but not guaranteed: Chromium-based browsers that can load this extension in developer mode.
- Not implemented as a first-class feature: browser selection, browser-specific routing, or a guarantee that one relay setup can orchestrate multiple different browsers as separate managed backends.
- Shipped in
v0.0.12: relay status now reports the detected host browser and a persistent browser-profile id so operators can tell which browser/profile is actually connected.
npx skills add Mindgames/Agent-browser-relay -g -yTry this folder in Chrome first:
~/agent-browser-relay/extension
If that folder is missing, use:
~/.agents/skills/agent-browser-relay/extension
If you are working from this repository directly, load this folder in Chrome:
<your-checkout>/extension
Examples:
/Users/you/Projects/Agent-browser-relay/extension/Users/you/code/agent-browser-relay/extension
To print the exact paths for the copy you are using, run npm run extension:path from that copy.
It now prints the primary extension folder plus stable absolute paths for read-active-tab.js and preflight.sh.
Notes:
skills addguarantees the installed skill at~/.agents/skills/agent-browser-relay~/agent-browser-relay/extensionis only a convenience path when it exists- if
~/agent-browser-relay/extensionis missing, runnpm run extension:installor load the hidden skill path directly
- Open
chrome://extensions - Enable Developer mode
- Click Load unpacked
- In the Chrome file picker, press
Command+Shift+G - Paste the exact extension folder path:
~/agent-browser-relay/extensionfor a global install, if that folder exists- otherwise
~/.agents/skills/agent-browser-relay/extensionfor a global install <your-checkout>/extensionfor a project or local checkout
- Press Enter, then select the
extensionfolder if Chrome does not open it automatically - Pin the Agent Browser Relay toolbar icon
This Command+Shift+G step is the easiest option on macOS because it works even when .agents is hidden.
If you prefer to browse manually in Finder:
- press
Command+Shift+.to show hidden files - then browse to
agent-browser-relay/extension - if that folder is missing, browse to
.agents/skills/agent-browser-relay/extension
relay:start, relay:global:install, and read-active-tab.js also print the primary load path + version sync status as stderr hints.
If hidden folders are still getting in the way, use a ZIP fallback instead:
- Download the repo ZIP: Mindgames/Agent-browser-relay main.zip
- Unzip it anywhere convenient
- In Chrome, click Load unpacked
- Select the
extensionfolder inside the unzipped repo
This is a fallback only. The primary install path is still the one from your global install or local checkout.
- Start the relay
- Open the Chrome tab you want the agent to use
- Open the Agent Browser Relay popup from the toolbar icon
- Wait for the popup status to show
Relay connected on <port> - Or confirm from the terminal:
npm run extension:status -- --port "18793" --wait-for-connected --connected-timeout-ms 120000When connected, this command now also prints the detected browser host and profile id for that relay port.
- With the popup open on the target tab, click Attach this tab and confirm the badge shows
ON. - If you want the agent to create its own background tabs, enable Allow agent to create new background tabs in the popup.
- In Connections, click Copy ID for the attached tab and send it to your agent, for example:
Use tab 4581930. - Repeat for every tab you want the agent to access.
Example prompt to your agent:
Use Agent Browser Relay to audit the current page and summarize the most important links. Use tab 4581930.
Expected flow:
- Agent starts relay (for example
npm run relay:global:install -- --ports 18793 --timeout 12000). - On a new machine, agent tells you which extension folder to load first:
~/agent-browser-relay/extensionfor a global install, if that folder exists- otherwise
~/.agents/skills/agent-browser-relay/extensionfor a global install <your-checkout>/extensionfor a project or local checkout- or asks you to run
npm run extension:path
- Agent asks you to open the popup once so Chrome proves the extension is loaded, then checks
npm run extension:status -- --port "18793" --wait-for-connected --connected-timeout-ms 120000. - If the workflow needs a specific existing tab, agent asks you to click Attach this tab in the popup.
- If the workflow only needs a new agent-created tab, the agent can create it itself once Allow agent to create new background tabs is enabled.
- Agent runs
npm run relay:doctor -- --port "18793" --tab-id 4581930 --jsonfor existing-tab workflows, ornpm run relay:doctor -- --port "18793" --require-target-create --jsonfor first-tab creation workflows, and continues only when it returns success.
agent-browser-relay is its own skill. It does not live inside or depend on the separate agent-browser skill.
The paths that matter for this project are:
~/.agents/skills/agent-browser-relay- Canonical global install path for the
agent-browser-relayskill.
- Canonical global install path for the
~/.claude/skills/agent-browser-relay- Optional Claude-side skill path, usually created by the installer when Claude Code is present.
~/agent-browser-relay/extension- Optional visible convenience copy created by
npm run extension:installwhen writable.
- Optional visible convenience copy created by
Why this matters: the relay extension should be loaded from an agent-browser-relay path, not from agent-browser.
extension/background.js: attach/detach and tab bridge lifecycle.relay-server.js: local relay + CDP tunnel.scripts/relay-service.js: globallaunchd/systemd --userservice lifecycle.scripts/read-active-tab.js: read/check CLI that prints JSON.scripts/relay-manager.js: relay lifecycle plus the canonicaldoctorpreflight wrapper.scripts/extension-status.js: confirms whether Chrome has actually loaded the extension into the running profile.- Relay tab leasing (
--tab-id): isolates concurrent agents to specific tabs. scripts/extension-install-helper.js: prints the primary extension path and optionally refreshes the visible convenience copy.
read-active-tab.js includes a machine-readable capability block in each JSON payload (source.capabilities), including:
- CLI switches (
--check,--check --require-target-create,--metadata,--screenshot,--expression,--expression-file,--expression-stdin, presets,--tab-id, etc.) - Relay methods used by the client (
Grais.relay.*) - Bridge methods (
Grais.debugger.*) - Common CDP methods exposed for extraction and screenshots
- Installed/observed extension version metadata (
source.extension) so tooling can warn on mismatches and redirect humans to updates.
Defaults are set directly in code:
- Host:
127.0.0.1 - Port:
18793 - Attach timeout:
120000ms
Override per command with flags such as --host, --port, and --attach-timeout-ms when needed.
npm run relay:global:install -- --ports 18793 --timeout 12000
npm run relay:global:statusOther lifecycle commands:
npm run relay:global:start
npm run relay:global:stop
npm run relay:global:restart
npm run relay:global:update
npm run relay:global:uninstallnpm run relay:start
npm run relay:status -- --status-timeout-ms 3000Or explicit host/port:
npm run relay:start -- --host 127.0.0.1 --port 18793 --status-timeout-ms 3000relay:start now keeps the relay running until you stop it or the underlying process is restarted. If you want a bounded one-off session, pass --auto-stop-ms explicitly:
node scripts/relay-manager.js start --auto-stop-ms 10800000After relay startup, a human must confirm the extension is loaded before any read or tab-create workflow:
- Open/focus target tab in Chrome.
- Open Agent Browser Relay popup once and confirm the popup shows
Relay connected on <port>. - Confirm from the terminal with
npm run extension:status -- --port "18793" --wait-for-connected --connected-timeout-ms 120000. - If you want the agent to work on an existing tab, click Attach this tab.
- If you want the agent to create its own first tab, enable Allow agent to create new background tabs in the popup.
Then run the canonical preflight for an existing attached tab:
npm run relay:doctor -- --port "18793" --tab-id "<TAB_ID>" --jsonIf your workflow will create the first agent-controlled tab via Target.createTarget, require that readiness explicitly:
npm run relay:doctor -- --port "18793" --require-target-create --jsonnode scripts/read-active-tab.js --check ... remains available as the lower-level equivalent for local/manual debugging, but relay:doctor is the canonical agent preflight.
For multi-agent runs, always target a specific tab lease:
npm run relay:doctor -- --port "18793" --tab-id "<TAB_ID>" --jsonIf relay:doctor reports TAB_LEASED_BY_OTHER_SESSION, inspect relay status and choose another attached tab without an active lease:
npm run relay:status -- --all --status-timeout-ms 3000If your workflow will open background tabs via Target.createTarget, require that readiness explicitly:
npm run relay:doctor -- --port "18793" --require-target-create --jsonNever run bare curl for relay checks. Use timeout:
curl --max-time 3 -sS "http://127.0.0.1:18793/status"Continue only when status reports:
extensionConnected: true- low queue depth
- for tab-spawn workflows:
allowTargetCreate: true(or run--check --require-target-create)
Default extraction (url, title, text, links, metaDescription):
node scripts/read-active-tab.js --pretty falseSpecific tab lease:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --pretty falseFull DOM:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --expression "document.documentElement.outerHTML" --pretty falsePrefer --expression-file or --expression-stdin for multi-line or quote-heavy expressions.
Complex custom expression from file:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --expression-file "./tmp/expression.js" --pretty falseComplex custom expression from stdin:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --expression-stdin --pretty false < ./tmp/expression.jsScreenshot:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --screenshot --screenshot-full-page --screenshot-path "./tmp/page.png" --pretty falseWhatsApp messages:
node scripts/read-active-tab.js --tab-id "<TAB_ID>" --preset whatsapp-messages --selector "#main [data-testid=\"conversation-panel-messages\"], #main" --max-messages 200 --pretty false- Use
npm run relay:status -- --all --status-timeout-ms 3000before choosing a tab for a concurrent run. - Treat
attachedTabsas the candidate pool andtabLeases/leasedSessionIdas the current lease view. - Prefer
leaseSummary.availableAttachedTabIdswhen choosing the next--tab-id. - Pass
--tab-idon every check/read command so the relay can keep controller routing scoped to that tab. - If doctor reports
TAB_LEASED_BY_OTHER_SESSION, wait for that session to release the tab or choose another attached tab id from status. - Lease isolation only controls relay command routing for attached tabs. It does not create a separate Chrome profile, cookie jar, or full browser-process isolation.
- Tabs without a saved mapping use the relay default port (
18793). - After successful attach, tab-to-port mapping is stored and reused.
- Closing a tab clears its mapping.
- Multi-port routing helps separate attached-tab workflows. It is not the same thing as first-class multi-browser support.
Inspect all ports/tabs:
npm run relay:status -- --all --status-timeout-ms 3000- Relay unreachable:
npm run relay:status -- --status-timeout-ms 3000-
Relay start timed out:
- Read the startup error and relay log printed by
npm run relay:start. - Do not assume sandbox/network restrictions unless the output shows an actual permission error.
- Read the startup error and relay log printed by
-
Unsure which extension folder to load:
- Run
npm run extension:pathfrom the installed skill directory. - Load the primary path it prints in
chrome://extensions.
- Run
-
Unsure whether Chrome actually loaded the extension:
- Open the Agent Browser Relay popup once.
- Run
npm run extension:status -- --port "18793" --wait-for-connected --connected-timeout-ms 120000. - Continue only when it reports
Chrome extension connected.
-
Agent says it cannot create a new tab yet:
- Enable Allow agent to create new background tabs in the popup.
- Re-run
npm run relay:doctor -- --port "18793" --require-target-create --json. - If you want the agent to use an existing tab instead, attach that tab and pass its
tabId.
-
Unsure whether relay + extension + attached tab are all actually ready:
- Run
npm run relay:doctor -- --port "18793" --tab-id "<TAB_ID>" --json. - Inspect
blocker.code,blocker.summary, andblocker.nextAction.
- Run
-
Doctor says the requested tab is leased by another session:
- Run
npm run relay:status -- --all --status-timeout-ms 3000. - Inspect
attachedTabs,tabLeases, and the blockerdetailfield to see which session owns which tab. - Retry with another attached
tabId, or wait for the owning session to release the tab.
- Run
-
Agent says a tab is already leased by another session:
- Run
npm run relay:status -- --all --status-timeout-ms 3000. - Inspect
attachedTabs[].leasedSessionId,tabLeases, andleaseSummary.availableAttachedTabIds. - Wait for the owning session to finish, or retry with another attached tab id that has no active lease.
- Run
-
Visible convenience folder missing after install:
- Run
npm run extension:installfrom the installed skill directory. - Confirm
~/agent-browser-relay/extensionnow exists if you want that shortcut.
- Run
-
Attachment lost after navigation/reload:
- Re-open popup on that tab.
- Click Attach this tab again.
- Re-run the
--check --wait-for-attachcommand.
-
Sparse checkout accidentally hid folders in canonical skill copy:
cd ~/.agents/skills/agent-browser-relay
git sparse-checkout disable
git config --unset-all core.sparseCheckout || true
git config --unset-all core.sparseCheckoutCone || true
git checkout -- .Use only these script names:
npm run relay:global:installnpm run relay:global:statusnpm run relay:global:startnpm run relay:global:stopnpm run relay:startnpm run relay:statusnpm run relay:doctornpm run relay:stopnpm run extension:pathnpm run extension:statusnode scripts/read-active-tab.js
Do not restart relay just because local files changed. Restart only when explicitly requested by a human, or for hard recovery failures.
If CAPTCHA/human verification appears, stop immediately, alert the user, and wait for explicit confirmation before continuing.
For human-in-the-loop workflows, we recommend using attention-please so the user gets an immediate alert when manual action is needed (for example CAPTCHA or verification gates).