WebDriver BiDi Protocol Migration for Headless Agent Stacks

Firefox's CDP removal forces teams to migrate headless stacks to a new cross-browser standard.

Cover illustration for “WebDriver BiDi Protocol Migration for Headless Agent Stacks”
Written by
Marcus Trelawney
Updated
October 14, 2026
Reading time
10 min read
Sources cited
8 sources ↓

Migrating a headless agent stack to WebDriver BiDi means swapping out Chromium-only CDP calls for a protocol that was built cross-browser from the start, and it forces a hard look at which capabilities carry over cleanly and which don't. That's not a hypothetical risk. Selenium's maintainers ended up keeping a separate CDP version binding for every single Chrome release, a maintenance treadmill that became one of the main reasons the BiDi effort started in the first place.

Classic WebDriver had its own limitation, just a different one. It ran request-response over plain HTTP: the agent sends a command, waits, gets a response back. No streaming, no push. For a headless agent trying to catch console logs, watch network traffic, or track JS errors as they happen, that absence of event streaming meant building polling loops just to fake what should have been a live feed.

Firefox made the choice for a lot of teams. Starting with Firefox 129, CDP support got disabled by default, and while it could still be switched back on manually for a while, that was clearly a deprecation path, not a permanent accommodation Deprecation of experimental CDP support in Firefox. It's already the forcing function Deprecation of experimental CDP support in Firefox. Putting the pieces together makes the shape of the problem clear. Agents built on CDP are Chromium-locked by architecture, a constraint baked into the protocol's first design decisions. They're Chromium-locked by architecture, baked in from the protocol's first design decisions. CDP was Chromium-only by design (every CDP call in an agent stack was code that would never run on Firefox and broke whenever Chrome bumped its DevTools version).

WebDriver BiDi: protocol model, transport, and session structure

BiDi doesn't throw out WebDriver and start over. It extends it. Every HTTP-based WebDriver command an agent stack already relies on keeps working exactly as before; BiDi adds a WebSocket channel that runs alongside it.

The session model reflects that dual nature. When a BiDi session opens, the client ends up holding a standard HTTP WebDriver session and a WebSocket connection at the same time; commands can travel over either channel, but events only ever arrive over BiDi. The driver process, chromedriver or geckodriver, sits beneath that client-facing interface and does the actual bridging between BiDi messages and whatever protocol the browser speaks internally, producing the split between the standard the client talks to and the mechanism running inside the browser. BiDi is the standard the client talks to, not necessarily the mechanism running inside the browser itself.

One practical gain appears immediately in remote deployments. Where CDP needs two separate roundtrips to fetch an object ID and then its value, BiDi does both in one. That's not a cosmetic efficiency. When client and server aren't sitting on the same machine, which describes most cloud agent deployments, every roundtrip you cut is latency you don't pay for on every single call.

As of 16 September 2026, the specification itself is still a W3C Working Draft on the Recommendation track, not a finished Recommendation, and it keeps absorbing new modules rather than settling into a frozen shape W3C WebDriver BiDi. Some of those are mature. Others are placeholders for coverage that hasn't landed in browsers yet. The spec's CommandData union defines named domains including BrowserCommand, BrowsingContextCommand, EmulationCommand, InputCommand, NetworkCommand, ScriptCommand, SessionCommand, StorageCommand, and WebExtensionCommand.

Browser support as it stands

Safari does not, and no timeline for when that changes has been established. Chrome keeps speaking CDP alongside BiDi, mostly because DevTools itself still depends on it; Chromium's BiDi implementation is actually built on top of CDP internally, so the CDP bridge stays available for whatever Chrome-specific features BiDi hasn't absorbed yet.

Firefox's timeline is the one to track closely. From Firefox 129 onward CDP was no longer enabled by default, and it could be manually re-enabled via the remote.active-protocols preference in Firefox 129–140, but that workaround is no longer available: CDP and the preference itself were fully removed in Firefox 141 Deprecation of experimental CDP support in Firefox. For any agent stack that needs to run against both Chrome and Firefox, BiDi is now the only standardized path that handles event subscriptions under one code path for both browsers. Testplane's documented minimums for BiDi support are chrome@128 and firefox@129, which gives a concrete floor for anyone planning a rollout Deprecation of experimental CDP support in Firefox.

Safari is not yet supported, with no timeline established for it. For teams whose agents only ever touch Chromium, CDP still works as a bridge for whatever BiDi hasn't picked up yet, but that's a transitional posture, not a durable architecture. Testplane's documentation states that Chrome, Firefox, and Edge are supported. Edge is aligning, but Safari's absence is a real gap for any agent stack that must cover Apple's browser, so plan around it rather than assuming imminent support.

The coverage gap in CDP capabilities BiDi has absorbed

Diagram: BiDi Coverage vs. CDP: What's Migrated, What's Missing. Visualizes: Show the split between what WebDriver BiDi covers and what still requires CDP, anchored by the concrete 70% figure from QASkills.sh tracking.

QASkills.sh's tracking shows BiDi covers roughly 70% of CDP's total surface area. That's a meaningful majority, but it leaves a real remainder, and the shape of that remainder matters more than the headline number. Console log capture in particular is one of the cleanest wins, covering console log capture and JS error streaming.

What's missing is narrower but genuinely hard to replace. Heap profiling and performance tracing, the kind of deep instrumentation used for serious performance analysis, still requires CDP, and BiDi doesn't cover any of it yet. Mozilla's own roadmap lists several items not yet shipped: emulation of locales, timezones, and mobile devices, plus retrieval of network response bodies, all flagged as strongly requested and specifically called out as necessary for reaching parity with what Playwright's users expect.

The practical takeaway sits between those two lists. Sort each dependency into one bucket or the other before writing a single line of migration code. The spec's CommandData union already lists EmulationCommand and WebExtensionCommand, which signals where coverage is headed. But a domain existing in the spec text and a domain actually working in a shipped browser build are two very different states, and conflating them is how migration timelines slip. What BiDi handles well covers mainstream agent needs. In Selenium bindings, the script, log, and network modules are stable. As of Selenium 4.30, the input and storage modules are still experimental in Selenium bindings.

How the Tooling Ecosystem Has Moved

Selenium has been the most visible mover here, and its version history maps the whole transition almost step by step. BiDi opt-in has been available since Selenium 4.16, activated through the webSocketUrl capability W3C WebDriver BiDi QASkills.sh. Selenium 5.0 is currently planned to reach alpha builds in the third quarter of 2026, and it's expected to remove CDP-only APIs entirely in favor of BiDi QASkills.sh. Anyone tracking this closely should also expect some friction along the way: BiDi's method and class names have shifted between minor releases as the API stabilizes, so pinning a version and reading release notes before upgrading is necessary. Java and Python bindings are ahead of the pack in maturity; other language bindings are catching up on a delay. The hardest case by far is a large Selenium Grid deployment, since the ecosystem's dependence on the HTTP request-response model means adopting BiDi often calls for real refactoring, not a single flag flip.

Puppeteer has taken a hybrid approach. It still connects to Chromium natively over CDP, but BiDi is now the default transport for Firefox and available as an opt-in, standards-compliant alternative for Chromium. What's notable is what this shift means structurally: BiDi adoption is what actually makes Puppeteer a cross-browser tool, something it wasn't before.

WebdriverIO generates its protocol commands straight from the current living WebDriver BiDi specification, and enabling it follows the same pattern as Selenium: set webSocketUrl: true in the capabilities. The WebdriverIO docs carry their own caution flag too, noting that browser support isn't guaranteed and interfaces can still change while the spec stays under active development.

Testplane added BiDi support in version 8.27.0, with documented minimums of chrome@128 and firefox@129, matching the numbers cited elsewhere in the ecosystem. Testplane's team made the switch specifically to drop its Puppeteer dependency and get away from instability they'd run into with CDP-based implementations. The same webSocketUrl: true flag applies inside capabilities, following the same opt-in pattern as Selenium.

Cypress started adopting BiDi as of February 2025 for parts of its testing infrastructure Medium. WebDriver BiDi is also relevant here, even though it isn't undergoing the same migration, since Playwright runs its own patched Firefox builds rather than the branded release. Agents that need to test the actual, real Firefox browser should look to WebDriver BiDi rather than Playwright's Firefox channel. In Selenium 4.27–4.28, CDP support for Firefox was deprecated. Selenium and Medium report that in Selenium 4.29.0, released February 2025, CDP support for Firefox was fully removed, and new BiDi capabilities were added including setCacheBehavior, getClientWindows, and network response handlers. In Selenium 4.27–4.30, BiDi parity was added for the network, script, log, and input modules, and the CDP-based DevTools API was officially deprecated, slated for removal in 5.0. Network response body retrieval is noted in Mozilla's roadmap as a key requirement for Playwright-parity scenarios.

Cloud and infrastructure considerations for BiDi agent deployments

Cloud providers aren't uniform in how far along they are here. BrowserStack moved early to make WebDriver BiDi production-ready, and it holds status as an official Development Partner of Selenium, with several core Selenium maintainers on staff. SauceLabs is following the same trend, building out its own BiDi support.

Infrastructure teams need to know one thing before anything else: Selenium WebDriver doesn't natively attach to an arbitrary remote CDP WebSocket the way Playwright or Puppeteer do. A hosted browser sitting inside a Selenium Grid is a different integration problem entirely. It's a different integration problem entirely. That means any team running Selenium Grids has to check, deliberately, that both the grid version and the cloud provider support BiDi sessions before routing production agent traffic through them.

The WebSocket transport itself behaves differently from HTTP in ways that go beyond the automation code. Connection lifecycle, timeout handling, proxy traversal, all of it works differently under BiDi, and that's an infrastructure team's problem as much as it is an automation engineer's. For agents running inside CI pipelines, whether BiDi is usable comes down to one detail: whether the grid or cloud endpoint returns a webSocketUrl in the session capabilities. If it doesn't, BiDi isn't available, no matter what the client is asking for.

Mapping CDP calls to BiDi domains: a migration reference for common agent patterns

The useful way to think through this isn't protocol-first, it's use-case first: an engineer looking at this wants to know, "here's what I do with CDP today, what's the BiDi equivalent".

BiDi replaces network interception with network.addIntercept set to the beforeRequestSent phase, with responses handled through network.provideResponse. The subscription model underneath asks the agent to register a listener for events like network.beforeRequestSent or network.responseCompleted through session.subscribe.

Console log and JS error capture moves from CDP's Runtime.consoleAPICalled and Runtime.exceptionThrown over to a single subscription: the log module's log.entryAdded event.

Script execution changes shape more than most people expect. CDP needed two separate calls, Runtime.evaluate followed by Runtime.getProperties, to evaluate a script and then inspect its result. BiDi's script module folds that into one roundtrip, which is a real latency win for any remote deployment where the client and the browser aren't sitting next to each other.

Selenium 4.29.0 added browser.getClientWindows to use instead of CDP target enumeration.

Full-page screenshots run through browsingContext.captureScreenshot with origin set to "document", which captures the entire page rather than just the visible viewport. Session lifecycle overall runs through session.new, session.end, session.subscribe, and session.unsubscribe, with subscriptions scoped by event name and, optionally, by browsing context or user context.

In Selenium's bindings as of version 4.30, the storage and input modules are functional but still marked experimental, usable with caution, with the expectation that interfaces might still change before 5.0 lands. In CDP, this is handled via Fetch.enable / Fetch.requestPaused. In CDP, target management is handled via Target.* commands. Heap snapshots, performance traces, and very low-level Chromium instrumentation remain CDP-only for now, noted explicitly so engineers know where the hybrid boundary sits.

Sequencing the migration without breaking production agent traffic

Diagram: Migration Sequence: Three Phases from Audit to Production. Visualizes: Illustrate the recommended three-phase migration sequence for moving an agent stack from CDP to BiDi without breaking production traffic.

Start with an audit, not a rewrite. List every CDP call the agent stack makes, then classify each as (a) covered by a stable BiDi module, (b) covered by an experimental BiDi module, or (c) not yet covered. That single exercise tells you almost everything about how long the migration is going to take and where the risk sits.

The mechanism that makes a gradual migration possible is the opt-in flag itself. Setting webSocketUrl: true in capabilities turns BiDi on without ripping out the HTTP WebDriver layer underneath it. BiDi and CDP calls can then run side by side for as long as the transition needs. That coexistence is the whole reason this migration doesn't have to be a cutover.

Sequence by module, not by feature. Phase one should be the log and script subscriptions: both are stable across Selenium, WebdriverIO, and Testplane, and they carry the lowest risk of anything on the list. Phase two moves to network interception, which is stable inside Selenium's bindings too, but before flipping that switch in production, confirm that the cloud or grid provider actually returns a usable webSocketUrl. If that check is skipped, the migration doesn't fail loudly, it just silently doesn't happen, which is worse.

Methodology & sources

  1. WebDriver BiDi

    Provided the foundational specification details for the WebDriver BiDi protocol model, transport, and session structure discussed throughout the article.

  2. WebDriver Bidi Protocol | WebdriverIO

    Provided details on WebdriverIO's BiDi implementation, including generating protocol commands from the living spec and the webSocketUrl capability opt-in pattern.

  3. WebDriver BiDi protocol support | Testplane

    Provided Testplane's documented minimum browser versions for BiDi support and details on Testplane's reasons for adopting BiDi over CDP.

  4. Selenium BiDirectional BiDi Protocol Complete Guide 2026 | QASkills.sh

    Provided the figure that BiDi covers roughly 70% of CDP's total surface area and details on Selenium's BiDi opt-in history.

  5. Selenium WebDriver BiDi 2026: Official CDP Replacement | QASkills.sh

    Provided information on Selenium 5.0's planned removal of CDP-only APIs and its expected alpha timeline.

  6. WebDriver BiDi | Selenium

    Provided details on Selenium's BiDi module stability, the addition of browser.getClientWindows, and CDP removal from Firefox bindings.

  7. Vibe Engineering: Chrome DevTools Protocol from Extensions: You Don’t Need to Fork Chromium | by Dzianis Vashchuk | Medium

    Cited as a source for the February 2025 Selenium 4.29.0 release details, including CDP removal for Firefox and new BiDi capabilities added.

  8. WebDriver BiDi

    Provided the specific date cited for the W3C Working Draft status of the WebDriver BiDi specification.

Marcus Trelawney