The browserfor your agent.

Install BetterWright
npm install -g betterwright
Works with
Claude CodeagentCodexagentCursoragentPiagentAny MCP clientagentKernelcloud browserBrowserbasecloud browserSteelcloud browserAnchorcloud browserHyperbrowsercloud browserBrowser Usecloud browserBrowserlesscloud browserBright Datacloud browserOxylabscloud browser

The loop

Snapshots

One snippet in.One snapshot back.

Every call is a string of Playwright code run in a sandboxed worker. What comes back is a compressed accessibility tree with a ref on every element the model can act on, sized for a context window.

agent.tsTypeScript
import { withBrowser } from "betterwright/sdk"

const proof = await withBrowser(async (bw) => {
  await bw.run("await page.goto('https://shop.example/cart')")

  // what the model reads: the tree on the right
  const tree = await bw.run("return snapshot({ interactive: true })")

  // act on a ref with a human-shaped click
  await bw.run("await human.click(page.locator('aria-ref=e7'))")

  // only what changed, not the page again
  await bw.run("return snapshot({ diff: true })")

  return bw.run("return screenshot({ kind: 'proof', name: 'checkout' })")
})
raw accessibility dumpevery turn
compressed snapshot55–85% fewer tokens

What the agent sees

Static text is dropped. Every actionable element keeps a ref the model targets directly, so it never re-derives a selector.

Diff mode

Only what changed

After an action, the next snapshot returns the lines added and removed, or says nothing did.

Verified input

Honest failures

Typing reads the field back and fails the step when the text is not there. An editor that swallowed keystrokes is a miss, not a success.

Proof

Evidence the agent cites

A proof screenshot is an artifact, not a wall of pixels in the context. The result points at it.

Architecture

Your agent decides.BetterWright drives.The browser stays open.

Three layers, one clean seam between each. The agent never touches the session; the session never touches the task. Swap the bottom layer with one configure command.

  1. 01

    Decides

    Your agent

    Owns the task. Reads a snapshot, picks the next step.

    skill · MCP · SDK · exec

  2. 02

    Drives

    BetterWright

    Owns the session. Network policy, credential vault, snapshots, live view.

    one persistent session · policy floor · vault · diffs

  3. 03

    Stays open

    The browser

    Managed BetterChromium by default: downloaded once, coherent identity, GPU when the host has one.

    or any of nine cloud browsers

Cloud browsers

Browser providers

Full lifecycle start · list · stop

  • Kernel
  • Browserbase
  • Steel
  • Anchor
  • Hyperbrowser
  • Browser Use

Connect bring your own endpoint

  • Browserless
  • Bright Data
  • Oxylabs

Benchmarks

Cheaper observations.Fewer turns. Same answer.

Read the benchmark reports

55–85%

fewer observation tokens per turn

compressed snapshot vs a raw accessibility dump

3×

fewer tokens for a task end to end

login to checkout, same model, same effort, snapshots vs page dumps

4×

faster than standard Playwright

same tasks, same model, BetterWright snapshots vs raw page dumps

The head-to-head ran the same model at the same effort across 15 tasks and 3 rounds, BetterWright's scaffold against another. Token savings depend on the page; the range is what a 12-turn task measured. Method and raw runs are in the repo.

Entry points

Two ways to drive it.One session underneath.

The same persistent sessions, vault, network policy, and snapshots sit under every entry point, so you can start with one and mix in the others later.

Skill or MCP

Your agent, step by step

Claude Code, Codex, Cursor, Pi, or any MCP client mans the browser as one part of a bigger job. No server, no glue code.

TypeScript SDK
title.ts
import { withBrowser } from "betterwright/sdk"

const title = await withBrowser(async (bw) => {
  await bw.run("await page.goto('https://example.com')")
  const r = await bw.run<string>("return page.title()")
  return r.ok ? r.result : null
})

Your own code, one import

withBrowser builds the client, runs your function, and closes it on the way out and on a thrown error. Results are values; check ok and decide.

Built-in agent
terminal
$ betterwright exec "find the top HN story and give me its title" --model gpt-5.6-sol
{
  "ok": true, "steps": 4, "toolCalls": 5,
  "answer": "…",
  "usage": { "inputTokens": 6880, "outputTokens": 1330 },
  "proof": "…/proof-hn.png"
}
# the 30 snapshots in between never reach your main agent

Hand over the whole task

One shell command in, one JSON object out. The browsing transcript stays inside the sub-agent, so a 30-turn checkout costs your main agent one tool call.

Live view and handoff

Take the wheel when it matters

Watch the run in a browser tab, chat between turns, and take over for MFA or a consequential click. Hit Done and the agent resumes.

Install it. Point your agent at it. Read the answer.

Give your agent a browser it can fit.