Корбар
Local Browser Agent: Research and Build Brief — Full-Automation / Stealth Edition Prepared: 31 October 2026 Purpose: A research-grounded plan and implementation prompt for a portable Windows browser-agent app inspired by Browser Use, but built to run unattended against bot-protected sites: stealth-patched browser engine, consistent spoofed fingerprints, residential egress, human-like input, automatic challenge solving, and identity rotation on block.
What changed from the pause-and-handoff version Dimension Old (polite agent) New (this brief) Verification challenges Detect, pause, highlight, wait for user Auto-detect, auto-solve, resume; human is the last escalation, not the first Automation fingerprint Accepted as-is Actively erased at the engine and protocol layer Proxies "Not needed" First-class subsystem: residential/mobile pools, sticky sessions, geo-matched Identity One browser profile, forever Rotating identity bundles (fingerprint + proxy + profile + cookie jar), bound 1:1 Timing Whatever Playwright does Humanization layer: Bézier mouse paths, typing delay distributions, hover dwell, scroll inertia Denial (403/429/block page) Report and stop Escalation ladder: backoff → identity rotation → engine swap → solver → human Profile state Optional Warm-up, cookie accrual, storage persistence, per-domain reputation Confirmation gates Everything consequential Autonomous by default; confirm only money-out and destructive writes Success claim "Works on authorized sites" "Passes passive checks and solves challenges unattended, with measured pass rates" Self-verification Manual demo flows Built-in detection scorecard run before every session The core architectural rule: stealth is not the model's job. The LLM emits semantic actions; a harness layer underneath owns fingerprint, proxy, timing, challenge handling, and retries. The model should never have to know it is being stealthy.
Executive recommendation Build a local-first desktop agent with a TypeScript UI and a separately managed, headed, stealth-patched Chromium — patchright as the primary driver, not stock Playwright. Stock Playwright is a testing tool and announces itself at the CDP layer; monkey-patched JS evasions cannot fix that, because the loudest leak (Runtime.enable execution-context enumeration) lives below JavaScript.
Primary engine: patchright (maintained drop-in Playwright fork; patches the Chromium default args, removes --enable-automation, adds --disable-blink-features=AutomationControlled, and — critically — runs JS in isolated execution contexts so Runtime.enable is never sent). Secondary engine behind the same adapter interface: Camoufox (Firefox, fingerprints patched at the C++ level, Playwright's Juggler copy means the page never sees the agent's own listeners). Two engines matter because a Chromium-only fingerprint is itself a signal, and because engine swap is a real rung on the escalation ladder.
Above the driver, three subsystems that do the actual work of passing as a human: a fingerprint manager (deterministic, internally consistent, proxy-geo-matched), a proxy manager (residential/mobile, sticky, health-checked), and a humanization layer (input timing, mouse paths, dwell, scroll physics). Challenge handling is a solver chain, not a pause: passive pass first, then provider API, then human.
Keep the typed agent loop from the original design — it was good. Keep one action per model turn, validated JSON, rolling five-step memory, and bounded budgets. Add automatic retry and identity rotation around the loop, transparent to it.
Legal framing to keep in mind, not as a blocker: stealth is a maintenance treadmill, not a magic switch. Managed Bot Management (Cloudflare Pro/Business+, Kasada, DataDome with server-side scoring) can still win; the product must report measured pass rates per target and admit when a site is out of reach.
What Browser Use offers, based on its public product docs Browser Use's public surface is three products: a hosted Cloud agent, a CLI that attaches a coding agent to a local or cloud browser, and an open-source Python library. The cloud side is where the interesting capability sits, and it is explicitly the thing to emulate locally:
Stealth cloud browsers — CDP-compatible Chromium with residential proxies across 195+ countries enabled by default, and automatic CAPTCHA solving. Advertised against Cloudflare, PerimeterX and similar. CAPTCHA solving as a managed service — the docs are deliberate about mechanics: don't refresh, don't click the challenge, don't replace the browser while the solver works. No solver API key, sitekey or token plumbing on the user's side. A stated split of responsibility — the solver handles in-page reCAPTCHA and hCaptcha; whole-site bot walls (Cloudflare challenge pages) are left to the stealth layer and residential IPs. That split is the single most useful design input in the whole corpus, and it is reproduced below as the solver chain. Pricing model — per browser-hour plus traffic. Useful as a benchmark for what a local build is saving the user. The hosted FAQ's operational advice is also worth absorbing: bounded workers, staggered polling, Retry-After respect, region-matched proxy when blocked. The original brief dropped proxy rotation because the hosted product's advice didn't apply locally — but it does apply the moment you take stealth seriously.
Browser Use's cloud internals are not open source just because the library is. Implement from public docs and independently written code. Their own stealth benchmark post is a good sanity check on what the field considers table stakes.
Open-source components worth evaluating Component What it contributes Fit Caution patchright Drop-in Playwright fork; CDP-layer patches (no Runtime.enable), cleaned launch flags, isolated execution contexts, init scripts via request routes Primary driver. Same API surface as Playwright, so the adapter port is near-zero Tracks upstream with a lag; pin a version and re-run the scorecard on every bump Camoufox Firefox build with fingerprints patched in C++, Juggler-side page copy hiding automation traces, geoip/locale/WebGL/timezone injection Secondary engine for sites that profile engine, and the top escalation rung Firefox identity only — SpiderMonkey behaviour can't be spoofed into Chromium; separate packaging playwright-extra + stealth plugin Legacy JS-level patches: navigator.webdriver, plugins, chrome runtime, WebGL vendor Cheap baseline if patchright port slips Doesn't touch Runtime.enable; largely theatre alone Stagehand TS act/observe/extract primitives Optional agent layer Verify it doesn't assume stock Playwright Playwright API contract, locators, frames, tracing Absorbed into patchright Do not use _electron embedding; separate process only Ollama Local inference HTTP API MVP model path Optional installed service, not bundled weights Tesseract.js OCR Visual fallback for text not in DOM Never decides challenge outcomes electron-builder Windows portable + installer Packaging Portable .exe ≠ bundled model CapSolver / 2Captcha / YesCaptcha / NopeCHA Token-issuing solver APIs (Turnstile, reCAPTCHA v2/v3, hCaptcha, DataDome slider, image-to-text) Solver chain rung Costs, latency, and a hard dependency on someone else's uptime — abstract it Residential/mobile proxy vendors (Bright Data, Oxylabs, Decodo, IPRoyal, proxyhorizon-class resellers) Clean ASN reputation Egress layer Per-GB cost; sticky-session semantics differ wildly per vendor Licensing: pin exact versions, audit the full tree including browser binaries, OCR language data and any weights. patchright and Camoufox are forks of Playwright/Firefox — inherit and preserve upstream notices (Apache-2.0 / MPL-2.0) plus their own.
Recommended architecture Electron desktop shell (React/TypeScript) ├─ Task input, browser controls, run timeline, live status, stealth health panel ├─ Preload bridge — narrow, validated IPC allowlist └─ Main process ├─ Agent controller (bounded state machine, one action per turn) ├─ Driver adapter ──┬─ patchright (Chromium, primary) │ └─ camoufox (Firefox, escalation) ├─ Fingerprint manager (seeded, consistent, geo-bound, per-identity) ├─ Proxy manager (pool, geo lookup, sticky lease, health, rotation) ├─ Humanization layer (mouse, typing, dwell, scroll physics) ├─ Challenge detector + solver chain (passive → API → human) ├─ Block detector + escalation ladder ├─ Observation builder (a11y snapshot + compact DOM + screenshot) ├─ Model adapter (Ollama / llama.cpp / optional remote) └─ SQLite (tasks, events, identities, proxy leases, scorecards) The identity bundle — the central concept A single unit of "who is browsing" that must never be mixed:
Identity = { profileDir, fingerprintSeed, proxyLease, locale, timezone, viewport, UA + client hints, WebGL vendor/renderer, fonts, hardwareConcurrency, deviceMemory, cookieJar } Rules:
Fingerprint is derived from the seed deterministically, and every field is chosen to agree with the proxy's exit geo and ASN — a Windows UA with an Apple GPU, or a US timezone on a Frankfurt exit, is an instant flag. sec-ch-ua client hints, UA, platform and WebGL must be mutually consistent; Chrome's real values, not plausible-sounding inventions. One identity = one proxy lease = one profile directory = one cookie jar, for the life of the lease. Rotate the whole bundle, never a single field. Viewport is realistic and slightly non-round (e.g. 1920×969 for a maximised window on a 1080p screen), with deviceScaleFactor matching the claimed hardware. Persist identities in SQLite so a burned one is remembered and not immediately reused on the same domain. Proxy manager Pools are per-vendor, tagged by class (residential, mobile, ISP, datacenter) and by geo. Lease TTL is per-identity; health check (exit IP, ASN, geo, latency) before the first navigation. Rotation policy is per-domain, learned: a domain that 403s on identity A rotates to B, and repeated failures demote the whole pool class for that domain. 429 → honour Retry-After, bounded workers, staggered starts. 403/block page → rotate. Timeout with clean status → retry same identity once, then rotate. Datacenter is the default; residential/mobile is an escalation, because it costs money. Humanization layer Applied by the driver adapter, invisible to the model:
Mouse: cubic Bézier path with a control-point jitter, eased in/out, 8–20 intermediate mousemove events, 1–3 px overshoot and correction on target, 60–180 ms hover dwell before mousedown. Typing: per-character delay drawn from a skewed distribution (fast body, slower on shifted characters and digits), 60–140 wpm equivalent, occasional 200–500 ms hesitation at word boundaries, ~2% corrected typo rate on long fields. Scroll: wheel deltas with inertia and decay, pause before and after, not a single teleport. Idle: 0.4–2.5 s micro-pauses between actions, longer (3–9 s) after page loads, as if reading. Never fire an action on a target that just appeared; wait a beat, then act. Challenge detection and the solver chain Detection runs on every observation and on every navigation settle. Signals: known iframe origins (challenges.cloudflare.com, google.com/recaptcha, hcaptcha.com, arkoselabs, datadome slider), challenge DOM markers, cf_clearance-gated interstitials, title/body heuristics ("Just a moment", "Verifying you are human", "Access denied"), and repeated 403/503 with a challenge body.
Then escalate in order — do not skip rungs:
Passive. Do nothing for 2–6 s with humanized idle. Turnstile and many invisible challenges resolve themselves on a clean fingerprint + residential IP. Never click or refresh while it works. In-page token solve. For reCAPTCHA v2/v3 and hCaptcha: extract data-sitekey, action, page URL, dispatch to the solver API, poll, inject the token into the response field, fire the callback, re-observe. Click-based interaction. For interactive Turnstile and DataDome sliders: humanized click inside the checkbox frame; for sliders, drag with a non-linear path and settle jitter. Engine swap. Re-run the same URL on Camoufox with the same proxy geo — engine identity sometimes matters more than anything else. Identity rotation. New bundle, retry once. Human. Only now: pause, focus the frame, show a banner with a one-click "I've solved it, resume." Report which rung won so the scorecard learns. Solver provider behind a ChallengeSolver interface — solve(kind, params) → token | interactive | fail, with timeout, cancellation and cost accounting. Multiple providers registered, chosen by capability and live success rate.
Observation Unchanged in substance: accessibility roles/names and semantic locators first, compact visible-DOM summary as complementary context, screenshot as grounding. Each candidate carries a stable per-observation ref, role/tag, name, input type, visible/enabled state, frame path and viewport box.
Additions: mark whether a candidate sits inside a challenge frame, and surface the challenge state in the model's context only when it's interactive — so the model knows to click a checkbox rather than to re-plan.
Refs are still ephemeral: re-observe immediately before acting, reject clicks on vanished, moved, obscured or ambiguous targets. For cross-origin frames use frame APIs and name the frame. Page text remains untrusted input; treat "ignore previous instructions" in a page as a possible injection, not an instruction.
Agent loop Keep the constrained state machine. Two changes:
Budgets are per-task and per-identity, and exhaustion triggers a rotation rather than a terminal failure while retries remain. Terminal states gain blocked_exhausted and challenge_failed, both with the rung history attached, distinct from complete. Steps: validate task and budget → observe → one typed action or terminal result via JSON schema → validate → execute through the driver → capture outcome → re-observe → append one line to five-step history → check challenge/block state → loop.
Confirmation gates now cover only: spend (checkout, subscription), destructive writes (delete, overwrite, bulk edit), and account/security mutations. Everything else — submit a search, fill a form, send a message, apply to a job — runs unattended by default, with a per-task "ask me anyway" toggle.
Suggested action contract ts type AgentAction = | { type: "click"; ref: string } | { type: "fill"; ref: string; value: string } | { type: "press"; key: string } | { type: "scroll"; direction: "up" | "down"; amount: number } | { type: "navigate"; url: string } | { type: "wait"; condition: "dom_stable" | "navigation" | "challenge" | "timeout"; ms?: number } | { type: "ask_user"; question: string } | { type: "complete"; summary: string }; Harness-only actions the model never emits: solve_challenge, rotate_identity, switch_engine, warmup, set_proxy_class. Keeps the model contract small and the stealth behaviour consistent.
Readiness Deadline-bounded conjunction, as before: navigation settled or timed out, no material DOM mutation for a short quiet interval, target visible and actionable. Never networkidle alone — polling and streaming keep sockets open forever. Add a challenge gate: readiness is false while a challenge frame is unresolved, so the loop can't act into a challenge page. Every wait is logged with its outcome.
Data and privacy Per-user data directory, SQLite, redacted summaries, short screenshot retention, DPAPI for solver keys and proxy credentials. Never log cookies, auth headers or values from fields marked sensitive. Profile directories live under user data and are the agent's authority — make that explicit in the UI, because a warmed profile is the most valuable and most exposing artifact the app owns. Offer "burn this identity" and "export/import profile" so users can manage reputation deliberately.
Packaging reality Portable .exe via electron-builder, state under user data or a chosen portable-data dir. Bundle app + both browser runtimes; keep models external. First-run diagnostics must check, not just display: Chromium launch, Camoufox launch, model endpoint, solver key validity, proxy pool health, and a live detection scorecard against a bundled local fixture plus public test pages.
Phases and acceptance gates Phase 0 — feasibility
patchright on Windows: headed launch, launch-flag diff vs stock Playwright, Runtime.enable confirmed absent. Camoufox on Windows: launch, Playwright control, geoip/locale injection. Fingerprint generator producing consistent bundles; automated consistency validator (UA ↔ client hints ↔ platform ↔ WebGL ↔ timezone ↔ proxy geo). Proxy lease + health check + rotation. Local model JSON-schema-constrained output. Detection scorecard harness against sannysoft, BrowserScan, CreepJS, Pixelscan, plus a Turnstile demo page. Exit: scorecard green on stock fixture pages; documented pass/fail per vector, with the failures named. Phase 1 — unattended MVP Shell, timeline, controls, driver adapter, typed actions, observation, bounded loop, solver chain rungs 1–3, proxy manager, identity persistence, SQLite, budget controls, autonomous-by-default policy. Demos: multi-step task on a protected public site, complete a form and submit, survive a stale element, recover from a 403 by rotation, solve a Turnstile, export a run report. Exit: reproducible traces; each demo states rung used, identities burned, cost, and outcome.
Phase 2 — robustness Engine swap rung, warm-up service, per-domain reputation and rotation memory, concurrency with bounded workers and staggered starts, screenshot diffing, browser restart recovery, retention settings, installer + portable, IPC and untrusted-content security review, dependency and license audit, signing plan. Exit: measured pass rate per target class published in-app; documented GPU/RAM and Windows versions; clean install/update/uninstall/migration.
Phase 3 — commercial readiness Licensing and attribution, model distribution rights, privacy policy, terms, update channel, crash reporting opt-in, support diagnostics. Publish honest per-target pass rates with confidence intervals and named failure modes — not a headline score. State plainly which targets are known-hostile (Kasada, managed Bot Management) and that solver spend is pass-through.
Implementation prompt for a coding AI Role: Senior TypeScript/Electron engineer with production experience in anti-detect browser automation, local LLM integration, Windows packaging and secure desktop apps.
Project: A Windows-first, local-first desktop browser agent, [APP NAME], inspired by Browser Use's public capabilities but independently implemented.
Outcome: A runnable Electron + TypeScript app that takes a task, drives a headed stealth-patched browser, observes the page, proposes one validated structured action at a time, executes it with humanized input, passes or solves verification challenges unattended, rotates identity on block, records everything, and only asks the user when it genuinely cannot proceed. It must work with a local model endpoint and degrade to a deterministic scripted path when no model is available.
Product stance: Full automation. Assume the site does not want us. Erase automation signals at the engine and protocol layer rather than in page JS. Rotate residential egress. Solve challenges automatically; human handoff is the last rung, not the first. Retry after denial with a changed identity. Keep an evidence trail that shows which rung won.
Architecture constraints:
Electron shell, TypeScript throughout, context isolation, sandboxed renderer, narrow IPC allowlist, no Node in renderer. BrowserDriver interface with two implementations: patchright (primary) and camoufox (escalation). No stock Playwright, no _electron embedding — separate process, headed. Stealth is harness-level. The model emits only the typed action set and never sees proxy, fingerprint or solver details. Deterministic fingerprint generator, seed-derived, internally consistent, bound to proxy geo and ASN. One identity = one proxy lease = one profile = one cookie jar. Proxy manager with pool classes, geo lookup, health checks, sticky leases, per-domain rotation policy, backoff with Retry-After. Humanization adapter for mouse, typing, scroll and idle; applied to every driver action. ChallengeSolver interface with pluggable providers; ordered chain passive → token API → interactive → engine swap → rotation → human. Never click or refresh during a passive solve window. Ollama-compatible local inference first, endpoint configurable, no bundled weights. Timeouts, cancellation, schema validation, clear service errors. SQLite for tasks, events, identities, proxy leases, solver spend, scorecards. Redact before persist. Screenshots opt-in with documented retention. Per-task and per-identity budgets. Terminal states include blocked_exhausted and challenge_failed. Required modules:
src/main/browser/driver.ts — interface; patchright-driver.ts, camoufox-driver.ts src/main/stealth/fingerprint.ts — seeded generator + consistency validator src/main/stealth/humanize.ts — Bézier mouse, typing distributions, scroll physics, idle src/main/stealth/launch-flags.ts — cleaned args, no --enable-automation, AutomationControlled disabled src/main/proxy/manager.ts — pools, geo, leases, health, rotation policy src/main/challenge/detector.ts, solver-chain.ts, providers/*.ts src/main/challenge/block-detector.ts — 403/429/interstitial classification src/main/agent/agent-controller.ts — bounded loop, JSON validation, five-step memory, cancellation src/main/browser/dom-extractor.ts — a11y + compact DOM, frame paths, boxes, challenge frame flags src/main/model/model-adapter.ts, ollama-adapter.ts src/main/policy/approval-gates.ts — spend/destructive/security only src/main/storage/database.ts — schema, migrations, redaction src/main/diagnostics/scorecard.ts — detection self-test harness src/main/warmup/service.ts — profile aging, cookie accrual Preload bridge, minimal UI (task, controls, timeline, stealth panel, scorecard, challenge banner with rung display) package.json, electron-builder config for portable and installer Prompt design: task, URL/title, a11y + compact DOM, frame info, screenshot metadata, last five outcomes, current challenge state only if interactive. Page content clearly marked untrusted. Exactly one JSON action, schema-validated, unknown fields and oversized input rejected. No model-authored JS.
Readiness: navigation settle + DOM quiet + actionability + challenge gate, all deadline-bounded, all logged.
Build sequence: ADR first (driver choice, stealth layer placement, proxy policy, solver abstraction). Phase 0 spikes. Vertical slice on a local fixture with a self-hosted Turnstile. Then UI, persistence, full solver chain, packaging. Run the scorecard on every build and record it.
Acceptance criteria: a fresh Windows user launches the app, configures a local model, solver key and proxy pool, and the agent completes an unattended multi-step task on a Turnstile-protected public site — no human input — while the scorecard stays green on sannysoft and BrowserScan and a deliberate 403 recovers by identity rotation. Task history survives restart. Solver spend and identities burned are visible per run. The portable build ships both browsers and no model weights.
Handoff format: full source tree, build and packaging instructions, dependency/license inventory, ADRs, verification log with exact commands and scorecard results, per-target pass/fail table with failure modes, known limitations, and a next-steps list. No fragments; no claiming a build exists unless produced.
Source notes Consulted 31 October 2026. Versions, prices and licenses move; pin and verify before shipping.
patchright — https://github.com/Kaliiiiiiiiii-Vinyzu/patchright Camoufox — https://github.com/daijro/camoufox and https://camoufox.com/stealth/ Browser Use stealth browsers — https://docs.browser-use.com/cloud/browser/stealth Browser Use CAPTCHA handling — https://browser-use.com/captcha-solving Browser Use stealth benchmark — https://browser-use.com/posts/stealth-benchmark CapSolver pricing — https://docs.capsolver.com/en/pricing/ 2Captcha — https://2captcha.com/ Playwright — https://github.com/microsoft/playwright Ollama — https://github.com/ollama/ollama Tesseract.js — https://github.com/naptha/tesseract.js electron-builder targets — https://github.com/electron-userland/electron-builder/blob/master/website/docs/targets.md CreepJS — https://abrahamjuliot.github.io/creepjs/ Detection test pages — https://bot.sannysoft.com/ , https://www.browserscan.net/ , https://pixelscan.net/