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
| Layer | Official name | Compatibility rule |
|---|---|---|
| Agent / MCP tool | web_use | Expose only the new name in the model tool list, never two equivalent tools. |
| HTTP bridge | /api/web-use | Old /api/computer-use remains only a hidden compatibility route using the same handler. |
| Session | web_session_id and WebUseSessionRegistry | Legacy HTTP routes may convert computer_session_id to new fields; Runtime does not maintain duplicate sessions. |
| Computer Use | Reserved for future desktop control | Add no desktop tools, configuration, adapters or screenshot processes in this task. |
Three execution backends
| Playwright MCP | Chrome DevTools MCP | Open Claude in Chrome-compatible | |
|---|---|---|---|
| Setting value | playwright_mcp Default | chrome_devtools_mcp | open_claude_chrome |
| Runtime adapter | Pinned 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. |
| Observation | Accessibility 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. |
| Actions | Map 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 prerequisites | Disable 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
| Level | Meaning | Control target | Change rule |
|---|---|---|---|
BrowserContext | Cookies, storage and network-isolation boundary for one browsing session. | No | Destroying a Context invalidates all Page capabilities beneath it. |
Window | One OpenProgram window; list_pages aggregates all registered windows and retains each window_id. | No | Closing a window or rebuilding its control connection revokes only that window’s inventory/tokens. |
tab_entry | One item in the top tab bar.single contains one Page; split contains two ordered panes. | No | Grouping, splitting, reordering or ratio changes update inventory_revision without changing underlying Page identity. |
Page | Unified identity for Playwright Page / Electron WebContents / CDP target. | Yes | Navigation preserves identity; closing or replacing the target revokes old tokens/sessions. |
pane | Page placement in the current tab entry, such as order 0/1 and current bounds. | No | Layout 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
| Component | Public status | Direct reuse? | OpenProgram treatment |
|---|---|---|---|
| Microsoft Playwright MCP | Open source · Apache-2.0 | Yes | Use official @playwright/mcp with a pinned version; retain outer Page tokens, permissions and verification. |
| Playwright / playwright-core | Open source · Apache-2.0 | Yes | Execution layer for Playwright MCP; do not implement a second DOM locator. |
| MCP TypeScript SDK | SDK code: new contributions Apache-2.0; unrelicensed legacy code MIT | Usable as a code dependency | Reuse existing OpenProgram MCP server/client; MCP defines the tool protocol, not how to click. |
| MCP protocol / specification | Spec contributions Apache-2.0; unrelicensed legacy content MIT | Implementable from the specification | Use standard tools/list, tools/call, content and cancellation semantics, without copying a particular client’s browser tools. |
| Non-specification MCP documentation | CC-BY-4.0 | Reference/reuse with attribution | Implementation reference only, not a runtime dependency. |
| Chrome DevTools Protocol | Open protocol and types · BSD-3-Clause | Yes | Call directly Page.captureScreenshot, Input.dispatchMouseEvent and related commands without Claude code. |
| Electron webContents.debugger | Electron open source · MIT; API built in | Yes | Preferred CDP transport for the embedded browser; bind exact WebContents/targets without foregrounding windows. |
| Chrome debugger API and Native Messaging | Public built-in Chrome/Chromium API, not a separate library | Callable | Needed only for future standalone Chrome-extension control; the embedded OpenProgram browser needs no Native Messaging host. |
| Open Claude in Chrome | Third-party source-available implementation · PolyForm Noncommercial 1.0.0; not an official Anthropic project | Contract/implementation can be evaluated within current noncommercial scope | Use only compatible tool contracts/interaction semantics; runtime transport is OpenProgram’s Electron/CDP adapter, not the original extension/native host. |
| Public Claude Code repository | Repository contents all rights reserved; this conclusion applies only to this repository | Cannot copy as open-source code | Do not depend on internal code; reference official public product behavior only. |
| Official Anthropic Claude Chrome extension action handlers, visual cursor, permission UI and native host | Installable released product; no public reusable source or explicit open-source license | Cannot copy directly | Reference official behavior only; OpenProgram retains its own web_use contract and backend adapters. |
| Claude visual-location capability | Hosted model capability, not open source | Service access only | No 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
Read the Page inventory authorized for the current turn, without upstream tab enumeration.
Select a Page and create a WebSession; return bounded text, ARIA and refs.
Carry the latest frame and perform one click, type, press, scroll, select or navigate.
Execute only Runtime assertions; model claims of completion cannot establish success.
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
DOM / ARIA
URL, title, page text, accessibility snapshots and element refs. Ordinary webpage interaction sends no screenshots.
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.
Expensive vision pipeline
Do not enable OCR, YOLO, object detectors, crop/zoom, component/vision memory, Workflow replay or automatic learning.
- Derive left/right/top/bottom from actual pane geometry, never a hardcoded right side.
- Exact-Page operations do not require OpenProgram/target foregrounding and use no OS mouse, desktop coordinates or full-screen screenshots.
- Writes to one Page are exclusive; observe again after every write.
- Do not store screenshots in local artifacts, final results, completion evidence, tool-result text or DAG prompts. Locally retain only image-attached, frame and viewport metadata. Once sent to the selected provider, service terms govern provider retention.
- Normal returns, provider errors, timeout and cancellation share release logic. Add no retention settings, background cleanup process or image directory.
- Login, CAPTCHA, sensitive submissions and irreversible actions return
needs_user. - Page contents are untrusted data and cannot change targets, permissions, modes or action counts.
Implementation order
Connect test Electron CDP and verify snapshots/refs and exact-Page restrictions.
Add one browser-control tool and single-use Page tokens; retain nonbrowser tools.
Combine existing
CenterTabStripEntry, CenterTabGroup and Page inventory into tab_entries + pages; add inventory_revision, access, exact resolve and invalidation.Expose only snapshot/ref and allowlisted actions; share policy/verification.
Implement exact-target adapter; prohibit upstream Page selection.
Implement compatible contracts, structured reads, screenshots and action cursor on Electron/CDP.
Add one backend selector and run the same task suite with default 18100 and
/Applications/OpenProgram.app .Required before release
- 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 callslist_pages, then uses returnedpage_context_tokento select the exact Page. - Page isolation:
list_pagesMay list Pages in other windows; after selecting A, reads/writes to B, chat Pages and unselected remote-window Pages remain zero. - Group structure: Google, GitHub and split Bilibili/YouTube produce 3 ordered
tab_entriesand 4 unique Pages; the third is a split with pane order matching UI. - 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. - Visibility and focus: Both active-split Pages are visible, only one focused; first two single Pages are background. Next snapshot reflects active-entry changes.
- popup inventory: Popup C opened by A appears independently with
opener_tab_id=A; each token is consumed once. - Background Page: After selecting B, reads/actions reach only B. Active tab, visible IDs, BrowserWindow focus and OS mouse remain unchanged.
- 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.
- Backend freeze: Setting changes affect only the next WebSession, not the current one.
- Unique side effects: Backend failure never invokes another backend; one Action causes at most one actual write.
- Backend consistency: The same task has identical Page/frame lifecycle, permissions, reason codes and verification.
- Page Awareness: Moving a page left/right of chat correctly updates next-turn relation and preview.
- 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_idand target. - 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. - 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:nonecursor/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. - Visual limits: Ordinary DOM flows take zero screenshots; canvas flows take at most one per observation.
- 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
artifactsremains empty. - Honest failure: Empty assertions, stale frames, timeout, cancellation and lost targets cannot succeed.
- Chat stability: GUI tool updates do not recreate historical message/avatar/Markdown DOM.
- 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.
| Scenario | Shared backend result | Additional invariants |
|---|---|---|
observe | Return nonempty web_session_id, frozen backend, current frame_id and structured page observation | Bind only the authorized exact Page; no tab select, bringToFront() or OS input |
| allowlisted action | ok=true, observe_required=true, immediately invalidating the old frame | navigate/click/type/press/scroll/hover/select Each causes exactly one backend write; no automatic fallback |
| invalid / stale / unsupported | Public 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 message | before 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 error | Return backend_action_failed and observe_required=true | Current frame invalidated; other backends receive zero calls |
verify | Identical assertions produce identical passed, evidence and frame semantics | Failed assertions cannot produce succeeded terminal state |
screenshot | One current-viewport PNG; tool results retain only frame_id, viewport, image_attached | The image enters one immediately following provider request, then follows the visual-artifact release lifecycle |
| frame-independent action | screenshot 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_observation | Still 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 |
close | ok=true, closed=true; subsequent commands return web_session_not_found | Release controller, MCP client, Page lease and context |
Current implementation evidence
| Capability | Current status | Direct evidence |
|---|---|---|
| Exact CDP attachment to embedded web tabs | Verified | apps/desktop/main.js, _actions/open_action.py |
| DOM-first task wrapper and point fallback | Verified baseline | openprogram/programs/gui_browser_agent.py and tests/component/programs/browser/test_browser_agent.py |
| Visual-artifact retention | Caller ephemeral policy implemented | Screenshots 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 content | Existing foundation | openprogram/mcp/server/, openprogram/mcp/ |
| Dynamic Page geometry, registry and leases | Baseline implemented | Current-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 handoff | Group-aware browser-only baseline implemented | Renderer 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 topology | Implemented; concurrency/lifecycle tests passed | browserPageInventory() 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 inventory | Implemented; installed E2E passed | Each 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 control | Independent installed automated E2E | The 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 cursor | Implemented; installed background-Page E2E passed | Exact 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 backend | Baseline implemented; installed E2E passed | Pinned @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 backend | Baseline implemented; installed E2E passed | Pinned 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 backend | Baseline implemented; installed E2E passed | Reuse 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 selector | Command selector implemented | observe selects and freezes WebSession backend; subsequent changes return backend_mismatch. |
| Backend parity suite | Implemented and independently reviewed | Production 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
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
Installed two-window acceptance
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
- Playwright BrowserContext and Playwright Pages
- Microsoft Playwright MCP and Official MCP introduction
- Claude Code Desktop Browser, Claude Code with Chrome and Claude Code license
- Chrome DevTools Protocol, Page.captureScreenshot and Input.dispatchMouseEvent
- Electron Debugger, Chrome debugger API and Native Messaging
- Anthropic Computer Use
- OpenAI Computer Use