OpenProgram · technical specification

Web Use technical specification

This page contains only implementation interfaces, Page identity, failures and acceptance specifications. For architecture diagrams and Web Use / Computer Use boundaries, see design overview.

WEB_USE PUBLIC API IMPLEMENTED · BROWSER-ONLY RUNTIME

Public names and compatibility boundaries

LayerOfficial nameCompatibility rule
Agent / MCP toolweb_useExpose only the new name in the model tool list, never two equivalent tools.
HTTP bridge/api/web-useOld /api/computer-use remains only a hidden compatibility route using the same handler.
Sessionweb_session_id and WebUseSessionRegistryLegacy HTTP routes may convert computer_session_id to new fields; Runtime does not maintain duplicate sessions.
Computer UseReserved for future desktop controlAdd no desktop tools, configuration, adapters or screenshot processes in this task.

Three execution backends

Playwright MCPChrome DevTools MCPOpen Claude in Chrome-compatible
Setting valueplaywright_mcp Defaultchrome_devtools_mcpopen_claude_chrome
Runtime adapterPinned official @playwright/mcp connected as a private child process over stdio.Pinned official chrome-devtools-mcp, binding an authorized CDP target through the MCP adapter.OpenProgram implements the compatible tool contract through its first-party Electron/CDP adapter bound to authorized WebContents targets; it does not run the original Chrome extension/native host.
ObservationAccessibility snapshots, DOM/ARIA and stable refs.DOM snapshots, accessibility information and bounded console/network diagnostics.Page structure and refs; return one viewport screenshot when semantic location is insufficient.
ActionsMap each canonical Action to one allowlisted upstream action.Map the same Action to one bound CDP action.Map the same Action to one bound Electron/CDP action.
Integration prerequisitesDisable out-of-scope tabs/eval/storage/files tools; pass exact-Page isolation tests first.No independent Page enumeration/switching; Runtime adapters specify every target.Reuse only tool contracts and interaction semantics; Runtime owns Page identity, permissions, transport and verification.

Settings affect only new WebSessions. Freeze the backend at creation; failures, timeouts and stale frames must not switch it automatically, avoiding duplicate clicks/submissions.

Each WebSession of an official-MCP backend owns one private MCP process, launched with npx --prefer-offline. Both packages are pinned to exact versions, so a cached copy is used without a registry round trip; a slow or broken network therefore cannot outlast the MCP start timeout. A start that times out cancels the pending supervisor on its own event loop, which terminates the npx child before the loop closes. When the private process stops later, the next observe on the same WebSession starts a new process and rebinds the same exact target; that restart is read-only. act never writes through a process it had to restart, because upstream refs did not survive it: it returns computer_use_backend_unavailable, action_dispatched=false, observe_required=true and invalidates the frame. The backend never changes.

BrowserContext, Window, top tabs and Page

LevelMeaningControl targetChange rule
BrowserContextCookies, storage and network-isolation boundary for one browsing session.NoDestroying a Context invalidates all Page capabilities beneath it.
WindowOne OpenProgram window; list_pages aggregates all registered windows and retains each window_id.NoClosing a window or rebuilding its control connection revokes only that window’s inventory/tokens.
tab_entryOne item in the top tab bar.single contains one Page; split contains two ordered panes.NoGrouping, splitting, reordering or ratio changes update inventory_revision without changing underlying Page identity.
PageUnified identity for Playwright Page / Electron WebContents / CDP target.YesNavigation preserves identity; closing or replacing the target revokes old tokens/sessions.
panePage placement in the current tab entry, such as order 0/1 and current bounds.NoLayout metadata for a Page, not a second observe/act API.

Target structure returns both tab_entries and pages: the former describes window arrangement, the latter lists controllable webpages. A split tab has two Pages, so the example window has 3 top-tab entries and 4 Pages. Single-entry ID comes directly from tab:<tab_id>, split-entry ID from group:<CenterTabGroup.id>; pane IDs derive from group and current order. No new database tables, registry or background process. Only identity, layout and tokens are shown below; title, origin, opener and capabilities use existing Page fields.

{
  "browser_context_id": "ctx_1",
  "window_id": "win_1",
  "inventory_revision": 42,
  "active_tab_entry_id": "group:g3",
  "focused_page": "p4",
  "tab_entries": [
    { "id": "tab:w:google", "mode": "single", "pages": ["p1"] },
    { "id": "tab:w:github", "mode": "single", "pages": ["p2"] },
    {
      "id": "group:g3",
      "mode": "split",
      "pages": ["p3", "p4"],
      "split": {
        "axis": "horizontal",
        "ratio": 0.5,
        "panes": [
          { "pane_id": "pane:g3:0", "order": 0, "page": "p3" },
          { "pane_id": "pane:g3:1", "order": 1, "page": "p4" }
        ]
      }
    }
  ],
  "pages": [
    { "page": "p1", "tab_id": "w:google", "tab_entry_id": "tab:w:google", "visible": false, "focused": false, "placement": { "mode": "single" }, "page_context_token": "pct_…" },
    { "page": "p2", "tab_id": "w:github", "tab_entry_id": "tab:w:github", "visible": false, "focused": false, "placement": { "mode": "single" }, "page_context_token": "pct_…" },
    { "page": "p3", "tab_id": "w:bilibili", "tab_entry_id": "group:g3", "visible": true, "focused": false, "placement": { "mode": "split", "pane_id": "pane:g3:0", "order": 0 }, "page_context_token": "pct_…" },
    { "page": "p4", "tab_id": "w:youtube", "tab_entry_id": "group:g3", "visible": true, "focused": true, "placement": { "mode": "split", "pane_id": "pane:g3:1", "order": 1 }, "page_context_token": "pct_…" }
  ]
}

axis and order is canonical; left/right/top/bottom derive from current bounds and are not Page identity. Current UI mainly uses horizontal dual panes, but the API never fixes the target to the right webpage. After moving a Page, adjusting ratio or changing active tab entry, the next list_pages returns new layout and increments inventory_revision; affected visible Pages continue rejecting old coordinates through geometry revision. Each window has at most one focused_page, but an active split may contain two visible=true Pages.

Open source, open APIs and non-reusable parts

ComponentPublic statusDirect reuse?OpenProgram treatment
Microsoft Playwright MCPOpen source · Apache-2.0YesUse official @playwright/mcp with a pinned version; retain outer Page tokens, permissions and verification.
Playwright / playwright-coreOpen source · Apache-2.0YesExecution layer for Playwright MCP; do not implement a second DOM locator.
MCP TypeScript SDKSDK code: new contributions Apache-2.0; unrelicensed legacy code MITUsable as a code dependencyReuse existing OpenProgram MCP server/client; MCP defines the tool protocol, not how to click.
MCP protocol / specificationSpec contributions Apache-2.0; unrelicensed legacy content MITImplementable from the specificationUse standard tools/list, tools/call, content and cancellation semantics, without copying a particular client’s browser tools.
Non-specification MCP documentationCC-BY-4.0Reference/reuse with attributionImplementation reference only, not a runtime dependency.
Chrome DevTools ProtocolOpen protocol and types · BSD-3-ClauseYesCall directly Page.captureScreenshot, Input.dispatchMouseEvent and related commands without Claude code.
Electron webContents.debuggerElectron open source · MIT; API built inYesPreferred CDP transport for the embedded browser; bind exact WebContents/targets without foregrounding windows.
Chrome debugger API and Native MessagingPublic built-in Chrome/Chromium API, not a separate libraryCallableNeeded only for future standalone Chrome-extension control; the embedded OpenProgram browser needs no Native Messaging host.
Open Claude in ChromeThird-party source-available implementation · PolyForm Noncommercial 1.0.0; not an official Anthropic projectContract/implementation can be evaluated within current noncommercial scopeUse only compatible tool contracts/interaction semantics; runtime transport is OpenProgram’s Electron/CDP adapter, not the original extension/native host.
Public Claude Code repositoryRepository contents all rights reserved; this conclusion applies only to this repositoryCannot copy as open-source codeDo not depend on internal code; reference official public product behavior only.
Official Anthropic Claude Chrome extension action handlers, visual cursor, permission UI and native hostInstallable released product; no public reusable source or explicit open-source licenseCannot copy directlyReference official behavior only; OpenProgram retains its own web_use contract and backend adapters.
Claude visual-location capabilityHosted model capability, not open sourceService access onlyNo Claude-specific implementation dependency; provide one screenshot to the selected model’s native vision.

CDP is not restricted to Claude. Anthropic’s official orchestration, extension implementation and product permissions lack public reusable source; CDP screenshot/click commands are public APIs. Third-party clean-room projects are references, not proof the official implementation is open source.

Agents need only five commands

01list_pages

Read the Page inventory authorized for the current turn, without upstream tab enumeration.

02observe

Select a Page and create a WebSession; return bounded text, ARIA and refs.

03act

Carry the latest frame and perform one click, type, press, scroll, select or navigate.

04verify

Execute only Runtime assertions; model claims of completion cannot establish success.

05close

Release WebSession, Page binding and caller-held capabilities.

web_use({ command: "list_pages" })
web_use({ command: "observe", page_context_token: "pct_…", arguments: { detail: "interactive" } })
web_use({ command: "act", web_session_id: "cs_…", arguments: { action: "click", expected_frame_id: "f_…", ref: "e1" } })
web_use({ command: "verify", web_session_id: "cs_…", arguments: { action: "verify", expected_frame_id: "f_…", assertion: "text_contains", value: "Saved" } })
web_use({ command: "close", web_session_id: "cs_…" })

list_pages returns a group-aware snapshot of all registered OpenProgram windows: windows[] stores each window’s window_id, inventory_revision, active/focused identity and tab_entries; flattened pages lets the Agent select Pages directly, each carrying its owning window_id. Original top-level window fields still describe the originating primary window for compatibility. Each Page has stable tab_id, owning tab_entry_id, placement, title, origin, focused, visible, optional opener_tab_id and single-use page_context_token. Agents do not act on windows, tab entries or panes; the first observe simply returns the exact Page token. Tokens bind the caller owner, window_id, exact Page target and server-owned page/access revision. Reject missing/replayed/cross-owner tokens, rebuilt connections or replaced targets before backend calls. Repeating web_session_id without observe for the same owner, Page and backend reuses the WebSession and ID. Different owners/backends remain subject to exclusive leases. Chat owner is fixed to current session/assistant turn; dispatcher teardown releases all its WebSessions/Page leases on completion, retry, cancellation or exception, without relying on explicit close. Every subsequent observe, act and verify revalidates exact binding. Visible Pages also validate geometry revision; background Pages resolve targets without changing tabs, stores or window focus.web_use Page inventory, WebSession registry and renderer WebSocket registry must run in the worker main process. Chat runtime cards may retain agentic-function UI but must not move this tool to an isolated subprocess. A subprocess GUI Agent Harness loop calls the same worker-owned WebSession through a parent bridge and forwards revisions unchanged. Any mismatch returns page_context_stale without executing page actions.

list_pages does not depend on the originating window. When the originating window or the turn's binding is disconnected, the worker lists every other registered window; a workflow subprocess stays confined to its originating window and lists only that window again. While no Desktop window is registered, as when the App reopens after a worker or App restart, discovery waits up to 20 seconds for a window to register. It then returns desktop_unavailable with retry_command=list_pages instead of raising. An online originating window whose inventory request fails still fails at once and never promotes another window.

A page_context_token names its Page in every command. act or verify with a token and no live web_session_id reuses the live WebSession that token already opened. With an unused token, Runtime first opens the WebSession with a read-only observe. navigate, screenshot, wait and ref-free verify then continue on that frame. Ref or coordinate actions return the observation with action_performed=false, because their targets must come from an observation the caller has read. A token never falls back to the owner's latest WebSession on another Page, and an unknown web_session_id next to a token yields to the token.

Ended handles stay recoverable without being replayed. In memory, bounded, the registry keeps only the exact window_id/tab_id of WebSessions that ended (turn teardown, binding loss, failed start) and of tokens that expired or were consumed by an ended WebSession. Turn owners of one chat share a scope across turns; any other owner matches only itself. A later page_context_not_found, page_context_consumed or web_session_not_found from the same scope carries recovery_tab_id, recovery_window_id and recovery_command=observe. The Agent tool then lists Pages, observes that exact tab with a fresh token and returns the observation with recovered_page=true and previous_action_replayed=false. It never replays the previous command and never opens a URL it does not know. A caller-closed WebSession is not remembered. Without a remembered tab, for example after a worker restart cleared the registry, the failure carries the current pages with fresh tokens and one instruction: observe the task Page with its token. Every such failure also carries a message that names the next call.

Web popups create separate tabs and WebContents/Pages, preserving opener_tab_id. They do not replace the opener or transfer existing WebSessions. The next list_pages discovers the popup; the Agent explicitly opens a new session with its token. GUI Agent Harness refreshes bounded inventory after every write and switches only by token. Cross-window aggregation expands discovery only, leaving bindings, leases and session lifecycles unchanged.

Each visible web tab also has renderer-owned monotonic geometry_revision. Existing bounds lifecycle measures viewport-relative bounds on mount, resize, ancestor scroll, pane reorder, window resize, visibility changes and transfer. Only normalized x/y/width/height or in-app visibility changes increment revision; focus changes do not. Turn surface refs carry the revision; preview must validate and echo it before native preview. WebSession stores it and forwards it unchanged through the process bridge to the exact renderer for observe/act/verify. Renderer compares again before Electron activate. Missing revisions from old clients stay compatible; once capability revision is nonzero, any mismatch returns page_context_stale without native preview, activate or backend calls. Geometry alone does not recreate Page identity/lease; callers relist/observe and continue with the same Page. Visibility means in-App pane layout, not macOS stacking, occlusion or minimization.

Costs and functional boundaries

DEFAULT

DOM / ARIA

URL, title, page text, accessibility snapshots and element refs. Ordinary webpage interaction sends no screenshots.

FALLBACK

One viewport screenshot

Only for visual acceptance, canvas or failed semantic location. The image enters only the immediately following model request; prompt text records frame/viewport metadata only. Clear caller-held copies and revoke coordinate permission immediately afterward.

OFF

Expensive vision pipeline

Do not enable OCR, YOLO, object detectors, crop/zoom, component/vision memory, Workflow replay or automatic learning.

Implementation order

PHASE 0
Verify official MCP isolation
Connect test Electron CDP and verify snapshots/refs and exact-Page restrictions.
Exit: A/B web Pages and chat-Page authorization tests pass; otherwise use an upstream-contract adapter.
PHASE 1
Extend existing OpenProgram MCP server
Add one browser-control tool and single-use Page tokens; retain nonbrowser tools.
Exit: actual initialize/list/call/cancel pass; reject forgery/replay before the backend.
PHASE 2
Complete Page Registry and group-aware inventory
Combine existing CenterTabStripEntry, CenterTabGroup and Page inventory into tab_entries + pages; add inventory_revision, access, exact resolve and invalidation.
Exit: 3-tab/4-Page example, pane swapping, vertical split, background Pages, two windows and target replacement pass.
PHASE 3
Integrate Playwright MCP backend
Expose only snapshot/ref and allowlisted actions; share policy/verification.
Exit: DOM, canvas, background, multi-Page, popup and failure paths pass.
PHASE 4
Integrate Chrome DevTools MCP backend
Implement exact-target adapter; prohibit upstream Page selection.
Exit: shared parity suite with Playwright passes.
PHASE 5
Integrate Open Claude in Chrome-compatible backend
Implement compatible contracts, structured reads, screenshots and action cursor on Electron/CDP.
Exit: same-Page, permission, failure and verification parity passes; no extension/native host at runtime.
PHASE 6
Settings and installed-App acceptance
Add one backend selector and run the same task suite with default 18100 and /Applications/OpenProgram.app .
Exit: current session backend frozen; no duplicate execution or automatic fallback.

Required before release

  1. Tool interface: Browser control exposes only one web_use. It is available once Desktop Page inventory is registered. Turn-start preview only enriches current-Page context; one preview failure cannot remove the tool or expose the legacy independent-browser launcher. The Agent first calls list_pages, then uses returned page_context_token to select the exact Page.
  2. Page isolation:list_pages May list Pages in other windows; after selecting A, reads/writes to B, chat Pages and unselected remote-window Pages remain zero.
  3. Group structure: Google, GitHub and split Bilibili/YouTube produce 3 ordered tab_entries and 4 unique Pages; the third is a split with pane order matching UI.
  4. Layout and identity: Swapping split panes, changing ratios or splitting preserves Page identity and updates inventory_revision ; affected visible Pages reject stale geometry before writes, without a separate split-action API.
  5. Visibility and focus: Both active-split Pages are visible, only one focused; first two single Pages are background. Next snapshot reflects active-entry changes.
  6. popup inventory: Popup C opened by A appears independently with opener_tab_id=A; each token is consumed once.
  7. Background Page: After selecting B, reads/actions reach only B. Active tab, visible IDs, BrowserWindow focus and OS mouse remain unchanged.
  8. Popup switching: Creating C does not rebind A’s session. GUI Agent creates C’s session only after selecting its token; closing C invalidates its token/session before the next action.
  9. Backend freeze: Setting changes affect only the next WebSession, not the current one.
  10. Unique side effects: Backend failure never invokes another backend; one Action causes at most one actual write.
  11. Backend consistency: The same task has identical Page/frame lifecycle, permissions, reason codes and verification.
  12. Page Awareness: Moving a page left/right of chat correctly updates next-turn relation and preview.
  13. Cross-window discovery: Two registered OpenProgram windows return two ordered snapshots; same-name tabs/Pages are not merged. Tokens still resolve to original connection, window_id and target.
  14. Independent window state: DOM/ref and viewport screenshots operate directly on the same Page/WebContents. macOS stacking, occlusion, position and minimization are not selection/screenshot/action conditions. No focus, bringToFront(), full-screen screenshot or OS-input calls.
  15. Agent cursor: All three backend click commands temporarily enable capture listeners only in the exact Page. On actual pointer events, show a text-free, aria-hidden, pointer-events:none cursor/click ripple at viewport coordinates, removing it within 900ms. User clicks, failed actions without pointer events, other Pages and OS mouse are unaffected. Feedback rendering failure cannot change click results.
  16. Visual limits: Ordinary DOM flows take zero screenshots; canvas flows take at most one per observation.
  17. Image retention: Screenshots appear only in the immediately following provider request. Text prompts contain no raw bytes/base64; after return/error, caller content and ToolReturn hold no image, and final artifacts remains empty.
  18. Honest failure: Empty assertions, stale frames, timeout, cancellation and lost targets cannot succeed.
  19. Chat stability: GUI tool updates do not recreate historical message/avatar/Markdown DOM.
  20. Installed two-window transaction: Connect only to the default-18100 /Applications/OpenProgram.app, creating an actual hidden target window through the official preload transfer API. After target inspect/accept/stage, source rejection of the temporary payload must roll back and close the target. Window inventory, source tabs and persistent storage must match before/after.

Canonical backend parity matrix

Run the same cases through WebUseSessionRegistry.execute() against actual adapter classes for playwright_mcp, chrome_devtools_mcp and open_claude_chrome in order. Recording test clients may replace official-MCP process/network boundaries, but three equivalent fake adapters cannot replace production mappings. Compare normalized OpenProgram results, not upstream-specific ARIA text, ref names or diagnostic wording.

ScenarioShared backend resultAdditional invariants
observeReturn nonempty web_session_id, frozen backend, current frame_id and structured page observationBind only the authorized exact Page; no tab select, bringToFront() or OS input
allowlisted actionok=true, observe_required=true, immediately invalidating the old framenavigate/click/type/press/scroll/hover/select Each causes exactly one backend write; no automatic fallback
invalid / stale / unsupportedPublic Agent and MCP tools share one command-conditioned schema. Unused optional strings may be omitted; empty strings from old sessions/models mean unspecified.act Missing action or expected_frame_id returns invalid_arguments with complete missing_arguments; verify requires frame, assertion and nonempty value; close requires a nonempty session. A genuinely old frame returns stale_observation; unknown actions return unsupported_action. A successful write clears the remembered frame, so the next ref action without a frame returns invalid_arguments for expected_frame_id instead of filling in the replaced frame. stale_observation and a missing frame both carry observe_required=true, recovery_command=observe and a messagebefore backend writes, with zero calls. Missing arguments must not clear a valid observation; callers can fill them and reuse the same frame.list_pages Empty legacy optional fields must not fail before Runtime
backend action errorReturn backend_action_failed and observe_required=trueCurrent frame invalidated; other backends receive zero calls
verifyIdentical assertions produce identical passed, evidence and frame semanticsFailed assertions cannot produce succeeded terminal state
screenshotOne current-viewport PNG; tool results retain only frame_id, viewport, image_attachedThe image enters one immediately following provider request, then follows the visual-artifact release lifecycle
frame-independent actionscreenshot and wait without a current frame (after a write or a desktop timeout) first observe read-only and run on that new frame; if their frame changed underneath, they observe and retry once. navigate does the same only when the caller named no frame; an explicit old frame still returns stale_observationStill exactly one backend write. Refs from an automatic frame are rejected with stale_observation until an explicit observe; coordinates from the screenshot on that frame stay valid. One screenshot per frame still holds
closeok=true, closed=true; subsequent commands return web_session_not_foundRelease controller, MCP client, Page lease and context

Current implementation evidence

CapabilityCurrent statusDirect evidence
Exact CDP attachment to embedded web tabsVerifiedapps/desktop/main.js, _actions/open_action.py
DOM-first task wrapper and point fallbackVerified baselineopenprogram/programs/gui_browser_agent.py and tests/component/programs/browser/test_browser_agent.py
Visual-artifact retentionCaller ephemeral policy implementedScreenshots go only to the immediately following model request; planner text contains metadata only. After return/error clear content image blocks and ToolReturn.images, revoke coordinate capability, write no local artifact and leave final artifacts empty. Provider retention is outside local policy.
Generic MCP server/client and image contentExisting foundationopenprogram/mcp/server/, openprogram/mcp/
Dynamic Page geometry, registry and leasesBaseline implementedCurrent-turn surfaces derive left/right/center from actual pane order. Server assigns monotonic connection/Page/access revisions; renderer assigns visible-tab geometry revisions; Page revision forms the canonical key. Recapture in one connection preserves identity. Bounds/in-app visibility changes stale old WebSessions without recreating identity. WebSession exclusively leases the canonical key, serializes operations and revalidates bindings before backend calls for all observations/actions/verification. After preview/activate promises, recheck readiness, bounds and nonzero revision. Removed/transferred panes return page_context_stale without exposing stale previews/targets. Background Pages use resolve without requiring active-tab status; macOS occlusion, stacking, position and minimization are not revisions. Owner disconnect immediately revokes connection bindings and pending exact-socket requests. Passed 137 related Python tests, complete Web checks, TypeScript and production build; installed hidden-target staging, rollback, destroy and no-residue acceptance passed
Same-window multi-Page and popup handoffGroup-aware browser-only baseline implementedRenderer provides exact targets, live URL/title, visibility/focus/placement and optional opener for each web tab. Background Pages resolve via native WebContents/CDP without foregrounding. Agents choose via single-use tokens; new popups do not rebind sessions. GUI Agent switches only after explicit switch_page .
Tab entry / split pane topologyImplemented; concurrency/lifecycle tests passedbrowserPageInventory() Return from one store snapshot tab_entries + pages, active_tab_entry_id, focused_page with monotonic inventory_revision; pane order follows actually rendered visibleIds. Each Page capability owns its binding context; closing one Page session does not revoke others in the batch.
Cross-window Page inventoryImplemented; installed E2E passedEach Desktop renderer first registers window_id; list_pages Return windows[] and flattened Page list; binding retains connection/window/tab/target/revision. Connection-revision CAS rejects delayed disconnected results. Other windows remain discoverable if origin disconnects; an online origin with inventory failure does not promote secondary. Installed acceptance selected a detached-window Page across two windows and completed observe → screenshot → close without changing the foreground application.
Internal background-Page controlIndependent installed automated E2EThe test creates a temporary Page and restores the original tab, leaving target WebContents visible=false; an exact-Page CDP session then performs DOM observation, one viewport screenshot, ref click, observation, DOM verification and close. Original tab stays visible after each Agent action. Web Use reads no system screen and does not inspect/change foreground, minimization, position or occlusion; it calls no browser.close(), window focus, bringToFront() or OS input.
Agent click cursorImplemented; installed background-Page E2E passedExact Page installs one capture listener during Agent click commands; actual pointer events show a text-free, noninteractive cursor/ring removed within 900ms. Component acceptance covers DOM refs, viewport coordinates, all backends, user-click isolation and feedback failures not affecting actions. With installed target visible=false and original tab unchanged, the page click handler records cursor=true. No OS mouse movement, Page switch, screenshot dependency or labeling user clicks as Agent actions.
Playwright MCP backendBaseline implemented; installed E2E passedPinned @playwright/mcp@0.0.79; official createConnection(contextGetter) exposes only exact Pages without tab select or bringToFront(); map allowlisted actions only. Installed acceptance completed on one exact target navigate → observe → click → observe → verify → close.
Chrome DevTools MCP backendBaseline implemented; installed E2E passedPinned chrome-devtools-mcp@1.7.0; markers bind exact pageId with routing enabled. Installed acceptance completed the full command sequence and DOM assertion on one target.
Open Claude in Chrome-compatible backendBaseline implemented; installed E2E passedReuse exact-Page DOM/refs, single screenshots and coordinate actions without extension/native host. Installed acceptance completed the full sequence and DOM assertion on one target.
multi-backend selectorCommand selector implementedobserve selects and freezes WebSession backend; subsequent changes return backend_mismatch.
Backend parity suiteImplemented and independently reviewedProduction adapters run the shared matrix through the public Registry: seven write actions, zero writes for stale/unsupported cases, normalized backend errors/real Playwright timeouts, frame invalidation, PNG screenshots, verification, lease reacquisition after close and no automatic fallback.

Implemented here refers to multi-backend, geometry, transfer, visual-artifact lifecycle and parity suites. Dedicated permission cards remain future UI work, not release prerequisites. Current evidence is maintained in implementation records.

Further implementation detail

Page identity and lifecycle
Canonical Page binding window_id + registry_page_id + tab_id + cdp_target_id + revisions.tab_entry_id, pane order, split ratio and inventory_revision are not Page identity; they describe one inventory snapshot. First observe binds WebSession and stores page/access/geometry revisions. Later commands compare page/access in the parent worker, then geometry in the exact renderer before native activate. Any failure avoids Electron/backend calls and clears the session. Focus does not change targets; pane placement/bounds/visibility preserve identity but stale geometry-dependent sessions. Transfer, close, replacement, disabled access or disconnect revoke bindings before the next side effect.
Popups and concurrency
The browser-only increment uses Electron’s existing synchronous popup request: main rejects native BrowserWindow, renderer creates a separate tab/WebContents and records opener. Popups do not automatically prove action success or rebind sessions; GUI Agent explicitly discovers/selects them after writes. Exact-Page write leases remain exclusive; different Pages may run concurrently.
Installed two-window acceptance
Scripts may connect only to the installed App CDP endpoint, never launch source Electron, a second worker or new production test APIs. Record shell targets/source local state, then use official tabTransfer.prepare/detach to submit a unique temporary NTP payload absent from source store. Set a temporary CDP breakpoint at the source failure sourceRemoved call and connect to the actual target renderer to verify temporary-tab DOM, target windowId and token journal exist. Resume source so main rolls back, closes the never-shown target and clears the transaction. In finally use official bounded cancellation; succeed only after shell targets match baseline, source bytes are unchanged, three target storage keys are empty and the transaction is gone.
Visual-artifact lifecycle
screenshot PNG bytes become a provider image block only in memory, attached only to the immediately next planner request. The previous result sent to _step_prompt is first converted to frame, viewport and image_attached metadata; bytes/base64 may not enter text. On return or exception, finally removes image blocks from caller content, clears original ToolReturn.images and revokes coordinate capability; final results retain empty artifacts.
Shared failure semantics
invalid_arguments, invalid_capability, ambiguous_page, page_context_stale, stale_observation, page_access_disabled, target_lost, needs_user, timeout, cancelled and verification_failed are generated by Runtime. Backend text cannot override reason codes or completion. Failures the caller can recover from (stale_observation, web_session_not_found, page_context_not_found, page_context_consumed, computer_use_backend_unavailable, desktop_unavailable) carry a Runtime message that names exactly one next call, and that call works when made.

Official references