Skip to content

feat: native drives the browser by default (0.2.0) - #16

Merged
unohee merged 8 commits into
masterfrom
feat/native-ci-gates
Oct 25, 2026
Merged

unohee merged 8 commits into
masterfrom
feat/native-ci-gates

Conversation

@unohee

@unohee unohee commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

What

auto now picks lyra's own DevTools driver (native) whenever Chrome/Edge/Chromium is installed, and Playwright leaves the dependencies. A plain pip install lyra-browser installs no Playwright and downloads no browser.

  • auto = native → patchright → playwright; native is skipped only when no browser is found (session.load_driver, channels=).
  • playwright → [playwright] extra (dev keeps it for the parity gates).
  • Bug found by measurement and fixed: an auto launch that lands on native read guard_backend=route from the shared Config and asked native for context.route → BrowserContext.route is not supported. The session now launches on _launch_config() (native ⇒ cdp guard), and exposes effective_guard_backend(). The shared Config is untouched.
  • npx install-browser installs lyra-browser[playwright]==<v>; the plugin's install-browser skill pip-installs playwright first. npm/constraints.txt keeps playwright pinned (--extra playwright).
  • Native grows enable_request_filter, page.off, expect_download — what VEGA needs to drop Playwright from its own runtime (web_fetch fallback, Drive shared-file download).
  • Gates: redirect_hop asserts the guarantee of the guard that actually ran; cdp_guard finds/ends its browsers on macOS via ps. New .github/workflows/browser-gates.yml: 13 real-browser gates nightly + on demand, both drivers, ubuntu + Xvfb.
  • Version 0.2.0 across pyproject / npm / plugin.

Verification (macOS, Chrome 155)

  • Unit: 1809 passed (+ 5 new native hook tests), npm launcher 14 passed, ruff clean.
  • Fresh venv with no Playwright installed, default config (auto):
    • auto -> native
    • browser 31/31, click (all pass), click_refusal 134/134, redirect_hop 177/177, scriptredirect 25/25, dialogs 55/55, downloads 99/99, forms 29/29, snapshot 73/73, wait 65/65, motion 41/41, tabs 57/57 — 0 FAIL.
  • testing/native_expect_download_261025_v1.py: PASS.
  • AGT-4652 popup two-step at N=200 on native: running (result will be posted).

Linux: browser-gates.yml runs on ubuntu after merge (workflow_dispatch available).

Refs: AGT-4829 (M4), AGT-4834, AGT-4833, AGT-4837.

A plain `pip install lyra-browser` needs no Playwright and no browser download:
auto tries native first, which drives the Chrome, Edge or Chromium already on the
machine over raw DevTools, and falls back to patchright, then playwright, only on a
machine with no browser at all. Pinning LYRA_BROWSER_DRIVER=native skips the check,
so a launch there still answers browser_unavailable itself.

playwright moves from a dependency to the [playwright] extra (dev keeps it: the
parity gates run both drivers). Docs follow: README Drivers/Installation,
VEGA_INTEGRATION (the elicitation premise was stale since vega-agent PR #896),
HERMES_INTEGRATION, CLAUDE_CODE_PLUGIN.

Also adds .github/workflows/browser-gates.yml: thirteen real-browser gates nightly
and on demand for both drivers, on ubuntu with Xvfb, each gate its own process.
…ncies

The shim installs the base set, which no longer carries playwright; the test now
reads the direct dependencies from pyproject.toml instead of naming three, and
asserts playwright stays out.
…he server does not

Playwright left the server's dependencies, so the two paths that install a bundled
Chromium for a machine with no Chrome or Edge now install Playwright first:

- npx `install-browser` asks uvx for lyra-browser[playwright]==<version> (the extra
  between the name and the version, as PEP 508 has it).
- The plugin's install-browser skill pip-installs playwright into the plugin venv
  before `playwright install chromium`.

npm/constraints.txt keeps playwright pinned (exported with --extra playwright):
install-browser must get the release the lock was tested with, and a constraint
installs nothing by itself, so the server's own start is unchanged.
auto picks the driver only at launch, but the guard backend and the launch
switches were read from the shared Config, which still said auto/route. On a
plain install with Chrome that meant the native driver was asked for
context.route and raised "BrowserContext.route is not supported" before any
page opened.

The session now runs its launch on _launch_config(): for native, a copy of the
Config with driver=native and guard_backend=cdp (paths a caller set by hand are
kept). The shared Config the rest of the server reads is untouched.

Measured: in a fresh venv with no Playwright installed, verify_browser_e2e.py
--headless-only with no driver set passes 31 of 31.
auto can land on native, which always takes the cdp guard. The gate chose between
the 'after' and 'before' promises from LYRA_BROWSER_GUARD alone, so that run
asserted 'after' and failed ten cases the cdp backend passes (measured: 10 FAIL,
177 PASS once it followed the session).

session.effective_guard_backend() reports the guard in force without changing the
shared Config; the gate defaults --guarantee from it.
Version goes to 0.2.0 across pyproject.toml, npm/package.json and the plugin
manifest (the release gate checks all three agree). Minor, not patch: the default
driver changed, Playwright left the dependencies, and the plugin description no
longer implies every navigation asks first (consent still defaults to elicit in
the plugin; VEGA runs it off).
What VEGA needs to drop Playwright from its runtime, not only from lyra:

- NativePage.enable_request_filter(keep): judge this tab's top-level document
  requests at Fetch.requestPaused. A refused request is answered 204 (the page
  stays where it was), and a filter that raises refuses. web_fetch uses it to pin
  one origin.
- page.off(event, handler), Playwright's pair to page.on.
- NativePage.expect_download(timeout): the next download the page offers, for the
  Drive shared-file fallback. Verified against a real Content-Disposition
  attachment (testing/native_expect_download_261025_v1.py: PASS).

Unit tests on a fake connection: 204 refusal, fail-closed filter, off removes
exactly one handler, expect_download hands over and times out without leaking a
listener.
browsers_of/read_cmdline/process_alive read /proc, so on macOS the scenarios
section ran to the end (906 s, every case) and then raised FileNotFoundError in
the rig's cleanup, failing the section with its table lost. Off Linux they now
read ps instead. What genuinely needs /proc (open sockets, exact argv bytes)
stays skipped off Linux, as the gate already reports.
@unohee
unohee merged commit be3ef4f into master Oct 25, 2026
4 checks passed
@unohee
unohee deleted the feat/native-ci-gates branch October 25, 2026 15:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant