# Better Agent Browser

> 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'.

- Skill: `psylch/better-agent-browser` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add psylch/better-agent-browser`
- Raw SKILL.md: https://api.skillmd.com/api/skills/psylch/better-agent-browser/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: psylch (https://skillmd.com/u/psylch)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/psylch/better-agent-browser

---


# 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:

1. **Tool routing** — pick agent-browser (default) / agent-bridge (piggyback into user's real Chrome, when built) / camofox-browser (anti-detect escalation).
2. **Login Watch** — auto-detect login walls, notify user, poll until login completes.
3. **CAPTCHA / Automation Block Detection** — tell solvable CAPTCHAs apart from unsolvable automation blocks.
4. **Site Experience** — per-domain accumulated patterns. Read before navigating, write after solving problems.
5. **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:

```bash
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)

1. **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).
2. **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.
3. **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`.
4. **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.
5. **`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.
6. **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:

```bash
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):

```bash
agent-browser open --enable react-devtools http://localhost:3000
agent-browser react tree
agent-browser react inspect <fiberId>
agent-browser vitals                      # LCP / CLS / TTFB / FCP / INP + hydration
agent-browser pushstate /next-page        # SPA navigation hint
```

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:

```bash
# First time: log in interactively (headed), save state
agent-browser --headed --state ./.auth/github.json open https://github.com/login
# (user logs in, session lands in github.json)
agent-browser --state ./.auth/github.json state save ./.auth/github.json

# Subsequent runs: start already-logged-in (headless)
agent-browser --state ./.auth/github.json open https://github.com/notifications
agent-browser snapshot -i
```

Or `--session-name` for auto-save/restore:

```bash
AGENT_BROWSER_SESSION_NAME=github agent-browser open https://github.com
# Session is auto-saved + restored on subsequent runs with the same name.
```

For credentials, prefer agent-browser's auth vault over shell-history-leaking env vars:

```bash
agent-browser auth save my-app --url https://app.example.com/login \
  --username user@example.com --password-stdin
# (paste password, Ctrl+D)

agent-browser auth login my-app   # fills + clicks + waits
```

**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):

```bash
# 1. User starts their daily Chrome with debug port (one-time, after Cmd+Q)
/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 &

# 2. agent-browser auto-discovers and reuses
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:

```bash
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**:

```bash
# 1. Quit the daily Chrome (Default profile is locked while running)
# 2. Clone Default + Local State to a CDP-eligible path
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/"

# 3. Launch headless against the clone on a dedicated port
/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 &

# 4. Connect with serialization-aware lock
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:

```bash
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:

```markdown
---
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:**

```bash
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:

1. Tell the user: "Please log in to [site] in your Chrome."
2. Start login-watch in the background:

```bash
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.

3. 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
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:**

```bash
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
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

```bash
# Reuse if already running
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

```bash
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
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.

```bash
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)

```bash
# Check
curl -sf http://localhost:9377/health 2>/dev/null && echo "camofox running" || echo "needs install"

# Install + start (one time)
git clone https://github.com/jo-inc/camofox-browser /opt/camofox-browser
cd /opt/camofox-browser && npm install                  # NOT bun — bun skips postinstall, missing 300MB Camoufox binary
node scripts/postinstall.js                             # if you must use bun, run postinstall manually
CAMOFOX_CRASH_REPORT_ENABLED=false PORT=9377 node server.js &
```

### Usage shape (REST API, not CLI)

```bash
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')

# Snapshot (note: ?userId=agent param required)
curl -s "http://localhost:9377/tabs/$TAB_ID/snapshot?userId=agent" | jq -r '.snapshot'

# Click by ref
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). |

