Repository navigation
feat(native): a Playwright-free driver over raw DevTools (phases 1-2) - #9
Merged
Merged
Conversation
This was referenced Oct 23, 2026
LYRA_BROWSER_DRIVER=native launches an installed Chrome itself and drives it over lyra's own WebSocket (cdp_socket), with no Playwright, Node or chromedriver. It implements the slice of Playwright's async API that BrowserSession and the page-level tools use, so neither changes: - launcher: Playwright 1.64's measured default switches (the keychain pair that keeps the shared profile's cookies readable, the bfcache switch the guard depends on, popup blocking off), minus --no-sandbox: the sandbox stays on unless LYRA_BROWSER_SANDBOX is off. The endpoint comes from DevToolsActivePort; a held profile is recognised from the exit (0 when a windowed Chrome hands over, 21 / SingletonLock headless) and worded as 'profile is already in use' for session. - connection: commands by id, events by flat session, every waiting call failed as TargetClosedError when the stream ends. - context: tabs by browser-level auto-attach without waitForDebuggerOnStart, so cdp_guard keeps holding each new tab until its document filter is in place; downloads into a private dir; unheard dialogs dismissed. - page: goto/go_back/reload waited on per loader with Playwright's 'interrupted by another navigation' and 'net::ERR_ABORTED; maybe frame was detached?' wording, document responses, evaluate in an isolated world by default (never Runtime.enable) with world='main' on request, screenshots, tab front/close/opener. - errors are named and worded as Playwright's, because describe_failure, session and capture read class names and message text. Wiring: native forces guard_backend=cdp; CdpGuard.start takes the endpoint the launcher already read (the file is deleted on read); describe_failure counts lyra_browser.native errors as the driver's. read_form, read_draft and set_editor now evaluate in the page's own world on patchright, whose Page and Locator evaluate both take isolated_context (window.CKEDITOR is invisible from an isolated world, so the CKEditor sync silently did nothing there). On native, read_form and read_draft do too; set_editor's sync goes through a Locator, which native gets in phase 3. Element tools (click, type_text, the tree view) arrive in phases 3-4; until then native is opt-in and README says so. Verified: pytest 1707 passed (the 2 test_claude_plugin sha256sum failures are pre-existing on macOS); tests/native 48 unit tests; a real-Chrome smoke through the tools (testing/native_driver_smoke_261023_v1.py, 18/18 headless and headful): navigate with status, back, reload, screenshots, tabs, download saved, a dialog answered (the tab keeps evaluating), a popup adopted and closed back to its opener, read_form seeing window.CKEDITOR, and both a page-started navigation and a popup to a denied origin refused by the guard with no request reaching the server.
unohee
marked this pull request as ready for review
October 25, 2026 03:53
unohee
force-pushed
the
feat/native-cdp-driver
branch
from
October 25, 2026 03:53
3fc4686 to
dc9805a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
lyra depends on Playwright for every browser action. That has three costs:
playwright, and a 1.58 bundle loses element refs.Runtime.enable, andcontext.routedisables the cache; Kasada and DataDome refuse browsers that do either.This PR adds an opt-in
nativedriver: Chrome over raw DevTools on lyra's own WebSocket, with no Playwright, Node or chromedriver. Plan and phase-0 spike: AX-tree parity 324/324, isolated worlds withoutRuntime.enable, no document escaping the guard, cookies readable across drivers.What (phases 1–2 of 6)
LYRA_BROWSER_DRIVER=nativemakessession.load_driverreturnlyra_browser.native.async_native. It has the same shape asasync_playwright, soBrowserSessionand the page-level tools are unchanged.--no-sandbox.LYRA_BROWSER_SANDBOX=offturns it off for containers that cannot sandbox. The Cloudflare container will need that if it ever moves to native.DevToolsActivePort.evaluateruns in an isolated world by default, withworld="main"on request;nativeforcesguard_backend=cdp.CdpGuard.start(endpoint=)takes the endpoint the launcher already read, because the file is deleted on read.describe_failurecountslyra_browser.nativeerrors as driver errors.read_form,read_draftandset_editornow evaluate in the page's own world (isolated_context=False/world="main"). Before this,window.CKEDITORwas invisible to them under patchright.Not in this PR: element tools (
click,type_text, the tree view). They need selectors, actionability and input (phase 3) and the AX snapshot (phase 4).keyboard/mouseraise "not implemented by the native driver yet".autonever picks native.Verified
pytest: 1707 passed. Twotests/test_claude_plugin.pytests fail on master as well:bootstrap.shusessha256sum, which macOS does not have on/usr/bin:/bin.tests/native/: 48 unit tests.describe_failureandsession's matchers.Real-Chrome smoke through the tools (
testing/native_driver_smoke_261023_v1.py, gitignored), 18/18 headless and headful:read_formreadingwindow.CKEDITORfrom the main worldA held profile was measured live, headless and headful, and reads as
profile is already in use.