| name | better-agent-browser |
| description | Architectural routing layer over agent-browser CLI: picks the right tool (agent-browser fresh Chromium / agent-browser shared real-Chrome / camofox-browser anti-detect) for each task, and adds login-watch, CAPTCHA/automation-block detection, per-domain site experience, and parallel multi-tab via CDP proxy. Triggers: 'parallel browse', 'open multiple tabs', 'batch scrape', 'captcha check', 'automation blocked', 'site pattern', 'better browser', 'cdp proxy', 'parallel tabs', 'login watch', 'cloudflare blocked', 'datadome', 'anti-detect scrape'. |
Better Agent Browser
Architectural routing layer above agent-browser CLI. Decides which tool to use for each task and adds capabilities agent-browser doesn't have:
- Tool routing — pick agent-browser (default) / agent-bridge (piggyback into user's real Chrome, when built) / camofox-browser (anti-detect escalation).
- Login Watch — auto-detect login walls, notify user, poll until login completes.
- CAPTCHA / Automation Block Detection — tell solvable CAPTCHAs apart from unsolvable automation blocks.
- Site Experience — per-domain accumulated patterns. Read before navigating, write after solving problems.
- Parallel Tabs — operate many tabs simultaneously via CDP proxy, sharing one Chrome and one login state.
This skill complements agent-browser, not replaces it. agent-browser is the execution layer; this skill adds detection, experience, routing, and coordination.
${SKILL_PATH} is set by skills.sh to this skill's install directory.
Language
Match user's language.
First: load agent-browser core skill
Most command details (refs @e1, snapshot -i, click, fill, find role, state save, auth vault, react tree, vitals, doctor, network route, record, ...) live in agent-browser's bundled core skill, not here. Load it first:
agent-browser skills get core
This skill (better-agent-browser) only adds the layers core doesn't cover.
Routing decision (run this first)
Before doing browser work, classify the task:
Q1: Is the target localhost / public / no-login-needed?
Yes → Layer 0a (agent-browser fresh)
No → Q2
Q2: Will one-time login + persistent state work? (most SaaS: yes)
Yes → Layer 0b (agent-browser --state / --session-name)
No → Q3 (DBSC site, passkey-only, or "use my live session right now")
Q3: Need to share user's already-logged-in real Chrome with multiple agents?
Yes → agent-bridge piggyback (when built — until then, Layer 0c clone fallback)
No → Q4
Q4: Site is fingerprint-detecting agent-browser (Cloudflare/DataDome challenge)?
Yes → Layer 3 (camofox-browser sidecar)
No → revisit Q1 — should fit one of the above.
Always start at the smallest layer that fits. Escalate only on a specific failure.
Hard Rules (read first)
- Default to headless and
--state-persisted login. Don't clone the user's daily Chrome profile by reflex. For most SaaS the path is agent-browser --state ./auth.json (one-time login, persisted across runs) or --session-name <name> (auto-save/restore).
- macOS: NEVER use
open -a "Google Chrome". It foregrounds Chrome and steals focus on every navigation. agent-browser core handles browser launch correctly; don't bypass it.
- Browser is a singleton resource per
--user-data-dir. Chrome's SingletonLock allows one process per profile dir. Use agent-browser --session <name> for isolated parallel runs (separate Playwright contexts) OR Layer 2 CDP proxy for parallel multi-tab in one Chrome. Never launch a second Chrome against the same user-data-dir.
- Do NOT naively parallelize browser-using subagents. If you dispatch a subagent pool and only one is supposed to touch the browser, explicitly tell the others NOT to load this skill. If multiple subagents legitimately need the browser, route them all through one Layer 2 CDP proxy.
agent-browser doctor is the default diagnostic. Before debugging by hand (Failed to connect, stale daemons, version mismatches, missing Chrome): agent-browser doctor (or --fix for destructive repairs). Falls back to ${SKILL_PATH}/scripts/check-deps.sh only for proxy/CDP-port-specific checks not covered by upstream.
- Project-local
CLAUDE.md recipes are site-specific. A Canvas recipe saying "headed window + open -a" exists because CAS SSO needs a GUI; do NOT generalize that. Headless is the default.
Layer 0a: Fresh Chromium (no login needed)
Local dev, public pages, dev self-test, scraping non-logged-in content. Just use agent-browser core directly:
agent-browser open http://localhost:3000
agent-browser snapshot -i
agent-browser click @e3
agent-browser screenshot page.png
For dev SPA work, agent-browser core has built-in React introspection and Web Vitals (recently shipped):
agent-browser open --enable react-devtools http://localhost:3000
agent-browser react tree
agent-browser react inspect <fiberId>
agent-browser vitals
agent-browser pushstate /next-page
See core skill for full surface.
Layer 0b: One-time login + persisted state
The new default for sites that need login. Avoids the clone dance entirely:
agent-browser --headed --state ./.auth/github.json open https://github.com/login
agent-browser --state ./.auth/github.json state save ./.auth/github.json
agent-browser --state ./.auth/github.json open https://github.com/notifications
agent-browser snapshot -i
Or --session-name for auto-save/restore:
AGENT_BROWSER_SESSION_NAME=github agent-browser open https://github.com
For credentials, prefer agent-browser's auth vault over shell-history-leaking env vars:
agent-browser auth save my-app --url https://app.example.com/login \
--username user@example.com --password-stdin
agent-browser auth login my-app
Use this layer unless you specifically need Layer 0c or beyond.
Layer 0c: Borrow user's daily Chrome (last resort)
Only when both of the following are true:
- The site rejects one-time login persistence (DBSC, TPM-bound passkey, mTLS auto-select, server-side device binding); and
- agent-bridge piggyback isn't built / doesn't fit.
If only one is true, prefer Layer 0b.
Path A — connect to a running Chrome via --auto-connect (preferred, simpler than clone):
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--remote-debugging-port=9333 '--remote-allow-origins=*' \
--user-data-dir="$HOME/.chrome-debug-profile" \
--no-first-run --no-default-browser-check 2>/dev/null &
agent-browser --auto-connect open https://github.com/settings
agent-browser --auto-connect snapshot -i
For multi-agent serialization on the same Chrome, fall through to ${SKILL_PATH}/scripts/browser-connect.sh lock — it acquires a flock, auto-routes to Layer 2 (CDP proxy) on collision:
RESULT=$(bash ${SKILL_PATH}/scripts/browser-connect.sh 9333)
MODE=$(echo "$RESULT" | jq -r '.mode')
mode | Meaning | Action |
|---|
layer0c | Lock acquired, connected | Use agent-browser --auto-connect |
layer2 | Another agent holds the lock | Use CDP proxy (Layer 2) instead |
unavailable | Chrome not running on debug port | Guide user to start Chrome |
Path B — clone Default profile (use only when running Chrome with debug port is unacceptable):
Chrome 136+ refuses remote debugging on the real default user-data-dir:
DevTools remote debugging requires a non-default data directory. Specify this using --user-data-dir.
You must operate on a copy:
DEST="$HOME/.chrome-borrowed-profile"
rsync -a --exclude='Singleton*' --exclude='*.lock' \
"$HOME/Library/Application Support/Google/Chrome/Default/" "$DEST/Default/"
cp "$HOME/Library/Application Support/Google/Chrome/Local State" "$DEST/"
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
--headless=new --disable-gpu \
--remote-debugging-port=9334 '--remote-allow-origins=*' \
--user-data-dir="$DEST" --profile-directory=Default \
--no-first-run --no-default-browser-check 2>/dev/null &
bash ${SKILL_PATH}/scripts/browser-connect.sh 9334
Clone reliability — read before blaming cookies. Chrome's Safe Storage key is scoped per-OS-user, not per-profile. Cloning Default/ + Local State on the same machine under the same OS user decrypts the Cookies SQLite normally — persistent cookies survive the clone. Session-only cookies are still dropped because Chrome exited between source and clone (see Layer 0d below for the session-cookie footgun), which is an orthogonal issue.
When clone fails (first navigation shows logged-out page because Cookies really won't decrypt):
- Cross-machine copy — source machine's keychain entry isn't on the destination.
- Cross-OS-user copy — different macOS login = different keychain = different Safe Storage key.
- Windows cross-install with App-Bound Encryption (Chrome 127+) — ABE binds the key to the installing user's DPAPI scope.
Same machine + same OS user does not fit any of these. If a clone on that setup looks logged-out, suspect session-cookie expiry or stale rsync, not decryption failure. Fallback in all failing cases is the same: log in once inside the cloned profile — new encryption happens under destination keychain and persists from there.
Layer 0d: Session-only cookies (CAS / Shibboleth / enterprise SSO)
CAS / Shibboleth / enterprise SSO (e.g., university Canvas) issue cookies without Expires / Max-Age, so Chrome drops them at exit regardless of --user-data-dir persistence. Symptom: every fresh launch lands on the IdP login page even though the profile is "logged in".
Fix: set "session": {"restore_on_startup": 1} inside ~/.chrome-debug-profile/Default/Preferences (stable Chromium behavior since Chrome 19, crbug.com/130291). Chrome re-opens previous tabs on startup; session cookies survive process restarts. Only Cmd+Q clean-quit preserves them; kill -9 followed by dirty exit_type:"Crashed" still loses them.
When agent-bridge piggyback ships, this footgun disappears — Chrome doesn't exit between agent runs.
Layer 1: Login Watch + CAPTCHA Watch + Site Patterns
These are this skill's exclusive value-add. agent-browser core does not cover them.
Site Patterns — Read Before, Write After
Per-domain accumulated experience. Grows as agents encounter and solve problems.
Before navigating to an external domain:
PATTERN_FILE="${SKILL_PATH}/references/site-patterns/${DOMAIN}.md"
- File exists → Read. Treat as hints (may be outdated), not guarantees.
requires_cdp: true and not connected to real Chrome → STOP, escalate to Layer 0c.
anti_detect: true → escalate to Layer 3 directly, don't try Layer 0a/0b.
- File doesn't exist → Proceed. May create one later.
After successful operations, write back what you learned:
---
domain: example.com
aliases: [示例, Example]
requires_cdp: true
anti_detect: false
updated: 2026-05-08
---
## Platform Characteristics
Architecture, anti-bot, login needs, content loading.
## Effective Patterns
Verified URLs, selectors, strategies. Tag each with date.
## Known Traps
What fails and why. Tag each with date.
Rules: only verified facts, not guesses. Tag findings with dates — patterns decay. If a pattern stops working, update the file.
List available patterns:
ls ${SKILL_PATH}/references/site-patterns/*.md | grep -v _template
Login Watch — Auto-Detect and Wait
Don't pre-assume login is needed. Try to access content first. Only if the page shows a login wall:
- Tell the user: "Please log in to [site] in your Chrome."
- Start login-watch in the background:
bash ${SKILL_PATH}/scripts/login-watch.sh --interval 5 --timeout 300
MUST run in background with long timeout — user may take minutes to log in.
- When it returns
logged_in: true, navigate to the target URL and continue.
| Exit | Meaning | Action |
|---|
0 | Login detected | Navigate to target and continue |
1 | Timeout | Tell user login-watch timed out, ask them to confirm when done |
2 | Error | agent-browser not connected, fix connection (agent-browser doctor) |
Output: { "logged_in": bool, "url": string, "elapsed": number, "hint": string }
Login detection keywords: "Sign in", "Log in", "Create account", "Enter your password", etc. If a site uses unusual login wall text, detection may miss — fall back to asking the user.
CAPTCHA Watch — Check After Navigating
Run only when you suspect a block (blank page, challenge page, Cloudflare interstitial):
bash ${SKILL_PATH}/scripts/captcha-watch.sh --cdp <PORT>
| Exit | Meaning | Action |
|---|
0 | No CAPTCHA | Continue |
1 | Solvable CAPTCHA (real Chrome) | Screenshot → notify user → poll (background, same pattern as login-watch) |
2 | Automation block (unsolvable) | STOP. Escalate to Layer 3 (camofox). Never retry in fresh-Chromium mode. |
3 | Error | Retry once, then agent-browser doctor |
Solvable CAPTCHA polling:
agent-browser screenshot captcha.png
echo "CAPTCHA detected. Please solve it in Chrome."
for i in $(seq 1 24); do
sleep 5
RESULT=$(bash ${SKILL_PATH}/scripts/captcha-watch.sh --cdp <PORT>)
if echo "$RESULT" | jq -e '.captcha == false' >/dev/null 2>&1; then
break
fi
done
Layer 2: CDP Proxy (Parallel Multi-Tab in Real Chrome)
HTTP API to manage multiple tabs in the user's real Chrome. The only way to do parallel multi-tab while sharing login state and clean fingerprint.
Why not agent-browser --session? --session spawns isolated Playwright contexts — navigator.webdriver=true, blocked by anti-bot, no shared login state. Layer 2 reuses the user's real Chrome via CDP, so all tabs share cookies and fingerprint.
Preflight
bash ${SKILL_PATH}/scripts/check-deps.sh [CDP_PORT]
Output: { ready, mode, agent_browser, node, chrome_cdp, proxy, hint }.
ready | mode | Action |
|---|
true | cdp | Proceed |
true | session | Layer 2 unavailable. Fall back to Layer 0a/0b. |
false | — | STOP. Run agent-browser doctor first; then fix per hint if not resolved. |
Start Proxy
curl -sf http://127.0.0.1:3456/health >/dev/null 2>&1 || \
node ${SKILL_PATH}/scripts/cdp-proxy.mjs &
PROXY="http://127.0.0.1:3456"
Batch Operations
TABS=$(curl -s -X POST "$PROXY/batch" \
-H 'Content-Type: application/json' \
-d '{"urls":["https://site1.com","https://site2.com"]}')
TARGETS=$(echo "$TABS" | jq '[.[].targetId]')
curl -s -X POST "$PROXY/batch-eval" \
-H 'Content-Type: application/json' \
-d "{\"targets\":$TARGETS,\"expression\":\"document.title\"}"
curl -s -X POST "$PROXY/batch-close" \
-H 'Content-Type: application/json' \
-d "{\"targets\":$TARGETS}"
Parallel Sub-Agents
Main Agent
├── Start CDP proxy (once)
├── Sub-Agent A: /new → targetId-A → /eval → /close
├── Sub-Agent B: /new → targetId-B → /eval → /close
└── Sub-Agent C: /new → targetId-C → /eval → /close
All share same Chrome, login state, cookies
CAPTCHA Watch for Proxy Tabs
bash ${SKILL_PATH}/scripts/captcha-watch.sh --proxy-eval <targetId>
Cleanup
Tab discipline applies. Only close tabs you opened. Never close user's tabs. Don't kill the proxy if other agents may be using it.
curl -s -X POST "$PROXY/batch-close" \
-H 'Content-Type: application/json' \
-d "{\"targets\":$TARGETS}"
Full API: references/api.md.
Layer 3: Anti-Detect Escalation (camofox-browser sidecar)
When CAPTCHA-watch returns exit 2 (automation block), or a site-patterns/<domain>.md file marks anti_detect: true, escalate to camofox-browser. It's a separate sidecar (NOT bundled with this skill), built on Camoufox — a Firefox fork with C++-level fingerprint patching that agent-browser can't match.
When to escalate
- Layer 0a/0b/0c CAPTCHA-watch returns exit 2 (true automation block, not solvable CAPTCHA).
- Site is on a known anti-detect list: Cloudflare-protected, DataDome, PerimeterX, Akamai Bot Manager.
- The job is bulk scraping at scale — not personal automation. (For one-off "use my account" tasks, prefer Layer 0c.)
When NOT to escalate
- Just because login is required (Layer 0b is enough).
- Just because the site loaded slowly (probably not anti-bot).
- For dev self-test of localhost (anti-bot doesn't apply).
- For sites where you need the user's actual login state (camofox starts with empty cookies; one-time login required there too).
Setup (sidecar — install if missing)
curl -sf http://localhost:9377/health 2>/dev/null && echo "camofox running" || echo "needs install"
git clone https://github.com/jo-inc/camofox-browser /opt/camofox-browser
cd /opt/camofox-browser && npm install
node scripts/postinstall.js
CAMOFOX_CRASH_REPORT_ENABLED=false PORT=9377 node server.js &
Usage shape (REST API, not CLI)
TAB=$(curl -s -X POST http://localhost:9377/tabs \
-H 'Content-Type: application/json' \
-d '{"userId":"agent","sessionKey":"work","url":"https://target.example.com/"}')
TAB_ID=$(echo "$TAB" | jq -r '.tabId')
curl -s "http://localhost:9377/tabs/$TAB_ID/snapshot?userId=agent" | jq -r '.snapshot'
curl -s -X POST "http://localhost:9377/tabs/$TAB_ID/click" \
-H 'Content-Type: application/json' \
-d '{"userId":"agent","ref":"e1"}'
Full API: see camofox-browser README at /opt/camofox-browser/README.md. Note: snapshot ref format is similar but not identical to agent-browser's; refs are interactive-only by default.
Costs of escalating
- Slower — first click ~2.3s vs agent-browser's ~170ms; full self-test scenario ~9.6s vs agent-browser's ~3.5s.
- Firefox-only — Chrome-specific APIs (DBSC, PWA install, certain WebGL extensions) unavailable.
- 300MB binary — postinstall.
- Empty cookies — must re-login per site.
Worth it for the fingerprint bypass; not worth it otherwise.
Scripts Reference
| Script | Purpose | Layer | Run in background? |
|---|
browser-connect.sh [PORT] | Multi-agent serialization lock for Layer 0c | 0c | No |
browser-disconnect.sh [PORT] | Release lock | 0c | No |
check-deps.sh [PORT] | Proxy/CDP-port diagnostics (use after agent-browser doctor) | 0c+ | No |
login-watch.sh --interval N --timeout N | Poll until login wall disappears | 1 | Yes |
captcha-watch.sh --cdp PORT | Detect CAPTCHA / automation block | 1 | Polling loop: yes |
captcha-watch.sh --proxy-eval ID | Same, for proxy tabs | 2 | Polling loop: yes |
cdp-proxy.mjs | HTTP API for parallel tabs in shared Chrome | 2 | Yes (daemon) |
Degradation
| Missing | Impact |
|---|
| agent-browser CLI | Fatal. Run npm i -g agent-browser && agent-browser install. |
| Chrome at debug port | Layer 0c/2 unavailable. 0a/0b still work via fresh Chromium. |
CDP proxy (cdp-proxy.mjs) | Layer 2 unavailable. 0/1 still work. |
| Node.js 22+ | Layer 2 unavailable. 0/1 still work. |
| camofox-browser sidecar | Layer 3 unavailable — agents on anti-bot sites must STOP, not retry in fresh mode. |
| Site pattern file | Proceed without experience. Risk: unexpected blocks. |
Troubleshooting
| Symptom | Fix |
|---|
| Anything install / connect / version-mismatch | agent-browser doctor first. Auto-cleans stale socket / pid / version sidecar files. --fix for destructive repairs. |
connect fails | Chrome not running with debug port. See Layer 0c setup. Or --auto-connect if it is running but you didn't know. |
"DevTools remote debugging requires a non-default data directory" | Chrome 136+ blocks CDP on real default profile. Either use --auto-connect against a Chrome started with --user-data-dir=$HOME/.chrome-debug-profile, or clone the profile (see Layer 0c Path B). |
| Multiple agents fighting over one Chrome | Someone bypassed browser-connect.sh or dispatched parallel browser subagents without Layer 2. Serialize, or route all through one CDP proxy. |
| macOS focus stolen on every navigation | You launched Chrome via open -a. Use the direct binary path. agent-browser core handles this — don't bypass it. |
| Cookies still show logged-out after profile clone | Only expected when the clone came from another machine, another OS user, or a Windows install with App-Bound Encryption. Same-machine same-user clones should decrypt fine. Suspect session-cookie expiry, not keychain mismatch. Fallback: log in once inside the cloned profile. |
| CAPTCHA unsolvable (exit 2) | Automation block. Escalate to Layer 3 (camofox). Don't retry in fresh-Chromium mode. |
webdriver=true in CDP | Connected to Playwright/Electron. Use real Chrome via Layer 0c. |
| Proxy port 3456 in use | CDP_PROXY_PORT=3457 node cdp-proxy.mjs & |
/new hangs | Slow page. ?timeout=30000. |
| Tabs accumulate | /batch-close. Check /list. |
| login-watch misses login wall | Site uses unusual text. Fall back to asking user. |
| Need React introspection / Web Vitals on a SPA | Use agent-browser core: agent-browser open --enable react-devtools <url> then react tree, react renders start, vitals, pushstate. |
| Need to mock network / record video / annotate screenshots | All in agent-browser core — see core skill network route, record start/stop, screenshot --annotate. |
References (read on demand)
| Document | When |
|---|
references/api.md | Layer 2: proxy endpoints, batch examples |
references/site-patterns/*.md | Layer 1: before navigating to known domains |
agent-browser core skill | Command surface: refs, snapshot, click, fill, state save, auth vault, react/vitals, doctor, network, record, etc. Load via agent-browser skills get core. |
Infra/docs/agent-bridge-thesis.md | Architectural roadmap: agent-bridge piggyback extension (planned). |