BLOG
Browser Agent AI: Runtime Architecture for Production
A browser agent AI needs a real runtime, not a local Chrome tab. Learn how hosted Chromium, CDP, and session isolation make agents reliable.

A browser agent AI is a language model loop that observes a web page, decides on an action, and executes that action against a real browser. The model is the easy part to swap. The hard part is the runtime underneath it: the browser process, the session state, the network path, and the debugging surface. Most teams that ship a working browser agent AI demo discover within a week that their local Chrome tab does not survive concurrency, restarts, or a second developer running the same script.
This guide covers what a browser agent AI actually needs from its runtime, where local setups break, and how to wire a hosted Chromium session over the Chrome DevTools Protocol (CDP) so the agent loop stays deterministic. It is written for engineers who already have an agent loop and are now deciding where the browser lives.
What a browser agent AI actually is
Strip away the branding and a browser agent AI is three components:
- A perception layer that turns the DOM or a screenshot into something the model can reason about.
- A decision layer that maps that observation to a next action (click, type, navigate, extract).
- An execution layer that performs the action in a browser and returns the new state.
Frameworks like browser-use, Stagehand, and the various agent-browser CLIs mostly own the first two layers. The third layer — the browser itself — is where production failures concentrate. A perception bug shows up as a wrong click. An execution bug shows up as a session that died mid-task, a login that did not persist, or a page that rendered differently because the browser was a different build.
The distinction matters because it tells you what to buy and what to build. You should build the agent loop. You should not build the browser fleet, the CDP proxy, the session store, and the live viewer unless that is genuinely your product.
Why local Chrome breaks under agent workloads
Running Playwright or Puppeteer against a local Chromium install works fine for a single script. It stops working for browser agent AI workloads for four concrete reasons.
State leaks between tasks. A local browser profile accumulates cookies, localStorage, and cached service workers. Agent task A logs into a dashboard; agent task B inherits that session and behaves unpredictably. You can launch a fresh context per task, but then you lose the persistent login that made the agent useful in the first place. You need per-session isolation *and* opt-in persistence, which is a runtime feature, not a library flag.
Concurrency exhausts the machine. Each Chromium instance is roughly 100–300 MB of RSS before the page loads anything heavy. Ten parallel agents on a laptop is already a memory problem. Fifty is not a laptop workload.
The browser build drifts. Your CI runs one Chromium version, your laptop runs another, and the agent's selectors or timing assumptions break on one but not the other. Pinning helps, but you are now maintaining a browser distribution.
Debugging is blind. When an agent fails at step 14, you want to see the page as it was at step 14. A headless local run gives you a stack trace and maybe a screenshot if you remembered to capture one.
A hosted runtime addresses all four by moving the browser off the agent host and giving each task its own isolated session with a stable build.
The runtime criteria that matter
Before comparing options, define what "good" means for a browser agent AI. These are the criteria that separate a demo runtime from a production one.
| Criterion | Why it matters for agents | What to check |
|---|---|---|
| Session isolation | Prevents cross-task state leaks | Fresh profile per session; explicit opt-in for persistence |
| CDP access | Lets you use Playwright, Puppeteer, Selenium, or raw CDP | WebSocket endpoint exposed; standard protocol, no proprietary wrapper |
| Persistent profiles | Keeps logins across runs without leaking between tenants | Named profiles scoped to a workspace |
| Live viewer | Lets a human watch or take over a stuck session | Real-time view, not a delayed recording |
| Network controls | Some sites behave differently by region or IP class | Configurable proxy settings per session |
| Usage controls | Agent loops can run long; you need caps | Per-session timeouts and concurrency limits |
| Observability | You need to replay failures | Session recordings or logs tied to a session ID |
Notice that "model quality" is not on this list. The model is your choice. The runtime is the part that determines whether the model's decisions actually land.
If you want the deeper background on why this layer exists at all, the remote browser for AI agents piece covers the architectural case, and the remote browser online guide covers the operational side of running Chromium without a local install.
Wiring a hosted session into an agent loop
The integration path is the same whether you use Playwright, Puppeteer, or a framework built on top of them: create a session, get a CDP WebSocket endpoint, connect, drive. The documentation has the current endpoint format and session lifecycle; the code below shows the shape of a Playwright connection.
import { chromium, Browser, Page } from "playwright";
interface SessionInfo {
id: string;
cdpUrl: string;
}
async function createSession(): Promise<SessionInfo> {
// Replace with the current session-creation endpoint from /documentation.
const res = await fetch(`${process.env.RB_API_BASE}/sessions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.RB_API_KEY}`,
},
body: JSON.stringify({
// Named profile keeps logins across runs; omit for a clean session.
profile: "agent-checkout",
// Configurable browser settings, e.g. proxy and locale.
proxy: { country: "us" },
}),
});
if (!res.ok) throw new Error(`session create failed: ${res.status}`);
return (await res.json()) as SessionInfo;
}
async function runAgentTask(task: string) {
const session = await createSession();
let browser: Browser | undefined;
try {
browser = await chromium.connectOverCDP(session.cdpUrl);
const context = browser.contexts()[0] ?? (await browser.newContext());
const page: Page = context.pages()[0] ?? (await context.newPage());
// Your perception + decision loop lives here. Each step:
// 1. Read page state (DOM snapshot, accessibility tree, or screenshot).
// 2. Ask the model for the next action.
// 3. Execute it and wait for a stable signal, not a fixed sleep.
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
// Example of a deterministic wait an agent loop should prefer.
await page.getByRole("button", { name: /continue/i }).click();
await page.waitForLoadState("networkidle");
return await page.title();
} finally {
await browser?.close();
// Session teardown is separate from the CDP disconnect; see /documentation.
}
}Three details in that snippet are worth calling out because they are where most integrations go wrong.
`connectOverCDP` versus `launch`. You are not launching a browser; you are attaching to one that already exists. That means browser.close() disconnects your client but does not necessarily terminate the remote session. Teardown is a separate API call. If you skip it, you pay for idle sessions.
Contexts are not always empty. When you attach to a remote browser, browser.contexts() may already contain the default context. Reusing it is usually correct for a single-agent-per-session model, but if you run multiple agents against one session, create explicit contexts and keep them separate.
Waits must be signal-based. Agents that use fixed sleep() calls are the single largest source of flaky automation. Prefer waitForLoadState, waitForResponse, or role-based locators with auto-waiting. The runtime cannot fix a bad wait strategy.
For the protocol-level details, the Chrome DevTools Protocol documentation is the authoritative reference, and Playwright's `connectOverCDP` docs describe the client-side contract.
Local versus hosted: a practical comparison
The table below is the decision most teams actually face. It is not "hosted is always better" — it is "hosted is better once you have more than one agent or more than one developer."
| Dimension | Local Chromium | Hosted Chromium session |
|---|---|---|
| Setup time | Install browser + drivers per machine | One API call per session |
| Isolation | Manual context management | Per-session by default |
| Concurrency | Bounded by host memory | Bounded by plan limits (see /pricing) |
| Browser version | Drifts across machines | Pinned by the provider |
| Debugging | Screenshots you remembered to take | Live viewer plus session recordings |
| Persistent logins | Local profile directory | Named profiles scoped to a workspace |
| Network controls | Whatever your machine has | Configurable proxy settings per session |
| Cost model | Your compute | Metered per session-hour |
| Best fit | Prototyping, single-agent scripts | Multi-agent workloads, team use, CI |
The honest trade-off: local is cheaper and simpler for a single developer iterating on one task. Hosted wins the moment you need reproducibility across machines, parallel agents, or a human in the loop watching a live session. The remote control browser article goes deeper on the human-takeover pattern, and the remote web browser guide covers the broader automation case.
Where the agent loop and the runtime meet
A few design decisions sit on the boundary and are worth getting right early.
Session granularity. One session per task is the safe default. One session per user, reused across tasks, gives you persistent logins but requires careful cleanup between tasks. One session shared across parallel agents is almost always a mistake — the agents will fight over the active tab.
Profile strategy. If the agent needs to stay logged in, use a named profile and treat it as a credential. Rotate it on a schedule, scope it to one workspace, and never share it across tenants. If the agent does not need persistence, use a clean session every time; it is cheaper and eliminates a whole class of bugs.
Failure handling. Agents fail in the middle of a task. The runtime should let you capture the page state at failure, then either retry in a fresh session or resume in the same one. Decide which before you ship, because it changes how you structure the loop.
Cost control. Agent loops can run longer than expected when a model gets stuck. Set per-session timeouts and a concurrency ceiling. Current limits and metering are on the pricing page; do not hardcode assumptions about them into your agent.
Observability. Tie every log line to a session ID. When a task fails at 2 a.m., the session ID is the only thing that lets you reconstruct what the agent saw.
Common failure modes and fixes
These are the failures that show up repeatedly in browser agent AI deployments, with the runtime-level fix for each.
- Login lost between runs. The agent used a clean session when it needed a named profile. Fix: assign a profile to the task type, not the individual run.
- Selectors break on a different browser build. Local and CI browsers drifted. Fix: pin the browser version by using a hosted runtime with a stable build.
- Agent clicks the wrong element after a slow render. The loop used a fixed sleep. Fix: switch to role-based locators and signal-based waits.
- Parallel agents corrupt each other's state. They shared one session. Fix: one session per task, or explicit isolated contexts.
- Session runs forever after the model gives up. No timeout was set. Fix: per-session timeouts plus a supervisor that tears down orphaned sessions.
- Cannot reproduce a failure. No recording. Fix: enable session recordings and store the session ID with the task result.
None of these require a better model. All of them require the runtime to expose the right controls.
When to build versus when to use a hosted runtime
Build your own browser fleet if browser infrastructure is your product, or if you have regulatory constraints that forbid a third-party runtime. For everyone else, the calculus is straightforward: the engineering cost of a reliable browser fleet — CDP proxying, session lifecycle, profile storage, live streaming, configurable network routing, version pinning — is measured in engineer-months, and it is not the part of your product that customers pay for.
A hosted runtime gives you the execution layer as an API. You keep the agent loop, the prompts, the tool definitions, and the evaluation harness — the parts that actually differentiate your product. The documentation covers the session API, CDP endpoint, and profile management in detail.
The short version
A browser agent AI is only as reliable as the browser it drives. Local Chromium is fine for a prototype and breaks under concurrency, state leakage, and version drift. A hosted Chromium session gives you isolation, a stable build, CDP access for Playwright and Puppeteer, persistent profiles when you need them, and a live viewer for debugging. Wire it with connectOverCDP, use signal-based waits, scope sessions to tasks, and tie every log to a session ID. That is the whole architecture — the rest is your agent loop, which is the part worth building yourself.