Skip to content

Releases: vernikr/browser-agent

v1.0.23

Choose a tag to compare

@github-actions github-actions released this 11 Oct 15:43

Changed

  • A private helper is no longer imported across module boundaries.
    vision_agent.py imported _extract_text_from_html from chrome.py — a
    leading underscore that crossed a module boundary, which is the exact shape
    Python's underscore convention exists to prevent. The function is used by
    two modules, so it is not internal to either; the underscore was a lie.

    Renamed to extract_text_from_html at all five sites: the definition in
    chrome.py, two call sites inside chrome.py, the import in
    vision_agent.py, and the call site there. No behaviour change — pure
    navigation.

v1.0.22

Choose a tag to compare

@github-actions github-actions released this 11 Oct 15:34

Changed

  • The package root no longer re-exports internal classes. __init__.py used to put
    RealChromeSession, X11Display, bezier_curve, is_cloudflare_challenge and
    is_warp_proxy_ready into __all__ alongside BrowserClient, although nothing in
    either repo imports any of them from the package root — every consumer uses a
    submodule (browser_agent.chrome, browser_agent.x11_input, …). The README names
    only BrowserClient.

    __all__ is now ["BrowserClient", "__version__"]. The modules stay importable by
    their full paths, so no working code breaks. tests/test_public_surface.py pins the
    published set and verifies the submodules still resolve, so the surface cannot grow
    back silently the way it grew in the first place.

v1.0.21

Choose a tag to compare

@github-actions github-actions released this 11 Oct 14:03

Added

  • Five CLI subcommands that were missing: goto, key, scroll, screenshot, html. The daemon has always
    implemented all of them and the MCP bridge exposes them, but browser-agent could only move, click and type. An
    agent with SSH access and no bridge installed could act on whatever page fetch happened to leave behind and
    nothing else — it could not navigate, press a key, scroll, look at the page, or read it back. The plan listed
    four of the five; goto was the one it missed, and it is the one that matters most, because a session you cannot
    navigate is a session you cannot use.

    A test now reads /action's own action set out of the daemon and the subcommand list out of the parser, and fails
    when they diverge — so the gap cannot reopen silently the way it opened.

    screenshot writes the PNG to a path (a temp file when none is given) and reports its size, and never puts the
    base64 on stdout. html prints the page, or writes it to a file with --out and reports only its length — a
    rendered page is routinely a megabyte.

Fixed

  • scroll --x without --y failed in the daemon with a 500 naming a browser. The daemon validates x/y as a
    pair before it starts the browser, so the CLI now raises the same message before sending anything. Failing in the
    CLI gives the caller the real reason instead of a browser-start failure they did not ask for.

Changed

  • goto omits use_proxy unless --proxy is given, matching the bridge: sending false would forbid WARP and
    drag a proxied session back to direct, and the daemon treats an absent field and null identically, so omitting
    costs nothing and keeps the payload shape equal to what was actually requested.

  • The new action subcommands share the existing "daemon must be running" behaviour rather than fetch's in-process
    fallback, and the reason is now written down: every one of them acts on a live session, so a one-shot session would
    move the mouse in one Chrome and click in another.

v1.0.20

Choose a tag to compare

@github-actions github-actions released this 11 Oct 13:49

Added

  • tests/test_troubleshooting_doc.py — the other half of the P1-7 guard. The bridge's CI checks its
    troubleshooting doc against its hint table; this asserts the daemon's doc keeps deferring, and that the advice a
    CLI or API caller needs still lives where that caller reads it (docs/api.md for use_proxy and the
    hibernation cold start, docs/deployment.md for the WARP route).

    Structural rather than vocabulary-level, and deliberately so: it catches a revert to the old shape, not a single
    re-added row. The vocabulary check belongs with the hint table, because that is the only place the advice
    actually is.

    Removing the duplicated rows from this doc cost a CLI user nothing, and this test is what says so — a CLI caller
    gets no hint line, so the assertion that the advice survives elsewhere is the part that makes the removal safe.

v1.0.19

Choose a tag to compare

@github-actions github-actions released this 11 Oct 13:42

Documentation

  • The daemon's troubleshooting doc no longer restates the bridge's failure advice. The cold-start, bot-wall and
    daemon-unreachable guidance lived in three places — the hint table in browser-agent-mcp/src/server.ts, the
    bridge's docs/troubleshooting.md, and this repo's — so all three had to be updated together and nothing
    failed when one was missed. The hint table is the one an agent actually reads: it is printed inline with the
    error, at the moment the agent is still looking at the response.

    The daemon's doc now keeps only what the hint cannot know — symptoms of this host, and repairs that are not
    about a single failed call: a unit that is down, memory pressure, a stale X lock, xdotool driven by hand, PDFs,
    a profile directory that moved after an upgrade, a CDP port conflict. It opens by saying to read the hint:
    line first, and why the duplication was removed.

    Nothing here was removed from the product. The advice still reaches an agent on every failure; it now reaches it
    from exactly one place.

v1.0.18

Choose a tag to compare

@github-actions github-actions released this 11 Oct 13:22

Documentation

  • Volatile measurements removed from the evergreen docs. The repo's own AGENTS.md rule is to omit them,
    and the docs had drifted a long way from it: resting RAM (~12 MB in three files), the 55–125 ms hold
    range, the JS heap cap, the 300–600 MB transient fetch figure, OOMScoreAdjust=500, the -n 100 /
    -n 200 log tails, the 28×32 checkbox inset and the ~11% chrome band. Each was a number the code
    owns and nobody re-measures, so each was a future drift ticket paid for in review attention.

    They are replaced with the behaviour and a pointer to the live source: browser-agent doctor reports what
    the daemon actually costs on the host it is running on, and the env table owns the config defaults. The
    log-tail commands become time-bounded (--since "10 min ago"), which is better advice for a cold-start
    problem than a line count anyway.

  • docs/architecture.md described a design that no longer exists. It explained that the viewport origin
    is derived from the browser window's outer rectangle — which is the fallback, not the mechanism. Since
    1.0.9 the origin is measured once per session by locating a calibration marker, and the geometric estimate
    only runs when that cannot. An agent reading the old text would have looked for a chrome-height constant
    that was deliberately removed, and would have concluded the calibration did not exist. Rewritten to describe
    the measurement first and the estimate as its fallback, including why the estimate is a few pixels off and
    when a calibration is trusted.

  • AGENTS.md no longer restates the ruff selectors and line length (they live in [tool.ruff]) or the size
    of the Pillow wheel and trafilatura's dependency count.

v1.0.17

Choose a tag to compare

@github-actions github-actions released this 11 Oct 11:57

Added

  • The opt-in integration suite that AGENTS.md has been documenting all along.
    The guide promised BROWSER_AGENT_IT=1 pytest -m integration. The marker was
    registered in pyproject.toml, but nothing implemented the gate and no test carried
    the marker — so the documented command selected zero tests while implying the
    X11/Chrome layer was covered. The layer that actually breaks in production (Xvfb
    start, xdotool, scrot, the CDP handshake) had no automated coverage anywhere.

    tests/conftest.py implements the gate: integration-marked tests skip unless
    BROWSER_AGENT_IT is truthy, and skip again — with a different reason — when the
    host lacks Xvfb, xdotool or a capture tool. A missing opt-in is the operator's
    choice; a missing binary is a provisioning problem. Neither is a test failure,
    because a check that fails for the wrong reason gets disabled.

    tests/integration/test_x11_smoke.py covers X11 only, on a display that is not the
    daemon's :99 so it cannot drive a running daemon's cursor. The assertion that
    matters reads the cursor back from the X server with getmouselocation rather than
    trusting move_mouse's return value, which is the point it asked for and therefore
    proves only that the method ran. Also covered: the geometry the daemon asked for,
    a decodable PNG at the right size, the exit-0-with-stderr keysym quirk that P2-4 was
    written for, and that the display is re-bindable afterwards.

  • tests/test_integration_gate.py — the coverage claim is now itself asserted.
    The marker is registered, at least one test carries it, the gate is off by default
    and on for a truthy value, and a plain run reports the skip reason. Verified
    negative in both directions: deleting the tests fails two of these, and deleting
    the gate fails two others. A guard that needs Xvfb would itself be skippable, so
    these four run everywhere and cost about a second.

Documentation

  • AGENTS.md now states what is and is not covered, in those words. The old line
    said "real-Chrome tests are opt-in", which overstated it twice: the harness is X11
    only, and Chrome, CDP and the Turnstile solver still have no automated coverage
    anywhere — they are exercised against fakes, never against a real browser, and
    browser-agent doctor is the only thing that touches them. A reader now learns
    that from the guide instead of assuming the opposite.

v1.0.16

Choose a tag to compare

@github-actions github-actions released this 11 Oct 03:30

Added

  • /action accepts use_proxy — and now says so. The daemon has read the field on
    /action for its whole life without documenting it, which is exactly why nothing
    used it: an undocumented parameter is one no caller dares depend on. docs/api.md
    now documents it under /action, and tests/test_action_use_proxy.py pins the
    pass-through together with the documentation, so the two cannot drift apart the way
    the field itself did.

Documentation

  • Two consequences of the field that were previously invisible, both of them silent:

    • Switching the route restarts Chrome. Cookies and in-page state are lost, so the
      field belongs once per session, not once per click.
    • use_proxy: true with WARP not running degrades to direct without an error. The
      route is chosen only when is_warp_proxy_ready(). goto returns {ok, url, title}
      with no blocked field, so an agent that asked for WARP and got a datacenter-ASN
      wall sees a successful-looking response. The docs now say to check
      warp_proxy_ready from GET /status first, rather than leaving the reader to
      discover it.

    This is a pre-existing property of the field, documented rather than changed: making a
    forced route fail loudly would change /fetch behaviour for callers that currently
    pass use_proxy: true on hosts without WARP, and that is a separate decision.

  • use_proxy on /action means something different from use_proxy on /fetch. A
    fetch auto-retries a blocked page through WARP when the field is omitted; an action
    cannot, because it is already driving a live session. Omitting it there means "leave
    the session on its current route", and false actively forbids WARP. The tests pin
    that the default is None and not False — collapsing the two would make every plain
    click silently drag a proxied session back to direct.

v1.0.15

Choose a tag to compare

@github-actions github-actions released this 11 Oct 03:15

Added

  • /status is now a pinned contract instead of prose. The endpoint both repos and
    every script read lived only as a hand-maintained example in docs/api.md and a
    hand-copied object in the bridge's mocks, so nothing failed when either drifted —
    the failure surfaced in production as a confusing 500 from a field that no longer
    existed.

    tests/test_status_contract.py now asserts the exact field set the daemon emits,
    the type of each one, and that docs/api.md documents every field — in both
    directions, so a documented field that is not emitted fails too. A rename fails
    with the added and removed fields named.

v1.0.14

Choose a tag to compare

@github-actions github-actions released this 11 Oct 02:58

Added

  • doctor now hands the agent a runnable repair instead of a report. Every check
    that fails carries a fix field with the exact command that clears it — the apt
    package, the systemd unit to enable, the lock to remove, the key to set — and the
    plain-text output prints it indented under the check it belongs to. A check that
    passes carries no fix, because a repair for something that works is noise.

    This is what turns doctor --json from something an agent has to interpret into
    something it can act on, which is the difference between a diagnostic and a
    repair script.

Fixed

  • The repair for a missing binary named the binary, not the package. Xvfb the
    command ships in the xvfb package, so apt-get install -y Xvfb would not have
    installed it. A repair command that does not work is worse than none.

  • browser-agent fetch silently fell back to an in-process session when the daemon
    was down.
    It launched a whole Chrome in-process and printed the results exactly
    as if nothing had happened, so the agent never learned the daemon was down — and
    every subsequent invocation paid the cold start again. One line now goes to stderr;
    stdout stays the JSON contract.