Skip to main content
Glama
README.md
# AgentBrowser

**A real, visible-cursor browser for AI agents.** Real Chromium. Real humanlike physics. Real audit trail. Agents drive it the way a human would: drag, click, type, scroll - while every action is recorded, replayable, and verified.

**Works with every major LLM**: Anthropic Claude, OpenAI GPT, Google Gemini, Groq, Together, Fireworks, DeepInfra, Mistral, Cohere, xAI Grok, OpenRouter, Perplexity, Ollama (local), Ollama Cloud (hosted), vLLM, LM Studio, llama.cpp - or anything OpenAI-compatible. **Zero lock-in.**

```text
┌──────────────────────────────────────────────────────────────────┐
│                          Agent (your code)                       │
│   "submit the payment form"  ──┐                                 │
└────────────────────────────────┼─────────────────────────────────┘
                                 │  HTTP POST /sessions/:id/plan
                                 ▼
┌──────────────────────────────────────────────────────────────────┐
│                      AgentBrowser runtime                        │
│  Planner  ──▶  findAndClick (DOM ▶ vision-LLM)  ──▶  cursor      │
│     │              │                                  │          │
│     │              ▼                                  ▼          │
│     │     verifier (DOM diff)               Bezier trajectory    │
│     │              │                          + CDP raw events   │
│     ▼              ▼                                  │          │
│  action      action.completed event ──▶  recorder (JSONL)        │
│  memory                                              │           │
│  (skip LLM                                           ▼           │
│   on visit                                  WebSocket / SSE      │
│   #2+)                                       to operator UI      │
└──────────────────────────────────────────────────────────────────┘
                                 │
                                 ▼
                     Real visible cursor moves on real Chromium
```

---

## Why this exists

Every existing browser-automation tool was built for humans first and retrofitted for agents. They speak DOM operations. They produce 8000-token HTML dumps. They re-learn each site every run. They have no audit trail. They get blocked by every cookie banner.

**AgentBrowser inverts this.** The cursor is real and visible. All input goes through CDP raw mouse events. The API speaks in goals, not selectors. Failed actions auto-recover. Every action gets verified. The system learns each site permanently and shares knowledge across domains.

```text
Other tools                       │   AgentBrowser
─────────────────────────────────│──────────────────────────────────
8000 tokens of HTML              │   50 tokens of structured meaning
agent guesses #submit-btn-v2     │   { goal: "submit the form" }
no replay, no audit              │   JSONL trace, deterministic replay
re-learns every visit            │   action memory, 7x faster on visit 3
blocked by every cookie wall     │   auto-dismiss + force-removal
no captcha story                 │   2Captcha / hCaptcha / Turnstile
no fingerprint defenses          │   per-context WebGL/canvas/audio noise
single integration: library      │   library + MCP + HTTP + WS + SSE + replay
locked to one LLM vendor         │   17 providers, one env var swap (Claude/GPT/
                                 │   Gemini/Ollama/vLLM/Groq/Together/Fireworks/...)
```

---

## Five-minute demo

```bash
git clone https://github.com/AshtonVaughan/agentbrowser
cd agentbrowser
npm install && npx playwright install chromium && npm run build

# Pick ANY LLM provider:
ANTHROPIC_API_KEY=sk-ant-...   npm run http   # Claude (default)
# or
OPENAI_API_KEY=sk-...          npm run http   # GPT
# or
GOOGLE_API_KEY=...             npm run http   # Gemini
# or
GROQ_API_KEY=gsk_...           npm run http   # Groq (Llama on LPUs)
# or run fully local with Ollama:
ollama serve &
AGENTBROWSER_LLM_PROVIDER=ollama OLLAMA_MODEL=llama3.2-vision npm run http
# or use Ollama Cloud:
OLLAMA_CLOUD_API_KEY=... npm run http
```

In another terminal:

```bash
# Create a session
curl -X POST localhost:3100/api/v1/sessions
# → { "session_id": "abc123..." }

# Plan + execute a goal end-to-end
curl -X POST localhost:3100/api/v1/sessions/abc123/plan \
  -H 'content-type: application/json' \
  -d '{"goal":"go to news.ycombinator.com and click the top story"}'
# → { success: true, steps: [...], duration_ms: 4200 }

# Watch it live
open ui/operator/index.html
```

The agent's cursor moves humanly across the screen. Every cursor.move, click, page change streams to the operator UI in real time. Every action is recorded to `~/.agentbrowser/traces/` for replay.

---

## What you get

### Visible humanlike cursor
- SVG cursor sprite injected via context init script
- Bezier-curve trajectories with jitter, ease-in-out, optional overshoot
- All input via CDP `Input.dispatchMouseEvent` (not Playwright locators)
- Click ripple animation, fading 14-point cursor trail
- Per-trajectory deterministic seed for replay reproducibility

### Hybrid action layer
| Layer | What it does |
|---|---|
| `cursor.click(x, y)` | Direct viewport click via CDP |
| `cursor.clickBySelector(sel)` | Bbox-resolve, scroll-into-view, humanlike click. Stale-element auto-recovery via accessible-name lookup. |
| `cursor.clickByText(text)` | Text disambiguation across visually similar elements |
| `cursor.clickByRole(role, {name})` | ARIA-driven targeting |
| `findAndClick({goal, ...})` | DOM selector → text → role → vision-LLM, every step verified |
| `executor.executeAction(name)` | Action-memory fast path → fallback to find-and-click |
| `planner.planAndExecute(goal)` | LLM goal decomposition → multi-step run with retry budget |

### Vision pipeline
- `extractElementBoxes(page)` returns rich element catalog: id / role / tag / accessible name / value / bbox / selector / disabled
- `bboxScreenshot(sessionId)` returns a viewport PNG with numbered cyan boxes drawn on every interactive target + the element list
- `VisionLLM.decide(goal, screenshot, elements)` sends to Claude Sonnet, parses `{element_id, action, rationale}`
- `cursor.clickByBox(bbox)` clicks vision-derived coordinates with the visible cursor

### Self-healing
- **Action verifier** snapshots ElementBoxes before every action, diffs after settle, declares verified=true on URL change / added / removed / textChanged / moved elements
- **Stale-element recovery** in `cursor.clickBySelector` falls back to `getByText(originalText)` on selector failure
- **Modal interrupter** detects fixed/absolute high-z-index dialogs at viewport center, classifies as blocker (cookie/consent/subscribe keywords) or user-relevant (login dialogs), auto-dismisses blockers and retries
- **Selector library learns from verified outcomes only** - no entry in memory unless the click actually changed the page

### Site + action memory
- SQLite WAL for concurrent reads. Per-domain selector library + page-model cache.
- `ActionMemory` - SHA-1 page signature × goal hash → selector × success/fail counters. Visit #2 to a known page costs zero LLM calls.
- **Cross-domain transfer**: `recallByGoal(goal, excludeDomain)` returns winning selectors from OTHER domains for the same logical goal. The system learned "submit payment → button#pay-btn" on stripe.com; it tries the same selector on paddle.com as a hypothesis.
- `decay(unusedSinceMs)` halves stale entry counts so the library stays healthy as sites change.

### Recorder + replay
- Every action streamed as JSONL to `~/.agentbrowser/traces/<session-id>.jsonl`
- `ReplayEngine` reads a trace, dispatches events to a fresh session at configurable speed
- `compactTrace()` collapses 60-event cursor.move trajectories into 1, merges consecutive cursor.type events, drops micro-waits
- **Plan audit** captures screenshot + element list at every step boundary - compliance-grade replay primitive

### Skill library
- A "skill" is a recorded trace with named slot tokens (`$email`, `$password`)
- `SkillLibrary.parameterize(events, slots)` replaces literal values with tokens (longest-first to avoid partial-match bugs)
- Save / load / list / delete via JSON files at `~/.agentbrowser/skills/`
- **Portable .skill.json packages** with format magic + version + metadata (author, license, tags) - bundle a skill with your agent code or publish to a registry; users import + bind their own credentials

### CAPTCHA
- `TwoCaptchaSolver` for hCaptcha + reCAPTCHA v2 + Cloudflare Turnstile via 2Captcha API
- Page-side `DETECT_CAPTCHA_SCRIPT` finds sitekeys for all three types
- `solveCaptchaIfPresent(page, solver)` chain: detect → solve → inject token → fire change/input events → invoke data-callback
- Pluggable via `CaptchaSolver` interface (drop in AntiCaptcha, CapMonster, etc.)

### Anti-fingerprinting
- `applyFingerprintShield(context)` per-context init script
- Spoofs WebGL UNMASKED_VENDOR/RENDERER (5 GPU profiles), navigator.hardwareConcurrency, navigator.deviceMemory, AudioContext (1e-7 noise), Canvas toDataURL (~0.08% pixel jitter), navigator.plugins
- Per-context deterministic seed - fingerprint stays stable within a session, differs across sessions

### Multi-tab
- `engine.newTab(sessionId, url?)` opens a tab in the same context (shares cookies/auth)
- Each tab has its own `HumanCursor`. `switchTab` / `closeTab` / `listTabs`.
- HTTP: GET/POST/DELETE `/sessions/:id/tabs`, POST `/tabs/:tab/switch`

### Universal LLM provider support (zero lock-in)
- **17 providers wired** - swap any of them in by setting one env var
- `LLMProvider` interface (`complete()` + `completeWithImage()`); analyzer + vision-LLM + planner all use this abstraction, never SDKs
- Auto-detection at startup picks the right provider from env vars

| Provider | Set | Notes |
|---|---|---|
| Anthropic Claude | `ANTHROPIC_API_KEY` | default if nothing else set |
| OpenAI | `OPENAI_API_KEY` | gpt-4o-mini default |
| Google Gemini | `GOOGLE_API_KEY` or `GEMINI_API_KEY` | gemini-2.5-flash, vision native |
| Groq | `GROQ_API_KEY` | super-fast Llama/Mixtral on LPUs |
| Together AI | `TOGETHER_API_KEY` | open-source models |
| Fireworks | `FIREWORKS_API_KEY` | open-source models |
| DeepInfra | `DEEPINFRA_API_KEY` | open-source models |
| Mistral | `MISTRAL_API_KEY` | mistral-large-latest |
| Cohere | `COHERE_API_KEY` | command-r-plus via /compatibility |
| xAI Grok | `XAI_API_KEY` | grok-2 with vision |
| OpenRouter | `OPENROUTER_API_KEY` | 300+ models behind one API |
| Perplexity | `PERPLEXITY_API_KEY` | online-search models |
| Azure OpenAI | `AZURE_OPENAI_API_KEY` + `AZURE_OPENAI_BASE_URL` | enterprise tenant |
| **Ollama (local)** | `OLLAMA_BASE_URL` (default `localhost:11434`) | llama3.2, llama3.2-vision, qwen2.5vl, etc |
| **Ollama Cloud** | `OLLAMA_CLOUD_API_KEY` | hosted Ollama with turbo models |
| vLLM | `VLLM_BASE_URL` (default `localhost:8000`) | self-hosted production inference |
| LM Studio | `LMSTUDIO_BASE_URL` (default `localhost:1234`) | desktop GUI |
| llama.cpp server | `LLAMACPP_BASE_URL` (default `localhost:8080`) | tiny self-hosted |
| **Anything OpenAI-compatible** | `presets.openaiCompatible(url)` | drop in your URL |

```ts
// Pick a provider explicitly (any of the 17):
import { AgentBrowserHttpServer, presets } from 'agentbrowser';

const server = new AgentBrowserHttpServer({
  llm_provider: presets.ollamaCloud(),   // or .openai() or .groq() etc
  // ...
});
```

Or just set `AGENTBROWSER_LLM_PROVIDER=ollama` and the server auto-wires. Override the model with `<PROVIDER>_MODEL=<model-id>`.

### Chrome extension (drive YOUR Chrome with YOUR cookies)
- Install `extensions/chrome/` in dev mode (`chrome://extensions` → Load unpacked)
- Click the AgentBrowser icon, paste server URL + API key, click Connect
- Now an agent calling `POST /api/v1/agents/<your-id>/cmd` drives YOUR Chrome with YOUR cookies and login state
- Manifest v3 + `chrome.debugger` for real CDP mouse events + `chrome.scripting` for vision/extract
- See `extensions/chrome/README.md` for the full security model

### Operator + recorder + memory + skills UIs
- `ui/operator/index.html` - live screenshot + cursor trail overlay + event timeline + quick actions panel
- `ui/recorder/index.html` - real-time WebSocket event stream + multi-lane timeline canvas + replay scrubber + JSONL export
- `ui/memory/index.html` - paginated action memory browser per domain + decay control + JSON export
- `docs/pricing.html` - 4-tier pricing page wired to `/api/v1/billing/checkout` for self-serve Stripe payments
- `docs/skills.html` - skills marketplace landing with 8 curated skills (login-stripe, login-google, amazon-add-to-cart, github-create-issue, etc.)
- All are single-file vanilla HTML/CSS/JS. No build step.

### Transcription helpers (agents that "watch" videos)
- `findCaptionTracks(page)` detects HTML5 `<track>` + YouTube `playerCaptionsTracklistRenderer` + custom player markup
- `parseVTT(text)` / `parseJSON3(json)` convert standard caption formats to typed `TranscriptSegment[]`
- `transcribeFromCaptions(page)` one-shot: detect → fetch → parse
- `transcriptToText(segments)` concatenate for LLM consumption

### Production runtime
- HTTP REST API + WebSocket + SSE on Fastify with bearer-token auth + per-key rate limiting
- 30+ endpoints, OpenAPI 3.1 spec at `/api/v1/openapi.json`
- Stripe billing wired (Checkout + webhook + signature verification + license issuance)
- Prometheus `/metrics` + readiness probe + dashboard summary endpoint
- 4-tier license scaffold (free / pro / team / enterprise) with feature gates and quota tracking
- Multi-stage Dockerfile, docker-compose with persistent volume + 1GB shm_size for Chromium
- Python + TypeScript SDK clients

---

## Architecture

```text
┌─────────────────────────────────────────────────────────────────┐
│  Operator UI  (browser-based dashboard)                         │
│   - live screenshot stream         - cursor trail viz           │
│   - action log + reasoning         - manual takeover            │
│  Recorder UI                                                    │
│   - timeline scrubber              - replay export              │
└─────────────────────────────────────────────────────────────────┘
                          ▲
                          │  WebSocket events + SSE frames + REST
┌─────────────────────────┴───────────────────────────────────────┐
│                   AgentBrowser HTTP Control Plane               │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐ │
│  │  REST + WS + SSE │  │  Bearer auth +   │  │  License +     │ │
│  │  Fastify         │  │  per-key rate    │  │  quota system  │ │
│  └──────────────────┘  └──────────────────┘  └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                          ▲
                          │
┌─────────────────────────┴───────────────────────────────────────┐
│                    AgentBrowser Core Runtime                    │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐ │
│  │  Planner         │  │  findAndClick    │  │  Recorder +    │ │
│  │  (goal → steps)  │  │  hybrid action   │  │  Replay engine │ │
│  └──────────────────┘  └──────────────────┘  └────────────────┘ │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐ │
│  │  Verifier        │  │  Modal           │  │  Action memory │ │
│  │  (diff snapshots)│  │  interrupter     │  │  (skip LLM)    │ │
│  └──────────────────┘  └──────────────────┘  └────────────────┘ │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐ │
│  │  Vision pipeline │  │  HumanCursor     │  │  Site memory   │ │
│  │  bbox + annotate │  │  Bezier + CDP    │  │  WAL SQLite    │ │
│  │  + VisionLLM     │  │  trail + ripple  │  │  + selectors   │ │
│  └──────────────────┘  └──────────────────┘  └────────────────┘ │
│  ┌──────────────────┐  ┌──────────────────┐  ┌────────────────┐ │
│  │  Anti-fingerprint│  │  Captcha solver  │  │  LLM provider  │ │
│  │  (canvas/WebGL)  │  │  + auto-inject   │  │  (Anthropic /  │ │
│  │                  │  │                  │  │   OpenAI / etc)│ │
│  └──────────────────┘  └──────────────────┘  └────────────────┘ │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Browser Engine - Playwright + stealth + multi-tab     │    │
│  │  ↓                                                      │    │
│  │  Chromium (the real browser, with the visible cursor)  │    │
│  └─────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘
```

---

## Quick start - 4 ways

### As a TypeScript library

```ts
import { AgentBrowser } from 'agentbrowser';

const browser = new AgentBrowser({
  anthropic_api_key: process.env.ANTHROPIC_API_KEY,
  headless: false,    // watch the cursor move
  stealth: true,
});
await browser.launch();

const state = await browser.navigate('https://news.ycombinator.com');
console.log(state.page_type);          // 'listing'
console.log(state.available_actions);  // [{ name: 'navigate_to_new', ... }, ...]

await browser.action('navigate_to_new');

const data = await browser.extract({
  top_story: 'title of the top story',
  points: 'upvote count',
  author: 'submitter username',
});

await browser.close();
```

### Via HTTP

```bash
curl -X POST http://localhost:3100/api/v1/sessions
curl -X POST http://localhost:3100/api/v1/sessions/$ID/navigate -d '{"url":"https://example.com"}'
curl -X POST http://localhost:3100/api/v1/sessions/$ID/find_and_click -d '{"goal":"submit the form"}'
curl http://localhost:3100/api/v1/sessions/$ID/screenshot/bbox
```

### Python SDK

```python
from agentbrowser import AgentBrowserClient

client = AgentBrowserClient("http://localhost:3100", api_key="...")
with client.create_session() as s:
    s.navigate("https://example.com")
    s.click(selector="a")
    png, elements = s.annotated_screenshot()
```

### MCP server (Claude Code, etc.)

Add to `~/.claude.json`:
```json
{
  "mcpServers": {
    "agentbrowser": {
      "command": "node",
      "args": ["/path/to/agentbrowser/dist/server/mcp.js"],
      "env": { "ANTHROPIC_API_KEY": "sk-..." }
    }
  }
}
```

---

## Repository structure

```text
src/
├── engine/             Browser lifecycle, sessions, tabs
│   ├── browser.ts      Playwright wrapper, cursor wiring, popup handling
│   └── tabs.ts         Multi-tab manager
├── input/              Cursor + physics + fingerprint shield
│   ├── cursor.ts       HumanCursor: SVG overlay, CDP input, click/drag/type
│   ├── trajectory.ts   Bezier path generator with jitter + overshoot
│   └── fingerprint.ts  WebGL/canvas/audio anti-fingerprint patches
├── vision/             Vision pipeline
│   ├── bbox.ts         Element catalog extraction
│   ├── annotate.ts     Numbered-box screenshot annotator
│   ├── diff.ts         Snapshot diff (added/removed/moved/textChanged)
│   └── llm.ts          Claude vision integration
├── runtime/            Action execution + autonomy
│   ├── executor.ts     Action runner with action-memory hot path
│   ├── find.ts         Hybrid DOM-then-vision findAndClick
│   ├── verifier.ts     Action verification via snapshot diff
│   ├── modal-interrupter.ts  Cookie/consent/popup detection
│   ├── planner.ts      LLM goal decomposition + multi-step execution
│   ├── plan-audit.ts   Per-step screenshot + element capture
│   ├── recorder.ts     JSONL action stream
│   ├── replay.ts       Deterministic trace replay
│   ├── compactor.ts    Trace compaction (collapse cursor.move runs)
│   ├── events.ts       In-process event broker (pub/sub)
│   ├── captcha.ts      2Captcha API integration
│   └── captcha-solver.ts  Detect → solve → inject pipeline
├── memory/             Persistent storage
│   ├── store.ts        Site memory (page model cache + selector library)
│   └── action-memory.ts  Per-(page,goal) selector cache + cross-domain transfer
├── llm/                Provider abstraction
│   ├── provider.ts     LLMProvider interface
│   ├── anthropic.ts    Anthropic SDK wrapper
│   ├── openai.ts       OpenAI-compatible (works with vLLM/Ollama too)
│   └── index.ts        autoDetectProvider
├── semantic/           Page analysis
│   └── analyzer.ts     Page-to-SemanticPageModel via LLM
├── skills/             Skill library
│   ├── skills.ts       Parameterize/save/load/run
│   └── package.ts      .skill.json export/import format
├── server/             Production server surfaces
│   ├── http.ts         Fastify + REST + WS + SSE + auth + rate limit
│   ├── openapi.ts      OpenAPI 3.1 spec
│   ├── license.ts      Tier system + quota tracking
│   └── mcp.ts          MCP server for Claude Code et al.
├── bin/
│   └── http.ts         HTTP server entry point
├── util/
│   └── coords.ts       Viewport ↔ document ↔ screenshot coord conversions
├── types.ts            Shared types (SemanticPageModel, ActionDefinition, ...)
└── index.ts            Public API barrel

tests/                  Vitest suite (119 tests, 21 files)
clients/
├── python/             Python SDK (zero deps, stdlib urllib)
└── typescript/         TypeScript SDK (browser + Node compatible)
ui/
├── operator/           Live dashboard (single HTML file)
└── recorder/           Real-time trace timeline (single HTML file)
docs/                   Public docs site (single HTML file)
examples/               Working agent demos
.agentbrowser-meta/     Internal build planning + iteration log
Dockerfile              Multi-stage slim image (~700MB with Chromium)
docker-compose.yml      Local stack with persistent volume
```

---

## API surface

22 HTTP endpoints, all bearer-authenticated when API keys are configured. Full OpenAPI 3.1 at `/api/v1/openapi.json`.

| Method | Path | Purpose |
|---|---|---|
| GET | `/health` | Liveness (no auth) |
| POST | `/api/v1/sessions` | Create session |
| DELETE | `/api/v1/sessions/:id` | Destroy session |
| POST | `/api/v1/sessions/:id/navigate` | Go to URL |
| POST | `/api/v1/sessions/:id/cursor/{move,click,drag,scroll,type,press}` | Cursor primitives |
| POST | `/api/v1/sessions/:id/find_and_click` | Hybrid DOM → vision-LLM action |
| POST | `/api/v1/sessions/:id/plan` | LLM goal decomposition + multi-step execute |
| POST | `/api/v1/sessions/:id/extract` | Schema-driven LLM extraction |
| POST | `/api/v1/sessions/:id/fill` | Fill a named form |
| GET | `/api/v1/sessions/:id/screenshot` | Viewport PNG |
| GET | `/api/v1/sessions/:id/screenshot/bbox` | Annotated PNG + element list |
| GET | `/api/v1/sessions/:id/screenshot/stream` | SSE PNG frames |
| WS | `/api/v1/sessions/:id/screenshot/ws` | Binary PNG frames |
| GET | `/api/v1/sessions/:id/state` | Current SemanticPageModel |
| GET | `/api/v1/sessions/:id/elements` | ElementBox[] |
| GET/POST/DELETE | `/api/v1/sessions/:id/tabs` | Multi-tab control |
| POST | `/api/v1/sessions/:id/solve_captcha` | Detect + solve + inject |
| WS | `/api/v1/sessions/:id/events` | Live event stream |
| POST | `/api/v1/sessions/:id/back` | History back |
| POST | `/api/v1/sessions/:id/forward` | History forward |
| POST | `/api/v1/sessions/:id/reload` | Reload current page |
| POST | `/api/v1/sessions/:id/dialog` | Accept/dismiss next native alert/confirm/prompt |
| GET/POST/DELETE | `/api/v1/sessions/:id/cookies` | Read/write/clear browser cookies |
| GET/POST/DELETE | `/api/v1/sessions/:id/storage` | Read/write/clear localStorage or sessionStorage (?kind=local\|session) |
| POST | `/api/v1/sessions/:id/upload` | Set files on a file input (`{selector, paths}`) |
| POST | `/api/v1/sessions/:id/print` | Render current page to PDF (returns application/pdf) |
| POST | `/api/v1/sessions/:id/route/block` | Block all requests matching a glob pattern |
| POST | `/api/v1/sessions/:id/route/headers` | Inject headers into requests matching a pattern |
| POST | `/api/v1/sessions/:id/route/mock` | Mock response body for matching requests |
| DELETE | `/api/v1/sessions/:id/route` | Remove all route handlers (passthrough) |
| POST | `/api/v1/sessions/:id/geolocation` | Override reported coords (or null to clear) |
| POST | `/api/v1/sessions/:id/viewport` | Resize viewport mid-session |
| POST | `/api/v1/sessions/:id/headers` | Set extra HTTP headers for all requests |
| POST | `/api/v1/sessions/:id/har/start` | Start HAR network capture |
| GET | `/api/v1/sessions/:id/har/peek` | Get current entries without stopping |
| POST | `/api/v1/sessions/:id/har/stop` | Stop and return all captured entries |
| POST | `/api/v1/sessions/:id/console/start\|peek\|stop` | Capture console messages + uncaught errors |
| POST | `/api/v1/sessions/:id/throttle/network` | Throttle network (downloadThroughput/uploadThroughput/latencyMs/offline) |
| POST | `/api/v1/sessions/:id/throttle/cpu` | CPU slowdown multiplier (1=native, 4=4x slower) |
| POST | `/api/v1/sessions/:id/locale` | Override navigator.language + Accept-Language |
| POST | `/api/v1/sessions/:id/timezone` | Override page timezone (e.g. "Asia/Tokyo") |
| POST/DELETE | `/api/v1/sessions/:id/permissions` | Grant/clear browser permissions (clipboard, notifications, etc.) |
| POST | `/api/v1/sessions/:id/record/start` | Begin in-memory recording for skill creation |
| GET | `/api/v1/sessions/:id/record/peek` | Live event count while recording |
| POST | `/api/v1/sessions/:id/record/stop` | Stop and (optionally) save events as a skill (`{name, slots, description}`) |
| POST | `/api/v1/sessions/:id/har/replay` | Re-execute HAR entries and compare statuses |
| GET | `/api/v1/sessions/:id/service-workers` | List active service workers |
| GET/POST | `/api/v1/sessions/:id/snapshot` | Export full session state (cookies + storage + IDB); POST `/snapshot/restore` to import |
| POST/GET | `/api/v1/sessions/:id/downloads/start\|stop` | Auto-capture all downloads to a session-tagged dir |
| GET/POST | `/api/v1/sessions/:id/clipboard` | Read/write the page's clipboard via navigator.clipboard |
| POST | `/api/v1/sessions/:id/wait/selector\|text\|network-idle\|function` | Smart waiters with timeouts |
| GET | `/api/v1/sessions/:id/markdown` | Extract clean RAG-friendly markdown from current page |
| GET | `/api/v1/skills/:name/versions` | List archived versions of a skill |
| POST | `/api/v1/skills/:name/rollback` | Restore a previous version (`{version: N}`) |
| GET | `/api/v1/sessions/:id/activity` | Idle time in ms for a session |
| POST | `/api/v1/sessions/:id/touch` | Reset idle counter |
| GET | `/api/v1/sessions/expired` | List sessions past idle timeout |
| POST | `/api/v1/snapshot/diff` | Diff two snapshots (`{a, b}`) returns added/removed/changed |
| POST | `/api/v1/sessions/:id/click_by_description` | Vision-only click ("the blue Submit button") |
| POST | `/api/v1/pool/warmup` | Pre-create N empty contexts for sub-100ms session creation |
| GET | `/api/v1/pool/status` | Warm pool size + oldest entry age |
| POST | `/api/v1/pool/drain` | Close all warm contexts |
| POST | `/api/v1/sessions/:id/copilot/install` | Inject highlight overlay for "AI is here" hints |
| POST | `/api/v1/sessions/:id/copilot/highlight` | Highlight a bbox with optional label |
| POST | `/api/v1/pool/auto_refill/start\|stop` | Background job that keeps pool topped up |
| POST | `/api/v1/action_memory/predict` | Predict next action from memory (`{url, elements, goal?}`) |
| GET | `/api/v1/skills/marketplace` | Static skill catalog (slug, tags, quality, runs, success_rate) for GitHub Pages hosting |
| GET | `/api/v1/vision/cache/stats` | Vision LLM cache hits/misses + persistent_size if SQLite-backed |
| POST | `/api/v1/vision/cache/clear` | Empty in-memory + on-disk vision cache |
| GET | `/api/v1/sessions/:id/har/export` | Captured HAR exported as standard HAR 1.2 (Chrome DevTools-importable) |
| POST | `/api/v1/vision/cache/prune` | Evict oldest entries beyond `cache_max_disk_entries` + VACUUM |
| POST | `/api/v1/traces/diff` | Diff two trace event arrays (regression testing) |
| POST | `/api/v1/sessions/:id/shortcut` | Execute named keyboard shortcut (newTab/copy/find/etc) |
| GET | `/api/v1/shortcuts` | List available named shortcuts |
| GET | `/api/v1/dump` | Single-call snapshot of all server state |
| POST/GET/DELETE | `/api/v1/plans[/:slug]` | Save / list / load / delete reusable Plan blueprints |
| POST | `/api/v1/sessions/:id/shortcut/chain` | Execute multiple named shortcuts in sequence |
| GET | `/api/v1/metrics/summary` | Per-histogram p50/p95/p99/mean/count percentiles |
| GET/POST/DELETE | `/api/v1/rate_limits` | Per-domain RPS limit (token-bucket throttle on navigate) |
| POST | `/api/v1/sessions/:id/plans/:slug/run` | Load + execute a saved Plan blueprint |
| POST | `/api/v1/skills/:name/to_plan` | Convert a recorded skill into a Plan (`{save?, slug?}`) |
| GET | `/api/v1/action_memory/search?pattern=...` | Search action memory by selector substring |
| POST | `/api/v1/parallel/extract` | Spawn N parallel sessions, extract markdown from each URL |
| POST | `/api/v1/plans/compose` | Chain N saved plans into a super-plan |
| POST | `/api/v1/sessions/:id/form/autofill` | Auto-fill form inputs by name/label/placeholder match |
| POST | `/api/v1/skills/diff` | Compare two skills' events (`{a, b}`) |
| GET/POST/DELETE | `/api/v1/webhooks[/:id]` | Subscribe to events, POST to external URL (HMAC-signed when `secret` set) |
| POST | `/api/v1/webhooks/:id/test` | Fire a test delivery to verify connectivity |
| POST | `/api/v1/batch/csv` | Process a CSV: navigate per row, extract markdown, return enriched results |
| GET | `/api/v1/webhooks/queue` | Pending webhook retry count |
| POST/GET | `/api/v1/skills/:name/cost` | Record / read per-skill LLM cost (token usage × rates) |
| GET | `/api/v1/skills/cost/leaderboard` | Top-N most expensive skills by total cost |
| POST | `/api/v1/skills/validate` | Validate a skill's structure before save (`{skill}`) |
| POST | `/api/v1/skills/:name/auto_tag` | LLM-suggest 3-5 tags from skill description + events |
| POST | `/api/v1/traces/render` | Render TraceEvent[] as a self-contained HTML timeline page |
| POST | `/api/v1/skills/:name/auto_describe` | LLM-write a one-line description from skill events |
| GET | `/api/v1/skills/:name/suggested_selectors` | Cross-skill: selectors that worked for the same goal on other domains |
| GET | `/api/v1/action_memory/export.csv` | Download action memory as CSV (`?domain=&limit=`) |
| POST | `/api/v1/action_memory/query` | Composite filter+sort query (domain, goal substr, selector substr, min_success_rate, min_runs, sort_by) |
| GET | `/api/v1/sessions/:id/a11y` | Accessibility audit (missing alt/label, heading skips, empty links, missing lang) |
| GET | `/api/v1/skills/:name/bundle` | Export skill as .agbpkg (skill + plan + stats + readme) |
| POST | `/api/v1/skills/bundle/import` | Import an .agbpkg bundle |
| GET | `/api/v1/analytics/domain/:domain` | Per-domain action memory analytics + top selectors |
| GET/POST/DELETE | `/api/v1/schedules[/:id]` | Recurring skill execution (`{skill_name, spec: "every 5m", bindings}`) |
| POST | `/api/v1/skills/recommend` | Recommend skills matching a goal text (`{goal, limit?, min_score?}`) |
| POST | `/api/v1/plan_templates/:name/to_skill` | Convert a built-in plan template into a runnable skill |
| POST/GET | `/api/v1/sessions/:id/network/start\|peek\|stop` | Per-session network bytes tracking |
| GET | `/api/v1/sessions/:id/contrast` | WCAG color contrast audit |
| GET | `/api/v1/fingerprints` | List browser fingerprint presets (mac-chrome, iphone-15-pro, tokyo-iphone, etc.) |
| POST | `/api/v1/sessions/:id/fingerprint` | Apply a preset (`{preset_id}`) - viewport + UA + locale + timezone |
| GET | `/api/v1/sessions/:id/memory` | Per-session heap/rss/external delta from session creation |
| POST | `/api/v1/sessions/:id/memory/snapshot` | Re-baseline the session memory snapshot |
| GET | `/api/v1/health/full` | Detailed system health: process, engine, scheduler, billing, auth posture |
| GET | `/api/v1/sessions/:id/cpu` | Per-session CPU delta (user/system/wall ms + cpu_percent) |
| POST | `/api/v1/sessions/:id/cpu/snapshot` | Re-baseline the session CPU snapshot |
| POST | `/api/v1/action_memory/distill` | Top selectors across distinct domains (ship as starter packs) |
| POST | `/api/v1/action_memory/import_patterns` | Seed memory with distilled patterns from another deployment |
| POST | `/api/v1/sessions/:id/skills/auto_run` | Match goal to best skill and execute (no LLM round-trip) |
| GET/POST/DELETE | `/api/v1/skills/ab[/:key]` | Register weighted A/B routes between skill versions |
| POST | `/api/v1/sessions/:id/skills/ab/:key/run` | Run an A/B-routed skill (weighted variant pick) |
| WS | `/api/v1/skills/events/ws` | Live skill outcome firehose (`?skill=<name>` filter) |
| GET/DELETE | `/api/v1/skills/ab/:key/stats` | Aggregated success/failure stats per variant |
| POST | `/api/v1/skills/ab/:key/promote` | Auto-promote winning variant (z-test gated) |
| GET/DELETE | `/api/v1/skills/percentiles[?skill=]` | Per-skill p50/p95/p99 latency histograms |
| GET/POST/DELETE | `/api/v1/skills` | Skill library CRUD |
| GET | `/api/v1/skills/:name/export` | Download .skill.json |
| POST | `/api/v1/skills/import` | Import .skill.json |
| POST | `/api/v1/sessions/:id/skills/:name/run` | Replay skill with bindings |
| GET | `/api/v1/action_memory/stats` | Memory stats |
| GET | `/api/v1/action_memory/by_domain/:domain` | What does the system know about :domain |
| GET | `/api/v1/action_memory/selectors/:domain` | Top selectors per domain with success/fail stats |
| POST | `/api/v1/action_memory/recall_by_goal` | Cross-domain selector hypotheses |
| POST | `/api/v1/action_memory/similar` | TF-IDF / embedding action memory search |
| POST | `/api/v1/action_memory/similar_embedded` | Force embedding-only search |
| POST | `/api/v1/action_memory/decay` | Halve stale entry counts |
| POST | `/api/v1/page_similarity` | Score similarity between two page snapshots |
| POST | `/api/v1/skills/discover` | Suggest skills relevant for the current page |
| POST | `/api/v1/skills/compose` | Run multiple skills in sequence |
| GET | `/api/v1/skills/stats` | Per-skill success/fail history (filter by `?skill=` for per-domain rows) |
| GET | `/api/v1/skills/leaderboard` | Top-N performing skills by success_count |
| GET | `/api/v1/skills/hot` | High-confidence skills (success_rate ≥ 0.9, ≥ 10 runs by default) |
| POST | `/api/v1/skills/prune` | Remove skills below a success threshold (dry-run by default) |
| DELETE | `/api/v1/skills/:name` | Delete a skill by name |
| GET | `/api/v1/plan_templates` | List built-in plan templates |
| GET | `/api/v1/diagnose` | Remote health diagnostic (mirrors `agb-doctor`) |
| POST | `/api/v1/traces/compact` | Compact a trace file |
| GET | `/api/v1/openapi.json` | Full OpenAPI 3.1 spec |
| GET | `/api/v1/dashboard` | Aggregated stats: site memory + action memory + skills + provider |
| GET | `/api/v1/billing/pricing` | Tier list with prices + limits (powers pricing page) |
| POST | `/api/v1/billing/checkout` | Stripe Checkout Session for `{tier, email}` |
| POST | `/api/v1/billing/webhook` | Stripe webhook receiver (signature-verified) |
| GET | `/metrics` | Prometheus scrape (no auth) |
| GET | `/ready` | Readiness probe (503 until engine launched) |
| GET | `/api/v1/agents` | List currently-connected Chrome extension agents |
| GET | `/api/v1/agents/:id/poll` | Long-poll for next command (used by Chrome extension) |
| POST | `/api/v1/agents/:id/cmd` | Send a command to a connected Chrome extension |
| POST | `/api/v1/agents/:id/result` | Extension posts back command results |
| DELETE | `/api/v1/agents/:id` | Drop agent state |

---

## Production deployment

### Docker

```bash
docker compose up -d
# AgentBrowser on http://localhost:3100
# Memory + traces persist in named volume
```

### Auth + rate limit

```bash
AGENTBROWSER_API_KEYS=key1,key2 \
ANTHROPIC_API_KEY=sk-ant-... \
node dist/bin/http.js
```

Rate limit defaults to 600 req/min/key. Override with `AGENTBROWSER_RATE_LIMIT_PER_MINUTE`.

### Observability

- Every action emits a typed `SessionEvent` to the in-process broker
- WebSocket clients subscribe at `/api/v1/sessions/:id/events` (with 200-event replay buffer for reconnect)
- JSONL traces at `~/.agentbrowser/traces/` are the audit log
- `compactTrace()` keeps long traces small without losing fidelity

---

## Tested at

| Capability | Status |
|---|---|
| Type check (`tsc`) | clean |
| Test suite | 119 / 119 passing across 21 files |
| Build (`npm run build`) | clean |
| Real Chromium navigation | verified |
| Bot detection bypass | passes Cloudflare interstitial, OneTrust, Cookiebot, Funding Choices, Reddit GDPR, Stack Overflow signup wall |
| Cursor click → DOM event | verified end-to-end |
| Action verifier | verified on URL change + element add/remove/text change |
| Vision pipeline | annotated PNG verified by magic-byte + element list |
| HTTP API | 13 integration tests against full Fastify stack |
| Replay determinism | trajectory generator deterministic per seed |

---

## License

MIT. Commercial use encouraged.

For acquisition or partnership inquiries: [ashtonluca@gmail.com](mailto:ashtonluca@gmail.com).