可见内置 tab 与 CDP 数据面
playwright_browser 默认 engine=app,只控制 OpenProgram 内可见 web tab。打开或选择 tab 后,桌面壳返回 CDP target_id,Playwright 按精确 target 附着。
证据:browser.py:37-52,148-152;_actions/open_action.py:142-240。
本页定义 OpenProgram 的 Computer Use 当前目标设计。核心要求不是单独提供一个浏览器函数,而是让每个 chat turn 自动知道同一窗口中当前可见、与该会话关联的 surface,并在需要时直接观察和操作。第一阶段执行目标是 OpenProgram 桌面端内置浏览器 tab;不控制整个操作系统,也不接管用户日常 Chrome。设计只复用现有 GUI Agent Harness 的规划与验证思路,不启用 OCR、YOLO/检测器、component memory、vision memory 或自动学习;OpenProgram Runtime 负责 surface 注册、目标绑定、权限、动作执行、取消、并发和证据。
TARGET DESIGN · NOT IMPLEMENTATION EVIDENCE自动 Surface Awareness:只要 chat 与可见 pane/window 同属当前 compound group,renderer 就在每次发送消息时提交 surface 候选。Runtime 校验后把当前 surface inventory 和一个有界的 DOM/ARIA preview 加入本 turn 上下文。模型从推理开始就知道“右侧有什么 surface、它是否可读写”,不会先回答自己没有屏幕或页面访问能力。
公开入口:新增面向任务的 computer_use。工具只接收任务和可选的语义 surface alias,例如 right 或 focused;不接收 window_id、tab_id 或 CDP target。第一阶段只有 web_tab adapter。现有 playwright_browser 保留为底层专家工具,现有 gui_agent 保留为桌面实验入口,三者不互相改名。
工具可用性:存在已验证、可控 surface 时,dispatcher 必须在该 turn 主动加入 computer_use schema,不受 deferred 默认加载影响。页面问题使用 turn-start preview 或调用工具继续观察;页面动作必须调用工具。用户显式关闭网页控制时,Runtime 不提供 preview 和执行工具,并把禁用状态写入上下文。
浏览器实现:复用 Electron WebContentsView、共享的 persist:webtabs profile、精确 tab 的 CDP target,以及 Playwright 数据面。用户和 agent 始终看到同一个 tab。
Harness 分工:第一阶段只复用 GUI Agent Harness 的 observe → verify → plan → act 思路;不启用 OCR、YOLO/检测器、component memory、vision memory、自动 component learning、多轮 crop 或局部放大,也不直接复用其进程全局 target、全屏截图、容错后继续执行、LLM 判定 success 和自动持久化 workflow 的行为。
动作策略:ARIA/DOM 语义定位优先,CSS 只作为确定性补充。只有 canvas、无标签控件或 DOM 不可用时,才把一张当前 viewport screenshot 直接交给多模态模型返回坐标;中间没有 OCR、检测器或视觉记忆。每个写动作执行后都重新观察;禁止跨页面状态批量执行写动作。
权限策略:本地项目页允许任务内自动验证;外部站点按 origin 授权。读取与写入分级,登录、CAPTCHA、跨 origin、敏感提交和不可逆操作进入 needs_user,不得由模型自行确认。
| 术语 | 所有者与生命周期 | 含义 |
|---|---|---|
| surface | Desktop Surface Registry;从 open/register 到 hide/move/close/disconnect | 一个已登记的可见 UI 区域。第一阶段只有可执行的 web_tab surface;整个桌面不是 surface。 |
| compound group | Desktop layout store | 把 chat 与相邻 pane 组成同一可见布局的 UI 关系;它决定哪些 surface 可以进入该 chat 的候选集合。 |
| Surface Context | Runtime;单个 provider request/turn | 经过校验的 surface inventory、alias、capability 和有界 preview。它是模型开始推理前的上下文快照。 |
| binding | Runtime;单个 turn | 把 Surface Context 中的 key/alias 映射到精确 window/renderer/tab target;模型不能创建或改写。 |
| lease | ComputerUseRunner;单个 run | 工具调用后固定的 target 使用权。写 lease 排他;target 失效或 run 结束时释放。 |
| preview | Runtime;单个 turn | 在首个模型 token 前提供的低成本结构化页面摘录,不含 screenshot 或 element refs。 |
| Observation | WebTabAdapter;单个 frame | 工具运行期间捕获的完整受限 DOM/ARIA/refs 状态;每个写动作后必须更新。 |
| turn / provider request / run | Chat / dispatcher / runner | turn 是一条用户消息;provider request 是该 turn 的一次模型调用;run 是一次 computer_use 执行。session 可以包含多个 turn 和 run。 |
这里记录的是仓库当前代码,不代表目标能力已经完成。
playwright_browser 默认 engine=app,只控制 OpenProgram 内可见 web tab。打开或选择 tab 后,桌面壳返回 CDP target_id,Playwright 按精确 target 附着。
证据:browser.py:37-52,148-152;_actions/open_action.py:142-240。
现有浏览器工具已支持 selector、HTML、正文提取、ARIA snapshot、console、frame、tab 等能力,可以承担浏览器专用的语义观察层。
证据:browser.py:56-160;_actions/read.py:7-72。
webtab.command 广播到全部 WebSocket 客户端,pending 表接收首个同 req_id 的回执;命令没有 window_id,服务端也没有校验回执来自哪个 renderer。多个桌面窗口同时存在时,目标不确定。
证据:webui/ws_actions/webtab.py:18-69;web/lib/desktop-bridge.ts:478-562。
composer 当前只提交文本、session、模型参数和工具开关,没有提交该 session 所在 compound group 的可见 web tab,也没有 turn-start preview。虽然 browser_agent 属于默认工具集合,但它被 deferred;模型开始推理时不知道右侧页面存在,也没有一个已绑定工具可用于观察和交互。这是当前分屏对 Agent 无效的直接原因。
证据:web/components/chat/composer/legacy-send.ts:118-213;webui/ws_actions/chat.py:369-426;programs/__init__.py:71-151。
现有 screenshot 默认 full_page=True。视觉模型返回的图像坐标不能直接作为当前 viewport 的 page.mouse 坐标,滚动页还会产生额外偏移。
证据:_actions/read.py:75-105。
planner 两次解析失败会返回 done;结论 prompt 固定要求模型输出 "success": true,顶层结果再采用该值。因此“模型不再调用动作”不能等同于“任务成功”。
证据:tasks/execute_task.py:575-607,792-834,886-949;main.py:168-193。
浏览器 session、Harness action target、VM screenshot patch 都使用进程全局可变状态;主执行循环没有 cancel 参数;没有 base memory 时会自动学习 component,每次任务还默认把完整 history 写入 workflow JSON。第一阶段 runner 不调用这些路径。
证据:browser.py:198-203;action/input.py:412-454;adapters/vm_adapter.py:20-31;main.py:98-102,116-193;tasks/execute_task.py:956-969。
gui_agent(task=...) 直接指向内置浏览器后作为产品能力发布。必须先由新的 session-scoped runner 接管 completion、target、cancel、artifact 和 approval;GUI Agent Harness 只提供可验证的共享算法或 adapter。比较对象分为三类:产品内置浏览器、连接用户日常浏览器的扩展、面向开发者的截图/动作 API。它们的权限边界不同,不能用同一套默认值。
| 官方产品 | 目标与 profile | 交互与权限 | 对 OpenProgram 的结论 |
|---|---|---|---|
| OpenAI 内置 Browser | ChatGPT desktop 内与用户共享页面视图;使用独立于日常浏览器的 profile,可在需要时单独登录。 | 面向网站研究、操作、本地 web app 预览;网页内容按不可信输入处理。 | 第一阶段产品形态的直接参考。 |
| OpenAI Chrome extension | 控制已有 Chrome tab,继承用户日常 Chrome 登录态。 | 连接现有 tab 和 profile,站点内容与权限需额外审查。 | 作为后续独立 backend;不能与内置 profile 隐式互换。 |
| OpenAI Computer Use API | 应用提供 browser/VM/harness;模型读取 screenshot,返回 actions[]。 |
执行动作后必须回传新 screenshot;官方要求隔离环境、域/动作 allowlist、高影响操作保留人工确认。 | 采用 action loop 与人工确认原则;不照搬纯坐标策略。 |
| Claude Code Desktop Browser pane | 独立 clean profile;与 chat、diff、terminal 并列显示;本地项目页和外部站点都可打开。 | 本地 dev server 可自动验证;外部站点首次 action 按站点允许一次、持续允许或拒绝;write action 受 classifier 检查。 | 最接近 OpenProgram 当前 UI 与开发测试场景。 |
| Claude Code with Chrome | 扩展连接已登录 Chromium;browser task 新开 tab,并实时可见。 | 站点权限来自扩展;plan mode 中 read-only 无提示,click/type/navigation 等写操作提示;登录或 CAPTCHA 暂停给用户。 | 采用 read/write 分类和人工接管;profile 接入延后。 |
| Anthropic Computer Use API | client-side screenshot、mouse、keyboard tool;应用负责实际执行与结果。 | 官方建议 VM/container、domain allowlist、敏感后果人工确认,并明确 coordinate、tool selection、scroll 和 prompt injection 的可靠性限制。 | 采用 client-owned execution 与显式 failure;不把模型输出当执行事实。 |
官方来源集中列在本页末尾。OpenAI 当前文档明确说明内置 Browser 不在 Codex CLI 或 IDE extension 中提供,而是在 ChatGPT web/desktop 产品面中提供;Computer Use plugin 可以在 desktop app 的 Codex 面中启用。因此这里参考的是 Codex 所在桌面产品面的 Browser/Computer Use 交互,不是假设 Codex CLI 自带浏览器。OpenAI 同时明确区分独立 Browser 与 Chrome profile;Claude Code Desktop 进一步明确 clean Browser profile、本地自动验证、外部站点授权和 Browser/Chrome 的选择规则。
每 turn 自动 surface inventory;有界的 DOM/ARIA preview;基于可见布局的语义 alias;动态工具注入;独立 browser profile;同一可见 tab;run 内固定 target;observe/action/observe;按 origin 授权;本地项目页自动验证;登录和 CAPTCHA 人工处理;stop;网页内容按不可信输入处理。
DOM/ARIA 优先,单张 viewport screenshot 的模型原生视觉坐标按需降级;授权默认只持续当前 task,不先提供永久 Always allow;read 可以批量,write 每次最多一个状态变化;completion 由 Runtime 验证状态决定。
用户日常 Chrome profile、整个桌面控制、隐藏/headless browser、任意 JavaScript、cookie/storage 导出、文件上传下载、模型自动输入密码、跨 origin 自动继续、无约束批量写动作、自动永久保存截图和 workflow。
第一阶段只有下面这一套固定执行路径,不建立 enable_ocr、enable_yolo、vision_memory_mode 等配置,也不加载对应模块。只有 baseline 的失败数据证明缺少某项能力时,才单独评估是否增加。
| 状态 | 能力 | 执行规则 |
|---|---|---|
| 开启 | URL、title、origin、viewport、DOM text、ARIA snapshot、interactive refs | 每步的默认 observation;先用结构化信息规划和定位,不发送图像。 |
| 按需开启 | 一张当前 viewport screenshot + 模型原生 vision | 仅用于视觉验收、canvas 或 DOM/ARIA 无法定位的控件;不做 full-page screenshot、切片或多轮放大。 |
| 关闭 | OCR、YOLO/object detector、候选框检测、外部 grounding pipeline | 不导入、不初始化、不调用,也不为它们增加后台进程或依赖。 |
| 关闭 | component memory、vision memory、workflow replay、自动 component learning | 不读、不写、不检索;运行结束不形成可复用视觉记忆。 |
| 关闭 | iterative zoom、coarse-to-fine crop、多截图候选排序 | 第一阶段不实现。直接视觉 fallback 失败后返回明确失败或请求用户处理。 |
成本约束:turn-start preview 只包含 surface 元数据、可见正文的有界摘录、ARIA landmarks 和交互元素计数,不包含 screenshot、完整 DOM 或完整 accessibility tree。普通 DOM 表单和导航任务可以完全不向模型发送 screenshot;视觉任务每个 observation 最多发送一张当前 viewport screenshot。verification 优先使用 DOM/ARIA assertion,只有任务本身要求视觉判断时才再次使用图像。
每次发送消息时生成。告诉模型当前有哪些可见 surface、它们在 left/right/focused 中的位置、title/origin、可用能力和一个有界 preview。它是当轮上下文,不是长期 memory。
以 window、renderer、compound group 和 layout revision 校验客户端候选,签发短期 binding。只有 Registry 中已验证的 surface 才能进入模型上下文或成为工具目标。
第一阶段只有 WebTabAdapter。后续内部 panel、terminal 或文件预览必须各自提供受限 adapter;不能通过一个任意坐标工具隐式扩大到整个桌面。
surface validation、turn preview、tool availability、target selection、tab lease、approval、action validation、CDP attachment、artifact lifecycle、cancel、timeout、completion、audit events。
规划结构、动作后验证建议和失败后的下一步候选。第一阶段不调用 Harness 的 OCR、检测、component/vision memory、自动学习或 iterative zoom;输出都视为建议,不具有执行权限。
{
"name": "computer_use",
"input_schema": {
"type": "object",
"properties": {
"task": {"type":"string", "minLength":1, "maxLength":8000},
"surface": {"type":"string", "enum":["s1", "right", "focused", "web:1"]},
"url": {"type":"string", "maxLength":2048, "pattern":"^https?://"},
"max_steps": {"type":"integer", "minimum":1, "maximum":100, "default":20},
"max_seconds": {"type":"integer", "minimum":1, "maximum":1800, "default":300}
},
"required": ["task"],
"additionalProperties": false
}
}
surface 只能使用 Runtime 在当前 Surface Context 中提供的 canonical key 或 alias,例如 s1、right、focused 或 web:1;provider 实际收到的 schema 将其展开为当轮 enum。若省略且 primary_surface_key 非空,Runtime 使用 primary;否则返回 ambiguous_surface。第一阶段只有 web_tab adapter。window_id、tab_id、cdp_target_id 从不成为 LLM 参数。
url 只表示在已经获得 lease 的同一个 web tab 中执行初始 navigation,不创建或切换 tab;它仍需该 surface 具有 navigate capability,并经过 origin/risk policy。工具出现在 provider schema 中不等于所有 action 都可用;每次调用和每个 Action 都重新检查 capability,缺少能力时返回 capability_denied。
desktop window registration (Electron main → Runtime)
window_id, renderer_instance_id, one_time_connection_nonce
registered over the authenticated loopback control channel
renderer WebSocket hello
one_time_connection_nonce
Runtime binds ws_connection_id → window_id + renderer_instance_id
surface registry event (renderer → Runtime)
op: open | update | focus_set | hide | move | close
registry_surface_id
window_revision, surface_revision, layout_revision
kind, group_id, local_surface_id, region, visible, focused
surface_refs (renderer → chat command)
chat_tab_id, chat_session_id
window_revision, layout_revision
focused_registry_surface_id
visible_registry_surface_ids[]
server-owned Surface Context (Runtime → model)
context_id, context_revision, turn_id, provider_request_id
primary_surface_key: "s1"
alias_map: {right: "s1", focused: "s1", "web:1": "s1"}
surfaces[]:
surface_key: "s1"
aliases: ["right", "focused", "web:1"]
kind: "web_tab"
region: "right"
title, origin
capabilities: ["observe", "interact"]
preview_frame_id, content_epoch, captured_at, preview_status: "ready"
preview: {
visible_text_excerpt, text_truncated,
aria_landmarks[], landmarks_truncated,
interactive_count, payload_truncated
}
untrusted_content: true
server-owned binding (never sent as a model-selectable target)
binding_id
ws_connection_id, window_id, renderer_instance_id
chat_session_id, group_id, registry_surface_id, local_surface_id
window_revision, surface_revision, layout_revision
issued_at, expires_at
state: ready | leased | invalid
Electron main 先通过已有本地认证边界为每个桌面窗口登记不可复用的 window_id + renderer_instance_id + one_time_connection_nonce。renderer 建立 WebSocket 时只能提交该 nonce;Runtime 消费一次后把连接绑定到已登记窗口。普通 Web 客户端不能自行声明 desktop window identity。连接关闭时,Runtime 删除该连接下全部 registry entries,并使关联 binding 和 lease 失效。
renderer 以事件维护 server-owned registry,而不是在 chat command 中临时声明完整页面状态。每个窗口的 window_revision 和每个 surface 的 surface_revision 都必须单调递增;重复或旧 revision 被拒绝。open/update/focus/hide/move/close 事件更新 visibility、focus、region、compound group 和 layout。focus-only 更新只影响下一 turn 的 alias,不改变运行中的 lease;hide、移出当前 group、跨窗口 move、close、renderer/window disconnect 会立即使 binding/lease 变为 invalid,下一次副作用前返回 target_lost。
focus 不是单 surface 的布尔补丁,而是一个 window/layout revision 内的原子 focus_set(focused_registry_surface_id | null):Runtime 在一次提交中清除旧焦点并设置新焦点,因此 registry 不会出现两个 focused surface。跨窗口 transfer 也不是 move patch;源连接必须先 close/invalidate 旧 entry 和 lease,目标连接再以新的 registry_surface_id authenticated open。binding、surface revision、alias 和 lease 都不跨窗口继承。
renderer 在每次 chat command 中只引用 registry 已登记的可见 surface ID 和 revision。候选可以来自右侧 pane、当前 focused pane,或用户从 sidebar/按钮打开并已登记到 center-tab registry 的窗口。普通独立 chat、隐藏 group member、其他窗口和未登记的 native window 不进入引用集合。Runtime 根据 connection-owned registry 校验 session、group、visibility、focus 和 layout 后签发短期 binding;未知 ID、旧 revision 或跨连接 ID 在生成 preview 前拒绝。最终 origin、title 和 capabilities 由已绑定 adapter 读取,不采用 renderer payload 中的同名值。
Runtime 在模型调用前对每个已验证且允许读取的 surface 生成有界 preview。序列化顺序固定为 title、origin、DOM 顺序的 viewport 可见文本、ARIA landmark、interactive count;合并连续空白后,可见文本最多 2,000 个 Unicode 字符,landmark 最多 12 项且每项 name 最多 160 个字符,整个 preview 最多 8 KiB UTF-8。每个受限字段都有独立 *_truncated,总大小触顶时还有 payload_truncated。preview 不包含 screenshot、完整 DOM、完整 accessibility tree、element refs、iframe 隐藏内容或不可见节点。
preview 携带 captured_at + preview_frame_id + content_epoch + window_revision + surface_revision + layout_revision + navigation_epoch。Adapter 为每个 tab 维护 DOM mutation/navigation 驱动的 content_epoch,并在同一 tab 的 snapshot barrier 内捕获 DOM text 与 ARIA。捕获前后各读取一次 target、navigation、viewport、content epoch 和 registry revision;任一值变化就丢弃整份结果并重试一次,第二次仍不一致则标记 preview_status=unavailable,不能混合两个 frame 的内容。捕获超过本地 deadline 或页面暂不可读时也只返回 unavailable descriptor,模型随后使用工具重试观察。preview 只服务当前 turn,页面变化后失效,不进入 component memory、vision memory 或 workflow replay。
Surface Context 中的 title、正文和 ARIA 文本都标记为 untrusted_content,与用户消息和系统规则分隔。网页文字不能要求模型更换 surface、启用工具、扩大权限或执行动作;Runtime 也不能根据 preview 文本修改 capability 和 risk tier。
只要存在至少一个具备 observe capability 的 surface,dispatcher 就在该 turn 将 computer_use schema 放入 provider tool array,并追加 Surface Context。interact 和 navigate 都以 observe 为前置能力;Registry 拒绝缺少 observe 的 interact/navigate 组合。该规则优先于工具的 deferred 默认状态。模型因此在生成第一个 token 前已经知道右侧/当前页面存在;询问“屏幕右边有什么”时可直接依据新鲜 preview 回答,信息不足时调用 computer_use(surface="right", task="...") 继续观察。任何 click、type、navigate 等页面变更都必须调用工具,不能只根据 preview 声称已执行。
用户显式关闭网页控制时,Runtime 仍可告诉模型“存在一个未授权 surface”,但不附带页面内容、不注入执行工具。UI 必须显示“Agent 无法访问”,避免模型把权限禁用误判为页面不存在。将 chat 与 web tab 合并为同一 compound group,默认只授予该 surface 的 R0 结构化读取;R1-R3 写操作仍按后文策略处理。
工具开始运行时,Surface Broker 使用 binding_id 向精确 renderer 请求对应的 CDP target,随后把 window_id + local_surface_id + cdp_target_id + layout_revision 固定到 lease。运行期间切换焦点不会改变 target;关闭、移出 compound group、跨窗口转移或 renderer 断开会使绑定失效,并返回 target_lost,不得自动选择另一个可见 surface。
Dispatcher 保存不可变映射 provider_request_id → turn_id + context_id + context_revision + alias_map + bindings。每个 tool call 的执行 envelope 由 Runtime 写入 tool_call_id + source_provider_request_id,模型不能提交或覆盖它;alias 只能在产生该 tool schema 的同一个 provider request 快照中解析。即使同一 chat session 有重叠 turn 或后续 Surface Context,旧工具调用也不得查询 session 的“最新 alias map”。模型 response stream 结束不立即删除映射:没有 tool call 时可以释放;存在 tool call 时保留到该 response 产生的全部 tool call settled,或 initiating turn/run 被取消、超时、teardown。异常遗留映射按短 TTL 清理。
surface_key(s1、s2),并生成显式 alias_map。key 和 alias 只在该 Surface Context revision 内有效。left、right 或 center;同一 region 有多个候选时不生成该空间 alias。focused 只指向提交消息瞬间拥有焦点的可控非 chat surface。focus 在 turn 进行中变化不会修改当前 alias_map,但会反映到下一 turn。web:1、web:2 按提交瞬间 center-tab registry 的可见顺序生成;顺序相同则以 registry_surface_id 排序。一个 surface 可以同时拥有 right、focused 和 web:1。primary_surface_key 优先取 focused controllable surface;没有时取唯一可见 controllable surface;仍有多个候选时为空。模型可以传 alias 或 canonical key;未知、过期或没有唯一映射的值在任何 CDP 请求前失败。computer_use schema。{
"status": "succeeded | failed | cancelled | needs_user",
"reason_code": "verified | step_limit | timeout | target_lost | ...",
"summary": "grounded user-facing result",
"target": {"kind":"web_tab", "tab_id":"...", "url":"..."},
"steps_taken": 7,
"completion_evidence": [{"kind":"assertion", "text":"...", "frame_id":"..."}],
"artifacts": [{"kind":"screenshot", "artifact_id":"artifact_...", "retained":true}]
}
summary 可由 LLM 撰写,但 status 和 reason_code 只能由 Runtime 根据验证记录生成。达到 step limit、planner 无效或 conclusion 模型声称成功都不能生成 succeeded。模型和 transcript 只收到 opaque artifact_id;本地绝对路径不进入结果。终态结果只列用户明确保留或验收要求保留的 artifact,临时 retained=false 文件在返回前删除。
ComputerSession
session_id, owner_run_id
target: {window_id, tab_id, cdp_target_id}
lease_id, state, started_at, deadline
current_origin, approved_origins[]
current_frame_id, navigation_epoch, action_epoch
cancel_event, step_budget, artifact_dir
Observation
frame_id, content_epoch, captured_at
target identity, url, origin, title
viewport: {width, height, device_scale_factor, page_zoom, scroll_x, scroll_y}
screenshot: optional viewport-only PNG
screenshot_size: optional {width, height}
aria_snapshot, aria_truncated
interactive_elements[], elements_truncated
optional filtered console_errors[], console_truncated
frame_id 必须绑定 target identity、navigation epoch、viewport 和当前 DOM revision;存在 screenshot 时还要绑定图像。坐标 Action 只能来自同一个 observation 的直接视觉 fallback,并且必须带 expected_frame_id;如果页面已经导航、滚动、resize 或 target 改变,Runtime 返回 stale_observation 并重新 observe。
完整 Observation 仍有硬上限:ARIA snapshot 最多 12,000 个 Unicode 字符,interactive elements 最多 200 项且每项 name 最多 200 字符,console error 最多 50 条且每条最多 1,000 字符;每类都带 truncation 标志。密码控件的 value、typed secret、authorization/token 字段和 URL query value 默认替换为 redacted marker;URL 保留 scheme、host、port、path 和 query key,不把 query value 写入模型上下文或事件。
{
"$defs": {
"frame": {"type":"string", "pattern":"^frame_[A-Za-z0-9_-]+$"},
"intent": {"type":"string", "minLength":1, "maxLength":240},
"elementTarget": {
"type":"object",
"properties":{"kind":{"const":"element"},"ref":{"type":"string","pattern":"^e[0-9]+$"}},
"required":["kind","ref"], "additionalProperties":false
},
"pointTarget": {
"type":"object",
"properties":{"kind":{"const":"point"},"x":{"type":"integer","minimum":0},"y":{"type":"integer","minimum":0}},
"required":["kind","x","y"], "additionalProperties":false
},
"anyTarget": {"oneOf":[{"$ref":"#/$defs/elementTarget"},{"$ref":"#/$defs/pointTarget"}]}
},
"oneOf": [
{"type":"object","properties":{"type":{"const":"click"},"expected_frame_id":{"$ref":"#/$defs/frame"},"target":{"$ref":"#/$defs/anyTarget"},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","target","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"type"},"expected_frame_id":{"$ref":"#/$defs/frame"},"target":{"$ref":"#/$defs/elementTarget"},"text":{"type":"string","maxLength":10000},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","target","text","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"press"},"expected_frame_id":{"$ref":"#/$defs/frame"},"target":{"$ref":"#/$defs/elementTarget"},"key":{"type":"string","minLength":1,"maxLength":64},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","target","key","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"scroll"},"expected_frame_id":{"$ref":"#/$defs/frame"},"delta_y":{"type":"integer","minimum":-2000,"maximum":2000},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","delta_y","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"hover"},"expected_frame_id":{"$ref":"#/$defs/frame"},"target":{"$ref":"#/$defs/anyTarget"},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","target","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"select"},"expected_frame_id":{"$ref":"#/$defs/frame"},"target":{"$ref":"#/$defs/elementTarget"},"value":{"type":"string","maxLength":2000},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","target","value","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"navigate"},"expected_frame_id":{"$ref":"#/$defs/frame"},"url":{"type":"string","maxLength":2048,"pattern":"^https?://"},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","url","intent"],"additionalProperties":false},
{"type":"object","properties":{"type":{"const":"wait"},"expected_frame_id":{"$ref":"#/$defs/frame"},"duration_ms":{"type":"integer","minimum":0,"maximum":5000},"intent":{"$ref":"#/$defs/intent"}},"required":["type","expected_frame_id","duration_ms","intent"],"additionalProperties":false}
]
}
type 是判别字段;每个分支只允许该 action 所需字段并设置 additionalProperties=false。element ref 优先;point 只允许 click/hover,必须位于当前 viewport,并来自同一 expected_frame_id 的单张视觉 fallback。navigate 只改变当前 lease 的 tab;scroll 和 wait 不接受 target。eval、cookie、storage、tab create、upload、download 不属于第一阶段 Action。
Canonical point 以当前 viewport 左上角为 (0,0),单位是 CSS pixel,不是 screenshot image pixel、device pixel 或页面文档坐标。Observation 同时记录 viewport CSS width/height、screenshot image width/height、device_scale_factor 和页面 zoom;provider adapter 按这组元数据把模型图像坐标归一化为 CSS pixel。Runtime 再检查有限数值和 0 ≤ x < viewport.width、0 ≤ y < viewport.height。device scale、zoom、resize 或 scroll 变化都会使原 frame 失效,不能复用旧 point。
该协议与模型 provider 无关。OpenAI 原生 computer_call、Anthropic tool_use 或 GUI Agent Harness planner 都必须先转换为同一个 Action,再经过相同的 target、frame、origin、approval 和 cancel 校验。provider adapter 可以处理各模型的 action schema 与坐标约定,但不能改变 Runtime 的权限和 completion 结论。
window_id + tab_id + cdp_target_id,并获得 tab lease。needs_user 状态并暂停 session。succeeded。| 级别 | 示例 | 第一阶段策略 |
|---|---|---|
| R0 观察 | Surface Context preview、ARIA/DOM text、URL/title、按过滤条件读取 console error;必要时当前 viewport screenshot | chat 与 surface 同组且网页控制开启时,结构化 preview 和观察无需重复提示;图像仍按需且不持久化。隐藏 surface、其他窗口和未登记 surface 不获得 R0。 |
| R1 本地项目变更 | 由 dev-server registry 或当前 project 显式证明归属的 origin/file 中 click/type/navigation | 用户明确要求测试时在 task 内允许;localhost 或 127.0.0.1 字符串本身不构成项目归属或自动授权,仍执行 cancel、frame 和 target 校验。 |
| R2 外部普通变更 | 外部 origin 上 click、type、same-tab navigation | 首次写动作按 exact origin 请求“本次动作”或“本任务”授权;subdomain 不继承。 |
| R3 敏感或高影响 | 登录凭证、发送/发布、购买、删除、账户/权限修改、接受条款、跨 origin 提交 | 返回 needs_user,展示拟执行动作并由用户完成或逐次确认;不允许任务级自动授权。 |
| Unsupported | CAPTCHA、任意 JS、cookie/storage 导出、upload/download、clipboard、浏览器设置 | 不执行;返回明确 reason code。 |
scheme + host + effective port,不是可模糊匹配的域后缀。action_epoch,才把 popup/new tab 归因于本 action。此时 Runtime 不自动采用新 target,而是中止 action、释放原 lease,并以 needs_user/new_surface_created 结束当前 run;用户显式关联后,只能在下一 turn 创建新 run 和新 binding。其他窗口或用户并发打开的无关 tab 只进入各自 registry,不影响当前 run。localhost。/tmp/gui_agent_screen.png。succeeded、failed、cancelled、终止型 needs_user、异常退出和 target loss 都经过同一个 finally 路径 detach CDP、释放 lease/approval,并删除临时 artifact;验收或用户明确保存的截图才标记 retained=true。needs_user 只在 run deadline 内保留 session 和临时 artifact;用户拒绝、超时、session teardown 或 worker shutdown 时执行相同 cleanup。worker 启动时按 owner-run 状态和 TTL 清理 orphan run 目录。artifact_id、kind、retained 与受控读取 URL;不返回本地绝对路径。learn_app_components、save_workflow_record 或任何 component/vision memory 读写,不自动保存个人页面截图。不新增第二套浏览器窗口。Computer Use 使用现有 center tab/split layout:chat 保持可见,右侧或相邻 pane 显示被控制的 web tab。UI 中可见关系与 Agent 收到的 Surface Context 必须一致。
chat composer 附近显示当前可见 surface 的 favicon、title/origin、位置 alias 和“Agent 可访问”状态。用户在 sidebar、center tab 或 split pane 中改变可见/focused surface 时立即更新;工具关闭时显示“网页控制未启用”。
发送后该 turn 的 surface chip 固定为提交时的 title、region 和 revision。模型已经收到同一份 Surface Context;用户可以展开查看“本轮 Agent 可见内容”的结构化 preview。后续切换 pane 不会静默改变正在运行的目标。
tab header 显示“Computer Use”状态、step 计数和 Stop。chat 中只显示当前 observation 摘要、计划动作、执行结果和验证结果;原始 DOM 与 screenshot 默认折叠。
permission card 必须显示 origin、target tab、拟执行动作、将发送的非敏感文本摘要、风险原因,以及仅本动作/本任务/拒绝。第一阶段没有永久允许。
登录、CAPTCHA 或敏感页面时 session 进入 needs_user。用户在同一可见 tab 中操作,点击 Continue 后 Runtime 生成全新 observation;旧坐标和旧 action 全部失效。
结果展示 status、reason、完成证据和保留 artifact。step_limit、timeout、target_lost 必须显示为未完成,不用 LLM summary 覆盖。
| reason_code | 触发条件 | 处理 |
|---|---|---|
surface_unavailable | turn 中没有经过验证且允许访问的可见 surface | 不注入执行工具;上下文明确说明不存在可用 surface,不使用任意 active tab 代替。 |
ambiguous_surface | 多个 surface 都符合用户措辞,且没有唯一 primary/focused 对应 | 不执行;列出可见 alias 并请求用户明确目标。 |
surface_access_disabled | 用户显式关闭当前 surface 的 Agent 访问 | 不提供 preview 或执行工具;UI 与模型上下文都显示禁用。 |
capability_denied | surface 或当前授权不包含请求的 observe/interact/navigate capability | 在 lease 或副作用前拒绝;工具已注入不能覆盖 capability。 |
stale_observation | frame、scroll、viewport、navigation epoch 已改变 | 不执行动作;重新 observe,不消耗 action step。 |
target_lost | surface hide、移出 group、跨窗口 move、tab/window close、CDP target replacement、renderer/window disconnect | 释放 lease;失败结束。不得自动选择另一个 tab。 |
origin_changed | same-tab redirect/navigation 进入新 origin | 保持 target,撤销旧 origin action grant,重新观察和分类。 |
new_surface_created | 匹配当前 leased opener/initiator 且晚于 action epoch 的 popup/new tab | 不跟随、不执行新 surface;中止 action、释放原 lease,以 needs_user 结束当前 run。用户关联后由下一 turn 创建全新 run/binding;无关 tab 不触发。 |
approval_required | R2/R3 action 未授权 | 暂停为 needs_user,不重试 action。 |
planner_invalid | 结构化 Action 两次无效或引用不存在 | 失败或重新规划;绝不能转换为 done。 |
verification_failed | 动作执行但预期效果未出现 | 携带差异重新规划;超过 recovery budget 后失败。 |
cancelled | 用户 Stop、run cancel、session teardown | 执行前检查并中止;清理 Playwright/CDP、lease、pending approval 和临时 artifact。 |
step_limit / timeout | 预算耗尽 | 返回 failed,保留已验证的部分结果但不宣告成功。 |
并发单位是 web tab:同一 tab_id 同时只能有一个写 lease,不同 tab 可以并行读取或执行。同一 tab 的 preview/Observation 捕获使用 snapshot read barrier,任何 action 使用 write barrier;两者的临界区不得重叠。页面自身的异步 DOM mutation 通过 content_epoch 检测,变化时丢弃整份 snapshot 并重试或返回 unavailable/stale。Surface Broker 必须把 command 发送给精确 window_id 对应的 renderer,并校验回执 connection/window/tab/target;禁止“广播后接受第一个 reply”。
发布约束:PHASE 0–4 是一个不可拆分发布的交付单元。完成中间阶段可以合并内部类型和测试,但在 authoritative registry、exact routing、capability enforcement、runner、安全 UI 与默认 18100 安装版 live E2E 全部通过前,只能在测试开关下调用,不得向真实 provider 动态注入 computer_use,也不得在 UI 标记“Agent 可访问”。
computer_use 注入和“Agent 可访问”状态。right surface 的 title、origin、有界 preview、capabilities 和 computer_use schema。computer_use(surface="right", task="读取回答所需的当前页面信息") 观察。全程不要求用户粘贴 URL、文字或截图,不发送 screenshot,也不回答没有页面访问能力。computer_use schema;UI 与回复都准确说明权限状态。target_lost,不得操作新焦点 tab。alias_map 的两个 alias 指向同一 canonical key;两个可见 web surface 使用稳定 web:1/web:2,重复 region 不生成歧义空间 alias;发送瞬间后的 focus 切换只影响下一 turn。preview_status=unavailable,不得混合 frame。capability_denied;动态工具注入本身不增加 capability。provider_request_id 解析 immutable alias/binding snapshot;旧 turn 的 right 不能指向新 turn 的 surface。tool_call_id + source_provider_request_id 解析原 immutable binding;全部 tool call settled 或 turn/run 终止后映射释放。device_scale_factor != 1、页面 zoom 和非零 scroll 的 fixture 上,图像 point 正确转换为 viewport CSS pixel;scale/zoom/scroll 改变后旧 frame 返回 stale_observation。content_epoch 改变时,整份 snapshot 被丢弃并重试或返回 unavailable/stale。localhost/127.0.0.1 origin 不进入 R1,外部写策略仍生效。stale_observation。needs_user/new_surface_created、释放原 lease并结束旧 run,不会替换当前 target。用户关联新 tab 后,下一 turn 创建新 run 才能控制。needs_user;用户接管后 Continue 使用新 frame,不复用暂停前 action。succeeded。playwright_browser selector API、普通用户手动 web tab、tab transfer 和 session UI 不受影响。不控制整个本机桌面;不接入 remote VM;不操作 iOS/Android simulator;不控制其他本地 app;不处理多 app workflow。
不接入用户日常 Chrome/Edge profile;不提供 sidecar、hidden 或 headless fallback;不自动接管 popup/new tab;桌面壳不在线时直接失败。
不开放 arbitrary JavaScript、cookie/storage 读写或导出、file upload/download、clipboard、浏览器设置、证书和扩展管理。
不设计 Windows 专用实现、测试、打包或兼容路径;不引入 OS Keychain、keyring 或 Credential Manager 集成。
| 能力 | 当前证据 | 状态 |
|---|---|---|
| 内置可见 web tab + exact CDP target attach | _actions/open_action.py 与 browser unit/source checks | 已有基础,尚缺 live acceptance |
| DOM/ARIA selector 操作 | _actions/read.py、_actions/interact.py | 已有底层工具,不是 Computer Use runner |
| DOM-first Browser Agent baseline | agentic_functions/browser_agent/__init__.py 与 tests/unit/test_browser_agent.py | 现有公开名仍是 browser_agent;基础单元检查存在,但没有 turn-start Surface Context 和安装版完整 E2E |
| GUI planning/verify | gui_harness/tasks/execute_task.py 与 harness tests | 规划和验证思路可提取;第一阶段不启用 grounding pipeline、OCR/detector 或 memory,completion/state 仍有 blocker |
| 每 turn surface candidates 与服务端 Surface Registry | chat payload 当前无 surface 字段;服务端无 window/renderer/group/layout 校验后的 binding registry | 未实现 |
| Turn-start preview 与动态工具可用性 | 当前没有有界 preview;Browser Agent 为 deferred default tool | 未实现 |
| Surface chip、访问开关与当轮目标展示 | 当前 composer 不显示 Agent 可访问的 surface | 未实现 |
| 精确 window/tab control routing | 当前为全局 broadcast + first reply | 未实现 |
| ComputerSession、tab lease、frame freshness | 无 | 未实现 |
| Origin permission card 与 risk tiers | 现有 browser approval 只覆盖少数 login-state 操作 | 未实现 |
| 确定性 completion、cancel、artifact lifecycle | 现有 gui_agent 不满足本设计 | 未实现 |
| 真实内置浏览器 E2E | 尚无本设计对应 artifact | 未验证 |
证据边界:本页是设计和当前差距记录,不是发布声明。只有代码合并并完成 stable desktop 的 live test 后,才能把对应条目改为“已实现/已验证”,并附测试命令和 artifact 路径。