15. 内置浏览器与远程能力
Craft 的内置浏览器不是打开外链按钮,而是一套 session-owned Chromium automation surface:模型可以导航、读 accessibility snapshot、交互、截图、看 console/network/download,并把窗口交还用户。远程模式又把这套能力从服务端反向代理到某个在线 Electron 客户端。
15.1 为什么既有 Source 又有 Browser
Section titled “15.1 为什么既有 Source 又有 Browser”Source 适合稳定、重复、结构化集成;Browser 适合:
- 一次性 UI 工作;
- 没有 API/MCP 的产品;
- 需要用户现有登录 cookie;
- Canvas/复杂前端;
- 临时绕过 source setup/auth 故障。
内置指南也提醒:重复/团队化/自动化任务仍优先 source。Browser 的 UI selector/流程更脆弱。
15.2 一个工具,一套命令语言
Section titled “15.2 一个工具,一套命令语言”Agent 只看到一个 browser_tool,schema:
{ command: string | string[] }而不是几十个独立工具。browser-tool-runtime.ts 解析命令:
open / windows / focusnavigate / back / forwardsnapshot / findclick / click-at / dragfill / type / select / key / scrollset-clipboard / get-clipboard / pastewait / console / network / downloadsscreenshot / screenshot-region / window-resizeevaluate / uploadrelease / hide / close源码:browser-tool-runtime.ts、browser-tools.md。
为什么用命令字符串
Section titled “为什么用命令字符串”优点:
- tool schema 很小,省 token;
- help/parse/batch 可复用 CLI 语法;
- 新 verb 不改变 provider tool schema;
- 单次可批处理多步。
代价:
- 再造 tokenizer/quote/escape;
- 细粒度 permission 较难;
- 编译期类型弱;
- 模型参数错误到运行期才发现。
15.3 Parser 与 batch
Section titled “15.3 Parser 与 batch”支持单双引号、反斜线 escape、分号 batch;引号内分号不是分隔符。Batch 左到右执行,但遇到可能导航的命令(navigate/click/back/forward)自动停止,原因是下一页的 @eN ref 会失效。
这是一个很好的“默认正确性”设计:模型想一口气填两格再 click 可以;click 后继续用旧 ref 则被阻止。
15.4 标准交互循环
Section titled “15.4 标准交互循环”flowchart LR O["open"] --> N["navigate"] N --> S["snapshot"] S --> F["find / inspect @refs"] F --> I["click/fill/select"] I --> V["snapshot/screenshot verify"] V -->|"next state"| F V --> R["release/hide/close"]snapshot 返回 accessibility tree 和临时 refs;find 在 role/name/value/description 中搜。DOM变化后 ref 可能 stale,错误会提示重新 snapshot。
15.5 Canvas UI
Section titled “15.5 Canvas UI”Google Sheets 等内容在 canvas 上,accessibility/DOM refs不完整。Browser 提供:
- annotated screenshot;
click-at;drag;- keyboard navigation;
- clipboard TSV paste/copy;
- export URL。
这是一种多模态 fallback:优先语义 DOM,缺失时才用像素坐标,避免所有操作都变成脆弱视觉点击。
15.6 BrowserPaneManager
Section titled “15.6 BrowserPaneManager”Electron main 的 browser-pane-manager.ts 约 3,600 行,拥有真实 WebContentsView 与:
- instance registry;
- session binding/owner;
- page + toolbar + overlay view;
- navigation/interaction/accessibility script;
- screenshot/console/network/download;
- cookie/session partition;
- window geometry/theme;
- Electron permission handler;
- lifecycle/reuse。
Renderer 只能通过受控 main/preload bridge操作,不直接拿 WebContents。
15.7 所有权模型
Section titled “15.7 所有权模型”Browser instance 记录:
ownerType: session | manual;ownerSessionId;boundSessionId;workspaceId;- agent control overlay state。
一次 session turn结束时可 unbind 但保留 window 给用户/下轮;同 workspace 的本地 session可按策略复用 manual window。跨 workspace 不复用,防 cookie/页面状态串线。
stateDiagram-v2 [*] --> SessionOwned: open by agent SessionOwned --> Manual: release/unbind Manual --> SessionOwned: same workspace/session rebind SessionOwned --> Hidden: hide Hidden --> SessionOwned: focus/open SessionOwned --> [*]: close/destroy Manual --> [*]: closerelease 只撤 agent overlay、保留窗口给用户;hide 保留状态但不显示;close 销毁。三者不能混成一个 stop。
15.8 Agent control overlay
Section titled “15.8 Agent control overlay”tool start 时 SessionManager/BPM 设置 setAgentControl(sessionId,{intent,displayName}),让用户知道窗口当前被 Agent 操作;turn结束、显式 release或 abort 时清。
这是安全/可理解性功能:自动点击发生在真实登录会话里,用户必须知道谁在控制。Overlay 状态也要在 error cleanup 中释放,避免“Agent 已停但窗口一直锁住”。
15.9 浏览器工具与权限
Section titled “15.9 浏览器工具与权限”内置指南说明 browser tools 在 Safe/Explore 默认允许。这是一个重要语义:Safe 模式的“只读”对网页并不严格成立——click/fill 可能提交表单、发消息或购买。
实际风险缓解依赖:
- 工具 guide要求确认/验证;
- browser command/runtime input validation;
- Electron page permission denylist;
- agent overlay和用户可见窗口;
- 高风险页面自身确认;
- remote evaluate/upload额外禁止。
若产品要求强“只读浏览”,应把 navigate/snapshot/screenshot 与 click/fill/select 分成权限类别,而不能把整个 browser_tool 视为 read-only。
15.10 页面权限与导航
Section titled “15.10 页面权限与导航”BrowserPaneManager 对 Electron session 设置 permission check/request handler,只允许明确集合,默认拒绝摄像头/麦克风/定位等;window.open只允许 HTTP/HTTPS,拒绝未知 protocol,并控制新窗口归属。
页面是外部不可信代码:
executeJavaScript返回值必须转 plain serializable snapshot;- 不能把 Electron native object直接跨 WS/IPC;
- download filename/path要 sanitize并限制 session downloads;
- upload path要检查敏感位置;
- console/network日志要限量/脱敏。
15.11 本地调用链
Section titled “15.11 本地调用链”Agent browser_tool→ executeBrowserToolCommand(parser)→ BrowserPaneFns callback→ SessionManager-wired IBrowserPaneManager→ Electron BrowserPaneManager→ WebContentsView / CDP-like JS→ structured result/image→ tool_resultbrowser-tools.ts 只知道函数接口,不依赖 Electron。Headless 时可换 RemoteBrowserPaneManager 或报告无能力。
15.12 远程问题
Section titled “15.12 远程问题”Headless server运行 Agent,但没有 GUI/cookie;用户 Electron 有 GUI,但不拥有远程 session文件。系统选择“借用客户端浏览器能力”:
sequenceDiagram participant A as Remote Agent participant SM as Remote SessionManager participant WS as WsRpcServer participant P as Electron preload/main participant BPM as BrowserPaneManager
A->>SM: browser_tool navigate SM->>SM: choose/pin capable client SM->>WS: invokeClient(client:browser:invoke) WS->>P: BrowserCapabilityRequest P->>BPM: dispatch owned method BPM-->>P: plain result / image bytes P-->>WS: invoke result WS-->>SM: result SM-->>A: tool_result15.13 Capability handshake 与 host pin
Section titled “15.13 Capability handshake 与 host pin”Electron握手声明 client:browser:invoke。SessionManager:
- 找同 workspace 的 capable clients;
- 为 session 选 host;
- pin session→client,保证一系列命令命中同一 window/cookies;
- client断线时清 pin;
- 下一次调用可重新选择或返回无能力错误。
若每命令随机客户端,snapshot 在 A、click @e1 在 B 会立刻失效。Pin 是浏览器会话一致性的必要条件。
15.14 Wire protocol
Section titled “15.14 Wire protocol”{ v: 1, method: BrowserCapabilityMethod, args: unknown[], sessionId, workspaceId}Method 白名单一一对应 IBrowserPaneManager,不是让服务端传任意 JS function name。
截图的 Node Buffer 不能直接跨 WS structured clone,于是 dispatcher转 Uint8Array wire,远端 manager再转 Buffer,见 ScreenshotResultWire。
15.15 RemoteBrowserPaneManager
Section titled “15.15 RemoteBrowserPaneManager”RemoteBrowserPaneManager 实现同一 IBrowserPaneManager,每 (workspaceId,sessionId) 一个实例。
invoke() 检查:
- host client存在,否则
BROWSER_NO_CAPABLE_CLIENT; - client仍声明 capability;
- 组 versioned request;
- await server→client RPC。
接口历史上有同步方法,但 WS 往返必然异步。Remote 实现对 sync方法 fire-and-forget并返回 remote-pending:<session> sentinel,同时为真正需要结果的路径增加 Async variant。这是抽象边界的一处技术债:更理想的 IBrowserPaneManager 应从一开始全 async。
15.16 客户端 owner namespace
Section titled “15.16 客户端 owner namespace”远程 sessionId 可能与客户端本地 session同名,且远端 server不能访问本地其他窗口。Dispatcher 把 owner变为:
remote:<workspaceId>:<sessionId>每个带 instanceId 的方法先 requireOwnedInstance(instanceId, ownerKey);listInstances 只返回此 owner的窗口。远程 session不能接管 manual/local window,通常获得 fresh window。
源码定位:browser-pane-manager.ts。
15.17 远程能力限制
Section titled “15.17 远程能力限制”Upload
Section titled “Upload”远程 agent不能上传客户端本地路径。Remote manager直接抛 BROWSER_REMOTE_UPLOAD_NOT_SUPPORTED,提示用户把文件附到 session。否则服务端传 /tmp/x,客户端可能误读另一文件,或允许任意本机路径窃取。
Evaluate
Section titled “Evaluate”远程 JS evaluation 默认可由客户端配置禁用;dispatcher返回 BROWSER_REMOTE_EVALUATE_BLOCKED。任意 evaluate比 click/fill能力强得多,可读取页面敏感 DOM/token。
Instance reuse
Section titled “Instance reuse”远程不复用未绑定 manual/local window,降低跨 session cookie/页面泄漏。
15.18 Result richness
Section titled “15.18 Result richness”Runtime 不只返回“ok”:
- URL/title before/after;
- scroll position;
- active element;
- selector/value assertion;
- window ownership/visibility;
- network/console摘要;
- screenshot image;
- download savePath;
- security challenge detection。
Rich feedback让模型做闭环验证,而不是把“click方法返回成功”误判为业务成功。
15.19 故障模型
Section titled “15.19 故障模型”| 故障 | 提示/恢复 |
|---|---|
| 无 desktop host | 打开同 workspace 的桌面 app |
| host断线 | 清 pin,重新选择/重试 |
| stale ref | 重新 snapshot |
| 页面导航未完成 | wait network-idle/selector |
| Canvas 无 ref | screenshot + click-at/keyboard |
| remote upload | 让用户附文件到 session |
| remote evaluate禁用 | 用 snapshot/标准命令或客户端显式授权 |
| ownership不匹配 | 拒绝,不能跨 session接管 |
| screenshot clone失败 | plain wire bytes |
| turn异常退出 | cleanup overlay/unbind/browser lease |
15.20 本章小结
Section titled “15.20 本章小结”内置浏览器把 GUI自动化做成 session-owned resource,而远程桥进一步证明 capability-based architecture 的价值:Agent runtime与能力宿主可以不在一台机器,只要协议明确所有权、限制和结果序列化。
下一章从 renderer 视角看产品表面:Electron、WebUI 和 Viewer 分别承担什么,UI state 为什么以纯事件投影为主。