dsh-inner-browser-use 0.34.28 → 0.34.95

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.
package/docs/BENCH.md CHANGED
@@ -1,8 +1,172 @@
1
+ ## Flash 镜像按栏改窗口(0.34.48,Electron Flash 运行时,右栏 747×1196)
2
+
3
+ Flash 路径原来采集一个固定的 1440×900 窗口,竖高右栏里上下留黑边。现在 `setViewport` 直接改窗口大小,采集源与栏同比例。
4
+
5
+ | 状态 | 窗口 | 画面占栏 | 单帧 |
6
+ |---|---|---|---|
7
+ | 0.34.47 | 1440×900 | 约 39% | 126 KB |
8
+ | 0.34.48 | 747×1196 | 100% | 111 KB |
9
+
10
+ 轮询帧率 9 fps 不变(Flash 桥上限),改的是画面比例与单帧体积。
11
+
12
+ ## 动画页下行节流(0.34.47,真实 Chrome headless,720×880,canvas 60 fps 动画)
13
+
14
+ | 状态 | 帧率 | 单帧 | 下行带宽 |
15
+ | --- | --- | --- | --- |
16
+ | 0.34.46 全推 | 60 fps | 11 KB | 654 KB/s |
17
+ | 0.34.47 空闲节流 | 30 fps | 11 KB | 326 KB/s |
18
+ | 0.34.47 指针活动(1.5 s 内) | 58 fps | 11 KB | 631 KB/s |
19
+
20
+ 节流只作用于下行:`latest`、`seq`、`still()` 与输入路径不变;`browser_shot` 仍走独立整帧截图。
21
+
22
+ ## 坏页读取成本(0.34.46,构造页:`querySelectorAll` 抛错被读 150 字符)
23
+
24
+ | 读取结果 | 字符 | 约 token |
25
+ | --- | --- | --- |
26
+ | 0.34.46 一行故障说明 | 150 | 45 |
27
+ | 0.34.45 同一页读成 0 控件 0 字 | 20 | 6(但模型会重试或猜内容,整轮更贵) |
28
+
29
+ ## 点击与多步延迟(0.34.45,headless Chrome,本地页)
30
+
31
+ | 场景 | 0.34.45 | 0.34.44 |
32
+ | --- | --- | --- |
33
+ | `browser_do` 单步 | 73 / 83 ms | 98 / 112 ms |
34
+ | 遮挡点击(自动清掉横幅) | 72 / 77 ms | 97 / 112 ms |
35
+ | `browser_do` 20 步 | 2035 ms(102 ms/步) | 2151 ms |
36
+ | 崩溃恢复(换新标签页) | 1390 ms | ~1.5 s |
37
+
38
+ 做法:`Page.frameStartedLoading` / `frameStoppedLoading` 打时间戳,点击后若页面已 `complete` 且没有新加载开始,`settle()` 一次求值就返回;预算不变(点击 ≤ 220、遮挡点击 ≤ 240、单步 ≤ 240、每步 ≤ 280、崩溃恢复 ≤ 2600 ms)。
39
+
40
+ ## 真实站点读页成本(0.34.44,headless Chrome,720×880 栏宽,`scripts/readcost.mjs`)
41
+
42
+ | 页面 | 读法 | 字符 | 估算 token | 控件 |
43
+ | --- | --- | --- | --- | --- |
44
+ | GitHub 仓库首页(rust-lang/rust) | text | 2562 | 754 | 90 |
45
+ | 同上 | links | 3763 | 1107 | 90 |
46
+ | GitHub Topics · computer-use | links | 2590 | 762 | 90 |
47
+ | 同上 | text | 1939 | 570 | 90 |
48
+ | GitHub 登录表单 | forms | 218 | 64 | 4 |
49
+ | 同上 | text | 533 | 157 | 13 |
50
+
51
+ - 读页本身 1–21 ms;`max_chars` 只对长页生效(links 4600→2200:3763→2800 字符,1107→824 token),短页两种预算结果一致。
52
+ - 空读取重试:内容在 700 ms 后才写入的页面,一次 `browser_view` 用 614 ms 拿到内容(此前直接读到空页)。正常页面不进入该分支,延迟与 token 不变。
53
+
54
+ ## 故障归因的 token 成本(0.34.43)
55
+
56
+ | 场景 | 读取结果多出的内容 | 代价 |
57
+ | --- | --- | --- |
58
+ | 页面正常 | 0 字符 | 0 |
59
+ | 1 个 404 请求 + 1 个脚本错误 | 1 行,约 130 字符 | ≈33 token,替代 2 次排查调用(通常 ≥600 字符) |
60
+ | 同一页面再读 | 0 字符(已报过) | 0 |
61
+
62
+ ## 点击与多步延迟(0.34.42,headless Chrome,本地页)
63
+
64
+ `node scripts/bench.mjs` 复现;末行 `budget` 是纳入退出码的预算断言。
65
+
66
+ | 场景 | 0.34.42 | 0.34.40 |
67
+ | --- | --- | --- |
68
+ | `browser_click` 普通页 | 97 ms | 439 ms |
69
+ | `browser_click` 横幅遮挡、自动清掉 | 107 ms | 593 ms |
70
+ | 被弹窗拦住的点击(拒绝) | 2 ms | — |
71
+ | `browser_do` 单步 | 95 ms | 440 ms |
72
+ | `browser_do` 20 步 | 2151 ms(108 ms/步) | 9067 ms |
73
+ | 崩溃恢复(换新标签页) | ~1.5 s | — |
74
+
75
+ 预算:点击 ≤ 220 ms、遮挡点击 ≤ 240 ms、单步 ≤ 240 ms、20 步每步 ≤ 280 ms、崩溃恢复 ≤ 2600 ms。
76
+
77
+ ## 点击与多步延迟(0.34.41,headless Chrome,本地页)
78
+
79
+ | 场景 | 耗时 |
80
+ | --- | --- |
81
+ | `browser_click` 普通页 | 141 ms |
82
+ | `browser_click` 横幅遮挡、自动清掉 | 235 ms |
83
+ | 被弹窗拦住的点击(拒绝) | 2 ms |
84
+ | `browser_do` 单步 | 131 ms |
85
+ | `browser_do` 20 步 | 3073 ms(154 ms/步) |
86
+ | 跟随点击开出的弹窗 | 22 ms |
87
+
88
+ `node scripts/bench.mjs` 复现;对照 0.34.40 的 439 / 593 / 440 / 9067 ms。
89
+
90
+ ## 广度回归(0.34.40,headless Chrome,本地构造页)
91
+
92
+ `node scripts/breadth.mjs` — 66 项操作全通过:shadow DOM 读/点、表单输入/退格/清空、按字段名输入、下拉、勾选、提交、只读变化、画布编号与点选、截图尺寸、遮挡弹窗拒绝、失效 ref 跟随、断线恢复。
93
+
94
+ ## 真实站点读页成本(0.34.39,headless Chrome,720×880 栏宽)
95
+
96
+ | 页面 | 字符 | 估算 token | 控件 |
97
+ | --- | --- | --- | --- |
98
+ | example.com | 976 | 305 | 1 |
99
+ | GitHub Topics · computer-use | 约 1.5k | 约 470 | 90 |
100
+
101
+ 读页脚本此前因正则转义缺失整段无法解析,同一测例读出 0 控件 0 字符;修好后为上面数字。
102
+
103
+ ## 读页 token 成本(0.34.38,构造页:300 行链接,字符预算 24000 以排除截断)
104
+
105
+ | 读法 | 字符 | 估算 token |
106
+ | --- | --- | --- |
107
+ | 列出全部控件(旧) | 9153 | 2860 |
108
+ | 控件折叠到 140 行(新) | 4277 | 1337 |
109
+
110
+ 正文不再被编号挤到预算之外;被折叠的控件滚动后重新出现。
111
+
112
+ ## 长文本输入延迟(0.34.37,真实 Chrome headless)
113
+
114
+ | 目标 | 方式 | 314 字符耗时 | 文本一致 |
115
+ | --- | --- | --- | --- |
116
+ | `<textarea>` | 逐键 `dispatchKeyEvent` | 1686 ms | 是 |
117
+ | `<textarea>` | 一次 `Input.insertText` | 2 ms | 是 |
118
+ | `contenteditable` | 一次 `Input.insertText` | 13 ms | 是 |
119
+
120
+ ## 右栏尺寸下的镜像带宽(0.34.36,真实 Chrome headless,720×880)
121
+
122
+ | 页面 | 3–4 s 内帧数 | 帧率 | 平均单帧 | 带宽 |
123
+ | --- | --- | --- | --- | --- |
124
+ | 静态真实站点(example.com) | 3 | <1 fps | 47 KB | 47 KB/s |
125
+ | 注入 `requestAnimationFrame` 动画 | 181 / 3 s | ~60 fps | 15 KB | 875 KB/s |
126
+
127
+ 结论:静止页面几乎不花带宽(Chrome 的 screencast 只在合成器变化时发帧);满帧动画按列宽排版后单帧只有 15 KB,60 fps 总计约 875 KB/s,走本机 SSE 无压力,不设帧率上限以免牺牲延迟。「同一张图不重复发」这道闸真正省掉的是 Flash 轮询分支与 `still()` 连发(单测:3 次连发 → 1 张)。
128
+
129
+ ## 镜像按右栏尺寸排版(0.34.35,真实 Chrome,`--headless=new`)
130
+
131
+ 页面不再按 1440×900 排版,而是按右栏实测的尺寸排版;下表的 `inner` 是页面自己报告的 `innerWidth×innerHeight`,证明不是缩放图片而是重新排版。
132
+
133
+ | 请求尺寸 | 页面 inner | `Emulation.setDeviceMetricsOverride` + 重开 screencast | 一张整帧 `captureScreenshot` | 整帧 JPEG(纯色测试页) |
134
+ | --- | --- | --- | --- | --- |
135
+ | 720×880 | 720×880 | 41 ms | 39 ms | 11.7 KB |
136
+ | 520×1000 | 520×1000 | 34 ms | 33 ms | 11.3 KB |
137
+ | 1440×900 | 1440×900 | 49 ms | 47 ms | 17.4 KB |
138
+
139
+ 冷启动到可排版 827 ms。点击链路在同一尺寸下实测:两次 `Input.dispatchMouseEvent`(按下+抬起)13 ms,页面 `mousedown` 处理器已计数,订阅后下一帧 8 ms 内到达。
140
+
141
+ ## Phase 46 — 开玩前的体检,0.34.30
142
+
143
+ `npm run ready [url]` 跑新工具 `browser_prepare`,整条链路一次调用,自带 Chrome、跑完关掉:
144
+
145
+ | 页面 | 调用耗时 | 找到的输入面 | 帧率 | 按键 |
146
+ | --- | --- | --- | --- | --- |
147
+ | 4399《怪物小汽车》 | 3297 ms | frame 748×336(在屏幕外,先滚进来再点) | 页面 58 fps / 框架 59 fps | 没进到框架,工具直说「先点进框架一次」 |
148
+ | 奥拉星 `http://aoqi.100bt.com/` | 573 ms | — | — | —,地址已连不上,返回 Chrome 错误页说明 |
149
+
150
+ `browser_prepare` 的判断顺序:连不上就直说 → 读页面 → 挑画布/框架(优先屏幕内的,否则滚进视野)→ 有 Flash 影片就说这个浏览器放不了 → 数帧率(页面与最大的框架各数一遍)→ 派一个真键看它是否真的到达框架 → 一行结论。
151
+
152
+ ## Phase 45 — 两种操作方式的实际差别,0.34.29
153
+
154
+ 同一张本地页(按钮 + 输入框,页面在捕获阶段记下每个事件的类型和 `isTrusted`),`npm run modes`:
155
+
156
+ | 路径 | 受信任事件 | pointerdown | click | keydown | input | 写进值 |
157
+ | --- | --- | --- | --- | --- | --- | --- |
158
+ | human 点按钮 | 3 | 1 | 1 | – | – | – |
159
+ | dom 点按钮 | 0 | 1 | 1 | – | – | – |
160
+ | human 输入 abc | 11 | – | – | 3 | 3 | abc |
161
+ | dom 输入 abc | 0 | – | – | 0 | 1 | abc |
162
+
163
+ `trustedEvents` 是这张表里唯一真正的分界:真人路径全为 true,`dom` 路径全为 false。要过反自动化、要看指针轨迹、要按键有 `code` 的游戏,只有 human 能走;被浮层盖住、被脚本接管了按键的登录框,`dom` 一次就过。
164
+
1
165
  ## Phase 44 — 真站页游场地,0.34.28
2
166
 
3
167
  `node scripts/check-game.mjs <url>`,真 Chrome、自己的 profile、跑完自己关掉。两个真站:
4
168
 
5
- | 站点 | 空闲时页面刷新 | 全屏截图(`sidecar_shot` 同类)| 按住 `d` 200 ms 到达页面 |
169
+ | 站点 | 空闲时页面刷新 | 全屏截图(`browser_shot` 同类)| 按住 `d` 200 ms 到达页面 |
6
170
  | --- | --- | --- | --- |
7
171
  | 奥拉星 `http://aoqi.100bt.com/` | **27 fps** | 中位数 **68 ms** | 是 |
8
172
  | 4399 `https://www.4399.com/` | **59.8 fps** | 中位数 **148 ms** | 是 |
@@ -17,10 +181,10 @@
17
181
 
18
182
  | 项 | 数字 |
19
183
  | --- | --- |
20
- | `sidecar_key` 带 `hold_ms: 300` 整次调用 | **305 ms**(含 300 ms 按住,收尾 5 ms) |
184
+ | `browser_key` 带 `hold_ms: 300` 整次调用 | **305 ms**(含 300 ms 按住,收尾 5 ms) |
21
185
  | 页面测到的按住时长 | **301 ms** |
22
186
  | `ArrowLeft+ArrowUp` | 两键都收到 `keydown`,都收到 `keyup`,顺序为倒序松开 |
23
- | `sidecar_click` 带 `hold_ms: 200` 页面测到 | **211 ms** |
187
+ | `browser_click` 带 `hold_ms: 200` 页面测到 | **211 ms** |
24
188
 
25
189
  对照 0.34.25 之前:同样的按住被 `settle` 拖到 727 ms,等于 0.7 s 才走一步——页游里这就是掉帧。
26
190
 
@@ -50,14 +214,14 @@
50
214
  | 镜像延迟 | 中位 8 ms / 最大 8 ms |
51
215
  | 遮挡检测 | 中位 3 ms / 最大 15 ms |
52
216
  | 点一下(普通页 / 有遮罩,含读回) | 439 / 590 ms,遮罩那次 `cleared: true` |
53
- | 一步 `sidecar_do` | 440 / 590 ms |
217
+ | 一步 `browser_do` | 440 / 590 ms |
54
218
  | 截图 | 73 ms / 115 KB |
55
219
  | 读 dense 页 text 档 | 6 ms / 1484 字 / ≈436 token |
56
220
  | 读 dense 页 forms 档 | 1 ms / 432 字 / ≈127 token |
57
221
  | 再读同一页 | 1–2 ms,给出「没有变化」一行 |
58
222
  | `changed_only` | 58 字 / ≈17 token(整页 1505 字) |
59
223
 
60
- 这轮 bench 的作用不是数字,而是**它第一次跑到 `sidecar_view` 就报 `ReferenceError` 退出**:那个工具在真实会话里同样会失败,而单测只测了它的纯函数部分。修完之后 bench 一次性跑完,数字在上面。
224
+ 这轮 bench 的作用不是数字,而是**它第一次跑到 `browser_view` 就报 `ReferenceError` 退出**:那个工具在真实会话里同样会失败,而单测只测了它的纯函数部分。修完之后 bench 一次性跑完,数字在上面。
61
225
 
62
226
  ## Phase 40 — the poll that names its chat, 0.34.19
63
227
 
@@ -106,9 +270,9 @@ video, a cross-origin frame — nothing in the text said "there is something her
106
270
  you cannot read". A model then planned as if the page were fully described.
107
271
 
108
272
  - `readPage` counts the unreadable visible things on the page and in frames, and `renderPage` prints exactly one line when the count is non-zero; zero adds no line (measured token cost of the feature is 0 on pages that have nothing to hide).
109
- - `chrome-error://` is no longer rendered as an empty page: it says the page did not load and names `sidecar_nav`.
273
+ - `chrome-error://` is no longer rendered as an empty page: it says the page did not load and names `browser_nav`.
110
274
  - `navigate` retries that error page once (300 ms + 8 s cap). Cold launch on a fresh profile: first navigation recovered on the retry; an unreachable host still returns `bad` in well under the cap.
111
- - `sidecar_shot` without an image channel appends what the result is missing and which tool fills it; file path alone is no longer the whole answer.
275
+ - `browser_shot` without an image channel appends what the result is missing and which tool fills it; file path alone is no longer the whole answer.
112
276
  - Suite: 101 tests, 0 failures.
113
277
 
114
278
  ## Phase 36 — a reload must not cost the browser, 0.34.14
@@ -121,7 +285,7 @@ you cannot read". A model then planned as if the page were fully described.
121
285
  ## Phase 35 — a review round that can be checked off, 0.34.13## Phase 35 — a review round that can be checked off, 0.34.13
122
286
 
123
287
  - `markRound()` 追加 verdict 行,`lastRound()` 折回 `marks`;`brief({already})` 只保留 open 条目,实测提问里出现「Already handled from your previous review」,已处理点不再重复。
124
- - 工具面新增 `sidecar_review_verdict`(`mark: [{point,status,evidence}]`,状态枚举 fixed/verified/invalid/open);越界编号丢弃、未知状态记为 open。
288
+ - 工具面新增 `browser_review_verdict`(`mark: [{point,status,evidence}]`,状态枚举 fixed/verified/invalid/open);越界编号丢弃、未知状态记为 open。
125
289
  - 测试 92 → 96 条全绿。
126
290
 
127
291
  ## Phase 34 — one gate in front of every write, 0.34.12## Phase 34 — one gate in front of every write, 0.34.12
@@ -217,7 +381,7 @@ in one place.
217
381
 
218
382
  ## Phase 26 — a screenshot the model can see, 0.34.3
219
383
 
220
- Observation was structured text only: `sidecar_shot` wrote a PNG and returned
384
+ Observation was structured text only: `browser_shot` wrote a PNG and returned
221
385
  a path, which is a file name the model cannot look at. Codex and Claude Code
222
386
  both hand screenshots back as image content, and that is the difference
223
387
  between planning from the picture and guessing.
@@ -333,7 +497,7 @@ answer), and a read now also asks for the frame tree.
333
497
  | key round trip | 13 ms | 2 ms |
334
498
  | screenshot | 82 ms, 133 KB | 77 ms, 113 KB |
335
499
 
336
- `sidecar_extract` on a 30-row list: 1106 chars for all 30 rows (≈330 tokens),
500
+ `browser_extract` on a 30-row list: 1106 chars for all 30 rows (≈330 tokens),
337
501
  against 1291 chars for the same page read as text — and at a 500-char read
338
502
  budget the text read stops around row 12 while the list still returns 30.
339
503
 
@@ -383,10 +547,10 @@ frame's corner is one `DOM.getFrameOwner` plus one call on the owner element.
383
547
  | What | Number |
384
548
  | --- | --- |
385
549
  | the "what is on top of this control" probe | **1 ms median, 2 ms max** (5 samples) — the same `Runtime.evaluate` hop, no round trip added |
386
- | `sidecar_click` on an uncovered control | 460 ms median (3 runs, page-load settle included) |
387
- | `sidecar_click` on the same control under a cookie banner | 630 ms median (3 runs) — the banner's own Accept click, a 150 ms beat, a re-probe, then the intended click |
550
+ | `browser_click` on an uncovered control | 460 ms median (3 runs, page-load settle included) |
551
+ | `browser_click` on the same control under a cookie banner | 630 ms median (3 runs) — the banner's own Accept click, a 150 ms beat, a re-probe, then the intended click |
388
552
  | the same under a modal that offers to delete the account | 456 ms — reported, nothing pressed |
389
- | one `sidecar_do` step on an uncovered control | 458 ms median (3 runs) |
553
+ | one `browser_do` step on an uncovered control | 458 ms median (3 runs) |
390
554
  | the same step under the cookie banner | 633 ms median (3 runs) — cleared, then the step ran |
391
555
 
392
556
  A batch pays it once per step that has something over it, not once per call: a
@@ -411,10 +575,10 @@ is now done inside the call, by looking the control up by what it is.
411
575
 
412
576
  | What | Number |
413
577
  | --- | --- |
414
- | `sidecar_click` on a control the page just redrew | **462 ms median, 483 ms max** (8 samples) — one extra read of the page's visible text, 600 chars, to find the control again |
415
- | `sidecar_click` on a control that was not redrawn | 445 ms median, 446 ms max (8 runs) — the probe costs nothing when the number is still good |
578
+ | `browser_click` on a control the page just redrew | **462 ms median, 483 ms max** (8 samples) — one extra read of the page's visible text, 600 chars, to find the control again |
579
+ | `browser_click` on a control that was not redrawn | 445 ms median, 446 ms max (8 runs) — the probe costs nothing when the number is still good |
416
580
  | stale refs recovered this way | **8/8** |
417
- | a control that really left the page | still refused, `Call sidecar_view and use the new number` — 2/2 in the breadth suite |
581
+ | a control that really left the page | still refused, `Call browser_view and use the new number` — 2/2 in the breadth suite |
418
582
 
419
583
  So the recovery costs about **17 ms** when it is needed and nothing when it is
420
584
  not, against a model round trip of two calls plus the tokens of a fresh read.
@@ -442,7 +606,7 @@ change mid-session. Changing it restarts the browser, so it costs a cold start:
442
606
  | windowed → windowless, same profile and page | 22.1 s | one process, cold start |
443
607
  | asking for the shape it already has | 0 ms | nothing is restarted |
444
608
 
445
- Measured through the tool layer (`sidecar_open` → `sidecar_state` → `ps` spawn
609
+ Measured through the tool layer (`browser_open` → `browser_state` → `ps` spawn
446
610
  args), profile `probe-tool`, 1 run each; the same-shape row is the third call in
447
611
  the same run. A browser start on this machine is 20–22 s either way, so the
448
612
  switch is not what costs — the process is.
@@ -458,9 +622,9 @@ bounded wait is the difference between a silent `viewers 0` and a sentence:
458
622
 
459
623
  | what | cost |
460
624
  | --- | --- |
461
- | `sidecar_open` with a tab watching | 0 ms |
462
- | `sidecar_open` with nothing watching | ≤ 1.2 s, and the line says so |
463
- | `sidecar_open` when the chat has no column to draw into | 0 ms (no wait) |
625
+ | `browser_open` with a tab watching | 0 ms |
626
+ | `browser_open` with nothing watching | ≤ 1.2 s, and the line says so |
627
+ | `browser_open` when the chat has no column to draw into | 0 ms (no wait) |
464
628
  | building the sentence on a normal state read | 0 ms, one comparison |
465
629
 
466
630
  Measured with `/tmp/probe-unseen.mjs` (tool layer, example.com, profile
@@ -490,7 +654,7 @@ reverse-contains (1).
490
654
 
491
655
  | what the caller has | what it used to take | now |
492
656
  | --- | --- | --- |
493
- | a name for the field, no number | read the page, find the number, then type | one `sidecar_type` with `field`, one round trip |
657
+ | a name for the field, no number | read the page, find the number, then type | one `browser_type` with `field`, one round trip |
494
658
  | the field's words live in a div | click landed on the div, text went wherever focus was | resolved to the field, `typed into "Work email"` |
495
659
  | a banner over the field | typed under the banner | banner cleared first, then typed |
496
660
 
@@ -500,7 +664,7 @@ Breadth check `type-by-words` on the `/bare` form: value lands in
500
664
  ## Phase 11 — choosing a dropdown by name, 0.21.0
501
665
 
502
666
  `/bare` now carries a `select[name=seats]` whose only name on screen is a div
503
- saying `Seats`. One `sidecar_select` with `field: "Seats"`, `value: "Two"`:
667
+ saying `Seats`. One `browser_select` with `field: "Seats"`, `value: "Two"`:
504
668
 
505
669
  | what | result |
506
670
  | --- | --- |
@@ -534,7 +698,7 @@ Exact equality still wins over the squeezed form, so `Sign in` never lands on
534
698
  | what | before | now |
535
699
  | --- | --- | --- |
536
700
  | `{field: "Birthday"}` | not found, fell back to clicking the div | found the hidden input, clicked its visible box |
537
- | value after one `sidecar_do` step | whatever already had focus | `2001-02-03` in `#bd`, `typed into "Birthday"` |
701
+ | value after one `browser_do` step | whatever already had focus | `2001-02-03` in `#bd`, `typed into "Birthday"` |
538
702
  | extra round trips | read the page, guess a number | none |
539
703
 
540
704
  The visible-box rule: a candidate is usable when it, or its nearest visible
@@ -547,7 +711,7 @@ something the person can see.
547
711
 
548
712
  | what | result |
549
713
  | --- | --- |
550
- | `sidecar_grab` | 6 ms, `picture-check.png`, 140x90 |
714
+ | `browser_grab` | 6 ms, `picture-check.png`, 140x90 |
551
715
  | before | three round trips: list the images, ask for the full address, fetch it |
552
716
 
553
717
  A picture page has no text at all, so a read of it costs one line and no tokens;
@@ -557,7 +721,7 @@ the picture comes back as a file path the person can open.
557
721
 
558
722
  Two 140x90 PNGs: one a gradient, one a single colour.
559
723
 
560
- | page | what `sidecar_grab` answers | time |
724
+ | page | what `browser_grab` answers | time |
561
725
  | --- | --- | --- |
562
726
  | `/pic` | `... 140x90 a picture with detail` | 21 ms (first, page cold) |
563
727
  | `/flat` | `... 140x90 mostly one colour` | 4 ms |
@@ -569,7 +733,7 @@ travels in the same line as the path.
569
733
 
570
734
  | page | read line | extra round trips |
571
735
  | --- | --- | --- |
572
- | `/pic` (gradient, no words) | `picture 140x90 a picture with detail "Chart" — sidecar_grab saves it as a file` | 0 |
736
+ | `/pic` (gradient, no words) | `picture 140x90 a picture with detail "Chart" — browser_grab saves it as a file` | 0 |
573
737
  | `/flat` (single colour) | `picture 140x90 mostly one colour "Chart" — …` | 0 |
574
738
  | `/words` (text page) | no picture line | 0 |
575
739
 
@@ -597,20 +761,20 @@ A changed scroll offset is still reported, so "nothing changed" is never a lie.
597
761
 
598
762
  ## Phase 20 — one call carries the question and the answer, 0.30.0
599
763
 
600
- | step | before | `sidecar_ask` |
764
+ | step | before | `browser_ask` |
601
765
  | --- | --- | --- |
602
- | type the message | `sidecar_type` + `submit` | same, inside the call |
603
- | read the page | `sidecar_view` | — |
604
- | read it again until the answer stops growing | 2–6 more `sidecar_view` calls | — |
766
+ | type the message | `browser_type` + `submit` | same, inside the call |
767
+ | read the page | `browser_view` | — |
768
+ | read it again until the answer stops growing | 2–6 more `browser_view` calls | — |
605
769
  | **turns for a chat reply** | 3–7 | **1** |
606
- | measured `sidecar_ask` on `/chat` (answer over 2 arrivals, 700 ms tail) | — | 2.5 s, one turn |
770
+ | measured `browser_ask` on `/chat` (answer over 2 arrivals, 700 ms tail) | — | 2.5 s, one turn |
607
771
 
608
772
  ## Phase 21 — a review round that closes the loop, 0.31.0
609
773
 
610
- | step | before | `sidecar_review` |
774
+ | step | before | `browser_review` |
611
775
  | --- | --- | --- |
612
776
  | write the request (repo, commit, focus) | by hand, every round | built from three fields |
613
- | send it, wait for the answer | `sidecar_ask` | same call |
777
+ | send it, wait for the answer | `browser_ask` | same call |
614
778
  | turn the answer into a list | read it, retype it | parsed, numbering kept |
615
779
  | know what the last round was about | — | round log, with the commit and the time |
616
780
  | **turns for a review round** | 3–7 + bookkeeping | **1** |
@@ -686,7 +850,7 @@ session), three were real.
686
850
  | --- | --- | --- |
687
851
  | popup lookup | any non-main tab, then navigated or closed | only a target that is neither this page nor another chat's (`isForeign`) |
688
852
  | focused password in a result line | echoed the `.value` (`DUMMY_SECRET_123`) | name only, value always empty |
689
- | `sidecar_do` on a step that did not land | kept going, typed into the wrong page | stops at that step, says to re-plan |
853
+ | `browser_do` on a step that did not land | kept going, typed into the wrong page | stops at that step, says to re-plan |
690
854
  | overlay that would not clear | clicked the control under it anyway | refuses the click/typing, names the overlay |
691
855
  | `changed_only` twice | repeated the same change | diff becomes the new baseline |
692
856
  | `/sidecar/state` | top-level `url` could be another chat's tab | `?session=` adds `chat.url/title/viewers` (live: doubao page, viewers 1) |
@@ -717,3 +881,14 @@ not.
717
881
  | human typing, >160 chars | 8-char bursts, 16–58 ms gaps, ≈6 ms per char |
718
882
  | headless UA | desktop Chrome UA, no `HeadlessChrome` |
719
883
  | suite | 70 pass, breadth 66, ui-copy clean |
884
+
885
+ ## Flash 分支(Electron 11.3.0 / Chromium 87,1440×900,Apple 芯片 + Rosetta)
886
+
887
+ | what | measured |
888
+ | --- | --- |
889
+ | cold start to a driven page | 1389 ms(热启 545–927 ms) |
890
+ | first painted frame | 8–10 s(SWF 自己的加载;之前是 11 KB 白帧) |
891
+ | one frame via capturePage | 16 ms |
892
+ | frame size, JPEG q72 | 96–103 KB |
893
+ | mirror SSE | 15 fps(66 ms 一轮),4 s 收 60 帧,均值 97.8 KB |
894
+ | switch cost | 一次浏览器重启(`browser_open({flash:true})` → `browser_open({flash:false})`) |
package/docs/FLASH.md ADDED
@@ -0,0 +1,84 @@
1
+ # Flash 之后:把兼容性放在正确的位置上
2
+
3
+ Chrome 88(2021 年 1 月)拿掉 Flash,不是商业决定,是插件模型的失败:一个能直接操作音频、图形、网络和本地文件的第三方二进制,安全面等于浏览器本身,而它承担的内容又都可以用开放标准重写。结论对自研同样成立:**不要再造插件槽位,要造内容层的运行时。**
4
+
5
+ ## 兼容的到底是什么
6
+
7
+ 一个 SWF 只有四样东西是「约定」:
8
+
9
+ 1. **标签流**——Shape/Sprite/Bitmap/Font/Sound/Frame 的二进制结构,决定画面怎么组出来;
10
+ 2. **时间线**——`ShowFrame` 与帧率构成保留模式动画,帧可以跳、可以循环;
11
+ 3. **AVM1 / AVM2 语义**——脚本看到的 DisplayObject、事件、`MovieClip`、`SharedObject`;
12
+ 4. **安全模型**——跨域、socket 策略、本地存储。
13
+
14
+ 只要这四样对得上,游戏就能跑。渲染用 Canvas2D 还是 WebGPU、时钟是实时还是虚拟、存储落 IndexedDB 还是宿主磁盘,**游戏看不见**。这就是「参考原理重做一个更好的」的立足点:兼容性锁在 AVM + 标签流,创新空间在其余全部。
15
+
16
+ ## 分层设计
17
+
18
+ ```
19
+ SWF ──▶ 摄入(读取标签流, 建 IR) ──▶ 显示列表 + 时间线 ──▶ 渲染(WebGPU, 损伤跟踪)
20
+ │
21
+ ├─ AVM 兼容核(Ruffle wasm / 自研 AVM1 解释器)
22
+ ├─ 虚拟时钟(固定步长, 输入带时间戳)
23
+ ├─ 能力沙箱(网络/存储/剪贴板 走宿主代理, 可录可回放)
24
+ └─ 对象图内省(每帧把状态压成 JSON 差异, 给 agent 读)
25
+ ```
26
+
27
+ 四层的分工决定了「比 Flash 好」不是同一维度的竞速:
28
+
29
+ | 维度 | Flash 的做法 | 这里的做法 | 可测指标 |
30
+ |---|---|---|---|
31
+ | 启动 | 装插件、NPAPI/PPAPI 握手 | 页面内 wasm,零安装 | 首帧 ms |
32
+ | 安全 | 插件进程 + 沙箱补丁 | 无原生代码,网络存储全代理 | 无提权面 |
33
+ | 确定性 | 真实时钟,帧率随机器 | 固定步长虚拟时钟 + 输入时间戳 | 同输入同结果 |
34
+ | 内省 | 无(黑盒像素) | 显示列表与 AVM 状态可导出 | 状态 → JSON 字节数 |
35
+ | 传输 | 屏幕像素 | 损伤区域 + 增量 | 每帧 KB |
36
+ | 回放 | 无 | 事件日志可重放 | 重放一致率 |
37
+
38
+ ## 已落地:PPAPI 分支做成插件里的开关(本版)
39
+
40
+ 「兼容层」和「能立刻玩」不是二选一。插件现在有两套运行时,同一个 CDP 管线、同一个镜像、同一套工具,切换只是一次重启:
41
+
42
+ ```
43
+ browser_open({ flash: true, url: 'http://aola.100bt.com/play/play.html?from_h5=true' })
44
+ ```
45
+
46
+ 开的时候宿主用 Electron 11.3.0(Chromium 87,最后一个还认 PPAPI 的 Chromium)当浏览器,关的时候切回 Chrome。输入注入、导航、读页面、镜像全都不用改,因为两边都说 CDP。
47
+
48
+ 三条必须同时成立的条件(缺一条就报「Flash 缺失」,且症状会骗人):
49
+
50
+ 1. `--ppapi-flash-path` 指向 bundle **里的 Mach-O 可执行文件** `PepperFlashPlayer.plugin/Contents/MacOS/PepperFlashPlayer`,不是 `.plugin` 目录。指错目录时插件报 `No PPP_GetInterface in plugin library`——那是假象,不是导出缺失。
51
+ 2. 必须加 `--no-sandbox`。否则 ppapi 进程报 `dlopen(...) (file system sandbox blocked open())`,页面提示没有 Flash。沙箱只对这一个隐藏的独立 profile 关,正常浏览器不关。
52
+ 3. 宿主必须是 x86_64(Apple 芯片跑 Rosetta):插件只有 i386/x86_64 切片,arm64 Electron 装不进它。所以下载固定取 `darwin-x64`。
53
+
54
+ Electron 11 的两个真实限制,代码里已按它们写:
55
+
56
+ - 它不会用当前 Chrome 的 profile 目录(会降级并上锁),Flash 分支固定用 `<profile>-flash` 独立目录;两边登录态各自独立。
57
+ - `/json/new` 在 Chromium 87 上是 404,单窗口应用也没有第二个窗口,所以 Flash 分支不建新页签:会话要页签时把唯一那个窗口导航过去。
58
+
59
+ 实测数字(Apple 芯片 + Rosetta,1440×900):
60
+
61
+ | 指标 | 值 |
62
+ |---|---|
63
+ | 冷启动到能驱动页面 | 1389 ms(首次含解压;同机热启 545–927 ms) |
64
+ | 页面首帧出图 | 约 8–10 s(SWF 自己的加载),之前是 11 KB 白帧 |
65
+ | 单帧 JPEG q72 | 96–103 KB |
66
+ | `capturePage` 取一帧 | 16 ms |
67
+ | 镜像 SSE 节奏 | 15 fps(66 ms 一轮),4 s 收 60 帧,均值 97.8 KB |
68
+ | PC 版判定 | 页面里出现 `EMBED .../weblogin/20171026_1.swf`、登录框是「多多号/手机/邮箱」而不是 H5 的样式 |
69
+
70
+ **不能随包分发 Adobe Flash Player**:插件本体留在本机状态目录(`~/.dsh/inner-browser-use/flash/plugin/`),由 `npm run flash:setup` 从本机已有的安装里找/拷,找不到就明说;Ruffle 那条路(MIT/BSD)仍是可分发的那条。
71
+
72
+ ## 分阶段
73
+
74
+ - **M0 摄入(已发布)**:读 SWF 标签流,给出尺寸/帧率/AVM/资源构成。`runtime/swf.mjs` + `npm run swf`。
75
+ - **MP 立刻能玩(本版)**:PPAPI 分支 + 插件开关,见上节;`npm run flash:setup` 备运行时。
76
+ - **M1 能跑**:自托管 Ruffle + 宿主路由 + `embed/object` 自动 polyfill,在一个真实 SWF 上量首帧、fps、字节数。
77
+ - **M2 能读**:把 Ruffle 的 player 对象与显示列表接到 `browser_eval`,agent 直接读游戏状态,不用截图。
78
+ - **M3 更快**:损伤跟踪 + 增量帧传输;对同一游戏给出「Flash 版 vs 这里」的毫秒与 CPU 对比。
79
+
80
+ ## 诚实边界
81
+
82
+ - AVM2 的兼容上限由兼容核决定;Ruffle 官方口径是「多数游戏能玩,仍有 API 缺口」,具体游戏要实测。
83
+ - Stage3D、加密 SWF、依赖本地插件的网游登录器,不在 M1–M3 的承诺内。
84
+ - ZWS(LZMA)本版明确拒绝;补 LZMA 是一天的事,但先要有真实影片来验证。
package/docs/ROADMAP.md CHANGED
@@ -50,7 +50,7 @@ stealing the column from other chats.
50
50
 
51
51
  ## Phase 2 — one call, many steps (target: a login flow in 1 tool call)
52
52
 
53
- - `sidecar_do`: ordered steps with per-step waits (`wait_text`, `wait_gone`,
53
+ - `browser_do`: ordered steps with per-step waits (`wait_text`, `wait_gone`,
54
54
  `wait_idle`, `wait_url`), one result: what changed, the final URL, tokens spent
55
55
  on one read instead of five.
56
56
  - Auto-wait after every action: navigation start/finish, DOM quiet, and
@@ -65,7 +65,7 @@ stealing the column from other chats.
65
65
  - File upload (`DOM.setFileInputFiles`), download capture with a returned path,
66
66
  print-to-PDF, full-page and element screenshots.
67
67
  - Tabs: new / switch / close / list; iframes addressed by frame id; dialog
68
- accept/dismiss; `sidecar_eval` for the last 1% of cases; cookies read/write for
68
+ accept/dismiss; `browser_eval` for the last 1% of cases; cookies read/write for
69
69
  debugging; console + failed requests surfaced in the state read.
70
70
  - Keyboard: modifiers, shortcuts, clipboard paste, IME composition unchanged.
71
71
 
@@ -86,14 +86,14 @@ stealing the column from other chats.
86
86
 
87
87
  - Human takeover (0.17.1): real input in the pane (click / text / wheel / key)
88
88
  holds the agent's next action for 6 s and says so; hover and move do not count;
89
- reads stay open; `sidecar_resume` takes the page back at once.
89
+ reads stay open; `browser_resume` takes the page back at once.
90
90
  - Guarded actions (upload, download, close tab, buy-ish submits) can require a
91
91
  confirm flag. Shipped in 0.14.0 for the narrow case that matters most: an
92
92
  overlay in the way is cleared only when its own way out is a safe word
93
93
  (accept / agree / close / 同意 / 关闭 …) and the overlay says nothing about
94
94
  deleting, paying or signing out; otherwise it is reported, never taken.
95
95
  Since 0.15.0 the same clearing runs before every control-naming step of a
96
- `sidecar_do` batch, so a multi-step flow through a cookie wall fails zero times.
96
+ `browser_do` batch, so a multi-step flow through a cookie wall fails zero times.
97
97
  - Crash recovery: attach to a still-running Chrome, rebuild the page, keep the
98
98
  profile; a killed host never leaves a zombie Chrome.
99
99
  - Every failure names what happened, what state the page is in, and the next
@@ -124,10 +124,10 @@ finding the control again by identity (kind + exact words + link destination),
124
124
  refusing when the words are not unique. Measured at 462 ms against 445 ms for a
125
125
  fresh control — 17 ms when needed, nothing when not.
126
126
 
127
- Since 0.16.1 the count is visible: `sidecar_state` reports `refs followed N`,
127
+ Since 0.16.1 the count is visible: `browser_state` reports `refs followed N`,
128
128
  and breadth asserts it moved after a redraw, so the next rounds can tell from
129
129
  real sites whether redrawing is frequent enough to justify more work here.
130
- Next in this direction: the same identity recovery for `sidecar_do` steps that
130
+ Next in this direction: the same identity recovery for `browser_do` steps that
131
131
  name a field by its label when the label element is rebuilt (today the step
132
132
  falls back to a fresh read).
133
133
 
@@ -142,7 +142,7 @@ then repeat the loop with whatever the numbers say is worst.
142
142
  Two things were wrong at the edge of the tool surface, and both were only
143
143
  reachable on real sites.
144
144
 
145
- The first was that the shape of the browser could not change. `sidecar_open`
145
+ The first was that the shape of the browser could not change. `browser_open`
146
146
  accepted `headless: false` and answered success; when a browser was already
147
147
  running nothing happened, because the flag was only read at launch. A site that
148
148
  refuses a windowless browser (chatgpt.com on this machine: a "just a moment"
@@ -150,7 +150,7 @@ page forever, versus a login page in a windowed one) therefore had no way out
150
150
  except killing a process by hand. Since 0.17.0 a request for the other shape
151
151
  restarts the browser on the same profile, cookie jar and address — 21.6 s
152
152
  windowless → windowed, 22.1 s back, 0 ms when the shape already matches — and
153
- both `sidecar_open` and `sidecar_state` say which shape is in front of the
153
+ both `browser_open` and `browser_state` say which shape is in front of the
154
154
  person.
155
155
 
156
156
  The second was silence on a check page. A site asking for proof of a person is
@@ -181,7 +181,7 @@ The tool used to treat "the control that says Email" as "the field called
181
181
  Email". Real forms separate the two, so the writing step now resolves the words
182
182
  to the element that takes text, and says which one it used.
183
183
 
184
- Next in this direction: the same resolution for `sidecar_type` when the caller
184
+ Next in this direction: the same resolution for `browser_type` when the caller
185
185
  names a field by its words instead of a ref, and for date/time pickers where the
186
186
  visible box is a div and the real input is hidden beside it.
187
187
 
@@ -189,15 +189,15 @@ visible box is a div and the real input is hidden beside it.
189
189
 
190
190
  Typing by name is in. The same resolution is what other steps should be able to
191
191
  use when the caller knows the destination but not the number — the next place
192
- worth doing this is `sidecar_select` (choose an option by the field it belongs
192
+ worth doing this is `browser_select` (choose an option by the field it belongs
193
193
  to), and after that the date/time case where the visible box is a div and the
194
194
  real input sits hidden beside it.
195
195
 
196
196
  ## Phase 12 — the same words, the same place, 0.21.0
197
197
 
198
- `sidecar_select` joined `sidecar_type` and `sidecar_do` on the shared resolver.
198
+ `browser_select` joined `browser_type` and `browser_do` on the shared resolver.
199
199
  Left in this direction: the date/time case where the box the user sees is a div
200
- and the real input is hidden beside it, and `sidecar_click` on a control whose
200
+ and the real input is hidden beside it, and `browser_click` on a control whose
201
201
  words are split across sibling nodes (`<span>Sign</span><span>in</span>`).
202
202
 
203
203
  ## Phase 13 — the page's spelling is not the caller's, 0.22.0
@@ -216,7 +216,7 @@ name, so the fallback has to be a picture plus a click, not a name lookup.
216
216
 
217
217
  ## Phase 16 — a file, not a path, 0.25.0
218
218
 
219
- `sidecar_grab` returns a path. Next: say one line about the picture (size, and
219
+ `browser_grab` returns a path. Next: say one line about the picture (size, and
220
220
  whether it is mostly one colour) so a model that cannot open files still learns
221
221
  "the chart is blank" without a full read.
222
222
 
@@ -248,7 +248,7 @@ at -3000,-3000, and the column shows the only picture.
248
248
 
249
249
  ## Phase 21 — a review round that closes the loop, 0.30.0
250
250
 
251
- `sidecar_ask` sends and returns an answer. Next: a round trip that carries the
251
+ `browser_ask` sends and returns an answer. Next: a round trip that carries the
252
252
  repository address there and the review back, and records in the round log which
253
253
  commit the review was about, so a later round can tell whether the advice was
254
254
  already taken.
@@ -298,6 +298,12 @@ The phases above were written one at a time as the work happened; the run from
298
298
 
299
299
  ## 页游这一档(2026-10-28 起)
300
300
 
301
+ - 操作方式已分叉(0.34.29):`browser_mode` 每个聊天记一种,`human` / `dom` 单次可覆盖。页游默认 human,登录/表单默认 dom 更省事。
302
+ - Flash 判死:headless Chrome 里 Ruffle 交得上 swf 但起不了画面(26 秒,canvas 0、metadata 0),不承诺。真页游只能走站方 H5 / 微端 / 老 360 浏览器,页面上线前先用 `scripts/probe-flash.mjs` 问一句它要不要 Flash。
303
+ - 开玩前先体检:`browser_prepare` 一次调用给出输入面、帧率、按键是否真的到得了框架、以及 Flash 结论(0.34.30)。4399《怪物小汽车》实测页面 58 fps、框架 59 fps —— 不少「Flash 小游戏」其实已经是 H5,先体检再下结论;`npm run ready <url>` 可复现。
304
+ - 读 SWF 标签流(`npm run swf`):尺寸、帧率、AVM 版本、资源构成,FWS/CWS 都读,ZWS 明确报不支持(0.34.32)。
305
+ - 空白画面的两种原因分开报(`viewers` / `listening chats` / `asks.log`),并在「浏览器在跑、没人画」时自己向所属会话补发一次开页签请求(0.34.31)。
306
+
301
307
  输入这一层已经到位:按住键、组合键、按住鼠标,一项 300 ms 的按住整次调用 305 ms,事件由页面自己证实。实时画面沿用同一条 CDP 帧路(60 fps、镜像延迟中位数 8 ms)。
302
308
 
303
309
  还差的一层是「看」:4399 / 7k7k / 奥拉星这类页游把画面画在 canvas 上,文本树里没有可读的东西,判断走位、Boss 血条、按钮位置都要靠像素。本会话的模型没有视觉能力,所以这里的正确做法不是假装能玩,而是把可判读的信号做出来:canvas 局部像素采样、颜色/区域变化率、画面是否在动(`blind` 行已经在报 canvas 数量)。有视觉能力的模型接着用同一套帧路就能玩。
@@ -311,7 +317,7 @@ Ranked by what a real task still loses:
311
317
  HTTP and by pid, not by a person clicking. A browser-level test that opens the
312
318
  column, kills the driver, and clicks restore is the missing evidence.
313
319
  2. **One-click "open here" when this chat has no seat.** `state.chats` can lack this
314
- chat, and the column then only tells the model to call `sidecar_open`. The button
320
+ chat, and the column then only tells the model to call `browser_open`. The button
315
321
  belongs in the column.
316
322
  3. **Competitor behaviours still unmatched.** Playwright MCP's accessibility refs are
317
323
  ours, and the blind line covers the vision-less read; Stagehand v4's