Frazer Browsers
Frazer Browsers gives agents browsers and desktops. Agents test and debug through the same screens people use. A browser here is a Chromium browser that one agent session drives, through a Rust executor that gives every harness the same compact view of the page, the same actions and a receipt for every step. It runs in the agent’s own cell, next to a preview, or on a devbox or laptop, and it records everything it does. Agents use it to check a change through the UI against the real backend, to reproduce an issue a user hit, and to hand a page to a person when only a person may act.
What an agent sees
Section titled “What an agent sees”| View | What it holds |
|---|---|
| interactive (the default) | One line per actionable element: its role, accessible name, state and ref, with landmarks and headings as context |
| content | The visible text in reading order, without refs |
| full | The whole accessibility tree with refs, on request |
| delta | What changed since the last snapshot of the same document, after every action |
A snapshot covers the viewport by default, or the full page, or one landmark or ref. Frames appear inline with their own refs, including cross-origin frames, and open shadow roots are included. Page text is built from what the page renders, never from raw source, so hidden text never reaches the model, and it is wrapped in content boundaries with a random nonce and its origin. A snapshot over its size limit returns its first part and a handle to page through. Screenshots are for layout, images and canvases, with zoom for small text; they carry no refs, so they are never the way to find a control.
--- PAGE nonce=7f3a origin=https://pv-4718.preview.frazer.run ---main "Checkout" textbox "Email" value="buyer@acme.example" @e4 textbox "Gift card code" value="" @e11 button "Apply" @e12 region "Payment card" (cross-origin frame: the payments fake) textbox "Card number" @f1e2 button "Pay $42.00" @e19--- END nonce=7f3a ---A ref names an element until the element is replaced or its document navigates; it is never reused for another element. Every action returns a receipt that the executor writes into the Fir session: the step, the action and its resolved target, the outcome (done, failed with the reason, or outcome_unknown when the executor cannot tell whether a change ran, which is never retried blindly), the effects (navigation, popups, dialogs, downloads, requests and their statuses, new console errors, the delta), the rule that allowed it, the time and the browser seconds it used.
Errors name the next call:
| Error | Means | Next |
|---|---|---|
STALE_REF |
The element was replaced or the page navigated | snapshot |
AMBIGUOUS_TARGET |
A locator matched several elements | snapshot --scope <landmark> |
NOT_ACTIONABLE |
Covered, disabled or outside the viewport | The receipt names what covers it |
ORIGIN_NOT_ALLOWED |
The origin is outside the run’s origin set | A person’s approval to widen it |
POLICY_ASK |
The action needs an approval | Wait for the approval card |
OUTCOME_UNKNOWN |
A change may have run | Observe (snapshot, net) before any retry |
NOT_SETTLED |
The page did not settle in time | wait --settled, or snapshot it as it is |
AUTH_REQUIRED |
The app asks for a login | login --persona <p>, or a hand-off for a real sign-in |
CAPTCHA_PRESENT |
A challenge blocks the page | Hand off: Frazer never solves one |
FUNDS_EXHAUSTED |
The budget or the balance cannot cover the run (“Not run: no funds”) | The balance call, then the top-up person task |
INJECTION_SUSPECTED |
Page content looked like instructions | The run’s policy: go on with the origin read-only, or hand off |
STEP_LIMIT_REACHED, BUDGET_REACHED |
The run stopped at a limit you set (exit 14); the result so far is kept | Raise the limit or continue from haltedAt |
STALE_CAPTURE |
A pixel action names a screenshot the screen no longer matches | Take a new screenshot |
METHOD_NOT_ALLOWED |
A protocol command the gate refuses | The receipt names the rule |
Waits and checks. wait takes a URL pattern, text, a ref’s state, a function or settled. A check records an assertion: a UI check (visible text, a value, a role’s state) or a state check (an API response, a row read through the preview’s data API, a page tool’s result). A test passes on state checks, never only on how the page looked, and each fact a plan item’s acceptance criteria expect is checked on its own. An agent’s “verified in the UI” counts only when its receipts show the path: the page opened, the action taken, the state read.
Page tools and the app map. Tools a page registers through WebMCP are offered as typed tools, and Frazer’s own console registers its actions that way. Agents set up and check through APIs and page tools, and drive the UI for what the test is about. A repository may keep an app map (.frazer/app-map.yaml: routes, landmarks, key flows, personas, readiness signals and page tools), written by an exploring agent and reviewed like code.
Four ways to act, one executor
Section titled “Four ways to act, one executor”- Scripts. An agent sends a short JavaScript program in a familiar end-to-end-test style (
page.goto,getByRole(...).click(),fill,check). It runs in an isolated engine inside the executor with no file, network or process access; the call has one receipt and each step a child receipt; policy and budget are checked per step, so a script can never carry an action the rules would refuse one step at a time. It stops at the first failed step and reports what ran. Limits by default: 60 seconds, 200 steps, the run’s origin set;--max-stepsand--budgetset your own, and a run stopped at either exits 14 with what it did so far. - Each model family’s own browser and computer tools. Models are trained on their own labs’ tool shapes, so the executor is the driver behind each family’s toolset, and the harness picks the face per model family by default. The harness Lab measures that choice on your own flows.
- Frazer’s tools. MCP tools (
browser_snapshot,browser_click,browser_runand the rest), the CLI and the API, with the same verbs and JSON results, for any harness. Every action takes--dry-runand--estimate. - The protocol endpoint.
frazer browser open --cdpreturns awss://endpoint bound to that browser and its Fir session, so existing end-to-end suites and agent frameworks connect unchanged; the same browser speaks WebDriver BiDi. A method gate refuses withMETHOD_NOT_ALLOWEDthe commands that would step around the policy: a context with its own proxy, ignoring certificate errors, downloads outside the cell, attaching to another target, and, for agents, page scripts on third-party origins. Every input and navigation still writes a receipt, and the cell’s network still enforces egress below the browser.
Suspended between steps. Models take seconds between steps, so an idle browser is suspended while it waits and resumes on the next action; open streams stay inside step mode’s held clock.
Logging in without real credentials
Section titled “Logging in without real credentials”- Reaching the preview. Previews on
frazer.runsit behind Frazer sign-in. A browser in the preview’s network reaches it with its cell’s workload identity: the egress gateway adds a short-lived token for that preview host only. No secret in the cell, no person task. - The app’s own login. The preview’s recipe declares personas (
buyer,admin).frazer browser login --persona buyerasks for a login handle that expires within 10 minutes and redeems it once at the preview’s own login path. For apps on Frazer sign-in, the preview’s front door mints the app’s session from the handle; for apps with their own login, Frazer seeds the persona in the app’s user store, runs the app’s own login once per persona and preview revision with generated credentials kept in the preview’s secrets, and installs that session. Passkeys come from a virtual authenticator seeded per persona, one-time codes from seeds Frazer holds, and email links and codes from the preview’s inbox. When the test is about the login itself, the persona signs in through the real login form against the real backend.
The session ends with the run (1 hour by default), cookies stay on the preview’s own host, and every mint is an audit row. A real third-party login is Sign in here: a person task, with the recording paused while the person signs in. On an allowed third-party origin the agent asks with frazer browser handoff --reason sign-in --origin <origin>, and the person gets a sign-in card: it shows the verified origin and the form’s fields, the person types into Frazer’s own interface and never the page, the executor fills the form and the model never sees the values; it expires in 5 minutes and a retry never fills the form twice. A take-over remains for what a card cannot carry, such as a CAPTCHA. A persona’s passkey is answered from outside the browser’s VM, so its private key never enters it. An agent’s run never gets a password in a prompt, a credentials file in the cell or a person’s own browser profile.
The flight recorder
Section titled “The flight recorder”| Captured | Format | By default |
|---|---|---|
| Steps and receipts | JSON, in the Fir session | Always |
| Snapshots around each step | The executor’s text views | Always |
| Network | HAR 1.2, bodies capped at 64 KiB and scanned | Always |
| Console and page errors | Source-mapped stacks | Always |
| DOM recording | The same open format as Frazer’s session replays, so one player serves both | Always |
| Video | Frames at 5 per second as WebM, with the agent’s cursor and each step as a chapter | Always on Frazer’s fleet; on request locally |
| Screenshots | At checkpoints and on failure | Always |
| Versions | Engine build, app build digests, preview, data branch, persona, flow revision | Always |
Password and card fields, and fields the app map marks, are never captured: they are masked in the DOM stream, the video and the HAR. Secret-bearing headers are dropped, bodies pass the gateway’s data-loss checks, persona data is synthetic, and each recording carries a redaction report. Recordings are stored by content in Frazer Storage and kept 14 days for passing runs and 90 days for failing ones by default; a recording attached as evidence keeps the evidence’s retention. Playback syncs the DOM, the video, the network and the console by step and compares two runs. A video is a view of a run, never its verdict.
Flows and reruns
Section titled “Flows and reruns”A flow (tests/flows/<name>.flow.yaml) is a recording’s steps as an editable file with role and name targets, the persona and data as parameters, UI and state checks and readiness. Flows come from recordings, a person’s walkthrough or an agent, and they are reviewed like code.
| Rerun | Environment | Speed | Catches |
|---|---|---|---|
| Fast | The new frontend build, with the network answered from the recording | Seconds | Frontend regressions, with very little flake |
| Deep | A preview at the exact version and data branch, with the real backend | Minutes | Everything a user would see, the backend included |
Each rerun pins the clock, randomness, time zone, locale, fonts, animations, network, viewport and device as asked. Verdicts are pass, fail (with the failing step and the failure signature), reproduced k of n, flaky, blocked (the environment failed) and Not run: no funds. Flows map to the code they exercise, so only the flows a change reaches rerun. When a flow fails for a reason that is not the product (a renamed label), an agent proposes the healed flow as a pull request with before and after recordings; a flow that proves an acceptance criterion is read-only to the implementing agent, and its change goes to the plan’s approver. An agent’s successful run can be proposed as a flow, so the next run needs no model.
Reproducing an issue
Section titled “Reproducing an issue”| Step | What happens |
|---|---|
| 1. Evidence | The issue’s evidence: a user’s session replay, a HAR, the error group, the release and the steps, redacted |
| 2. Environment | A preview of the issue’s moment: the services that ran then, data branched at that second from the masked replica, the failing requests’ trace IDs, and services isolated so the past never mixes with today’s |
| 3. Steps | A flow from the user’s replay, from the HAR, or written by an agent from the issue’s text; a masked field becomes a persona-filled parameter, never the original value |
| 4. Rerun | Three reruns by default with the clock, randomness and network pinned; the failure signature (a status and error code, an assertion, a console error) must match the issue’s |
| 5. Evidence back | The recording is attached to the issue as an attested reproduction: run by Frazer CI in a preview with these digests |
| 6. The test | The exported test goes into the fix pull request, and Frazer CI reruns it on its own: it fails at the issue’s version and passes at the fix |
On acme/storefront#4711 “Checkout fails with 500 on gift-card orders since v2.14.2”, filed by the monitor agent, the fixer agent’s session s-4a71 opens browser br-7a2c next to the reproduction preview pv-4718 as buyer and reruns the user’s steps: reproduced 3 of 3, signature POST /api/checkout → 500 GIFT_CARD_STATE. Recording rec-4711a goes on the issue; the exported test tests/e2e/repro/4711-gift-card-checkout.spec.ts fails at v2.14.2 and passes on the fix, pull request #4718 (demo data). A reproduction that fails every rerun ends not_reproduced with what was tried.
Testing a change through the UI
Section titled “Testing a change through the UI”For every version of a pull request that touches UI or a user flow, the test plan runs:
- Flow reruns with no model: the fast tier for every such pull request (two smoke flows by default), and the deep tier with the full flow set at the top of each stack.
- An agent verifies what flows do not cover: the plan item’s acceptance criteria, through the UI against the real backend, with state checks; new paths become proposed flows.
- Evidence on the pull request: the test run, its recordings and checks. Because a tester’s session produced them, not the author’s, they are an independent source of evidence for the second opinion and auto-merge.
- Outcomes link back: a UI bug that escapes links to the flows that ran on its pull request, and the gap becomes a flow.
Safe by default, below the browser
Section titled “Safe by default, below the browser”| Default | How |
|---|---|
| Network | The cell’s egress allows the preview’s origins and its declared fakes only; private and link-local ranges are blocked and checked again after redirects. A project admin widens it per origin, as a plan |
| Your own apps by default | A run’s origins are your previews on frazer.run, the domains your organization verified and the fakes your recipes declare; anything else is ORIGIN_NOT_ALLOWED. A preview’s own third-party resources (map tiles, fonts, a payment frame) are declared in its recipe: the page loads them, and the agent never navigates to or acts on them |
| Outside sites, by opt-in | A project admin adds an outside origin to the project’s browser policy as a reviewed change, with its purpose (vendor_portal, docs or research), its mode (read-only or read-write) and its rate limit (by default 1 request a second and 2,000 a day). The policy file is a sealed path: an agent may propose an origin (frazer browser policy propose), and a project admin who did not write the change approves it |
| Origin sets | Per run: the origins an agent may read and the origins it may act on; frames from other origins are not shown |
| Schemes | http and https only, always |
| Actions | Seven action classes for every model: payments and legal terms Hand off; creating accounts, sending messages and changing sensitive data Ask; other data changes Ask outside the preview; consent Auto on your previews with test personas, Hand off elsewhere |
| The action reviewer | A model of another family checks an action against the task from the action’s metadata, never from page content |
| Injection probes | The gateway’s classifiers scan page text and screenshots before the model reads them |
| Page scripts | Off for agents by default, on for flows and CI |
| Downloads, uploads | Downloads stay in the cell and are scanned; uploads come only from the workspace |
| Profiles | A fresh profile per run |
| Identity | No stealth, no fingerprint spoofing, no challenge solving. On outside sites agents sign their requests with Web Bot Auth by purpose: as your organization on vendor portals and docs, as Frazer for research reading, with your own rate limit. Agents slow down on 429 and follow robots.txt on research sites |
| Kill switch | Your organization’s kill switch stops every browser and desktop |
The renderer’s own sandbox stays on inside a microVM per browser, and engine security fixes ship within 72 hours, or 24 when a flaw is exploited.
Desktops
Section titled “Desktops”A desktop (dsk-…) is a Linux VM with a graphical session (Linux first; macOS on Macs dedicated to one customer and Windows on the customer’s own licenses come after) that one agent session drives, for desktop apps, flows that leave the browser (a file dialog, an OS prompt) and apps with no API. It has the same Fir session, receipts, policy, recording and live view as a browser. The agent uses the app’s own protocol first (Chromium-based desktop apps expose the same snapshots and refs), the operating system’s accessibility tree second, and pixels last. An allowlist names the apps a run may use; system settings, keychains and credential stores are off limits.
frazer desktop open --image ubuntu-26.04 --size 4x8Prices
Section titled “Prices”Browsers bill $0.02 per active browser-hour, by the second, where a browser-hour is the wall time of an active browser session: UI flows, exploratory testing and reproductions through the UI. Warm pools and idle browsers are not billed. Recordings bill as Frazer Storage; the agent that drives the browser bills its own runtime and tokens; every response reports the browser time it used. A run whose estimate does not fit the budget or the balance does not start and says so (“Not run: no funds”); it never falls back to a cheaper run. The price is an estimate, calibrated against measured use. The price drops to $0.01 per browser-hour at the first quarterly re-read whose measured cost is at or below $0.006 a wall-hour.
Commands
Section titled “Commands”frazer browser open https://pv-4718.preview.frazer.run/checkout --persona buyerfrazer browser snapshot --view interactivefrazer browser fill @e11 GC-2Q7Ffrazer browser click @e12frazer browser run --script checkout.js --max-steps 200 --budget 0.05frazer browser policy get --identityfrazer browser check --api 'GET /api/orders?last=1' --expect '{"state":"paid"}'frazer browser rerun rec-4711a --preview pv-4718 --times 3 --pin clock,random,networkfrazer browser repro --issue 4711 --preview pv-4718frazer browser … runs the browser’s own binary, which also runs alone on a devbox or laptop and follows the CLI contract: help as data, the error envelope with next, the exit codes, no prompts. frazer browser list, get and close act on hosted browsers (br-…), each bound to a Fir session.
CLI
| Command | What it does |
|---|---|
frazer browser open | Open a browser for your session at a URL, next to a preview and as one of its personas; returns the first snapshot. |
frazer browser snapshot | The page as an agent reads it, interactive (the default), content, full or the delta since the last snapshot, with refs such as @e12. |
frazer browser run | Run a short script of many steps in one call, with one receipt per step; it stops at the first failed step. |
frazer browser check | Record a check on the page or on state behind it (an API response, a row); a test passes on state checks. |
frazer browser login | Sign in as one of the preview's personas with a one-time login handle; no real credential reaches the agent. |
frazer browser handoff | Hand the page to a person for a real sign-in, a challenge or a payment, as a person task. |
frazer browser policy get | The project's browser policy, the outside origins it allows with their purpose, mode and rate limit. |
frazer browser policy propose | Propose an outside origin as a reviewed change; a project admin who did not write it approves. |
frazer browser rerun | Rerun a recording or a flow k times against a preview, with the clock, randomness and network pinned. |
frazer browser repro | Reproduce an issue from its evidence in a preview at the issue's moment, and export the failing test. |
frazer browser export | Export a recording as an editable flow or an end-to-end test file. |
frazer browser list | List hosted browsers (br-…) with their sessions, states and causes; get and close complete the set. |
frazer desktop open | Open a Linux desktop VM for your session, to drive apps outside the browser. |
All of them: CLI reference.
MCP tools
| Tool | What it does |
|---|---|
browser_open | Open a browser and return its first snapshot. |
browser_snapshot | Read the page as compact text with refs. |
browser_run | Run a script of many steps, with a receipt per step. |
browser_check | Check the UI or the state behind it. |
browser_repro | Reproduce an issue and return the recording and the exported test. |
browser_handoff | Ask a person for a sign-in, a challenge or a payment; a sign-in card on an allowed outside origin. |
browser_policy_get | The project's browser policy. |
desktop_screenshot | A screenshot of a desktop, for what the accessibility tree misses. |
All of them: MCP tools reference.