OpenProgram · technical specification

Web Use 技术规格

本页只保留实现需要的接口、Page 身份、失败和验收规格。结构图与 Web Use / Computer Use 边界见 设计概览

WEB_USE PUBLIC API IMPLEMENTED · BROWSER-ONLY RUNTIME

公开命名与兼容边界

正式名称兼容规则
Agent / MCP toolweb_use模型工具列表只公开新名称,不同时公开两个等价工具。
HTTP bridge/api/web-use/api/computer-use 只作为隐藏兼容路由,复用同一 handler。
Sessionweb_session_idWebUseSessionRegistry旧 HTTP 路由可以把 computer_session_id 转换为新字段;Runtime 不维护两份 session。
Computer Use保留给未来桌面控制本次不新增桌面 tool、配置、adapter 或截图进程。

三个执行 backend

Playwright MCPChrome DevTools MCPOpen Claude in Chrome-compatible
设置值playwright_mcp 默认chrome_devtools_mcpopen_claude_chrome
Runtime adapter固定版本的官方 @playwright/mcp,作为私有 child process 通过 stdio 接入。固定版本的官方 chrome-devtools-mcp,通过 MCP adapter 绑定已授权 CDP target。OpenProgram 实现兼容工具契约,并通过第一方 Electron/CDP adapter 绑定已授权 WebContents target;不运行原始 Chrome extension/native host。
观察Accessibility snapshot、DOM/ARIA、稳定 ref。DOM snapshot、可访问性信息和受限 console/network diagnostics。页面结构、ref;语义定位不足时返回一张 viewport screenshot。
动作一个 canonical Action 映射为一个 allowlisted upstream action。同一个 Action 映射为一次 bound CDP action。同一个 Action 映射为一次 bound Electron/CDP action。
接入条件禁用 tabs/eval/storage/files 等越界工具;先通过 exact Page 隔离测试。禁止自行枚举或切换 Page;所有 target 由 Runtime adapter 指定。只复用工具契约与交互语义;Page identity、权限、transport 和 verification 均由 Runtime 决定。

设置只影响新的 WebSession。session 创建后 backend 冻结;失败、超时或 stale frame 都不能自动切换 backend,避免重复点击和重复提交。

BrowserContext、Window、顶部标签与 Page

层级表示什么控制目标变化规则
BrowserContext同一浏览会话的 cookies、storage 与网络隔离边界。Context 销毁时,其下全部 Page capability 失效。
Window一个 OpenProgram 窗口;list_pages 聚合全部已登记窗口并保留各自 window_id某个窗口关闭或 control connection 重建时,只撤销该窗口的 inventory 与 token。
tab_entry顶部标签栏中的一个项目。single 含一个 Page;split 含两个有序 pane。分组、拆分、换位或比例变化更新 inventory_revision,不改变底层 Page identity。
PagePlaywright Page / Electron WebContents / CDP target 的统一身份。导航保留 identity;close 或 target replacement 撤销旧 token/session。
panePage 在当前 tab entry 中的布局位置,例如 order 0/1 与当前 bounds。它是 Page 的布局元数据,不创建第二种 observe/act API。

目标结构同时返回 tab_entriespages:前者回答窗口如何排列,后者列出 Agent 可以控制的网页。一个分屏 tab entry 有两个 Page,因此示例窗口是 3 个顶部标签项、4 个 Page。single entry ID 直接取 tab:<tab_id>,split entry ID 直接取 group:<CenterTabGroup.id>;pane ID 由 group 与当次 order 派生。这里不增加数据库表、第二套 registry 或后台进程。下面只展示 identity、布局和 token;title、origin、opener 与 capabilities 延用现有 Page 字段。

{
  "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_…" }
  ]
}

axisorder 是规范字段;left/right/top/bottom 由当次 bounds 派生,不能作为 Page identity。当前 UI 主要使用水平双 pane,但接口不把“右侧网页”写成固定目标。移动 Page、调整比例或切换 active tab entry 后,下一次 list_pages 返回新布局并递增 inventory_revision;受影响的 visible Page 继续通过 geometry revision 阻止旧布局坐标生效。一个窗口最多只有一个 focused_page,但 active split 中可以有两个 visible=true 的 Page。

开源、开放接口与不可直接复用部分

组件公开状态能否直接用在 OpenProgram 中的处理
Microsoft Playwright MCP开源 · Apache-2.0可以使用官方 @playwright/mcp,固定版本;外层保留 Page token、权限与 verification。
Playwright / playwright-core开源 · Apache-2.0可以作为 Playwright MCP 的执行层;不再实现第二套 DOM locator。
MCP TypeScript SDKSDK code:新贡献 Apache-2.0、未重许可 legacy code 为 MIT可以作为代码依赖复用现有 OpenProgram MCP server/client;MCP 只承担工具协议,不决定点击方式。
MCP protocol / specificationspec contributions:Apache-2.0;未重许可 legacy 内容为 MIT可以按规范实现采用标准 tools/list、tools/call、content 与 cancellation 语义,不复制特定 client 的浏览器工具。
MCP 非 specification 文档CC-BY-4.0可以引用或复用并署名只作为实现参考,不作为运行依赖。
Chrome DevTools Protocol开放协议与类型 · BSD-3-Clause可以直接调用 Page.captureScreenshotInput.dispatchMouseEvent 等命令,不需要 Claude 代码。
Electron webContents.debuggerElectron 开源 · MIT;API 已内置可以内置浏览器的首选 CDP transport;绑定 exact WebContents/target,不要求窗口置前。
Chrome debugger APINative MessagingChrome/Chromium 的公开内置接口,不是独立库可以调用只在以后控制独立 Chrome 扩展时需要;OpenProgram 内置浏览器不需要 Native Messaging host。
Open Claude in Chrome第三方 source-available 实现 · PolyForm Noncommercial 1.0.0;不是 Anthropic 官方项目当前非商业范围可评估其契约与实现内置浏览器只采用兼容工具契约与交互语义;运行 transport 是 OpenProgram Electron/CDP adapter,不依赖原始 extension/native host。
Claude Code 公开仓库仓库内容 all rights reserved;该结论只适用于此仓库不可以按开源代码复制不依赖其内部代码,只参考官方公开产品行为。
Anthropic 官方 Claude Chrome 扩展的 action handler、视觉光标、权限 UI 与 native host发布产品可安装;未公开可复用源码或明确的开源许可证不可以直接复制只参考官方公开行为;OpenProgram 保持自己的 web_use contract 和 backend adapter。
Claude 模型的视觉定位能力托管模型能力,不开源只能通过服务使用不依赖 Claude 专用实现;把单张截图交给当前所选模型的原生 vision。

这里没有“CDP 只能由 Claude 使用”的限制。Anthropic 官方工具编排、扩展实现和产品权限层没有公开可复用源码;截图与坐标点击所用的 CDP 命令是公开接口。第三方 clean-room 项目可以作为实现参考,但不代表 Anthropic 官方实现已经开源。

Agent 只需要五个 command

01list_pages

读取当前 turn 已授权 Page inventory,不调用 upstream tab 枚举。

02observe

选择 Page 并创建 WebSession;返回有界文本、ARIA 和 refs。

03act

携带最新 frame,只执行一个 click、type、press、scroll、select 或 navigate。

04verify

只执行 Runtime assertion;模型声称“完成”不能产生成功。

05close

释放 WebSession、Page binding 和调用方持有的 capability。

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 的目标返回全部已登记 OpenProgram window 的 group-aware snapshot:windows[] 分别保存每个窗口的 window_idinventory_revision、active/focused identity 与 tab_entries;扁平 pages 让 Agent 直接选择 Page,每项携带所属 window_id。原顶层窗口字段继续表示发起任务的 primary window,以兼容现有调用方。每个 Page 包含稳定的 tab_id、所属 tab_entry_id、placement、title、origin、focusedvisible、可选 opener_tab_id 与一次性 page_context_token。Agent 不用 window、tab entry 或 pane 执行动作,只在首次 observe 中原样回传目标 Page token。token 绑定 MCP control connection、window_id、exact Page target 和 server-owned page/access revision;缺少、重放、跨连接使用、连接重建或 target replacement 必须在 backend 调用前拒绝。首次 observe 之后,每次 observe、act 和 verify 都重新验证 exact binding。visible Page 继续验证 geometry revision;background Page 通过不改变 tab、store 或窗口焦点的 resolve path 校验 target。跨进程调用必须把 WebSession 保存的 revision 原样交给父 worker,任一不一致即返回 page_context_stale,且不得执行页面动作。

网页 popup 创建独立 web tab 和独立 WebContents/Page,并保存 opener_tab_id。popup 不替换 opener,也不隐式转移现有 WebSession;下一次 list_pages 发现 popup,Agent 使用它的 token 显式建立新 session。GUI Agent Harness 在每次写动作后刷新有界 Page inventory,并只通过 token 切换 Page。跨窗口聚合只扩展发现范围,不改变 exact Page binding、Page lease 或 session 生命周期。

每个可见 web tab 还持有 renderer-owned 单调 geometry_revision。现有 bounds lifecycle 在 mount、resize、ancestor scroll、pane 重排、窗口 resize、pane 显隐与 transfer 时测量 viewport-relative bounds;只有规范化后的 x/y/width/height 或应用内可见性变化才递增 revision,focus 变化不递增。发送 turn 时,surface ref 带当前 revision;preview 必须在调用 native preview 前验证并回显同一 revision。WebSession 保存该值,后续 observe、act 和 verify 经父子进程 bridge 原样传到 exact renderer;renderer 在调用 Electron activate 前再次比较。缺失的旧客户端 revision 保持兼容,但一旦 capability 含有非零 revision,任何不一致都返回 page_context_stale,不得调用 native preview、activate 或 backend。Page identity 与 lease 不因单纯 geometry 变化而重建;调用方重新 list/observe 后继续使用同一 Page。这里的可见性是 OpenProgram 内部 pane 布局状态,不是 macOS 窗口层级、遮挡或最小化状态。

成本与功能边界

DEFAULT

DOM / ARIA

URL、title、页面文本、accessibility snapshot 和 element refs。普通网页交互不发送截图。

FALLBACK

一张 viewport screenshot

仅用于视觉验收、canvas 或语义定位失败。图片只进入紧邻的下一次模型请求;提示文本只记录 frame 与 viewport 元数据,请求结束后立即清除调用方持有的图片副本并撤销坐标权限。

OFF

昂贵视觉管线

不启用 OCR、YOLO、object detector、crop/zoom、component memory、vision memory、workflow replay 或自动学习。

实施顺序

PHASE 0
验证官方 MCP 隔离能力
连接测试 Electron CDP,验证 snapshot/ref 和 exact Page 限制。
出口:A/B 两个 web Page 与 chat Page 的越权测试通过;否则选 upstream contract adapter。
PHASE 1
扩展现有 OpenProgram MCP server
新增一个 browser-control tool 和一次性 Page token;保留现有非浏览器 tools。
出口:真实 initialize/list/call/cancel 通过,伪造与重放在 backend 前拒绝。
PHASE 2
完成 Page Registry 与 group-aware inventory
把现有 CenterTabStripEntryCenterTabGroup 和 Page inventory 组合成 tab_entries + pages;加入 inventory_revision、access、exact resolve 与失效处理。
出口:3 个 tab entry / 4 个 Page 示例、左右互换、上下分屏、后台 Page、双窗口与 target replacement 通过。
PHASE 3
接入 Playwright MCP backend
只开放 snapshot/ref 和允许的动作,复用共同 policy/verification。
出口:DOM、canvas、非前台、多 Page、popup 和失败路径通过。
PHASE 4
接入 Chrome DevTools MCP backend
实现 exact target adapter,禁止 upstream 自行选择 Page。
出口:与 Playwright backend 共用的 parity suite 通过。
PHASE 5
接入 Open Claude in Chrome-compatible backend
在 Electron/CDP adapter 上实现兼容工具契约、结构化读取、截图和 action cursor。
出口:同一 Page、权限、失败与 verification parity suite 通过;运行时没有 Chrome extension/native host。
PHASE 6
设置与安装版验收
增加一个 backend selector,并在默认 18100、/Applications/OpenProgram.app 中运行相同任务集。
出口:当前 session backend 冻结;没有双执行或自动 fallback。

发布前必须证明

  1. 工具面:浏览器控制只有一个 web_use,不暴露 backend 自带的第二套工具。
  2. Page 隔离:list_pages 可以列出其他窗口的 Page;选择 A 后,对 B、chat Page 和其他窗口未选 Page 的读写调用次数都是 0。
  3. 分组结构:Google、GitHub、Bilibili/YouTube 分屏返回 3 个有序 tab_entries 和 4 个唯一 Page;第三项为 split,pane order 与实际 UI 一致。
  4. 布局与身份:交换 split pane、调整 ratio 或拆分后,Page identity 保持且 inventory_revision 更新;受影响 visible Page 的旧 geometry 在写入前 stale,不得新建另一套 split action。
  5. 可见与焦点:active split 的两个 Page 都是 visible,只有一个 focused;前两个 single Page 是 background。切换 active entry 后下一 snapshot 反映新状态。
  6. popup inventory:A 打开的 popup C 单独出现并带 opener_tab_id=A;每个 token 只消费一次。
  7. 后台 Page:选择 B 后读取和动作只到达 B;active tab、visible ids、BrowserWindow focus 与系统鼠标均不变化。
  8. popup 切换:A 的 session 不因 C 创建而改绑;GUI Agent 只有选择 C 的 token 后才建立 C session,关闭 C 后其 token/session 在下一动作前失效。
  9. backend 冻结:设置变更不影响当前 WebSession,只影响下一 session。
  10. 副作用唯一:backend failure 不触发另一 backend;一个 Action 最多一次真实写入。
  11. 跨 backend 一致:同一任务得到相同 Page/frame 生命周期、权限、reason code 和 verification。
  12. Page Awareness:页面换到 chat 左侧或右侧后,下一 turn relation 与 preview 正确更新。
  13. 跨窗口发现:两个已登记 OpenProgram window 返回两个有序 window snapshot;同名 tab/page 不合并,每个 token 仍解析到原 connection、window_id 与 target。
  14. 窗口状态独立:DOM/ref 与 viewport screenshot 直接作用于相同 Page/WebContents;macOS 窗口层级、遮挡、位置与最小化状态不进入 Web Use 的选择、截图或动作条件,且没有窗口 focus、bringToFront()、整屏截图或 OS input 调用。
  15. 视觉限制:普通 DOM 流截图数为 0;canvas 流每次 observation 最多 1 张。
  16. 图片保留:截图只出现在紧邻的一次 provider request;同一请求的文字提示不含原始 bytes/base64,返回或异常后调用方 content 与 ToolReturn 不再持有图片,最终 artifacts 仍为空。
  17. 失败诚实性:空 assertion、stale frame、timeout、cancel、target lost 都不能返回 succeeded。
  18. Chat 稳定:GUI tool update 不重建历史 message/avatar/markdown DOM。
  19. 安装版双窗口事务:只连接默认 18100 的 /Applications/OpenProgram.app,通过正式 preload transfer API 创建真实隐藏目标窗口;目标 renderer 完成 inspect/accept/stage 后由源端拒绝临时 payload,事务必须回滚并关闭目标窗口。验收前后的窗口集合、源窗口 tab 状态和持久存储必须一致。

Backend parity suite 的规范矩阵

同一组用例必须通过 WebUseSessionRegistry.execute() 依次运行 playwright_mcpchrome_devtools_mcpopen_claude_chrome 的真实 adapter class。官方 MCP 的进程和网络边界可由记录调用的 test client 代替,但不得用三个同构 fake adapter 代替生产映射。矩阵比较 OpenProgram 的规范化结果,不比较上游特有的 ARIA 文本、ref 名称或诊断文字。

场景三个 backend 的共同结果额外不变量
observe返回非空 web_session_id、冻结后的 backend、当前 frame_id 与结构化页面观察只绑定授权的 exact Page;不调用 tab select、bringToFront() 或 OS input
allowlisted actionok=trueobserve_required=true,旧 frame 立即失效navigate/click/type/press/scroll/hover/select 各产生一次且仅一次 backend write;不自动 fallback
stale / unsupported分别返回 stale_observationunsupported_action在 backend write 前拒绝,调用次数为 0
backend action error返回 backend_action_failedobserve_required=true当前 frame 失效;其他两个 backend 调用次数为 0
verify同一 assertion 得到相同 passed、evidence 与 frame 语义不通过的 assertion 不能形成 succeeded 终态
screenshot一个当前 viewport PNG;tool-result 仅保留 frame_idviewportimage_attached图片只进入紧邻一次 provider request,并按视觉 artifact 生命周期释放
closeok=trueclosed=true;后续命令返回 web_session_not_foundcontroller、MCP client、Page lease 与 context 全部释放

当前实现证据

能力当前状态直接证据
内置 web tab exact CDP attach已验证desktop/main.js_actions/open_action.py
DOM-first task wrapper 与 point fallback已验证 baselineagentic_functions/browser_agent/__init__.py 与 component tests
视觉 artifact retention已实现调用方 ephemeral policy截图只发送给紧邻的一次模型请求;planner 文字只含元数据。正常返回或异常后清除 content image block 与 ToolReturn.images,坐标能力同步撤销;不写本地 artifact,最终 artifacts 为空。provider 侧保留不在本地 policy 的控制范围内。
通用 MCP server/client 与 image content已有基础openprogram/mcp_server/openprogram/mcp/
动态 Page geometry、registry、leasebaseline 已实现当前 turn surface 已按实际 pane 顺序生成 left、right 或 center;服务端为 connection、Page 和 access 分配单调 revision,renderer 为每个实际可见 web tab 分配 monotonic geometry revision,并以 Page revision 生成 canonical key。同一连接内 recapture 保持 Page identity;pane bounds 或应用内可见性变化使旧 WebSession stale,但不重建 Page identity。WebSession 对 canonical key 持有排他 lease;session 内操作串行执行,首次及后续 observe、act 和 verify 在 backend 前重新验证 exact binding。preview/activate Promise 完成后再次检查 ready、当前 bounds 和非零 geometry revision;pane 从当前布局移除或 transfer 后返回 page_context_stale,不向 backend 暴露过期 preview/target。后台 Page 使用 resolve,不要求成为当前 tab;macOS 遮挡、窗口层级、位置与最小化不属于该 revision。owner disconnect 立即撤销该连接的全部 binding 和 pending exact-socket request。137 项相关 Python 测试、完整 Web check、TypeScript 与生产 build 通过;安装版隐藏目标窗口的 stage、rollback、destroy 与无残留验收通过
同窗口多 Page 与 popup handoffgroup-aware browser-only baseline 已实现Renderer 为当前 window 的每个 web tab 生成 exact target、实时 URL/title、visible/focused/placement 与可选 opener;后台 Page 通过 native WebContents/CDP 解析,不置前。Agent 通过一次性 Page token 选择目标,WebSession 不因新 popup 自动改绑;GUI Agent 只在显式 switch_page 后切换。
Tab entry / split pane topology已实现并通过并发与生命周期测试browserPageInventory() 从同一次 store snapshot 返回 tab_entries + pagesactive_tab_entry_idfocused_page 与单调 inventory_revision;pane 顺序使用实际渲染的 visibleIds。每个 Page capability 独立持有 binding context,关闭一个 Page session 不会撤销同批次其他 Page。
跨窗口 Page inventory已实现,安装版 E2E 通过每个 Desktop renderer 先登记 window_idlist_pages 返回 windows[] 与扁平 Page 列表,binding 保留 connection/window/tab/target/revision。connection revision CAS 拒绝断线后的延迟结果;origin 断开时其他在线窗口继续可发现,origin 仍在线但 inventory 失败时不提升 secondary。安装版在两个窗口中选择 detached window Page 完成 observe → screenshot → close,前台应用保持不变。
内部后台 Page 控制独立安装版自动化 E2E测试自行创建临时 Page 并切回原 tab,使目标 WebContents 保持 visible=false;随后通过 exact Page CDP session 完成 DOM observe、单张 viewport screenshot、ref click、observe、DOM verify 和 close,每个 Agent 动作后原 tab 均保持可见。Web Use 不读取系统屏幕,也不查询或改变窗口是否前台、最小化、位于何处或被遮挡;不调用 browser.close()、窗口 focus、bringToFront() 或 OS input。
Playwright MCP backend已实现 baseline,安装版 E2E 通过固定 @playwright/mcp@0.0.79;通过官方 createConnection(contextGetter) 只提供 exact Page,不调用 tab select 或 bringToFront();只映射 allowlisted action。安装版在同一 exact target 上完成 navigate → observe → click → observe → verify → close
Chrome DevTools MCP backend已实现 baseline,安装版 E2E 通过固定 chrome-devtools-mcp@1.7.0;marker 绑定 exact pageId;启用 pageId routing。安装版在同一 exact target 上完成完整命令序列与 DOM assertion。
Open Claude in Chrome-compatible backend已实现 baseline,安装版 E2E 通过复用现有 exact Page DOM/ref、单截图和坐标 action;不运行 Chrome extension/native host。安装版在同一 exact target 上完成完整命令序列与 DOM assertion。
multi-backend selector已实现 command selectorobserve 选择 backend 并冻结到 WebSession;后续变更返回 backend_mismatch
Backend parity suite已实现并独立复核生产 adapter 经公共 Registry 运行统一矩阵;覆盖 7 个写动作、stale/unsupported 的零写入、后端错误与真实 Playwright timeout 的统一 reason code、frame invalidation、PNG screenshot、verification、close 后 lease 重获及无自动 fallback。

表中的“已实现”表示当前 multi-backend、geometry、transfer、视觉 artifact 生命周期与 parity suite。专用 permission card 仍属于 UI 后续项,不是当前功能实现的发布条件。当前实现与验证证据维护在 实施记录

需要实现者展开时再读

Page identity 与生命周期
Canonical Page 绑定 window_id + registry_page_id + tab_id + cdp_target_id + revisionstab_entry_id、pane order、split ratio 和 inventory_revision 不是 Page identity;它们描述同一次 inventory snapshot。首次 observe 建立 WebSession binding,并保存 capability 中的 page/access/geometry revision。后续命令先在父 worker 比较 page/access revision,再由 exact renderer 在 native activate 前比较 geometry revision;任一比较失败都不调用 Electron 或 backend,并清理当前 session。focus 变化不改变 target;pane 位置、bounds 或可见性变化保留 Page identity,但会使依赖旧 geometry 的 session stale;transfer、close、target replacement、access disable 或 disconnect 仍会在下一次副作用前撤销 binding。
Popup 与并发
当前 browser-only 增量采用 Electron 已有的同步 popup request:main 拒绝原生 BrowserWindow,renderer 创建独立 web tab/WebContents,并记录 opener。它不把 popup 自动归因成当前 action 的成功结果,也不隐式改绑 session;GUI Agent 在写动作后的 Page inventory 中发现并显式选择。相同 exact Page 的 WebSession 写 lease仍排他,不同 Page 可并行。
安装版双窗口验收
验收脚本只能连接已安装 App 的 CDP endpoint,禁止从源码启动 Electron、启动第二个 worker或增加生产测试接口。脚本先记录现有 shell targets 和源窗口本地状态,再用正式 tabTransfer.prepare/detach 提交一个唯一、未出现在源 store 中的临时 NTP payload。脚本在源端失败 sourceRemoved 调用点设置临时 CDP breakpoint,并直接连接真实目标 renderer,确认临时 tab DOM、目标 windowId 与对应 token journal 已存在;随后恢复源端,让 main process 执行 rollback、关闭仍未显示的目标窗口并清除 transaction。脚本在 finally 中使用带超时的正式 cancel,并仅在 shell target 集合回到基线、源状态字节未变化、目标三个 storage key 均为空且 transaction 已消失时返回成功。
视觉 artifact 生命周期
screenshot 的 PNG bytes 只在内存中转换成一个 provider image block,并且只附加到紧邻的下一次 planner request。传给 _step_prompt 的 previous result 先转换成 frame、viewport 与 image_attached 元数据,不允许 bytes 或 base64 进入文字通道。无论该请求正常返回还是抛出异常,finally 都删除调用方 content 中的 image block、清空原 ToolReturn.images 并撤销截图坐标能力;最终结果继续返回空 artifacts
统一失败语义
invalid_argumentsinvalid_capabilityambiguous_pagepage_context_stalestale_observationpage_access_disabledtarget_lostneeds_usertimeoutcancelledverification_failed 都由 Runtime 生成。backend 文本不能覆盖 reason code 或 completion。

官方参考