dsh-agent-team-ui-testing 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -36
  3. package/annotation-capture.js +111 -0
  4. package/browser-safety.js +45 -1
  5. package/client.js +3721 -132
  6. package/context-menu.js +134 -0
  7. package/core/clock.js +88 -0
  8. package/core/config-store.js +566 -0
  9. package/core/config.js +892 -0
  10. package/core/contracts.js +240 -0
  11. package/core/errors.js +156 -0
  12. package/core/evidence.js +141 -0
  13. package/core/geometry.js +217 -0
  14. package/core/identity.js +180 -0
  15. package/core/index.js +24 -0
  16. package/core/p0.js +1101 -0
  17. package/core/readiness.js +175 -0
  18. package/core/registry.js +239 -0
  19. package/core/tab-access.js +1350 -0
  20. package/docs/adapters.md +160 -0
  21. package/docs/architecture.md +44 -9
  22. package/docs/capability-tiers.md +20 -4
  23. package/docs/capture-p0.md +39 -2
  24. package/docs/diagnose-reason-codes.md +157 -0
  25. package/docs/element-picker.md +24 -6
  26. package/docs/getting-started.md +47 -1
  27. package/docs/images/tutorial-01-overview.png +0 -0
  28. package/docs/images/tutorial-02-history-dropdown.png +0 -0
  29. package/docs/images/tutorial-03-pick-element.png +0 -0
  30. package/docs/images/tutorial-04-annotate.png +0 -0
  31. package/docs/index.md +11 -7
  32. package/docs/multi-tab.md +55 -20
  33. package/docs/network-p0.md +2 -2
  34. package/docs/panel-context-menu.md +41 -0
  35. package/docs/t2-structured-gui.md +2 -2
  36. package/docs/t3-vision.md +6 -1
  37. package/docs/t4-dynamic-behavior.md +1 -0
  38. package/docs/tools.md +65 -7
  39. package/docs/tutorial.md +125 -0
  40. package/docs/verification.md +26 -2
  41. package/docs/viewport-control.md +21 -8
  42. package/index.js +3263 -251
  43. package/integration/cross-environment.js +1535 -0
  44. package/integration/diagnose.js +945 -0
  45. package/integration/tab-principal.js +493 -0
  46. package/locale/en.json +1 -1
  47. package/locale/zh.json +1 -1
  48. package/network-p0.js +24 -7
  49. package/package.json +4 -3
  50. package/select-control.js +298 -0
  51. package/skills/ui-testing-common/SKILL.md +54 -0
  52. package/skills/ui-testing-fake/SKILL.md +43 -0
  53. package/skills/{ui-testing → ui-testing-web}/SKILL.md +64 -24
  54. package/skills/{ui-testing → ui-testing-web}/manifests/declared-capabilities.json +9 -2
  55. package/skills/{ui-testing → ui-testing-web}/references/contracts/capability.md +3 -3
  56. package/skills/{ui-testing → ui-testing-web}/references/contracts/evidence.md +1 -1
  57. package/skills/{ui-testing → ui-testing-web}/references/contracts/operation.md +1 -1
  58. package/skills/{ui-testing → ui-testing-web}/references/core/fallback.md +1 -1
  59. package/skills/{ui-testing → ui-testing-web}/references/core/page-identity.md +3 -2
  60. package/skills/{ui-testing → ui-testing-web}/references/core/selection.md +1 -1
  61. package/skills/{ui-testing → ui-testing-web}/references/core/tool-routing-and-limits.md +6 -3
  62. package/skills/{ui-testing → ui-testing-web}/references/platforms/index.md +1 -1
  63. package/skills/{ui-testing → ui-testing-web}/references/platforms/web.md +2 -2
  64. package/skills/ui-testing-web/references/scenarios/annotations.md +104 -0
  65. package/skills/{ui-testing → ui-testing-web}/references/scenarios/functional.md +1 -1
  66. package/skills/{ui-testing → ui-testing-web}/references/scenarios/visual-motion.md +1 -1
  67. package/skills/{ui-testing → ui-testing-web}/references/tiers/t1-programmatic.md +2 -2
  68. package/skills/{ui-testing → ui-testing-web}/references/tiers/t3-static-visual.md +2 -2
  69. package/skills/{ui-testing → ui-testing-web}/references/tiers/t4-dynamic-visual.md +2 -1
  70. package/targets/fake/adapter.js +1163 -0
  71. package/targets/fake/fixtures.js +177 -0
  72. package/targets/web/adapter.js +1231 -0
  73. package/targets/web/provider.js +415 -0
  74. package/skills/ui-testing/references/scenarios/annotations.md +0 -87
  75. /package/skills/{ui-testing → ui-testing-web}/references/core/dynamic-visual.md +0 -0
  76. /package/skills/{ui-testing → ui-testing-web}/references/core/network-and-state.md +0 -0
  77. /package/skills/{ui-testing → ui-testing-web}/references/core/report-template.md +0 -0
  78. /package/skills/{ui-testing → ui-testing-web}/references/core/reporting.md +0 -0
  79. /package/skills/{ui-testing → ui-testing-web}/references/core/safety.md +0 -0
  80. /package/skills/{ui-testing → ui-testing-web}/references/core/verdict-examples.md +0 -0
  81. /package/skills/{ui-testing → ui-testing-web}/references/core/verdict-gate.md +0 -0
  82. /package/skills/{ui-testing → ui-testing-web}/references/core/verification.md +0 -0
  83. /package/skills/{ui-testing → ui-testing-web}/references/core/workflow.md +0 -0
  84. /package/skills/{ui-testing → ui-testing-web}/references/tiers/t2-structured.md +0 -0
  85. /package/skills/{ui-testing → ui-testing-web}/templates/capability-note.md +0 -0
  86. /package/skills/{ui-testing → ui-testing-web}/templates/platform.md +0 -0
  87. /package/skills/{ui-testing → ui-testing-web}/templates/regression-case.md +0 -0
  88. /package/skills/{ui-testing → ui-testing-web}/templates/scenario.md +0 -0
@@ -1,23 +1,41 @@
1
1
  ---
2
- name: ui-testing
3
- description: 使用 browser_* 工具调试与测试已授权的 Web 页面:T1 程序化接口(导航、JS、截图、输入、网络元数据、计算样式、独立 HTTP、共享视口)、T2 结构化 GUI(snapshot→ref→动作→证据→等待/节点状态)、T3 静态视觉(PNG 裁剪/预处理/标注/确定性识别/坐标输入/像素差异/受管基线/回归)、T4 动态行为(帧序列、七类运动分析、WAAPI 控制与恢复、六向事件关联)、网络 P0(请求/WS/SSE 采集、时间轴、业务链路、拦截限速重放、JSONL/HAR 证据、多 Agent 订阅)。以 browser_capabilities 的实际运行态选路;证据不足时记 BLOCKED/INCONCLUSIVE。
2
+ name: ui-testing-web
3
+ description: 调试与测试已授权 Web 页面的默认入口——下列常规表述都算命中:打开/点一下/填一下/滚动这个页面;「这个按钮点了没反应/没跳转/提交报错」;「这个元素为什么不见了/被谁挡住/什么颜色字号/布局错位/文字截断」;「截个图/放大看看/和设计稿或上一版对比/这块颜色对不对」;「这个动画卡不卡/过渡顺不顺/有没有闪烁/拖拽跟手吗」;「接口返回什么/为什么 404 或 500/请求发了几次/慢在哪/有没有重复请求」;「看我框的/我圈的是这里/这个箭头指什么」(面板人工标注,读 browser_state.annotations 并 browser_screenshot {includeAnnotations:true});「新开了个标签页/弹窗没出来」;「这个环境能用吗/为什么工具不可用/Edge 还是 Chrome 在跑」。用 browser_* 工具取证:T1 程序化接口(导航/JS/截图/输入/网络元数据/计算样式/独立 HTTP/共享视口)、T2 结构化 GUI(snapshot→ref→动作→等待/节点状态)、T3 静态视觉(PNG 裁剪/给图片画框/确定性识别/像素差异/受管基线)、T4 动态行为(帧序列/七类运动分析/WAAPI/事件关联)、网络 P0(请求/WS/SSE/时间轴/链路/拦截重放/JSONL/HAR/订阅)、多标签;以 browser_capabilities 的实际运行态选路,证据不足记 BLOCKED/INCONCLUSIVE。
4
4
  ---
5
5
 
6
6
  # UI Testing · Web T1–T4 与网络操作规范
7
7
 
8
8
  ## 何时使用
9
9
 
10
- 用于已授权 HTTP(S) 网页的调试、UI 测试、元素定位与交互、计算样式、网络元数据、截图和运行态诊断。**T1 = 程序化接口**(导航/JS/输入/网络/CSSOM/截图/独立 HTTP);**T2 = 结构化 GUI**(AX 语义快照 + ref 定位 + 可信输入 + 节点状态 + 条件等待);**T3 = 静态视觉**(对有限张静态 PNG 做裁剪/缩放/预处理/标注/确定性识别/坐标输入/像素差异/受管基线/回归与缺陷用例;无 VLM、无 OCR、无语义命名,不主动读 DOM);**网络 P0 = 网络调试引擎**(HTTP/HTTPS 请求响应与头/体、WebSocket/SSE、iframe/OOPIF/Worker 采集,统一时间轴、分析与聚合、业务步骤链路、拦截/限速/断连/重放/Mock、JSONL+HAR 证据与多 Agent 独立订阅);**T4 = 动态行为**(真实 screencast/截图循环帧序列、时间轴、七类运动分析、WAAPI 控制与恢复、六向动态事件关联;帧证据带 sourceTimestamp/encoded+rgba 双 hash/dropped-evicted 计数,关联区分 explicit/redirect/initiator(confirmed-causal)与 observed/temporal(默认过滤))。宿主 OS 支持 Linux / macOS / Windows;尚未定义或实现的目标环境是桌面应用、Android、TUI、HarmonyOS、游戏与 3D。
10
+ 用于已授权 HTTP(S) 网页的调试、UI 测试、元素定位与交互、计算样式、网络元数据、截图和运行态诊断。**T1 = 程序化接口**(导航/JS/输入/网络/CSSOM/截图/独立 HTTP);**T2 = 结构化 GUI**(AX 语义快照 + ref 定位 + 可信输入 + 节点状态 + 条件等待);**T3 = 静态视觉**(对有限张静态 PNG 做裁剪/缩放/预处理/标注/确定性识别/坐标输入/像素差异/受管基线/回归与缺陷用例;无 VLM、无 OCR、无语义命名,不主动读 DOM);**网络 P0 = 网络调试引擎**(HTTP/HTTPS 请求响应与头/体、WebSocket/SSE、iframe/OOPIF/Worker 采集,统一时间轴、分析与聚合、业务步骤链路、拦截/限速/断连/重放/Mock、JSONL+HAR 证据与多 Agent 独立订阅);**T4 = 动态行为**(真实 screencast/截图循环帧序列、时间轴、七类运动分析、WAAPI 控制与恢复、六向动态事件关联;帧证据带 sourceTimestamp/encoded+rgba 双 hash/dropped-evicted 计数,关联区分 explicit/redirect/initiator(confirmed-causal)与 observed/temporal(默认过滤))。宿主 OS 支持 Linux / macOS / Windows;尚未定义或实现的目标环境是桌面应用、Android、TUI、HarmonyOS、小程序、游戏与 3D。
11
+
12
+ ### 常见表述 → 命中哪条链路(先对号,再动手)
13
+
14
+ | 用户这么说 | 先做什么 | 主链路 |
15
+ | --- | --- | --- |
16
+ | 打开/点开/进到这个页面、点一下那个按钮、帮我填一下、滚动到底 | `browser_navigate` → `browser_state` → `browser_snapshot` → 动作 | T1+T2 |
17
+ | 这个按钮点了没反应、点了没跳转、提交报错 | snapshot → click → 回读 state/节点状态;必要时开网络 | T2 + 网络 |
18
+ | 这个元素为什么不见了、被谁挡住、什么颜色/字号/间距、布局错位、文字截断 | `browser_node_state` + `browser_computed_style` + snapshot(boxes) | T1+T2 |
19
+ | 截个图、放大看看、和设计稿/上一版对比、这块颜色对不对 | `browser_screenshot` / `vision_image` / `vision_compare` / `vision_baseline` | T3 |
20
+ | 这个动画卡不卡、过渡顺不顺、有没有闪烁、拖拽跟手吗 | `dynamic_session` → `motion_observe/analyze/control` | T4 |
21
+ | 接口返回什么、为什么 404/500、请求发了几次、有没有重复、慢在哪 | `network_session start` → `events`/`detail`/`trace` | 网络 P0 |
22
+ | **看我标注/画的、框的、圈的是这里、这个箭头/笔迹是什么** | **先读 `browser_state.annotations`,再 `browser_screenshot {includeAnnotations:true}`** 查看标注像素,不要问他编号 | 通用标注 |
23
+ | **你圈一下 / 标给我看 / 在这儿写个问号**(要你在他眼前的调试视图上画) | 先看 `browser_state.annotations.foreground`,human 选中该标签时用 `browser_annotation {action:"draw"}` | P0-D |
24
+ | 新开了个标签页、弹窗怎么没出来、切到那个标签 | `browser_tabs` list/select/wait | P0-E |
25
+ | 这个环境能用吗、为什么工具不可用、现在跑的是 Chrome 还是 Edge | `browser_capabilities` + 就绪六轴 + `adapter.declaration` | 诊断 |
26
+ | 给我证据、复现一下、留个截图存档 | 证据 envelope + 落盘 PNG(`out`) | 全部 |
27
+
28
+ 不命中本 Skill:纯文案/文档写作、后端接口设计、与被调试页面无关的通用编程问题。
11
29
 
12
30
  ## 事实来源与边界
13
31
 
14
- 运行时是无头 Chromium + CDP,加上 Host 侧独立 HTTP(S) 客户端;Host 实现入口为 `index.js`,网络纯逻辑在 `network-p0.js`。**工具面 36 个**:T1 10 个(含共享视口 `browser_viewport`)+ P0-E 1 个(`browser_tabs`)+ T2 7 个(`browser_snapshot`、`browser_click`、`browser_type`、`browser_scroll`、`browser_fill_form`、`browser_node_state`、`browser_wait_for`)+ T3 6 个(`browser_vision_image`、`browser_vision_find`、`browser_vision_compare`、`browser_vision_act`、`browser_vision_baseline`、`browser_vision_check`)+ 网络 P0 7 个(`browser_network_session`、`browser_network_events`、`browser_network_detail`、`browser_network_trace`、`browser_network_control`、`browser_network_evidence`、`browser_network_subscribe`)+ T4 5 个(`browser_dynamic_session`、`browser_motion_observe`、`browser_motion_analyze`、`browser_motion_control`、`browser_dynamic_correlate`)。共享视口的权威契约是 `docs/viewport-control.md`(§3 数据模型、§4 Host 应用/兼容、§5 面板坐标映射与滚轮、§6 截图比例、§7 工具/schema/错误/limits、§8 验收);T2 的权威接口契约是本仓库 `docs/t2-structured-gui.md`(§2 工具表、§3 Ref、§4 动作/表单/等待、§5 证据、§6 错误);T3 的权威契约是 `docs/t3-vision.md`(§2 工具表、§3 坐标/证据/错误、§4 算法、§5 识别、§6 act、§7 缺陷、§8 基线/回归);网络 P0 的权威契约是 `docs/network-p0.md`(§2 七工具、§4 事件 schema/配额/完整性、§5 分析、§6 链路、§7 控制、§8 证据/安全/隔离、§9 靶机、§10 验收 C1–C52、§13 R1 裁决)。共享视口、T2、T3 与网络 P0 的可用性以运行态为准,不以文档推断。
32
+ 运行时是无头 Chromium + CDP,加上 Host 侧独立 HTTP(S) 客户端;Host 实现入口为 `index.js`,网络纯逻辑在 `network-p0.js`。**工具面 37 个**:T1 10 个(含共享视口 `browser_viewport`)+ P0-E 1 个(`browser_tabs`)+ P0-D 1 个(`browser_annotation`)+ T2 7 个(`browser_snapshot`、`browser_click`、`browser_type`、`browser_scroll`、`browser_fill_form`、`browser_node_state`、`browser_wait_for`)+ T3 6 个(`browser_vision_image`、`browser_vision_find`、`browser_vision_compare`、`browser_vision_act`、`browser_vision_baseline`、`browser_vision_check`)+ 网络 P0 7 个(`browser_network_session`、`browser_network_events`、`browser_network_detail`、`browser_network_trace`、`browser_network_control`、`browser_network_evidence`、`browser_network_subscribe`)+ T4 5 个(`browser_dynamic_session`、`browser_motion_observe`、`browser_motion_analyze`、`browser_motion_control`、`browser_dynamic_correlate`)。共享视口的权威契约是 `docs/viewport-control.md`(§3 数据模型、§4 Host 应用/兼容、§5 面板坐标映射与滚轮、§6 截图比例、§7 工具/schema/错误/limits、§8 验收);T2 的权威接口契约是本仓库 `docs/t2-structured-gui.md`(§2 工具表、§3 Ref、§4 动作/表单/等待、§5 证据、§6 错误);T3 的权威契约是 `docs/t3-vision.md`(§2 工具表、§3 坐标/证据/错误、§4 算法、§5 识别、§6 act、§7 缺陷、§8 基线/回归);网络 P0 的权威契约是 `docs/network-p0.md`(§2 七工具、§4 事件 schema/配额/完整性、§5 分析、§6 链路、§7 控制、§8 证据/安全/隔离、§9 靶机、§10 验收 C1–C52、§13 R1 裁决)。共享视口、T2、T3 与网络 P0 的可用性以运行态为准,不以文档推断。
15
33
 
16
- - **实现范围**:T1–T4、网络 P0 与 P0-E 共 36 个工具均已注册;T4 的 44 条门槛中 12 条通过、20 条部分通过、12 条未运行,未运行项不得写成通过。
34
+ - **实现范围**:T1–T4、网络 P0、P0-E 与 P0-D 共 37 个工具均已注册;T4 的 44 条门槛中 12 条通过、20 条部分通过、12 条未运行,未运行项不得写成通过。
17
35
  - **工程状态与运行态分开**:静态文档、manifest 与台账只记录工程状态,不构成可用性证明;是否可用以注册表与 `browser_capabilities` 为准。
18
36
  - **能力报告不能自证**:若 `browser_capabilities` 抛错或字段与注册 schema 不符,按证据不足处理(`BLOCKED`/`INCONCLUSIVE`),不要反向推断某能力可用或不可用。
19
37
  - 独立 HTTP 请求由 `browser_http_request` 提供:Host 侧 HTTP(S) 客户端,不启动 Chromium、不依赖页面/页面 Cookie、不受页面租约与串行队列约束;默认不跟随重定向、默认 5s 总超时与 64 KiB 保留上限,每跳重定向都复验目标策略。`browser_eval` 内的页面 `fetch` 仍属页面上下文(受 CORS/Cookie/CSP/导航状态影响),不能替代它。
20
- - **按 DSH 工作区隔离 page,不是一个 page 共享。** 同一时刻最多一个 headless Chromium/profile;每个工作区有 primary page,GUI 内部标签页(真人点击 `target=_blank`/`window.open`)会为同工作区创建受控 child page;每个 page 独立 URL/viewport/history/网络缓冲/串行队列/租约/ref 表。Agent 的 `browser_*` 工具默认路由到**当前会话的 selectedTab**(默认 primary),可用 `browser_tabs select` 切换、用 `wait` 等待新标签、用 `list` 看生命周期与 blocked-popup notices;多个 tab 之间不自动聚合网络/帧/证据,跨标签结论必须逐 tab 取证。Cookie、localStorage、cache 仍共享(明确非安全隔离)。Agent 没有 frame 选择、下载、上传、Cookie 导出、文件选择工具。
38
+ - **按 DSH 工作区隔离 page,不是一个 page 共享。** 同一时刻最多一个 headless Chromium/profile;每个工作区有 primary page,GUI 内部标签页(真人点击 `target=_blank`/`window.open`)会为同工作区创建受控 child page;每个 page 独立 URL/viewport/history/网络缓冲/串行队列/租约/ref 表。Agent 的 `browser_*` 工具默认路由到**当前会话自己的页**(写用 workSelection:默认指向自己已拥有的页,团队里没有自有页时是未绑定 → `TARGET_NOT_BOUND`,先 `open`),可用 `browser_tabs select` 切换、用 `wait` 等待新标签、用 `list` 看生命周期与 blocked-popup notices;多个 tab 之间不自动聚合网络/帧/证据,跨标签结论必须逐 tab 取证。Cookie、localStorage、cache 仍共享(明确非安全隔离)。Agent 没有 frame 选择、下载、上传、Cookie 导出、文件选择工具。
21
39
  - 共享视口:每个工作区 Runtime 一份权威 `view={version,mode,x,y,width,height,scale,source,revision}`(文档 CSS px),Agent 的 `browser_viewport` 与面板读写同一对象;默认 1280×800@100%、DPR 固定 1、mobile=false。`scale` 0.1–2(10–200%)只是显示比例与默认截图密度,**不改变页面 CSS 视口、DPR 或页面 zoom**;只有显式改 width/height 才发 `Emulation.setDeviceMetricsOverride`。
22
40
  - 面板 screencast 是事件驱动的 JPEG 预览(同源 WS 优先、HTTP 轮询回退);`/metrics` 是预览诊断,不是 Agent 页面证据或 T4 帧序列。
23
41
  - 关闭清空网络/请求/URL/标题/帧缓存并回收进程与 profile,同时清空 T2 的 snapshot/ref 表。引用记录仍须核对 action 时间,避免引用同一次运行中更早的页面。
@@ -46,7 +64,7 @@ description: 使用 browser_* 工具调试与测试已授权的 Web 页面:T1
46
64
 
47
65
  反例词(重复/重放/旧值/未变化/白色/闪烁/错位/409/冲突/0 项/无去重/超时)出现即**触发复核**,不能凭词直接判 FAIL,也不能忽略;契约允许时写进 `counterexamples` 并给来源(合成正反例见 `references/core/verdict-examples.md`)。
48
66
 
49
- ## 统一工具接口(36 个)
67
+ ## 统一工具接口(37 个)
50
68
 
51
69
  下面是各工具的业务字段;每次调用还返回并 render `actionId/sessionId/startedAt/finishedAt/target/args`(另有 `scope` 工作区身份),args 为参数摘要。实际返回与注册 schema 优先。
52
70
 
@@ -58,12 +76,12 @@ description: 使用 browser_* 工具调试与测试已授权的 Web 页面:T1
58
76
  | --- | --- | --- |
59
77
  | `browser_navigate` | `{url:string}` | `{ok:true,url}`;校验 HTTP(S) 目标/DNS/最终 URL,检查 `Page.navigate.errorText` 与错误页;**不保证加载完成**。失败抛分类错误。 |
60
78
  | `browser_eval` | `{expression:string}` | `{ok:true,value,type}`;顶层页面主世界、可序列化值;页面异常返回 `{ok:false,error:{code,category,retryable,message,details}}`,含描述/栈及位置。 |
61
- | `browser_screenshot` | `{scale?:number,region?:{x,y,width,height},persist?:boolean=false,out?:string}` | 显式 scale 连续 0.1–4;**省略时在调用内解析为当前共享 `view.scale`(cold 为 1)**,显式值只影响本次截图、不改 view/DPR/滚动/视口;region画布CSS px(O=0时同文档)、宽高≥1,两轴期望像素<1 → `INVALID_ARGUMENT(empty-output-dimensions)`;out相对.png。capture.request 带 `scaleSource(explicit\|view)`/`viewRevision`,mapping 带逐轴 `cssPerPixel` 与 `scrollAtCapture`。保留PNG base64/hash/附件兼容并新增capture v1(PNG结构/尺寸/全量均匀统计、JSON mapping、file/delivery/warnings);默认实例暂存绝对路径、dispose清理;out/persist:true/非空env显式持久相对路径,同路径不覆盖;无附件base64-fallback(内联 base64 上限 24 MiB,超过则 image=null 并加 `image_base64_omitted` 警告;附件优先);不提供像素差异/VLM/视觉判断。 |
79
+ | `browser_screenshot` | `{scale?:number,region?:{x,y,width,height},includeAnnotations?:boolean=false,persist?:boolean=false,out?:string}` | 显式 scale 连续 0.1–4;**省略时在调用内解析为当前共享 `view.scale`(cold 为 1)**,显式值只影响本次截图、不改 view/DPR/滚动/视口;region画布CSS px(O=0时同文档)、宽高≥1,两轴期望像素<1 → `INVALID_ARGUMENT(empty-output-dimensions)`;out相对.png。capture.request 带 `scaleSource(explicit\|view)`/`viewRevision`,mapping 带逐轴 `cssPerPixel` 与 `scrollAtCapture`。`includeAnnotations` 默认 false;true 按同一 region/scale 叠加 Host 已接受的六种面板形状,不含未提交草稿/工具栏;以 `capture.annotations` 核对文档 revision/context/新鲜度/截断/source.trusted=false。默认截图与 T3/T4 原始源不含面板标注。保留PNG base64/hash/附件兼容并新增capture v1(PNG结构/尺寸/全量均匀统计、JSON mapping、file/delivery/warnings);默认实例暂存绝对路径、dispose清理;out/persist:true/非空env显式持久相对路径,同路径不覆盖;无附件base64-fallback(内联 base64 上限 24 MiB,超过则 image=null 并加 `image_base64_omitted` 警告;附件优先);不提供像素差异/VLM/视觉判断。 |
62
80
  | `browser_input` | `{events:Event[]}` | `{ok:true,applied}`;单批 ≤ 200 条(超出 `INVALID_ARGUMENT`);applied 仅事件数组长度,未知 kind 仍可能被跳过,不能证明生效。key/mouse/wheel 支持 `modifiers`(`['ctrl','shift']` 或 CDP 位掩码 Alt=1/Ctrl=2/Meta=4/Shift=8),key 另支持 `virtualKeyCode/autoRepeat/unmodifiedText/commands/location/isKeypad`,mouse 支持 `buttons/pointerType`;非法修饰键名/类型抛 `INVALID_ARGUMENT`。仍无焦点管理工具。 |
63
- | `browser_network` | `{limit?:number}` | `{entries:[…]}`;默认 50、上限 300,URL 脱敏,记录响应与 `loadingFailed` 失败元数据;无头/正文。不启动浏览器,但受 owner 租约约束。 |
81
+ | `browser_network` | `{limit?:number}` | `{entries:[…]}`;默认 50、上限 300,URL 脱敏,记录响应与 `loadingFailed` 失败元数据;无头/正文。**只读**:不启动浏览器、不进页面队列、不取也不校验单 owner 租约(owner 持租约时仍可读);信封 `sessionId` 是采集会话,agent 会话见 `agentSessionId`。 |
64
82
  | `browser_computed_style` | `{selector:string,properties?:string[]}` | `{ok:true,style}`;首个匹配元素;CDP 只回 longhand,请求的简写属性(`border-color`/`margin`/`font` 等)经页面 `getComputedStyle` 回退补读,未知属性不返回;未命中返回分类 error 对象;无伪元素/iframe/Shadow DOM 内部定位,不证明视觉。 |
65
83
  | `browser_http_request` | `{url,method?,headers?,body?,timeoutMs?,maxBytes?,followRedirects?,maxRedirects?}` | 独立 HTTP(S):`{ok:true,method,url,status,statusText,headers,contentType,encoding,body,bodyBytes,bodyTruncated,redirects,timings}`。`ok:true` **不等于 2xx**;`headers` 是小写头名→非空 string[];`encoding` 为 utf8/base64;默认不跟随重定向、默认 5s/64 KiB(上限 20s/1 MiB/5 跳);无 cookie jar;失败抛分类错误。 |
66
- | `browser_viewport` | `{action*:"get"\|"set", mode?:"wide"\|"landscape"\|"tablet"\|"phone", x?, y?, canvasX?, canvasY?, width?, height?, scale?, expectedRevision?}` | **get = 只读缓存诊断**:不接受其他字段,不 ensure/不发 CDP/不进队列/不占租约/不改 recent/error,可读他人租约下的缓存,返回 `{ok,running,view,observed,viewMeta}`,`viewMeta.stale=true` 表示缓存不新鲜(不保证此刻页面没动)。**set = 页面操作**:至少一个 view 字段(mode 与 width/height 互斥;mode 只展开宽高,同时给出的 x/y/scale 仍生效),走预检→串行队列→懒启动+租约;只有宽高变化才发 metrics override,scale-only / x/y-only 不改 CSS 视口、DPR、页面 zoom。`x/y` 是页面顶层滚动位置(文档 CSS px,`Page.getLayoutMetrics` 实测回读;超内容范围会被 clamp,`clamped.x/y` 如实报告;RTL 负 x 如实返回,set 只接受非负);**`canvasX/canvasY` 是视口左上角在文档画布上的位置**(±10,000,000,可为负/超内容界,负值使内容在视口内右/下移并露纯白 blank);画布坐标与 x/y 不能同 patch(INVALID_ARGUMENT);面板 X/Y 绑定画布坐标,拖拽平移写 canvas 坐标;拖拽模式下画面滚轮缩放画布(5%/Shift 10%/Alt 1%、严格锁屏幕光标点、fit 自动取消、Ctrl/Meta 不缩放),非拖拽模式滚轮仍滚页面。`O=C-S` 时截图/T3 走画布合成(`capture.canvas.composite=true`,blank 不存在于 DOM;**面板 blank 显示为深色棋盘格,截图/T3 的 blank 是纯白 #ffffff**);`O=0` 与旧文档空间字节/像素一致。`width/height` 整数 200–4096;`scale` 0.1–2(10–200%),也是省略 scale 的截图默认密度。返回 `{ok,requested,before,view,observed,viewMeta,applied,settled,clamped,overrideApplied,appliedFields,canvasX,canvasY,offset,content}`;`expectedRevision` 与队列内当前 `view.revision` 不同 → `VIEWPORT_CONFLICT`(patch 零应用,带最新 view),别自动重放旧 patch。 |
84
+ | `browser_viewport` | `{action*:"get"\|"set", mode?:"wide"\|"landscape"\|"tablet"\|"phone", x?, y?, canvasX?, canvasY?, width?, height?, scale?, expectedRevision?}` | **get = 只读缓存诊断**:不接受其他字段,不 ensure/不发 CDP/不进队列/不占租约/不改 recent/error,可读他人租约下的缓存,返回 `{ok,running,view,observed,viewMeta}`,`viewMeta.stale=true` 表示缓存不新鲜(不保证此刻页面没动)。**set = 页面操作**:至少一个 view 字段(mode 与 width/height 互斥;mode 只展开宽高,同时给出的 x/y/scale 仍生效),走预检→串行队列→懒启动+租约;只有宽高变化才发 metrics override,scale-only / x/y-only 不改 CSS 视口、DPR、页面 zoom。`x/y` 是页面顶层滚动位置(文档 CSS px,`Page.getLayoutMetrics` 实测回读;超内容范围会被 clamp,`clamped.x/y` 如实报告;RTL 负 x 如实返回,set 只接受非负);**`canvasX/canvasY` 是视口左上角在文档画布上的位置**(±10,000,000,可为负/超内容界,负值使内容在视口内右/下移并露纯白 blank);画布坐标与 x/y 不能同 patch(INVALID_ARGUMENT);面板 X/Y 绑定画布坐标,拖拽平移写 canvas 坐标;拖拽模式下画面滚轮缩放画布(5%/Shift 10%/Alt 1%、严格锁屏幕光标点、fit 自动取消、Ctrl/Meta 不缩放),非拖拽模式滚轮仍滚页面。`O=C-S` 时截图/T3 走画布合成(`capture.canvas.composite=true`,blank 不存在于 DOM;**面板 blank 显示为深色棋盘格,截图/T3 的 blank 是纯白 #ffffff**);默认未叠加面板标注时,`O=0` 与旧文档空间字节/像素一致。`width/height` 整数 200–4096;`scale` 0.1–2(10–200%),也是省略 scale 的截图默认密度。返回 `{ok,requested,before,view,observed,viewMeta,applied,settled,clamped,overrideApplied,appliedFields,canvasX,canvasY,offset,content}`;`expectedRevision` 与队列内当前 `view.revision` 不同 → `VIEWPORT_CONFLICT`(patch 零应用,带最新 view),别自动重放旧 patch。 |
67
85
  | `browser_state` | `{}` | `mode/activity/url/title/viewport/seq/running/error/errorAt/errorActionId/errorTarget` 与 owner 等诊断信息;额外带安全 `view`(`view={version,mode,x,y,canvasX,canvasY,width,height,scale,source,revision}`)与 `ui.canvas` 投影但不保证新鲜(需要确定更新后事实用 `browser_viewport set` 的读回);从 `document.title` 刷新标题,不懒启动、不申请租约;成功的页面/网络操作会清空错误四字段,诊断与 HTTP 调用不清。 |
68
86
  | `browser_capabilities` | `{}` | 实际运行态 `available/unavailable/unsupported`、原因/限制及 `single-owner-lease`;T2 段应含 7 个工具项与 `web.t2.structured-dom`;T3 段应含 6 个工具项 + 聚合 `web.t3.static-vision` 与 `vision` 块(`version:1`、`algorithmVersion:'t3-v1'`、`tools`、`sourceKinds`、`limits`、`observed:null|{observedAt,lastOperation,lastVerdict}`),T4 段应含 5 个工具项 + 聚合 `web.t4.dynamic-behavior` + 旧 `web.t4.dynamic-vision` 别名与 `dynamic` 块(`version:1`、`tools`、`sources`(screencast jpeg 精确=false / screenshot-loop png 精确=true)、`adapters`、`correlation`、`limits`、`observed:null|{...}`)与 `limits.dynamic`;并报 `viewport` 段与 `limits.viewport`(比例 0.1–2、预设、stale/settle、dsf=1、get 只读 / set 页面操作;画布 v2 另报 `viewport.canvas` 与 `limits.viewport.canvas`,range ±1e7、blankColor #ffffff、compositeProvenance true)、`limits.structuredGui`、`limits.vision` 与P0的 `capture/limits.capture`(`defaultScaleSource:"view.scale"`、`scaleRange:[0.1,4]`);observed仅来自上次真实调用(截图或视觉),首次null,不含图像/绝对路径/页面文本;不懒启动、不建目录、不生成 product、不申请租约。附件服务缺失应明确降级,不能静态宣称图片可见。 |
69
87
 
@@ -89,18 +107,36 @@ browser_viewport { "action": "set", "canvasX": 300, "canvasY": 200 } // 画
89
107
 
90
108
  `browser_tabs` 是唯一的 page 组管理工具,权威行为见 `docs/multi-tab.md`。
91
109
 
92
- ```json
93
- { "action": "list | current | select | open | close | wait",
94
- "tabId": "select/close/wait 可选;close 省略=当前标签",
110
+ ```jsonc
111
+ { "action": "list | current | select | open | close | wait | acquire | release | transfer",
112
+ "tabId": "select/close/wait/acquire/release/transfer 使用;close 省略=当前标签",
113
+ "mode": "仅 select:work(默认)| observe",
95
114
  "kind": "仅 wait:primary | tab;source:primary | gui-popup | agent-open",
96
- "timeoutMs": "仅 wait:0–30000,默认 5000;pollMs:50–1000,默认 100" }
115
+ "timeoutMs": "仅 wait(等待预算)/acquire(等待安全边界):0–30000,默认 5000;pollMs 仅 wait:50–1000,默认 100",
116
+ "expectedControlRevision": "仅 acquire/release/transfer:必填",
117
+ "expectedOwnershipRevision": "仅 transfer:必填",
118
+ "toSessionId": "仅 transfer:本 root 内有效成员" }
97
119
  ```
98
120
 
99
- - `list` 只读:每项 `tabId/runtimeId/kind/title/url/status/selected/busy/owner/reset` 之外还有 `source`(primary/gui-popup/agent-open)、`parentTabId`(popup 的 opener;primary 为 null)、`openEventId`、`createdAt`/`lastUsedAt`、`idleRemainingMs`、`manageable`;同时返回 `notices`(被策略阻断的 popup:reason/parentTabId/openEventId/at,≤8)与 `recentClosed`(closed/idle/evicted,≤8)。
121
+ 字段按 action 归类:非所属 action 的字段(例如 `list` 带 `tabId`、`close` 带 `timeoutMs`)一律 `INVALID_ARGUMENT`,不会静默忽略。
122
+
123
+ - `list` 只读:每项 `tabId/runtimeId/kind/title/url/status/selected/busy/owner/reset` 之外还有 `source`(primary/gui-popup/agent-open)、`parentTabId`(popup 的 opener;primary 为 null)、`openEventId`、`createdAt`/`lastUsedAt`、`idleRemainingMs`、`manageable`,以及长期权属投影 `accessVersion/rootSessionId/creatorSessionId/ownerSessionId/controllerSessionId/ownershipRevision/controlRevision/accessState/permissions{read,write,close,manage}`;同时返回 `notices`(被策略阻断的 popup:reason/parentTabId/openEventId/at,≤8)与 `recentClosed`(closed/idle/evicted,≤8)。**判权限看 `permissions`**,`busy`/`owner`/`manageable` 只是短租约投影。
100
124
  - **`click` 成功 ≠ 有新页**:原生 `target=_blank`/`window.open` 只有在真人 GUI 手势下才会被接管成 child tab。Agent 经 T2 点击触发的新页通常被策略阻断;先 `list` 看 `notices` 的 `reason`(如 `automatic-popup`/`popup-limit`/`no-opener`),再决定写 BLOCKED 还是等真人操作。**不要用 `browser_tabs.open` 自建空白页冒充原生弹窗链路**——那是替代路径,必须在 `coverage` 里标 partial 并写清缺失断言(opener/焦点/postMessage)。
101
125
  - `wait` 是纯内存轮询(不启动 Chromium、不取租约):不带 `tabId` 时只匹配**调用开始后**新出现的标签;命中在 `matchedTab`(统一信封的 `tab` 仍是调用路由标签)。
102
- - `select` 原子 claim;被其他会话占用返回 `BUSY`。`close` 需要本人有效 lease;省略 tabId 关闭当前标签。child 被关闭/退休后旧 selection 进 `pendingReset`,下一次页面工具返回 `TARGET_NOT_FOUND(selected-tab-closed, fallbackTabId:primary)`,必须先 `current`/`select` 确认。
103
- - 每个 tab 独立 lease/队列/ref/网络/帧,`BUSY` 按 tab 计;不能把 A tab 的证据当作 B tab 的结论。
126
+ - **选择分两路**:`select{mode:"work"}`(默认)设写目标,需要本会话可写该页或本 root 的 Lead 可管理它;`select{mode:"observe"}` 只设只读目标,不改写目标。读工具走 observationSelection(未设则用 workSelection),写工具只走 workSelection。`select` **不 claim 租约、不切换前台**,被拒绝时不改变你已有的选择。
127
+ - `close` 按长期权属判定(owner 或当前 controller;本 root 的 Lead 也可以),不再要求"你持有 15 秒租约"。child 被关闭/退休后旧选择进 `pendingReset`,下一次页面工具返回 `TARGET_NOT_FOUND(selected-tab-closed, fallbackTabId:primary)`,必须先 `current`/`select` 确认。
128
+ - **队长接管(`acquire/release/transfer`)**:只有本 root 的 Lead 能用,且 `expectedControlRevision`(transfer 还要 `expectedOwnershipRevision`)必填——没有"默认取当前版本"。`acquire` 先关门再等当前操作到安全边界(有界、可取消,**不中断**成员的在途操作):超时 `BUSY` 且 controller 与版本原样不变、撤销 draining;取消 `ABORTED`;版本过期 `TAB_CONTROL_CHANGED`(不要自动重放)。Lead 对成员空闲页的普通写会**自动接管后派发**,响应带 `autoAcquired:true`,并一直持有到显式 `release`。
129
+ - **跨会话/跨团队根**:目标一律 not-found 语义(`TAB_NOT_FOUND`,不回显对方 id/URL/标题)。别用"能不能看到"推断"能不能写";被拒绝的码是 `TAB_FORBIDDEN`(角色/归属)、`TAB_CONTROL_CHANGED`(控制权/版本)或 `TAB_IDENTITY_UNAVAILABLE`(身份无法证明),`BUSY` 只表示短暂忙。
130
+ - 每个 tab 独立队列/ref/网络/帧;不能把 A tab 的证据当作 B tab 的结论。
131
+ - **限制(未实现,别当成承诺)**:只读工具既不占写租约、也不切前台(不调用 `activate()`),但**有头模式**下后台页可能不合成,截图/预览可能是旧帧(此时应报 `OBSERVATION_UNAVAILABLE`,该码尚未在全部观察路径接线);GUI 面板侧没有 Principal,也没有接管入口;`browser_network*`/T3/T4 证据仍按 workspace/session 归属,未按 root 隔离;DevTools 直连地址仍会投影。
132
+
133
+ ### P0-D(1 个,标注读写)
134
+
135
+ `browser_annotation` 读写调试视图里的**面板标注文档**(每个 Runtime 一份,与面板认证的 `POST /plugins/ui-testing/annotations` 是同一份;权威契约见 `docs/tools.md`、`docs/viewport-control.md`)。参数:`action` 必填,`read`/`clear` 只接受 `action`;`draw` 给 `shapes`(1–64,**追加**不是替换);`erase` 给 `ids`(1–64)和/或 `point{x,y,radius?}`(默认半径 8 画布 px);写动作可选 `expectRevision`(与当前文档 revision 不同则 `ANNOTATION_REVISION_CONFLICT`,零写入)。`read` 与写后返回同一形状:`revision/shapes/shapeCount/truncated/truncation/foreground`。
136
+ - 形状:`type` ∈ `circle|rect|line|arrow|brush|text`(面板的圆形/矩形/线段/箭头/画笔/文字);`id` 可省略(Host 续分配 `a<N>`);`color` `#rrggbb`(默认 `#ff3b30`);`strokeWidth` 1–20(默认 3,即「粗细」);`style` ∈ `stroke|fill`(「外框/填充」,仅 `circle`/`rect`;`text` 固定 fill、`line|arrow|brush` 固定 stroke;也可给 `fill`/`stroke` 布尔但不可与 `style` 同给);几何 `rect{x,y,width,height}`、`circle{cx,cy,rx,ry}`、`line|arrow{x1,y1,x2,y2}`(head 由 Host 派生)、`brush{points:[[x,y],…]}`、`text{x,y,text,fontSize?=20}`。
137
+ - **前台硬约束**:`draw`/`erase`/`clear` 只在目标标签页**正被人类在调试视图里选中且可见**时才允许(面板在认证请求上带前台心跳,约 1.5s 新鲜窗口;切走标签、关面板、DSH 标签页后台化都会过期),否则**零写入**并报 `ANNOTATION_NOT_FOREGROUND`;先读 `browser_state.annotations.foreground={active,ageMs,maxAgeMs}`,`read` 不受此限。页面未运行时报 `INVALID_ARGUMENT`(reason `annotation-runtime-not-running`)。
138
+ - **整份替换语义**:人类下一次编辑会整体发布自己的形状列表(其中已含 Agent 形状,因为面板采纳 Host 文档),人类「清除画布」会连 Agent 形状一起清掉——别把标注当持久存储。
139
+ - **限额与边界**:与面板写面同一软限额(128 图形/单笔 512 点/8192 点/200 字符/65536 字节,确定性裁剪并报 `truncated`);不启动浏览器、不进页面队列、不占租约、不写页面 `lastError`、不改 DOM/CSS,面板下一次 `/state` 轮询即画出;`browser_screenshot {includeAnnotations:true}` 会把两方的形状一起叠加。
104
140
 
105
141
  ### T2(7 个,结构化 GUI)
106
142
 
@@ -139,7 +175,7 @@ T3 只处理有限张静态 PNG。源图只能是 `Source = {kind:"live",scale?:
139
175
 
140
176
  **T3 选路**:语义元素与可交互目标优先 T2(ref/selector);canvas/图片/颜色/像素差异/受管基线/回归阈值才用 T3;持续运动、动画时序、连续视觉推理属 T4(已实现,见下「T4 动态行为工作流」)。T2 `NOT_INTERACTABLE` 的 `escalation:"T3"` 只提示需要视觉分析,不自动授权坐标输入;`browser_vision_act` 用当前截图里的顶层命中点,不是“知道调用者想要哪个 DOM 对象”。
141
177
 
142
- 6 个 T1 页面工具(navigate/eval/screenshot/input/computed_style 与 `browser_viewport` 的 `set`)与**全部 7 个 T2 工具**可懒启动;`browser_viewport` 的 `get` 是缓存诊断(不启动、不发 CDP、不占租约);`browser_network`/state/capabilities 不启动;网络 P0 的 7 个工具只有 `browser_network_session.start` 与需要 page 的 `browser_network_control` 动作会懒启动(其余只读/离线动作不启动浏览器);`browser_http_request` 既不启动浏览器也不受页面租约/队列约束。**T3**:live 源与 `act` 可懒启动;product/baseline 源与离线 baseline list/get/delete 不启动页面,但**全部 T3 工具仍过 scope 队列与 owner 检查**。未启动的页面工具可以标 available(constraint 明示启动可能失败),running/transport 另报真实连接;不能把 `running:false` 当 unsupported。其他 owner 占用时页面/network/T2/T3 标 unavailable、reason BUSY(**只读的 snapshot/node_state/wait_for 与离线 baseline 同样 BUSY**),state/capabilities/http_request 仍 available。实际调用结果仍优先。
178
+ 6 个 T1 页面工具(navigate/eval/screenshot/input/computed_style 与 `browser_viewport` 的 `set`)与**全部 7 个 T2 工具**可懒启动;`browser_viewport` 的 `get` 是缓存诊断(不启动、不发 CDP、不占租约);`browser_network`/state/capabilities 不启动;网络 P0 的 7 个工具只有 `browser_network_session.start` 与需要 page 的 `browser_network_control` 动作会懒启动(其余只读/离线动作不启动浏览器);`browser_http_request` 既不启动浏览器也不受页面租约/队列约束。**T3**:live 源与 `act` 可懒启动;product/baseline 源与离线 baseline list/get/delete 不启动页面,但**全部 T3 工具仍过 scope 队列与 owner 检查**。未启动的页面工具可以标 available(constraint 明示启动可能失败),running/transport 另报真实连接;不能把 `running:false` 当 unsupported。其他 owner 占用时页面写工具与 T2/T3 标 unavailable、reason BUSY(**只读的 snapshot/node_state/wait_for 与离线 baseline 同样 BUSY**);只读的 `browser_network`、state/capabilities、http_request 仍 available。实际调用结果仍优先。
143
179
  ### 网络 P0(7 个,采集/时序/分析/链路/控制/证据/订阅)
144
180
 
145
181
  公共约定:参数对象拒绝未知字段(条件必填/互斥在**执行期**校验,注册 schema 的 `required` 不表达这些约束);`action` 必填的是 `session`/`trace`/`control`/`evidence`/`subscribe` 五个动作型工具,`events`/`detail` 是纯查询工具、没有 `action`;成功返回 `{ok:true,…}`,失败抛 `BrowserError`,新失败一律 `NET_*`(`NET_SESSION_NOT_FOUND`/`NET_SESSION_ACTIVE`/`NET_EVENT_NOT_FOUND`/`NET_BODY_UNAVAILABLE`/`NET_QUOTA_EXCEEDED`/`NET_RULE_INVALID`/`NET_RULE_CONFLICT`/`NET_CONTROL_UNAVAILABLE`/`NET_EXPORT_FAILED`/`NET_IMPORT_FAILED`/`NET_HAR_INVALID`/`NET_SUBSCRIPTION_NOT_FOUND`/`NET_SUBSCRIPTION_FORBIDDEN`/`NET_INTEGRITY_FAILED`/`NET_SCOPE_FORBIDDEN`/`NET_CONTROL_TIMEOUT`/`NET_INVALID_ARGUMENT`/`NET_OUTPUT_ROOT_UNKNOWN`/`NET_OUTPUT_PATH_INVALID`/`NET_OUTPUT_WRITE_FAILED`),不降级 `INTERNAL`;平台级 `BUSY`/`ABORTED`/`CDP_*`/`RUNTIME_LIMIT` 按原码透传并在 evidence 注明。每次调用(成功或抛错)都带 `evidence`:`{version:1,operation,sessionId,fromSeq,toSeq,counters,integrity,redaction,truncated}`。`sessionId` 省略时用当前 scope 的当前会话(运行中优先,否则最近一次已停止会话);无会话时只读工具返回空并置 `session:null`,需要数据的动作抛 `NET_SESSION_NOT_FOUND`。
@@ -147,7 +183,7 @@ T3 只处理有限张静态 PNG。源图只能是 `Source = {kind:"live",scale?:
147
183
  | 工具 | 参数 | 业务返回与限制 |
148
184
  | --- | --- | --- |
149
185
  | `browser_network_session` | `action*:start|stop|status|reset`;`name?`(≤64 字节,默认 `default`);`config?`(仅 `start` 生效:`captureBodies/captureWebSocket/captureSse/captureWorkers`、`maxEvents/maxBodyBytes/maxTotalBytes/maxWsFrames`、`businessStatusPath?`、`maskRules?≤16`) | `{ok:true,changed,session{id,name,state,startedAt,stoppedAt,config,counters,integrity},sessions,limits,evidence}`。**唯一的网络懒启动动作**;`start` 同名幂等 `changed:false`、不同名运行中 `NET_SESSION_ACTIVE`;`status`/`reset` 不启动浏览器;`reset` 只清内存(磁盘证据不动)并关闭本 scope 全部订阅(返回 `subscriptionsClosed`)。 |
150
- | `browser_network_events` | `sessionId?`;`sinceSeq?`/`untilSeq?`/`cursor?`;`limit?`(1–200,默认 50);`order?`;`view?:summary|full|timeline|aggregate`;`filter?{method,status,statusClass,resourceType,urlIncludes,failed,kind,phase,targetId,stepId,sinceMs,untilMs,minDurationMs,maxDurationMs}`;`search?{text,in[],caseSensitive}`;`groupBy?`;`aggregate?[]` | `{ok:true,sessionId,view,fromSeq,toSeq,total,returned,dropped,evicted,truncated,cursor{next},events[],groups[],integrity,evidence}`。只读、不启动浏览器;`view:timeline` 是 http/ws/sse/marker 混排的单调时间轴;`view:aggregate` 必须给 `groupBy`;`search` 命中 body/ws/sse 只回**脱敏预览片段**;游标不透明,`cursor` 与 `sinceSeq` 二选一,指向已淘汰 seq 时返回 `gap`。 |
186
+ | `browser_network_events` | `sessionId?`;`sinceSeq?`/`untilSeq?`/`cursor?`;`limit?`(1–200,默认 50);`order?`;`view?:summary|full|timeline|aggregate`;`filter?{method,status,statusClass,resourceType,urlIncludes,failed,kind,phase,targetId,stepId,sinceMs,untilMs,minDurationMs,maxDurationMs}`;`search?{text,in[],caseSensitive}`;`groupBy?`;`aggregate?[]` | `{ok:true,sessionId,view,fromSeq,toSeq,total,returned,dropped,evicted,truncated,cursor{next},events[],groups[],integrity,evidence}`。只读、不启动浏览器;`view:timeline` 是 http/ws/sse/marker 混排的单调时间轴;`view:aggregate` 必须给 `groupBy`,`aggregate` 指标白名单 `count|bytes|duration|p50|p95|failures`(其它返回 `NET_INVALID_ARGUMENT`);`search` 命中 body/ws/sse 只回**脱敏预览片段**;游标不透明,`cursor` 与 `sinceSeq` 二选一,指向已淘汰 seq 时返回 `gap`。 |
151
187
  | `browser_network_detail` | `sessionId?`;`requestId`/`seq`(恰一);`include?[]`(默认除 body 外全部);`maxBodyBytes?`(1024–1048576) | 一键取完整详情:`request/response/headers/body/timing/redirects/initiator/dependencies/ws/sse/correlation/business/events/hops`。body 三态 `captured|empty|unavailable`,**读不到就 `available:false`(`NET_BODY_UNAVAILABLE`)不伪造**;detail 是纯只读,不为此启动 CDP、不重试;找不到 → `NET_EVENT_NOT_FOUND`。 |
152
188
  | `browser_network_trace` | `sessionId?`;`action*:begin-step|end-step|mark|link|window|query|reset`;`stepId?`/`name?`/`labels?`;`window?{fromWallMs,toWallMs}`;`objectRules?[]`;`correlationRules?[]`;`businessStatusPath?`;`mark?`;`link?`;`query?`;`includeTemporal?=false` | 步骤/对象/状态/因果/证据:`query` 返回每步 `counters/objects/states/evidence/causality`。begin/end/mark/link/window/query/reset **都是 Host 内存操作,不启动浏览器**;窗口归属按 wallMs 派生 `stepId`(`basis:"temporal"`)且**不改写事件本身**;`stepIds` 是显式链接、与派生的 `stepId` 并存;同名步骤 `NET_RULE_CONFLICT`。 |
153
189
  | `browser_network_control` | `sessionId?`;`action*:throttle|offline|intercept-add|intercept-remove|intercept-clear|intercept-list|hold|resume|replay|mock|reset`;`throttle{latencyMs,downloadKbps,uploadKbps}`;`offline?`;`rule?{id,pattern,behavior}`;`ruleId?`;`requestId?`/`seq?`;`then?`;`replay{count,via}`;`mock{status,headers,body}` | 需要 page 的动作自行懒启动。throttle 与 offline 是组合状态(Kbps = 1024 bit/s,0 表示不限速);规则最多 32 条、按顺序先命中先生效、同 id `NET_RULE_CONFLICT`、`hold` 上限 64 且超时自动 continue(`NET_CONTROL_TIMEOUT` 计数不抛错);`behavior.kind`=`block|fail|modify-request|fulfill|delay|hold`(`modify-response` 只在 Chrome 实测支持时可用,否则如实报不支持);`reset` 恢复网络条件、清规则、放行 held,但**不得 `Fetch.disable`**(既有 Document 导航守卫必须保留)。 |
@@ -447,14 +483,18 @@ browser_http_request {
447
483
  - 原始截图及可见附件仍不等于 T3 分析;T3 也只支持指定区域/分辨率/时间/阈值下的像素结论,不证明业务、交互、可访问性或整页正确。`/metrics` 只证明预览管线指标。
448
484
  不得伪造路径、附件或已通过状态。
449
485
 
450
- ## 读取面板标注(P0-D annotations)
486
+ ## 人类标注(调试视图上的画图):先读标注,别让用户改口述
487
+
488
+ 用户提到「标注」「我画的」「看我框的」「我圈的是这里」「这个箭头指什么」「这些笔迹」等、指代面板标注时,**先读 `browser_state.annotations`,再调用 `browser_screenshot {includeAnnotations:true}` 查看叠加像素**;不要让他把位置写成文字、也不要让他给候选编号。人类在调试视图上画图是**跨环境通用的输入通道**(环境中立规则见 `ui-testing-common`),这里只写 Web 侧的入口与坐标细则。
451
489
 
452
- 用户在调试面板里画的标注是**问题线索**,Agent 用结构化文档读,不用截图:
490
+ **术语**:本 Skill 里的「浏览器工具 / 工具面」= 当前 Web 环境**注册并准入**的 `browser_*` 工具(当前 37 个,含 `browser_state`、`browser_capabilities` 两个环境无关诊断)。环境或 provider 被禁用时这些工具根本不注册,调用会拿到 `ADAPTER_DISABLED`——那是"这个环境没启用",不是工具坏了。
453
491
 
454
- - **入口**:`browser_state()` 顶层 `annotations`(每工作区一份,与已认证 `/state` 的 `ui.annotations` 同源);`browser_capabilities().annotations` 给读取方式/`space`/`limits`。工具面仍是 30,没有标注工具;**标注不进 `browser_screenshot` 也不进 T3**(`capture.composite:true` 只是画布空白合成,与标注无关)。
492
+ - **入口**:`browser_state()` 顶层 `annotations`(与已认证 `/state` 的 `ui.annotations` 同源,含 `foreground` 前台心跳);`browser_annotation {action:"read"}` 读同一份文档;`browser_capabilities().annotations` 给读取方式/`space`/`limits`。`browser_screenshot` 的 `includeAnnotations?:boolean=false` 只控制本次截图叠加。默认截图、T3 原始源与 T4 两种帧源不含面板标注;T3 `annotations` 是独立的图片后处理参数。
493
+ - **叠加边界**:`includeAnnotations:true` 仅绘制当前目标已提交且经 Host 接受的 `circle/rect/line/arrow/brush/text`,颜色、粗细与填充/外框遵循面板语义;不含未提交草稿、工具栏或选中控件。图形与页面共用 region/scale 映射,截图连续像素坐标 `u=(d−region.origin)/mapping.cssPerPixel`(逐轴);用 `capture.annotations` 核对 revision/context、新鲜度、截断与 `source.trusted=false`,不能把旧文档当最新画面。`capture.canvas.composite` 描述画布合成,不能单凭它判断是否叠加标注。
494
+ - **反向:给他画**。用户说「你圈一下 / 标给我看 / 在这儿写个问号」时用 `browser_annotation`:先看 `browser_state.annotations.foreground.active`,为 false 就先请他选中那个标签页并保持面板可见,否则写入会被 `ANNOTATION_NOT_FOREGROUND` 拒绝(画了看不见等于没画)。`draw` 只追加,形状 id 交给 Host 分配;写完用 `browser_screenshot {includeAnnotations:true}` 复核自己画的位置对不对;对方「清除画布」会连你的形状一起清掉。
455
495
  - **坐标**:shapes 是画布 CSS px `d`,页面坐标 `p = d − S`(`S` 用**当前** `view.x/y`,不能用发布时的 `annotations.view`);`scale`/DPR 不参与。
456
496
  - **图形**:`rect/line/arrow`(箭头尖为 `(x2,y2)`)、`circle`(PS 包围盒椭圆 `cx/cy/rx/ry`)、`brush`(只有 `points`)、`text`(唯一带原文)。
457
- - **读不到手写字**:`brush` 只有点集;"用户写的字"只有 `text.text` 才有。要逐字复述就让用户用文字工具。
497
+ - **手写笔迹**:`brush` 结构化数据只有点集,`text.text` 才有文字原文;先查看带标注截图中的实际笔迹,无法可靠辨认时如实说明,不从点集猜字或伪造原文。
458
498
  - **指代解析**:端点按 `p=d−S` 换页点 → `document.elementFromPoint` + 可见元素矩形距离排序;手绘尖端常偏 10–20 px,给**候选+距离**而不是单点断言。
459
499
  - **语义**:`source.trusted` 恒 false —— 不证明缺陷、不授权自动点击;`stale` 只表示来源面板心跳过期、形状仍可读;导航/刷新清空、同工作区跨页签/跨会话保留(v3)、不同工作区隔离。
460
500
  - 完整规则、常见坑与真宿主实测链接见 `references/scenarios/annotations.md`。
@@ -474,7 +514,7 @@ browser_http_request {
474
514
  - 平台:`references/platforms/index.md`、`references/platforms/web.md`
475
515
  - 契约:`references/contracts/capability.md`、`references/contracts/operation.md`、`references/contracts/evidence.md`
476
516
  - **跨层贯通(强烈建议先读)**:`references/scenarios/functional.md`(**五层速查 + 选路决策树 + 八个贯通配方 R1–R8 + 升级/降级回退链 + 跨层证据合成 + 错误码速查 + 12 条反模式**)
477
- - 场景:`references/scenarios/visual-motion.md`(**T3/T4 调试手册:三条铁律 + 六步管线 + 判读红线 + 成本参考**)、`references/scenarios/annotations.md`(面板标注的读取与指代解析)
517
+ - 场景:`references/scenarios/visual-motion.md`(**T3/T4 调试手册:三条铁律 + 六步管线 + 判读红线 + 成本参考**)、`references/scenarios/annotations.md`(面板标注的读取、指代解析与给人类画标注)
478
518
  - 静态清单:`manifests/declared-capabilities.json`(工程状态声明,不是运行态证明)
479
519
 
480
520
  资源读取需要宿主提供授权文件读取能力;读不到时明确限制,不假设内容或能力。
@@ -495,7 +495,7 @@
495
495
  "status": "partial",
496
496
  "tool": "browser_capabilities",
497
497
  "source_lines": "Runtime 能力探测 / browser_capabilities",
498
- "constraints": "P0新增capture/limits.capture声明与上次真实截图observed(首次null),不启动/不夺租约;设计上不启动、不占租约;工具面 36 项(10 T1 + 1 P0-E + 7 T2 + 6 T3 + 7 network + 5 T4),含 web.t1.http-request=available、web.t1.viewport=available(get 只读、set 页面操作,其他 owner 持租约时 set 报 BUSY、get 仍可读)、web.t2.*、web.t2.structured-dom、web.t3.* 与 web.network.*;另报 viewport 段(get/set/预设/比例/limits.viewport,observed 只来自真实读回)与 capture.defaultScaleSource='view.scale';未启动的页面工具可 available(按需启动可能失败);其他 owner 占用时页面/network/T2/T3 unavailable+BUSY、诊断与 HTTP 仍 available。available 只是设计级状态,行为验证看各套件报告。"
498
+ "constraints": "P0新增capture/limits.capture声明与上次真实截图observed(首次null),不启动/不夺租约;设计上不启动、不占租约;工具面 37 项(10 T1 + 1 P0-D + 1 P0-E + 7 T2 + 6 T3 + 7 network + 5 T4),含 web.t1.http-request=available、web.t1.viewport=available、web.p0d.annotations=available(get 只读、set 页面操作,其他 owner 持租约时 set 报 BUSY、get 仍可读)、web.t2.*、web.t2.structured-dom、web.t3.* 与 web.network.*;另报 viewport 段(get/set/预设/比例/limits.viewport,observed 只来自真实读回)与 capture.defaultScaleSource='view.scale';未启动的页面工具可 available(按需启动可能失败);其他 owner 占用时页面/network/T2/T3 unavailable+BUSY、诊断与 HTTP 仍 available。available 只是设计级状态,行为验证看各套件报告。"
499
499
  },
500
500
  {
501
501
  "id": "web.t1.http-request",
@@ -767,7 +767,14 @@
767
767
  "status": "verified",
768
768
  "tool": "browser_tabs",
769
769
  "source_lines": "RuntimeRegistry selectedTab/browser_tabs",
770
- "constraints": "P0-E 第 36 个工具;list/current 只读;select 原子 claim;open 新建 about:blank;close 仅本人有效 lease;每 workspace 4 tabs/3 child、全局 8;错误 TAB_NOT_FOUND/TAB_LIMIT/TAB_FORBIDDEN/BUSY;Agent selectedTab 默认 primary,GUI active 独立。"
770
+ "constraints": "P0-E 第 36 个工具,九个动作(list/current/select/open/close/wait/acquire/release/transfer);list/current/wait 只读,select 只改调用者自己的选择、不 claim 租约;open 新建 about:blank;close 按长期归属/控制权判定(owner 或 controller,本 root Lead 亦可);acquire/release/transfer 仅本 root Lead,expectedControlRevision(transfer 另需 expectedOwnershipRevision)必填,acquire 先 draining 再等安全边界、超时 BUSY 且撤销 draining、不中断在途操作;页面组按 (workspace, provider, 团队根) 划分:child 最多 3 个按 (workspace, provider) 跨 root 合计,全局 live 8,primary 每个 (workspace, provider, 根) 一个;错误 TAB_NOT_FOUND(跨 root 统一,不泄露存在性)/TAB_FORBIDDEN/TAB_CONTROL_CHANGED/TAB_OWNERSHIP_STALE/TAB_IDENTITY_UNAVAILABLE/TAB_LIMIT/BUSY(仅短暂忙)/WAIT_TIMEOUT/INVALID_ARGUMENT;每会话独立 workSelection 与 observationSelection(work 默认 primary,observation 未设回退 work),GUI active 独立。"
771
+ },
772
+ {
773
+ "id": "web.p0d.annotations",
774
+ "status": "partial",
775
+ "tool": "browser_annotation",
776
+ "source_lines": "docs/viewport-control.md 标注节 + docs/tools.md 标注(P0-D);index.js browser_annotation + Runtime.agentAnnotation/guiForeground",
777
+ "constraints": "P0-D 标注读写(第 37 个工具)。每个 Runtime 一份标注文档,与面板认证写面 POST /plugins/ui-testing/annotations 共用同一份(read-modify-write;面板采纳更新的 Host revision 后画出)。action=read 返回 revision/shapes/shapeCount/truncated/truncation/foreground,不需要前台、不启动页面、不进队列、不占租约;draw 追加 1–64 个形状(circle/rect/line/arrow/brush/text,id 可省略由 Host 续分配 a<N>,color #rrggbb 默认 #ff3b30,strokeWidth 1–20 默认 3,style=stroke|fill 仅 circle/rect 且与 fill/stroke 互斥,arrow head 由端点与线宽派生);erase 按 ids 和/或 point{x,y,radius?=8} 命中(shape 几何到点距离 ≤ max(radius, strokeWidth/2, 6));clear 清空。draw/erase/clear 只在目标标签页被人类在调试视图选中且可见时允许(面板在认证请求上带 x-ui-testing-gui-foreground 心跳,1.5s 新鲜窗口),否则 ANNOTATION_NOT_FOREGROUND 零写入;expectRevision 不一致 ANNOTATION_REVISION_CONFLICT 零写入;页面未运行 annotation-runtime-not-running。软限额与面板写面一致(128 图形 / 单笔 512 点 / 文档 8192 点 / 200 字符 / 65536 字节,确定性裁剪并如实报 truncation)。不启动浏览器、不进页面队列、不占租约、不写页面 lastError、不改 DOM/CSS。真实宿主的前台心跳过期与面板采纳待 GUI 侧复验。"
771
778
  }
772
779
  ],
773
780
  "unsupported_tiers": [],
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## 注册表与运行态
4
4
 
5
- 当前 Agent 工具注册表决定可调用的名字;**工具面 36 个**:T1 10 个 + P0-E 1 个(`browser_tabs`)+ T2 7 个 + T3 6 个 + 网络 P0 7 个 + T4 5 个;`web.t1.http-request`、`web.t2.*` 七项与聚合项 `web.t2.structured-dom`、`web.t3.*` 六项与聚合项 `web.t3.static-vision`、`web.network.*` 七项与聚合项 `web.network.p0`、`web.t4.*` 五项与聚合项 `web.t4.dynamic-behavior`(旧名 `web.t4.dynamic-vision` 为别名)报 available(或 BUSY),并且必须报 `limits.structuredGui`(protocolVersion/targetFrame/ref 策略/各限额)、`vision`/`limits.vision`(version=1、algorithmVersion='t3-v1'、六个工具名、sourceKinds、全部预算、observed)、`network`/`limits.network` 与 `dynamic`/`limits.dynamic`(algorithmVersion='t4-draft-v1'、六向、预算、`maxSessionExtendMs`/`sessionCapMs`)。其他 owner active/idle 占用时页面/network/T2/T3 为 unavailable、reason BUSY(含 T3 的离线 baseline list/get/delete,它们仍过队列与 owner 检查),state/capabilities 与 http_request 仍 available。未启动的页面工具可以 available(按需懒启动,constraint 说明启动可能失败),running/transport 单独报告真实连接,不能把未启动当 unsupported。T3 只有 live 源与 `act` 会懒启动页面;product/baseline 源与离线 baseline 操作不启动浏览器。附件状态与截图 fallback 另报限制,不能回显静态 manifest 作为运行态。下载/上传/Cookie 导出、frame 级目标选择仍 unsupported。Agent 多标签选择已由 P0-E 的 `browser_tabs` 提供(list/current/select/open/close),每会话 selectedTab 默认 primary;GUI 内部标签页与 `+` 亦已支持。T4 与共享视口已实现;T4 的 media 适配器/WebGPU 真实渲染/16ms 闪白采样覆盖仍未验证。
5
+ 当前 Agent 工具注册表决定可调用的名字;**工具面 37 个**:T1 10 个 + P0-E 1 个(`browser_tabs`)+ P0-D 1 个(`browser_annotation`)+ T2 7 个 + T3 6 个 + 网络 P0 7 个 + T4 5 个;`web.t1.http-request`、`web.t2.*` 七项与聚合项 `web.t2.structured-dom`、`web.t3.*` 六项与聚合项 `web.t3.static-vision`、`web.network.*` 七项与聚合项 `web.network.p0`、`web.t4.*` 五项与聚合项 `web.t4.dynamic-behavior`(旧名 `web.t4.dynamic-vision` 为别名)报 available(或 BUSY),并且必须报 `limits.structuredGui`(protocolVersion/targetFrame/ref 策略/各限额)、`vision`/`limits.vision`(version=1、algorithmVersion='t3-v1'、六个工具名、sourceKinds、全部预算、observed)、`network`/`limits.network` 与 `dynamic`/`limits.dynamic`(algorithmVersion='t4-draft-v1'、六向、预算、`maxSessionExtendMs`/`sessionCapMs`)。其他 owner active/idle 占用时页面/network/T2/T3 为 unavailable、reason BUSY(含 T3 的离线 baseline list/get/delete,它们仍过队列与 owner 检查),state/capabilities 与 http_request 仍 available。未启动的页面工具可以 available(按需懒启动,constraint 说明启动可能失败),running/transport 单独报告真实连接,不能把未启动当 unsupported。T3 只有 live 源与 `act` 会懒启动页面;product/baseline 源与离线 baseline 操作不启动浏览器。附件状态与截图 fallback 另报限制,不能回显静态 manifest 作为运行态。下载/上传/Cookie 导出、frame 级目标选择仍 unsupported。Agent 多标签选择已由 P0-E 的 `browser_tabs` 提供(list/current/select/open/close/wait/acquire/release/transfer 九个动作):每个会话有独立的 workSelection 与 observationSelection(未显式选择时 work 默认指向自己已拥有的页、没有自有页则未绑定 → 写操作 `TARGET_NOT_BOUND`;observation 未设时回退到 work);页面组按 (workspace, provider, 团队根) 划分,跨 root 的目标统一 not-found 语义。GUI 内部标签页与 `+` 亦已支持,但面板侧没有 Principal,也没有 acquire/release/transfer 入口。T4 与共享视口已实现;T4 的 media 适配器/WebGPU 真实渲染/16ms 闪白采样覆盖仍未验证。
6
6
 
7
7
  **能力报告不能自证**:若 capabilities 自身抛错或字段与本文/注册 schema 不符(早期草稿曾在 limits 块引用未声明常量抛 `INTERNAL`,已修),按“证据不足”处理(BLOCKED/INCONCLUSIVE),不据此推断某能力可用或不可用;以注册表 + 真实工具调用为准。
8
8
 
9
- `browser_state {}` 与 capabilities 都是诊断,不懒启动、不申请页面租约;state 的 `running:false` 可表示未启动/已关闭/断连,不等于 unsupported。需要页面的 5 个 T1 工具与全部 7 个 T2 工具可在授权操作时懒启动,network 不启动但须过租约检查;`browser_http_request` 既不启动也不受租约。T2 中只读的 snapshot/node_state/wait_for 同样受其他 owner 的 BUSY 限制。
9
+ `browser_state {}` 与 capabilities 都是诊断,不懒启动、不申请页面租约;state 的 `running:false` 可表示未启动/已关闭/断连,不等于 unsupported。需要页面的 5 个 T1 工具与全部 7 个 T2 工具可在授权操作时懒启动,network 不启动但须过租约检查;`browser_http_request` 既不启动也不受租约。T2 中只读的 snapshot/node_state/wait_for/computed_style 只取**读**权限、不占写租约(不会把别人的写挡成 BUSY),但仍在该页队列里串行执行,因此可能排队等待;写工具才需要 controller 资格。
10
10
 
11
11
  ## network P0 声明与观测
12
12
 
@@ -61,7 +61,7 @@ observed首次为null,只能来自所属Runtime上次真实截图,含observe
61
61
  3. 执行授权操作,以真实返回和后续观察作证据。
62
62
  4. 缺工具不得走 CDP/面板 HTTP 绕过;结果未知按 BLOCKED/INCONCLUSIVE 报告。
63
63
 
64
- 静态台账使用 implemented、implemented-not-verified、verified、partial、unsupported;这些是工程验收状态,与运行时三态含义不同。**工具面 36 个(10 T1 + 1 P0-E + 7 T2 + 6 T3 + 7 network + 5 T4)**,`check-skill` 与 `check-skill-registry` 断言注册表恰好 36 个(含 `browser_tabs`)。台账只记录当前状态,不保留逐轮历史。
64
+ 静态台账使用 implemented、implemented-not-verified、verified、partial、unsupported;这些是工程验收状态,与运行时三态含义不同。**工具面 37 个(10 T1 + 1 P0-E + 1 P0-D + 7 T2 + 6 T3 + 7 network + 5 T4)**,`check-skill` 与 `check-skill-registry` 断言注册表恰好 37 个(含 `browser_tabs` 与 `browser_annotation`)。台账只记录当前状态,不保留逐轮历史。
65
65
 
66
66
  **`vision` 报告要求**:`browser_capabilities.vision` 必须报 `version:1`、`algorithmVersion:'t3-v1'`、六个工具名、`sourceKinds:['live','product','baseline']`、`limits`(maxProducts/maxTotalBytes/maxProductBytes/maxProductPixels/maxWorkingSetBytes/maxTemplateComparisons/algorithmBudgetMs/maxOutputPixels/maxOutputBytes,以及 evidenceBytes/regions、cases/assertions/facts、masks/annotations/preprocess、actMaxAgeMs)与 `observed:null|{observedAt,lastOperation,lastVerdict}`;`limits.vision` 另报坐标空间、productId 形态、diff 默认值与 warnings 词表、baseline 根/权限/action。观测只来自真实视觉调用,不含图像、绝对路径或页面文本;该调用不启动浏览器、不建目录、不生成 product、不申请租约。
67
67
 
@@ -34,7 +34,7 @@ escalation{tier:"T3"|"T4"|null,reason}, incomplete[], truncated
34
34
 
35
35
  截图保留 image base64;可用时 `ctx.attachments.saveImage` 保存附件并 render image block。缺少附件服务明确 render base64-fallback 文本。附件 ID 不是文件路径,保存成功不等于 provider 已显示图像;真实可见性需在视觉 provider 会话实测。不能宣称已落本地证据文件,除非有实际返回/授权保存证据。截图与 eval 内容不自动脱敏,image block 可能进入 provider 上下文。
36
36
 
37
- P0的 `capture.version=1` 与T2 evidence独立:含request/environment/png/expected/sizeCheck/mapping/anomaly/file/delivery/warnings,mapping仅JSON+公式;DPR未知null、DSF来源runtime-override,headlessSource来自product/userAgent。原图尺寸/hash/文件是采集事实,delivery仅附件metadata,不自证provider。默认staging(绝对path/ephemeral=true,插件dispose清理);out/persist:true/非空env才persistent(env或cwd根相对path/ephemeral=false)。file含pathKind/persistence/root/ephemeral,暂存不能当持久证据;observed.file只含persistence/relativePath|null不公开绝对路径。仅file非null且hash匹配可声明已有文件,暂存失败file=null+既有warning。路径规则见 [安全边界](../core/safety.md)。≤16M像素全量均匀/透明统计不替代视觉判定;C13前后布局相同不排除瞬时LBI,canvas位图细节不增加,TOCTOU未解决。真实宿主与 provider 行为需单独证据。
37
+ P0的 `capture.version=1` 与T2 evidence独立:含request/environment/png/expected/sizeCheck/mapping/anomaly/annotations/file/delivery/warnings,mapping仅JSON+公式;`includeAnnotations?:boolean=false` 默认不叠加面板标注,true 按同一 region/scale 渲染 Host 已接受的六种形状,不含未提交草稿/工具栏;`capture.annotations` 未开启时为 null,开启时用 revision/context、新鲜度、截断与来源核对文档(source 存在时 trusted=false);空文档 enabled=true/composited=false/source=null,shapeCount 是接收总量而非 region 内可见数量,完整字段见 `docs/capture-p0.md`;默认截图和 T3/T4 原始源不含面板标注;DPR未知null、DSF来源runtime-override,headlessSource来自product/userAgent。原图尺寸/hash/文件是采集事实,delivery仅附件metadata,不自证provider。默认staging(绝对path/ephemeral=true,插件dispose清理);out/persist:true/非空env才persistent(env或cwd根相对path/ephemeral=false)。file含pathKind/persistence/root/ephemeral,暂存不能当持久证据;observed.file只含persistence/relativePath|null不公开绝对路径。仅file非null且hash匹配可声明已有文件,暂存失败file=null+既有warning。路径规则见 [安全边界](../core/safety.md)。≤16M像素全量均匀/透明统计不替代视觉判定;C13前后布局相同不排除瞬时LBI,canvas位图细节不增加,TOCTOU未解决。真实宿主与 provider 行为需单独证据。
38
38
 
39
39
  没有独立JSONL证据包、自动assertion_status、帧序列或视觉差异分析;原始PNG落盘不等于独立证据包。`/metrics` 是预览管线诊断,不能替代页面断言。close 清空网络/URL/标题/帧缓存**与 T2 的 snapshot/ref 表**;引用记录仍要核对本次 action 时序。
40
40
 
@@ -6,7 +6,7 @@
6
6
  | --- | --- | --- |
7
7
  | `browser_navigate` | `{ok:true,url}` | 检查 errorText/最终 URL;不保证 load complete;失败抛 BrowserError |
8
8
  | `browser_eval` | `{ok:true,value,type}` | 页面异常 `{ok:false,error:{code,category,retryable,message,details}}`,含描述/栈/位置 |
9
- | `browser_screenshot` | `{viewport,bytes,image,capturedAt,sha256,attachmentId,capture}` | 可选显式scale 0.1–4(省略时跟随共享view.scale)/region文档CSS/persist:boolean=false/out持久根相对.png;默认staging,out/persist:true/非空env才persistent;file含pathKind/persistence/root/ephemeral,dispose清理暂存;CRC/尺寸/全量均匀统计/JSON映射/安全文件/附件metadata进入capture v1;保留base64/附件fallback,provider另验 |
9
+ | `browser_screenshot` | `{viewport,bytes,image,capturedAt,sha256,attachmentId,capture}` | 可选显式scale 0.1–4(省略时跟随共享view.scale)/region画布CSS/includeAnnotations?:boolean=false/persist:boolean=false/out持久根相对.png;true 按同一 region/scale 叠加 Host 已接受的六种面板形状,不含草稿/工具栏,capture.annotations 记录文档证据;默认截图及 T3/T4 原始源不含面板标注;默认staging,out/persist:true/非空env才persistent;file含pathKind/persistence/root/ephemeral,dispose清理暂存;CRC/尺寸/全量均匀统计/JSON映射/安全文件/附件metadata进入capture v1;保留base64/附件fallback,provider另验 |
10
10
  | `browser_input` | `{ok:true,applied}` | applied 是数组长度,不证明识别/生效;未知 kind 仍可能跳过;modifiers 支持字符串数组或 CDP 位掩码(Alt=1/Ctrl=2/Meta=4/Shift=8) |
11
11
  | `browser_network` | `{entries:[…]}` | 响应及 loadingFailed、URL 脱敏、记录有界;其他 owner 返回 BUSY |
12
12
  | `browser_viewport` | `{ok,running,view,observed,viewMeta}`(get)/ `{ok,requested,before,view,observed,viewMeta,applied,settled,clamped,overrideApplied,appliedFields}`(set) | get 只读缓存诊断(不 ensure/不发 CDP/不进队列/不占租约);set 走预检 → 串行队列 → 懒启动 + 租约;`expectedRevision` 冲突抛 `VIEWPORT_CONFLICT`(HTTP 409)且 patch 零应用;`x/y` 是文档 CSS 滚动位置、`scale` 0.1–2 且为默认截图密度;不改 DPR/页面 zoom |
@@ -1,6 +1,6 @@
1
1
  # Fallback & Stop · 恢复与停止(当前实现版)
2
2
 
3
- > 本文件只在按 `selection.md` 选路后参与决策。回退是换一个方法验证**同一个预先确定的断言**;不能降低验收标准,也不能用新结果覆盖已证实的缺陷。当前插件已实现 T1 + T2 + T3 + T4 + 网络 P0 + P0-E 多标签(36 工具):T2 → T3 → T4 的升级是**真实可用路径**,且跨层证据要分别记账。
3
+ > 本文件只在按 `selection.md` 选路后参与决策。回退是换一个方法验证**同一个预先确定的断言**;不能降低验收标准,也不能用新结果覆盖已证实的缺陷。当前插件已实现 T1 + T2 + T3 + T4 + 网络 P0 + P0-E 多标签 + P0-D 标注(37 工具):T2 → T3 → T4 的升级是**真实可用路径**,且跨层证据要分别记账。
4
4
 
5
5
  ## 决策流程
6
6
 
@@ -50,8 +50,9 @@ browser_tabs current {} # 确认本会话 selectedTab(多标签
50
50
 
51
51
  ## 4. 新标签 / 弹窗归属
52
52
 
53
- - 每个 DSH 工作区是**页面组**:primary + 最多 3 个 child tab(每 workspace 4,全局 8)。每个 tab 有独立 URL/viewport/网络缓冲/ref 表/租约。
54
- - **Agent 工具默认只操作当前会话的 selectedTab**;`browser_tabs` 的 `list/current/select/open/close` 是唯一的标签管理入口。`list/current` 只读,`select` 需要同 workspace 且目标未被其他 owner 占用(否则 `BUSY`)。
53
+ - 页面组按 **(workspace, provider, 团队根)** 划分:同一个团队根共享一组页,不同独立会话/团队根各有一组,互不可见、互不可写。每组一个 primary 槽 + child(child 最多 3 个是跨 root 合计,全局 live 8)。每个 tab 有独立 URL/viewport/网络缓冲/ref 表。
54
+ - **Agent 工具默认只操作当前会话的选择**:写工具用 workSelection,读工具优先用 observationSelection;`browser_tabs` 的 `list/current/select/open/close/wait/acquire/release/transfer` 是唯一的标签管理入口。`list/current` 只读。
55
+ - `select` **不 claim 租约、不切前台**:`mode:"work"` 需要本会话可写该页或本 root 的 Lead 可管理它,`mode:"observe"` 只要同 root 可读。判定顺序是 root → 角色 → controller/版本,**不是**"目标有没有被占用":跨会话/跨团队根的目标一律 `TAB_NOT_FOUND`(不回显对方 id/URL/标题),同 root 但无权写是 `TAB_FORBIDDEN`,控制权/版本不匹配是 `TAB_CONTROL_CHANGED`。`BUSY` 只表示短暂忙(队列被占/接管等待超时),不能当权限结论。
55
56
  - 真人 GUI 触发的 `target=_blank`/`window.open` 可能被接管为同工作区 child page;**自动/未授权 popup 仍被目标策略阻断**。工具不会自动把新标签设为 selectedTab——先 `browser_tabs list` 看它是否存在、再 `select`。
56
57
  - `browser_tabs open` 自建标签**不是**原生新页:它是替代路径,必须按 [`reporting.md`](reporting.md) 写明"替代了什么、仍缺哪条原始断言"(例如 opener 关系、焦点归属、postMessage 时序都无法由此证明)。
57
58
  - child tab 被关闭或 idle 回收后,旧 selection 进入 `pendingReset`:下一次页面工具会 `TARGET_NOT_FOUND(reason:selected-tab-closed, fallbackTabId:primary)` 且**零派发**;用 `browser_tabs current` 或 `select` 明确确认后再继续,别把这次失败当成"元素不存在"。
@@ -13,7 +13,7 @@
13
13
  | 页面 JS 状态 | browser_eval | 可序列化值,不冒充点击/网络/CSSOM/动画;不冒充 T2 结构化协议 |
14
14
  | 页面请求成功或失败 | browser_network | 响应及 loadingFailed、脱敏 URL,无头/正文;其他 owner BUSY |
15
15
  | 元素计算样式 | browser_computed_style | 首匹配值,不证明像素/遮挡 |
16
- | PNG采集/局部高密度证据 | browser_screenshot | 显式scale 0.1–4(省略跟随共享view.scale)/region画布CSS(O=0时同文档)/out相对.png;PNG校验、capture映射/文件/附件metadata/warnings;observed不等于本次图,provider另验;无T3/T4分析 |
16
+ | PNG采集/局部高密度证据 | browser_screenshot | 显式scale 0.1–4(省略跟随共享view.scale)/region画布CSS(O=0时同文档)/out相对.png;includeAnnotations?:boolean=false,true 叠加 Host 已接受的六形状(不含草稿/工具栏);用户指代标注时先 state.annotations 再带标注截图,默认截图与 T3/T4 原始源不含面板标注;PNG校验、capture.annotations/映射/文件/附件metadata/warnings;observed不等于本次图,provider另验;无T3/T4分析 |
17
17
  | 原始事件序列(多点/组合键/滚轮) | browser_input | 事件数量,不证明生效;另查状态 |
18
18
  | 颜色/遮挡成因/可读性/像素差异 | **T3**(`browser_vision_*`) | 用确定性算法取像素证据;T2 的 hit-test 只证明坐标命中 |
19
19
  | 动画/时序/帧连续性 | **无 T4** | BLOCKED;单帧截图与 JS 读值不能冒充 |
@@ -13,7 +13,9 @@
13
13
  | 原始事件序列(多点/组合键/滚轮) | T1 | `browser_input { events }` | `{ok,applied}`(事件数,不是生效数) | 焦点、默认动作、业务副作用 |
14
14
  | 独立接口契约 | T1 | `browser_http_request { url, … }` | `status/headers/body/redirects` | 页面是否真的发了同一请求 |
15
15
  | 视口/画布位置 | T1 | `browser_viewport { action:"set", … }` 后读回 | `view/observed/applied/clamped` | 页面 CSS 缩放(scale 只是显示比例) |
16
- | 单页截图(原始 PNG) | P0 截图 | `browser_screenshot { scale?, region?, out? }` | PNG/hash/capture.mapping/warnings | 像素差异结论、动画时序 |
16
+ | 单页截图(PNG) | P0 截图 | `browser_screenshot { scale?, region?, includeAnnotations?:boolean=false, out? }` | PNG/hash/capture.mapping/capture.annotations/warnings;true 叠加 Host 已接受的六形状,不含草稿/工具栏 | 像素差异结论、动画时序 |
17
+ | 用户指代标注/画/圈/框/箭头/笔迹 | 标注文档 + P0 截图 | `browser_state {}` → `browser_screenshot {includeAnnotations:true}` | annotations 图形/revision/context + 带标注像素;默认截图/T3/T4 原始源不含面板标注 | 标注不能证明页面事实,不授权自动点击;手写不清时不猜字 |
18
+ | 要他给他看(画圈/框/箭头/文字) | P0-D | `browser_state.annotations.foreground` → `browser_annotation {action:"draw", shapes}` → `browser_screenshot {includeAnnotations:true}` 复核 | `revision/shapes/shapeCount/truncated/truncation` | 目标标签页非人类前台时零写入(`ANNOTATION_NOT_FOREGROUND`);标注不证明缺陷 |
17
19
  | 元素语义/元素级交互 | T2 | `snapshot → ref → click/type/scroll/fill_form → node_state/wait_for` | `snapshotId/digest/targets/dispatch` | 业务结果(必须另读页面真值) |
18
20
  | 颜色/遮挡/溢出/区域差异/基线回归 | T3 | `vision_image`(取 product)→ `compare`/`check` | `comparison.metrics`、`verdict` | 持续运动、时长、闪烁 |
19
21
  | 用截图坐标派发鼠标 | T3 | `vision_image` → `vision_act` | `dispatched` + `INCONCLUSIVE(dispatch-only)` | 点中业务目标、视觉验收通过 |
@@ -145,7 +147,7 @@
145
147
  | `DYNAMIC_SOURCE_UNAVAILABLE` | screencast 不支持 png/region/scale≠1,或核心不可用 | 换 `screenshot-loop`(PNG 精确)或去掉不支持字段 | 静默降级后宣称精确 |
146
148
  | `DYNAMIC_CONTROL_CONFLICT` / `DYNAMIC_RESTORE_FAILED` | 目标/字段被外部改动,恢复冲突 | 重新定位目标;如实报告 `restoreStatus` | 覆盖外部新状态 |
147
149
  | `DYNAMIC_EVENT_NOT_FOUND` | frame/event id 不属于本会话 | 用 `motion_observe` 重新取 id | 跨会话使用 id |
148
- | `TAB_NOT_FOUND` / `TAB_LIMIT` / `TAB_FORBIDDEN`(P0-E) | 标签不存在/超 3 child 或全局 8/跨 workspace | 重新 `list` 选 tab;关掉不需要的 child | 跨 workspace 读 tab |
150
+ | `TAB_NOT_FOUND` / `TAB_LIMIT` / `TAB_FORBIDDEN` / `TAB_CONTROL_CHANGED` / `TAB_OWNERSHIP_STALE` / `TAB_IDENTITY_UNAVAILABLE`(P0-E) | 标签不存在或跨 root(统一 not-found)/超 child 或全局 8/同 root 但角色或归属不允许/控制权或 CAS 版本不匹配/身份无法证明 | 重新 `list` 选 tab;关掉不需要的 child;读 `control` 的最新版本再决策(`TAB_CONTROL_CHANGED` 不自动重放) | 用"能看见"推断"能写";把 BUSY 当权限结论 |
149
151
 
150
152
  ## 8. 坐标空间对照表(最容易错的地方)
151
153
 
@@ -192,4 +194,5 @@ imagePx = (docCss - originCss) * cssPerPixel # 文档 CSS → 帧图像像素
192
194
  - T2:ref 表 2000 项 LRU;`snapshot maxNodes` 1–2000、`depth` 0–32;`wait_for timeoutMs` 0–30000;`fill_form` 1–20 项、文本合计 ≤65536 字节。
193
195
  - 网络:`events limit` ≤200;会话 `maxEvents` ≤20000、`maxBodyBytes` ≤1 MiB、`maxTotalBytes` ≤32 MiB、`maxWsFrames` ≤20000;规则 ≤32、`hold` ≤64、订阅 ≤16。
194
196
  - 独立 HTTP:总超时 1–20000ms(默认 5000)、保留正文 1–1048576 字节(默认 65536)、重定向 ≤5 跳、body ≤64 KiB。
195
- - 多标签:每 workspace primary+3 child=4,全局 8,lease idle 15s。
197
+ - 标注(P0-D):`draw.shapes` 每次 1–64;`erase.ids` 1–64、`point.radius` 默认 8 画布 px;文档 `maxShapes` 128、单笔画笔 512 点、文档总点数 8192、文字 200 字符、文档 65536 字节(Host 确定性裁剪并回 `truncated`/`truncation`);`expectRevision` CAS → `ANNOTATION_REVISION_CONFLICT`;前台心跳窗口约 1.5s。
198
+ - 多标签:页面组按 (workspace, provider, 团队根) 分组,每组一个 primary 槽;child 最多 3 个按 (workspace, provider) 跨 root 合计,全局 live 8;短租约 idle 15s 只表示"短暂忙"(观察不占写租约),写权限由 root/角色/控制权决定。
@@ -6,7 +6,7 @@
6
6
 
7
7
  | 平台 | 文档 | 当前状态 |
8
8
  |---|---|---|
9
- | Web / Chromium(无头) | `web.md` | T1–T4、网络 P0 与 P0-E 多标签已实现(36 工具,含 `browser_tabs`);T4 44 条门槛中 12 通过 / 20 部分 / 12 未运行,provider 图像可见性未验证 |
9
+ | Web / Chromium(无头) | `web.md` | T1–T4、网络 P0、P0-E 多标签与 P0-D 标注已实现(37 工具,含 `browser_tabs`、`browser_annotation`);T4 44 条门槛中 12 通过 / 20 部分 / 12 未运行,provider 图像可见性未验证 |
10
10
  | Windows | 未创建 | 未定义 / 不可用 |
11
11
  | Linux | 未创建 | 未定义 / 不可用 |
12
12
  | Android | 未创建 | 未定义 / 不可用 |
@@ -1,6 +1,6 @@
1
1
  # Web 平台操作指南(M0–M2 + T2)
2
2
 
3
- Web 已有 **36 个工具**:T1 10 个(含独立 HTTP `browser_http_request` 与共享视口 `browser_viewport`)+ P0-E 1 个(`browser_tabs`)+ T2 7 个 + T3 6 个 + 网络 P0 7 个 + T4 5 个,工具名见各层表格与 [工具参考](../../../../docs/tools.md)。T3/T4 已实现,其中 T4 的 44 条门槛仍有 20 条部分通过、12 条未运行;provider 图像可见性未验证。按当前注册表与 `browser_capabilities` 实际三态选路,不把文档或 manifest 当运行态证明。
3
+ Web 已有 **37 个工具**:T1 10 个(含独立 HTTP `browser_http_request` 与共享视口 `browser_viewport`)+ P0-E 1 个(`browser_tabs`)+ P0-D 1 个(`browser_annotation`)+ T2 7 个 + T3 6 个 + 网络 P0 7 个 + T4 5 个,工具名见各层表格与 [工具参考](../../../../docs/tools.md)。T3/T4 已实现,其中 T4 的 44 条门槛仍有 20 条部分通过、12 条未运行;provider 图像可见性未验证。按当前注册表与 `browser_capabilities` 实际三态选路,不把文档或 manifest 当运行态证明。
4
4
 
5
5
  同一时刻最多一个无头 Chromium/profile;每个 DSH 工作区一个 page,默认共享视口 1280×800@100%/DPR1(`browser_viewport` 可改宽高/X-Y/显示比例;比例不改页面 CSS 视口与 DPR)。single-owner-lease 按工作区:空闲 15s、进行中保持、缺 exec 走 legacy、disposed 只释放对应 owner;同工作区其他 session 页面/network/T2 BUSY,不同工作区独立。面板与 Agent 在同一工作区同 page/串行队列;Cookie/localStorage/cache 共享,不是安全隔离。state/capabilities 不启动、不占租约。
6
6
 
@@ -14,4 +14,4 @@ Web 已有 **36 个工具**:T1 10 个(含独立 HTTP `browser_http_request`
14
14
 
15
15
  面板 screencast 的 WS 优先/HTTP 回退、metrics 都是预览诊断,不是 Agent 页面证据;内部 HTTP 路由不是 Agent API,不可绕过工具。
16
16
 
17
- 须验证:36 工具注册面/诊断不启动(含 `browser_tabs`)、标题/导航/错误、租约/anonymous/disposed/BUSY、按 tab 的 BUSY 与 selectedTab、面板队列与取消关闭、目标/DNS/Document 重定向、网络失败/脱敏/资源界限、action render、截图附件/fallback/provider、独立 HTTP 的预算/重定向/阻断/取消/零派发、T2 的快照/ref 生命周期/唯一 selector/可信动作/表单/滚动/等待/错误码/证据、network P0 的 7 工具注册/三态/限额、事件 schema 与时间轴、body 三态不伪造、脱敏、配额与 integrity、拦截/限速/重放/Mock 与导航守卫共存、JSONL/HAR 往返、订阅隔离与 pin/gap。没有下载/上传/Cookie 导出、Agent 多 target/frame(iframe 内部)选择、自动 popup 采集;`browser_viewport` 与 `browser_tabs`(按会话 selectedTab 路由、GUI active 独立)已可用,但不做跨标签聚合;T4 的 media 适配器、WebGPU 真实渲染、16ms 闪白采样覆盖仍未验证。
17
+ 须验证:37 工具注册面/诊断不启动(含 `browser_tabs` 与 `browser_annotation`)、标题/导航/错误、租约/anonymous/disposed/BUSY、按 tab 的 BUSY 与 selectedTab、面板队列与取消关闭、目标/DNS/Document 重定向、网络失败/脱敏/资源界限、action render、截图附件/fallback/provider、独立 HTTP 的预算/重定向/阻断/取消/零派发、T2 的快照/ref 生命周期/唯一 selector/可信动作/表单/滚动/等待/错误码/证据、network P0 的 7 工具注册/三态/限额、事件 schema 与时间轴、body 三态不伪造、脱敏、配额与 integrity、拦截/限速/重放/Mock 与导航守卫共存、JSONL/HAR 往返、订阅隔离与 pin/gap。没有下载/上传/Cookie 导出、Agent 多 target/frame(iframe 内部)选择、自动 popup 采集;`browser_viewport` 与 `browser_tabs`(按会话 selectedTab 路由、GUI active 独立)已可用,但不做跨标签聚合;T4 的 media 适配器、WebGPU 真实渲染、16ms 闪白采样覆盖仍未验证。
@@ -0,0 +1,104 @@
1
+ # 读取与写入面板标注(P0-D annotations)
2
+
3
+ 用户提到「标注」「我画的」「看我标的」「我圈的是哪里」「这个框/箭头指的是什么」「这些笔迹」等、指代面板标注时用本页;用户说「你圈一下」「标给我看」「在这儿写个问号」时也用它(反向:`browser_annotation` 往同一份文档里画)。标注是**用户在调试面板里画的问题线索**:先读结构化文档,再查看带标注截图;要给人类画标注时,先确认前台标签可见再写。
4
+
5
+ ## 1. 文档与像素读取入口
6
+
7
+ - `browser_state()` 返回顶层 `annotations`(与已认证 `/state` 的 `ui.annotations` 同源;旧 Host 无此字段 → 按 `unsupported` 处理,不要假装读到)。
8
+ - `browser_capabilities()` 的 `annotations` 段声明读取方式、`space`(恒为 `canvas-css-px`)与 `limits`;**工具面是 37 个已注册的 `browser_*`(当前 Web 环境注册并准入的那些)**。读取结构化标注既可以用 `browser_state` 的一个字段,也可以用 `browser_annotation {action:"read"}`(同一份文档,返回 `revision/shapes/shapeCount/truncated/truncation/foreground`)。
9
+ - `browser_screenshot {includeAnnotations:true}` 叠加当前目标已提交且经 Host 接受的六种形状,渲染与面板的颜色、粗细、填充/外框语义一致;不包含未提交草稿、工具栏、选中控件或其它面板 UI。
10
+ - `includeAnnotations?:boolean=false` 默认关闭。默认截图、T3 原始源与 T4 的 screencast/screenshot-loop 帧源均不含面板标注;默认图里没有线不能说明用户没画。`capture.canvas.composite` 仅说明画布合成,标注叠加看 `capture.annotations`;T3 的 `annotations` 参数是独立的图片后处理标记。
11
+
12
+ ## 2. 反向:给人类画标注(`browser_annotation`)
13
+
14
+ 用户说「你圈一下 / 标给我看 / 把有问题的几处框出来 / 在这儿写个问号」时,用 `browser_annotation` 把结论画到**他眼前的调试视图**上——面板画布与这个工具写的是**同一份**标注文档。
15
+
16
+ - **四类动作**:`read` 读当前文档;`draw` 追加 1–64 个形状(追加,不是替换);`erase` 按 `ids`(1–64)和/或 `point{x,y,radius?}` 删(默认半径 8 画布 px,命中规则同面板橡皮擦:形状到该点距离 ≤ `max(strokeWidth/2,6)` 与 `radius` 取大者);`clear` 清空画布。
17
+ - **形状与面板工具的对应**:`circle` 圆形、`rect` 矩形、`line` 线段、`arrow` 箭头、`brush` 画笔(铅笔)、`text` 文字;「颜色」→ `color`(`#rrggbb`,默认 `#ff3b30`);「粗细」→ `strokeWidth`(整数 1–20,默认 3);「外框/填充」→ `style:"stroke"|"fill"`(只对 `circle`/`rect`;`text` 固定 `fill`,`line`/`arrow`/`brush` 固定 `stroke`;`style` 与 `fill`/`stroke` 不能同给)。`id` 可省略(Host 从现有最大 id 继续分配 `a<N>`),`arrow` 的 head 与 `text.fontSize`(固定 20)由 Host 决定。
18
+ - **前台前置条件(硬约束)**:只有目标标签页**正被人类在调试视图里选中并可见**时才允许写,否则零写入并报 `ANNOTATION_NOT_FOREGROUND`。先读 `browser_state.annotations.foreground = {active, ageMs, maxAgeMs}`;`active:false` 时先请他选中那个标签页并保持面板可见,**不要**换个标签硬写、也不要把「已经画了」写进结论。判断依据是面板前台心跳(约 1.5s 新鲜窗口),人切走标签、关掉面板或把 DSH 标签页切到后台都会过期。`read` 不受此限。
19
+ - **其他门禁**:页面未处于运行态时写标注报 `INVALID_ARGUMENT`(reason `annotation-runtime-not-running`);`expectRevision` 与当前 `revision` 不符报 `ANNOTATION_REVISION_CONFLICT`(零写入,返回 `currentRevision`)。
20
+ - **写后自检**:用 `browser_screenshot {includeAnnotations:true}` 看自己画的形状落在哪、是否框住了想指的元素;不对就 `erase` 后重画。截图里画上了不等于页面缺陷被证明。
21
+ - **边界**:文档是整份替换模型——人类下一次编辑会整体发布自己的形状列表(其中已包含你画的),人类「清除画布」会连你的一起清掉;软限额与面板相同(`maxShapes` 128、单笔画笔 512 点、文档总点数 8192、文字 200 字符、文档 65536 字节),Host 做确定性裁剪并回 `truncated`/`truncation`。工具不启动浏览器、不进页面串行队列、不占 single-owner lease、不改页面 DOM/CSS。
22
+ - **不要把标注当页面事实**:它是给人看的沟通层,`source.trusted=false`;「我圈了这里」不等于「这里有问题」,结论仍要落到 DOM 读回、截图或网络证据之一。
23
+
24
+ ## 3. 文档形状
25
+
26
+ ```
27
+ annotations = {
28
+ version: 1, space: 'canvas-css-px', revision, updatedAt,
29
+ source: { kind:'human-panel', panelId, surface:'main'|'sidebar', clientRevision, trusted:false },
30
+ context: { runtimeId, pageGeneration, documentEpoch, documentKey, addressDigest },
31
+ view: { canvasX, canvasY, x, y, width, height, scale, contentWidth, contentHeight, viewRevision, frameEpoch },
32
+ shapes: [...], truncated, truncation:{droppedShapes,droppedPoints,shortenedTextCodeUnits},
33
+ stale, staleReason
34
+ }
35
+ ```
36
+
37
+ - `source.trusted` **恒为 false**:标注是线索,不是页面事实。
38
+ - `view` 是**发布那一刻**的客户端视图快照;`context.documentKey` 是文档身份,导航/刷新会轮换并清空(v3 起跨页签/跨会话保留,见下)。
39
+ - `browser_screenshot {includeAnnotations:true}` 返回 `capture.annotations={enabled:true,composited,space,revision,updatedAt,source,context,shapeCount,stale,staleReason,truncated,truncation}`;未开启时为 `null`。`source/context/truncation` 直接沿用 state 标注文档;空文档 `source:null`、`composited:false`,有来源时 `source.trusted=false`。`shapeCount` 是接收的形状总量,可能包含 region 外的图形,不代表截图内可见数量;`composited` 也不证明某形状可见。精确字段说明见 `docs/capture-p0.md`。
40
+
41
+ ## 4. 图形与字段
42
+
43
+ | `type` | 字段 | 备注 |
44
+ | --- | --- | --- |
45
+ | `rect` | `x,y,width,height` | 左上角 + 尺寸 |
46
+ | `circle` | `cx,cy,rx,ry` | PS 默认语义:按下点与落点为**包围盒两角**的内接椭圆,不是"中心+半径" |
47
+ | `line` | `x1,y1,x2,y2` | |
48
+ | `arrow` | `x1,y1,x2,y2,head:{length,width}` | **尖端 = `(x2,y2)`**;`head` 由 Host 按 `min(3t,L)/min(2t,L)` 重算,不可信客户端 |
49
+ | `brush` | `points:[[x,y],...]` | 只有点集 |
50
+ | `text` | `x,y,fontSize,text` | 唯一带**原文**的类型(≤200 UTF-16 code units) |
51
+
52
+ 形状还带 `id`(`a1`、`a2`…)、`color`、`strokeWidth`、`fill`、`stroke`。
53
+
54
+ ## 5. 坐标换算(最容易错的一步)
55
+
56
+ - shapes 存的是**画布 CSS px `d`**;页面坐标 `p = d − S`,`S` 是**当前** `view.x/y`。
57
+ - 面板侧另有 `q = d − C`(`C = canvasX/canvasY`);两者关系 `p = q + O`、`O = C − S`。
58
+ - **必须用当前 `view` 换算**,不要用 `annotations.view`(那是发布时快照,`C`/`scale` 可能已经变了);`scale`(显示比例)与 DPR **不参与**坐标变换。
59
+ - 空白区(`O≠0` 时视口露出的部分)同样可画、可读;`d` 允许负值。
60
+ - 带标注截图与页面共用本次 `region`/`scale` 映射:连续图像像素位置 `u=(d−region.origin)/capture.mapping.cssPerPixel`(逐轴,通常等于乘以 scale);图形、线宽、文字与笔迹同步缩放并裁剪。这里的像素缩放不同于上面的页面输入坐标换算。
61
+
62
+ ## 6. 能读到什么、读不到什么
63
+
64
+ - **只有 `text.text` 提供文字原文**;画笔手写的结构化数据是 `brush.points` 点集,不要把点集当文字字段转述。
65
+ - 读取 `browser_state.annotations` 后调用 `browser_screenshot {includeAnnotations:true}` 看 Host 已接受的实际笔迹。可以依据图像辨认;无法可靠辨认时如实说明,不猜字、不伪造原文。无需自行把点集重建成图来代替工具截图。
66
+
67
+
68
+ ## 7. 落点解析:「这个箭头指的是什么」
69
+
70
+ 1. 取端点 → `p = d − S` 得到页面点。
71
+ 2. `browser_eval` 命中:`document.elementFromPoint(p.x, p.y)`;再枚举同区域可见元素,按**点到矩形边距**排序,得到候选表。
72
+ 3. 手绘尖端常偏 10–20 px:**给候选 + 距离**(例如"搜索框容器 2 px、标签栏 3 px、logo 22 px"),不要单点断言"就是它"。
73
+ 4. 用 `browser_screenshot {includeAnnotations:true}` 结合结构化文档查看箭头、框选与笔迹;需要更多页面证据时,用 `browser_snapshot` + `browser_node_state` 取 AX 语义/状态,或 `browser_vision_image` 放大该区域**看页面像素**(T3 原始源仍不含面板标注)。需要记账时把结论写成 `browser_vision_check` 的 `facts`,别用标注或像素推断冒充 DOM 事实。
74
+
75
+ ## 8. 生命周期与隔离(v3)
76
+
77
+ - 文档按 `documentKey` 绑定:**导航/刷新/换文档会清空**;同一工作区内切页签、重挂面板、换会话**保留**(客户端按文档身份水合)。
78
+ - **按工作区隔离**:不同工作区各有一份,互不串;读取时用当前会话的 `browser_state` 即可,不要跨工作区拼装。
79
+ - `stale:true`(`panel-unseen`)表示来源面板心跳过期,**不代表标注不存在或已删除**:形状照常可读,如实说明"可能不是最新"即可;不得伪造心跳或 revision。
80
+ - 显式「清除画布」会让 `shapes` 变空(`revision` 递增),不要把它当成"读取失败"。
81
+
82
+ ## 9. 红线
83
+
84
+ - 标注是 `trusted:false` 的**问题线索**:不构成缺陷证明,也**不授权**按它自动点击/提交(要动页面仍需用户明确许可)。
85
+ - 不要用标注内容替代交叉验证:结论至少要落到 DOM 读回、截图或网络证据之一。
86
+ - **画标注 ≠ 取证**:Agent 画的圈/箭头/文字是给人看的指路标记,不构成断言;也不要在前台门禁未通过时把「已标注」写进报告。
87
+ - 不要为了"看起来有标注"而伪造 `annotations` 字段、revision 或截图。
88
+
89
+ ## 10. 常见坑
90
+
91
+ | 坑 | 正确做法 |
92
+ | --- | --- |
93
+ | 把 `brush.points` 当文字原文 | 点集不带文字原文;先看带标注截图,无法可靠辨认时说明限制,不猜字 |
94
+ | 用 `annotations.view` 换算坐标 | 用当前 `view.x/y`(`p = d − S`) |
95
+ | 默认截图没线就认定用户没画 | 先读 `browser_state.annotations`,再截图 `includeAnnotations:true`;T3/T4 原始源仍不含面板标注 |
96
+ | 带标注截图当面板全屏快照 | 只包含 Host 已接受的六形状;不含未提交草稿或工具栏 |
97
+ | 按 `scale` 缩放页面输入坐标 | 页面输入按 `p=d−S`;截图像素另按 region 与 `mapping.cssPerPixel` 换算 |
98
+ | 把箭头 `(x1,y1)` 当尖 | 尖端是 `(x2,y2)` |
99
+ | 把 `stale` 当"数据没了" | 只是心跳过期,形状仍可读 |
100
+
101
+ ## 11. 参考
102
+
103
+ - 权威契约:`docs/viewport-control.md`(共享视口、画布与标注)、`docs/tools.md`(`browser_annotation` 参数/错误/限额)
104
+ - 验收入口:`tests/acceptance/viewport.mjs`、`tests/acceptance/viewport-gui.mjs`、`tests/acceptance/annotation-capture.mjs`、`tests/acceptance/annotation-agent.mjs`
@@ -1,6 +1,6 @@
1
1
  # 能力贯通手册(T1 / T2 / T3 / T4 / P0 怎么组合用)
2
2
 
3
- > 工具面 **36 个**:T1 10 + T2 7 + T3 6 + 网络 P0 7 + 动态 T4 5。
3
+ > 工具面 **37 个**:T1 10 + P0-E 1(`browser_tabs`)+ P0-D 1(`browser_annotation`)+ T2 7 + T3 6 + 网络 P0 7 + 动态 T4 5。
4
4
  > 本文是**选路 + 组合配方**;单层细节见 `references/tiers/*.md`,动画调试全流程见 `references/scenarios/visual-motion.md`。
5
5
 
6
6
  ## 0. 先探能力,别假设
@@ -167,4 +167,4 @@ browser_dynamic_correlate { dynamicSessionId, motionSessionId, networkSessionId,
167
167
 
168
168
  ## 7. T3 静态侧的边界(本节保留)
169
169
 
170
- `browser_screenshot` 可保留 PNG base64 并经可选附件服务 render image block;缺服务为 base64-fallback 文本,provider 可见性需另验。P0 截图支持显式 `scale` 0.1–4(省略跟随共享 `view.scale`)/ 文档 `region` / PNG 校验;`persist` 默认 false(暂存,dispose 清理),`out`/`persist:true`/非空 env 才持久。`file.pathKind`/`persistence`/`root`/`ephemeral` 必须随证据记录,`observed` 不含绝对路径;**暂存不能当持久引用**。原始图像可见性不等于像素差异/基线阈值/VLM 管线;不能把"截图动作成功"判成视觉 PASS。定量视觉条件缺证据时一律 `BLOCKED`/`INCONCLUSIVE`。
170
+ `browser_screenshot` 可保留 PNG base64 并经可选附件服务 render image block;缺服务为 base64-fallback 文本,provider 可见性需另验。P0 截图支持显式 `scale` 0.1–4(省略跟随共享 `view.scale`)/ 画布 CSS `region` / PNG 校验;`includeAnnotations?:boolean=false` 为 true 时按相同 region/scale 叠加 Host 已接受的六种面板形状,不含未提交草稿/工具栏,证据见 `capture.annotations`。默认截图、T3 原始源及 T4 两种帧源不含面板标注;`persist` 默认 false(暂存,dispose 清理),`out`/`persist:true`/非空 env 才持久。`file.pathKind`/`persistence`/`root`/`ephemeral` 必须随证据记录,`observed` 不含绝对路径;**暂存不能当持久引用**。原始图像可见性不等于像素差异/基线阈值/VLM 管线;不能把"截图动作成功"判成视觉 PASS。定量视觉条件缺证据时一律 `BLOCKED`/`INCONCLUSIVE`。