AnyBrowser
A browser-automation and agent runtime you can take apart. Three interfaces — the browser, the transport, the model — each with a registry and a conformance suite, so "implement your own" is something you can verify rather than hope for.
from anybrowser import open_engine
async with await open_engine("playwright") as engine:
await engine.navigate("https://example.com")
page = await engine.snapshot()
await engine.click(page.elements[0])
Same code against Chrome attached to your own logged-in window, against Safari, or against a browser nobody has written a backend for yet.
Why this exists
Most browser automation assumes it owns the browser: a fresh profile, a clean window, a driver launched with the right flags. That assumption breaks the moment the job is to work in the browser a person actually uses — their tabs, their sessions, their logins, already open. You cannot ask them to log in again inside a throwaway profile, and you cannot attach WebDriver to a window that was not started for automation.
AnyBrowser assumes it does not own the browser. Every abstraction here comes from driving real Chrome and Safari windows in production, and every one of the three ideas below is a failure mode that cost someone a week.
Backends differ in kind, not quality. Safari has no CDP; Apple's Web Inspector protocol needs private entitlements. WebDriver cannot adopt your open window. So an engine declares what it can do and callers route around the gaps, instead of discovering them by catching an exception halfway through a task. See capabilities.
A no-op is not a success. The most common way an agent dies is a click that
hit nothing, reported as "success", read back by the model as progress, and
repeated forever. Every action returns changed alongside ok, and the
conformance suite fails an engine that clicks dead space and calls it a change.
See truthful outcomes.
The contract is executable. pytest --pyargs anybrowser_conformance --engine yours is the definition of a working backend.
Install
pip install anybrowser[playwright] # the reference engine, easiest start
pip install anybrowser[chrome] # attach to your own Chrome
pip install anybrowser[all] # everything
Python 3.10+. import anybrowser pulls in no browser driver, no HTTP server and
no LLM SDK — every backend is an extra.
The Playwright engine needs its browser binaries once, which pip cannot fetch for you:
playwright install chromium
Running a contract against something whose extra is missing skips rather than
fails, and says which extra to install — so pip install anybrowser[all] is the
shortcut if you would rather not think about it.
The three interfaces
| You want to | Implement | Registry group | Ships with |
|---|---|---|---|
| Drive a different browser | BrowserEngine |
anybrowser.engines |
playwright, chrome, safari |
| Change how clients reach the daemon | DaemonTransport |
anybrowser.transports |
websocket, unix, memory |
| Use a different model | ModelProvider |
anybrowser.models |
openai, anthropic |
Register with an entry point and yours is selectable by name everywhere:
[project.entry-points."anybrowser.engines"]
firefox = "my_package.engine:FirefoxEngine"
pytest --pyargs anybrowser_conformance --engine firefox # the engine contract
pytest --pyargs anybrowser_conformance --transport grpc # the transport one
Each flag selects a contract and skips the other. The suite is capability-gated. A backend that honestly declares it cannot drag is skipped, not failed. A backend that claims it can drag and then doesn't is failed — the suite tests truthfulness as hard as it tests function.
Full walkthrough: Writing an engine.
Engine status
| Engine | Attaches to your session | Trusted input | Engine contract | Gated by CI |
|---|---|---|---|---|
chrome (DevTools) |
no — needs the launch flag | yes | 28 passed, 4 skipped | yes |
chrome (extension) |
yes — your own windows | yes | passes (local run) | no — needs a browser with the extension loaded |
playwright |
no — own profile | yes | 28 passed, 4 skipped | yes, the reference engine |
safari |
yes — accessibility + extension | yes | needs a local setup (guide) | only that it refuses cleanly off-setup |
The engine contract is 32 tests. The 4 skips are the capability gate working as
intended: those tests assert the error an engine raises for something it does
not declare, so they stand down for an engine that declares it. Run the whole
suite directory and you will see more skips still — the transport and model
contracts, idle because you passed neither --transport nor --model.
Pointing the engine contract at the remote engine grades the whole daemon
stack — transport, protocol, codec, dispatch — and it scores the same 28
passed, 4 skipped over both the memory and websocket transports.
The Chrome engine has two pipes behind one interface. devtools talks to a
browser started with --remote-debugging-port — standard, and the only mode
that runs in CI. extension relays CDP through a browser extension and is the
one that matters, because it attaches to the browser you already have open,
with your tabs and your logins. Same engine either way; the connection reports
which it is, and the declared attach_to_user_session capability is derived
from that rather than asserted next to it.
Both Chrome pipes pass the contract. To drive the browser you already have
open, see driving your own Chrome — note that
--load-extension is silently ignored by Chrome stable, which is a memorable
afternoon if nobody tells you.
Safari is implemented in both halves: a Swift helper for trusted background input and occlusion-proof capture, and a Web Extension for the DOM and pointer gestures. It needs a local build and a one-time Accessibility grant, so it skips rather than fails where that has not been done — see driving Safari.
Layout
src/anybrowser/
core/ interfaces and value types — imports no backend
engines/ chrome, safari, playwright
perception/ turning a page into a snapshot, engine-agnostic
daemon/ protocol, transports, the server that owns an engine
agent/ planner, tools, executor, memory
models/ LLM providers
conformance/ the executable contract
docs/ architecture, guides, ADRs
Docs
- Architecture overview
- Capabilities — why backends declare instead of raise
- Truthful outcomes — why
changedexists - Writing an engine
- Writing a transport
- Writing a model provider
- Driving your own Chrome
- Driving Safari
- ADRs — the decisions and what they cost
Contributing
Start with CONTRIBUTING.md. Good first issues are labelled
good first issue;
a new backend is the highest-value contribution there is, and the conformance
suite means you can tell when it's done.
By participating you agree to the Code of Conduct. Security reports go to SECURITY.md, not the issue tracker.
License
Metadata
Release files for anybrowser 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| anybrowser-0.1.0.tar.gz | 149.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| anybrowser-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 304.4 kB
Release files / anybrowser-0.1.0.tar.gz
| Download URL | anybrowser-0.1.0.tar.gz |
|---|---|
| Size | 149.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ef03452de2998ae0eb2879e6a588d0ea9ea2ff8a528e0114cbb7ee4df60a2e4b
|
|
BLAKE2b-256 checksum How to use checksums |
33062922445a9b200dfab45f84b09aca77905bcbde85a23225a434e6735a3151
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency logRelease files / anybrowser-0.1.0-py3-none-any.whl
| Download URL | anybrowser-0.1.0-py3-none-any.whl |
|---|---|
| Size | 155.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fba0799211b379a09dbdb31dfadf749419afdec4b4292172bbfb2454a2d3f0f1
|
|
BLAKE2b-256 checksum How to use checksums |
6be87d32701629c0dd5eafa79f915e4e5f83adc3196c3b23dcfeeccf0385e6e1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency log