@browserbridge/bbx 1.7.8 → 1.9.0

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 (44) hide show
  1. package/PRIVACY.md +57 -12
  2. package/README.md +19 -8
  3. package/SECURITY.md +12 -0
  4. package/package.json +15 -6
  5. package/packages/agent-client/src/cli-args.js +59 -0
  6. package/packages/agent-client/src/cli-batch.js +121 -58
  7. package/packages/agent-client/src/cli-output.js +8 -1
  8. package/packages/agent-client/src/cli.js +272 -70
  9. package/packages/agent-client/src/client.js +296 -8
  10. package/packages/agent-client/src/command-registry.js +64 -55
  11. package/packages/agent-client/src/config.js +195 -0
  12. package/packages/agent-client/src/content-extract.js +198 -0
  13. package/packages/agent-client/src/npm-self-update.js +305 -0
  14. package/packages/agent-client/src/remotes.js +4 -2
  15. package/packages/agent-client/src/runtime.js +864 -39
  16. package/packages/agent-client/src/types.ts +169 -6
  17. package/packages/mcp-server/src/guidance.js +1 -1
  18. package/packages/mcp-server/src/handlers-capture.js +69 -6
  19. package/packages/mcp-server/src/handlers-dom.js +61 -4
  20. package/packages/mcp-server/src/handlers-navigation.js +2 -1
  21. package/packages/mcp-server/src/handlers-page.js +211 -54
  22. package/packages/mcp-server/src/handlers-utils.js +101 -10
  23. package/packages/mcp-server/src/handlers.js +14 -7
  24. package/packages/mcp-server/src/server.js +176 -11
  25. package/packages/native-host/src/artifact-store.js +353 -0
  26. package/packages/native-host/src/config.js +9 -1
  27. package/packages/native-host/src/daemon-logger.js +3 -1
  28. package/packages/native-host/src/daemon.js +701 -33
  29. package/packages/native-host/src/native-host.js +28 -3
  30. package/packages/protocol/src/capability-values.js +2 -0
  31. package/packages/protocol/src/defaults.js +31 -0
  32. package/packages/protocol/src/errors.js +102 -6
  33. package/packages/protocol/src/incidental-sanitizer.js +197 -0
  34. package/packages/protocol/src/index.js +2 -0
  35. package/packages/protocol/src/protocol.js +612 -6
  36. package/packages/protocol/src/recovery-telemetry.js +302 -0
  37. package/packages/protocol/src/registry.js +206 -38
  38. package/packages/protocol/src/summary.js +203 -30
  39. package/packages/protocol/src/types.ts +721 -2
  40. package/skills/browser-bridge/SKILL.md +54 -19
  41. package/skills/browser-bridge/references/interaction.md +55 -11
  42. package/skills/browser-bridge/references/patch-workflow.md +2 -0
  43. package/skills/browser-bridge/references/protocol.md +265 -86
  44. package/skills/browser-bridge/references/token-efficiency.md +29 -13
@@ -27,75 +27,89 @@ The table below includes the legacy capability bucket for each method so agents
27
27
  - `-` means the method is global/system-scoped and was never gated by a former capability bucket.
28
28
  - Capability names are descriptive coverage labels only. Browser Bridge access is window-scoped now; there are no capability-scoped sessions.
29
29
 
30
- ## All Methods (65)
31
-
32
- | # | Method | Tab? | CDP? | Group | Capability | Notes |
33
- | --- | ---------------------------------- | ---- | ---- | ----------- | -------------------- | ----------------------------------------------------------------------- |
34
- | 1 | `access.request` | No | - | system | `-` | Request window access; surfaces Enable prompt in extension UI |
35
- | 2 | `tabs.list` | No | - | tabs | `-` | Discover available tabs |
36
- | 3 | `tabs.create` | No | - | tabs | `tabs.manage` | Open a new tab; optional `url` and `active` |
37
- | 4 | `tabs.close` | No | - | tabs | `tabs.manage` | Close a tab by `tabId` |
38
- | 5 | `skill.get_runtime_context` | No | - | system | `-` | Live budget presets + method groups |
39
- | 6 | `setup.get_status` | No | - | system | `-` | Global MCP config + CLI skill install status |
40
- | 7 | `setup.install` | No | - | system | `-` | Install or uninstall MCP/skill integration targets |
41
- | 8 | `health.ping` | No | - | system | `-` | Connectivity check + access routing state |
42
- | 9 | `log.tail` | No | - | system | `-` | Recent bridge logs |
43
- | 10 | `daemon.metrics` | No | - | system | `-` | Daemon health and performance metrics |
44
- | 11 | `page.get_state` | Yes | - | page | `page.read` | URL, readiness, focus, scroll, viewport |
45
- | 12 | `page.evaluate` | Yes | CDP | page | `page.evaluate` | JS expression in page context; last resort |
46
- | 13 | `page.get_console` | Yes | - | page | `page.read` | Buffered console messages; filter by `level`, `limit` |
47
- | 14 | `page.wait_for_load_state` | Yes | - | wait | `page.read` | Block until tab `complete`; `timeoutMs` range 0.5-120 s |
48
- | 15 | `page.get_storage` | Yes | - | page | `page.read` | `localStorage`/`sessionStorage`; optional `keys` |
49
- | 16 | `page.get_text` | Yes | - | page | `page.read` | Full page text; `textBudget` limits size |
50
- | 17 | `page.get_network` | Yes | - | page | `network.read` | Intercepted fetch/XHR; `limit` entries |
51
- | 18 | `navigation.navigate` | Yes | - | navigate | `navigation.control` | Go to URL; `waitForLoad` default true |
52
- | 19 | `navigation.reload` | Yes | - | navigate | `navigation.control` | Reload; `waitForLoad` default true |
53
- | 20 | `navigation.go_back` | Yes | - | navigate | `navigation.control` | History back |
54
- | 21 | `navigation.go_forward` | Yes | - | navigate | `navigation.control` | History forward |
55
- | 22 | `dom.query` | Yes | - | inspect | `dom.read` | Query subtree with budget constraints |
56
- | 23 | `dom.describe` | Yes | - | inspect | `dom.read` | Single element details via `elementRef` |
57
- | 24 | `dom.get_text` | Yes | - | inspect | `dom.read` | Text content with `textBudget` |
58
- | 25 | `dom.get_attributes` | Yes | - | inspect | `dom.read` | Targeted attribute read |
59
- | 26 | `dom.wait_for` | Yes | - | wait | `dom.read` | Wait for DOM condition; MutationObserver + polling |
60
- | 27 | `dom.find_by_text` | Yes | - | inspect | `dom.read` | Find by visible text; returns `{nodes, count}` |
61
- | 28 | `dom.find_by_role` | Yes | - | inspect | `dom.read` | Find by ARIA role; optional `name` filter |
62
- | 29 | `dom.get_html` | Yes | - | inspect | `dom.read` | `innerHTML`/`outerHTML`; `maxLength` truncation |
63
- | 30 | `dom.get_accessibility_tree` | Yes | CDP | inspect | `dom.read` | Full a11y tree; `maxNodes`/`maxDepth` limits |
64
- | 31 | `layout.get_box_model` | Yes | - | inspect | `layout.read` | Element geometry (no budget needed) |
65
- | 32 | `layout.hit_test` | Yes | - | inspect | `layout.read` | Element at viewport point |
66
- | 33 | `styles.get_computed` | Yes | - | inspect | `styles.read` | Computed CSS; always set `properties` |
67
- | 34 | `styles.get_matched_rules` | Yes | - | inspect | `styles.read` | Matching CSS rules |
68
- | 35 | `viewport.scroll` | Yes | - | navigate | `viewport.control` | Window or element scroll |
69
- | 36 | `viewport.resize` | Yes | CDP | navigate | `viewport.control` | Set viewport via device emulation; `reset: true` |
70
- | 37 | `input.click` | Yes | - | interact | `automation.input` | DOM-level click |
71
- | 38 | `input.focus` | Yes | - | interact | `automation.input` | Focus element |
72
- | 39 | `input.type` | Yes | - | interact | `automation.input` | Type into input/textarea/contenteditable |
73
- | 40 | `input.press_key` | Yes | - | interact | `automation.input` | Single key event |
74
- | 41 | `input.set_checked` | Yes | - | interact | `automation.input` | Checkbox/radio toggle |
75
- | 42 | `input.select_option` | Yes | - | interact | `automation.input` | Native select by value/label/index |
76
- | 43 | `input.hover` | Yes | - | interact | `automation.input` | mouseenter/mouseover/mousemove; optional `duration` |
77
- | 44 | `input.drag` | Yes | - | interact | `automation.input` | Full drag-and-drop event sequence |
78
- | 45 | `input.scroll_into_view` | Yes | - | interact | `automation.input` | Explicitly scroll target into view before inspect/capture |
79
- | 46 | `screenshot.capture_element` | Yes | CDP | capture | `screenshot.partial` | Cropped element screenshot |
80
- | 47 | `screenshot.capture_region` | Yes | CDP | capture | `screenshot.partial` | Cropped viewport region |
81
- | 48 | `screenshot.capture_full_page` | Yes | CDP | capture | `screenshot.partial` | Full document screenshot; use only when page-level context is necessary |
82
- | 49 | `patch.apply_styles` | Yes | - | patch | `patch.styles` | Reversible CSS patch; `verify` returns computed result |
83
- | 50 | `patch.apply_dom` | Yes | - | patch | `patch.dom` | Reversible DOM mutation; `verify` returns result |
84
- | 51 | `patch.list` | Yes | - | patch | `patch.dom` | Active patches |
85
- | 52 | `patch.rollback` | Yes | - | patch | `patch.dom` | Revert one patch |
86
- | 53 | `patch.commit_session_baseline` | Yes | - | patch | `patch.dom` | Accept current state as baseline |
87
- | 54 | `performance.get_metrics` | Yes | CDP | performance | `performance.read` | Chrome performance counters |
88
- | 55 | `cdp.get_document` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools document tree |
89
- | 56 | `cdp.get_dom_snapshot` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools DOM snapshot |
90
- | 57 | `cdp.get_box_model` | Yes | CDP | cdp | `cdp.box_model` | DevTools-backed element geometry |
91
- | 58 | `cdp.get_computed_styles_for_node` | Yes | CDP | cdp | `cdp.styles` | DevTools-backed computed styles |
92
- | 59 | `cdp.dispatch_key_event` | Yes | CDP | cdp | `cdp.input` | DevTools keyDown/keyUp without foreground focus |
93
- | 60 | `tabs.activate` | No | - | tabs | `tabs.manage` | Bring a tab to the foreground in the enabled window |
94
- | 61 | `input.fill` | Yes | - | interact | `automation.input` | Set field value via native setter; `mode`: auto/setter/keystrokes |
95
- | 62 | `network.intercept.add` | Yes | CDP | page | `network.intercept` | Add interception rule; action fulfill/continue/block |
96
- | 63 | `network.intercept.remove` | Yes | CDP | page | `network.intercept` | Remove rule by `ruleId` |
97
- | 64 | `network.intercept.list` | Yes | CDP | page | `network.intercept` | List active rules (rules drop on debugger detach) |
98
- | 65 | `network.intercept.clear` | Yes | CDP | page | `network.intercept` | Remove all rules, detach debugger |
30
+ ## All Methods (75)
31
+
32
+ | Method | Tab? | CDP? | Group | Capability | Notes |
33
+ | ---------------------------------- | ---- | ---------- | ----------- | -------------------- | ------------------------------------------------------------------------------------------ |
34
+ | `access.request` | No | - | system | `intent?` | Request window access with bounded operation context in the extension UI |
35
+ | `tabs.list` | No | - | tabs | `-` | Discover tabs in the enabled window |
36
+ | `tabs.create` | No | - | tabs | `tabs.manage` | Open a new tab; optional `url` and `active` |
37
+ | `tabs.close` | No | - | tabs | `tabs.manage` | Close a tab by `tabId` |
38
+ | `tabs.activate` | No | - | tabs | `tabs.manage` | Bring a tab to the foreground in the enabled window |
39
+ | `skill.get_runtime_context` | No | - | system | `-` | Live budget presets and method groups |
40
+ | `setup.get_status` | No | - | system | `-` | Global MCP config and CLI skill install status |
41
+ | `setup.install` | No | - | system | `-` | Install or uninstall MCP/skill integration targets |
42
+ | `health.ping` | No | - | system | `-` | Connectivity, routing, capture, and daemon/routed-extension recovery summaries |
43
+ | `log.tail` | No | - | system | `-` | Recent redacted bridge logs |
44
+ | `daemon.metrics` | No | - | system | `-` | Daemon health, performance, and process-local recovery metrics |
45
+ | `page.get_state` | Yes | - | page | `page.read` | URL, readiness, focus, viewport, and observable dialog status |
46
+ | `page.evaluate` | Yes | CDP | page | `page.evaluate` | JS expression in page context; last resort |
47
+ | `page.get_console` | Yes | - | page | `page.read` | Buffered console messages; filter by `level`, `limit` |
48
+ | `page.handle_dialog` | Yes | CDP | page | `navigation.control` | Inspect or explicitly accept/dismiss the currently observable dialog |
49
+ | `page.wait_for_load_state` | Yes | - | wait | `page.read` | Wait for truthful tab `complete` state and/or a URL condition |
50
+ | `page.get_storage` | Yes | - | page | `page.read` | Bounded storage key/presence metadata; optional `keys`; never values |
51
+ | `sensitive.read` | Yes | - | sensitive | `sensitive.read` | One exact local/session storage key; sequential, non-retryable, whole-or-error |
52
+ | `page.get_text` | Yes | - | page | `page.read` | Full page text; `textBudget` limits size |
53
+ | `page.extract_content` | Yes | - | page | `page.read` | Node-processed semantic text/Markdown with bounded HTML snapshots and optional settlement |
54
+ | `page.get_network` | Yes | Conditional | page | `network.read` | Fetch/XHR by default; explicit CDP all-resource capture lifecycle |
55
+ | `network.export_har` | Yes | No* | page | `network.read` | Read an already armed CDP capture as bounded metadata-only HAR 1.2 |
56
+ | `network.intercept.add` | Yes | CDP | page | `network.intercept` | Add interception rule; action fulfill/continue/block |
57
+ | `network.intercept.remove` | Yes | CDP | page | `network.intercept` | Remove rule by `ruleId` |
58
+ | `network.intercept.list` | Yes | CDP | page | `network.intercept` | List active rules (rules drop on debugger detach) |
59
+ | `network.intercept.clear` | Yes | CDP | page | `network.intercept` | Remove all rules and release interception |
60
+ | `navigation.navigate` | Yes | - | navigate | `navigation.control` | Go to URL; `waitForLoad` default true |
61
+ | `navigation.reload` | Yes | - | navigate | `navigation.control` | Reload; `waitForLoad` default true |
62
+ | `navigation.go_back` | Yes | - | navigate | `navigation.control` | History back |
63
+ | `navigation.go_forward` | Yes | - | navigate | `navigation.control` | History forward |
64
+ | `dom.query` | Yes | - | inspect | `dom.read` | Query subtree with budget constraints |
65
+ | `dom.baseline.create` | Yes | - | inspect | `dom.read` | Retain a short-lived memory-only semantic snapshot; sequential only |
66
+ | `dom.baseline.compare` | Yes | - | inspect | `dom.read` | Compare current semantic DOM to an explicit retained baseline; batch-safe |
67
+ | `dom.baseline.describe` | Yes | - | inspect | `dom.read` | Return exact baseline scope, options, expiry, and snapshot statistics; batch-safe |
68
+ | `dom.baseline.release` | Yes | - | inspect | `dom.read` | Idempotently release retained baseline state; sequential only |
69
+ | `dom.describe` | Yes | - | inspect | `dom.read` | Single element details via `elementRef` |
70
+ | `dom.get_text` | Yes | - | inspect | `dom.read` | Text content with `textBudget` |
71
+ | `dom.get_attributes` | Yes | - | inspect | `dom.read` | Targeted attribute read |
72
+ | `dom.wait_for` | Yes | - | wait | `dom.read` | Wait for DOM condition; MutationObserver and polling |
73
+ | `dom.find_by_text` | Yes | - | inspect | `dom.read` | Find by visible text; returns `{nodes, count}` |
74
+ | `dom.find_by_role` | Yes | - | inspect | `dom.read` | Find by ARIA role; optional `name` filter |
75
+ | `dom.get_html` | Yes | - | inspect | `dom.read` | `innerHTML`/`outerHTML`; `maxLength` truncation |
76
+ | `dom.get_accessibility_tree` | Yes | CDP | inspect | `dom.read` | Full or uniquely selector-scoped AX tree with compact/interactive filters |
77
+ | `layout.get_box_model` | Yes | - | inspect | `layout.read` | Element geometry |
78
+ | `layout.hit_test` | Yes | - | inspect | `layout.read` | Topmost element at viewport point |
79
+ | `styles.get_computed` | Yes | - | inspect | `styles.read` | Requested properties; omission returns display/position/width/height/color |
80
+ | `styles.get_matched_rules` | Yes | - | inspect | `styles.read` | Element ref, class list, and inline style only; not stylesheet cascade data |
81
+ | `viewport.scroll` | Yes | - | navigate | `viewport.control` | Window or element scroll |
82
+ | `viewport.resize` | Yes | CDP | navigate | `viewport.control` | Set viewport via device emulation; `reset: true` |
83
+ | `input.click` | Yes | Optional | interact | `automation.input` | Actionability-aware DOM or CDP click |
84
+ | `input.focus` | Yes | - | interact | `automation.input` | Actionability-aware DOM focus |
85
+ | `input.type` | Yes | Optional | interact | `automation.input` | DOM key sequence or CDP text insertion |
86
+ | `input.fill` | Yes | Optional | interact | `automation.input` | DOM fill strategy or CDP clear/insert; see `mode` versus `executionMode` |
87
+ | `input.press_key` | Yes | - | interact | `automation.input` | DOM key event |
88
+ | `input.set_checked` | Yes | - | interact | `automation.input` | Checkbox/radio toggle |
89
+ | `input.select_option` | Yes | - | interact | `automation.input` | Native select by value/label/index |
90
+ | `input.hover` | Yes | Optional | interact | `automation.input` | DOM hover events or native CDP pointer move |
91
+ | `input.drag` | Yes | Optional | interact | `automation.input` | DOM drag events or native interpolated pointer drag |
92
+ | `input.scroll_into_view` | Yes | - | interact | `automation.input` | Explicitly scroll target into view before inspect/capture |
93
+ | `screenshot.capture_element` | Yes | CDP | capture | `screenshot.partial` | Complete element screenshot with explicit completeness metadata |
94
+ | `screenshot.capture_region` | Yes | CDP | capture | `screenshot.partial` | Cropped viewport region in PNG, JPEG, or WebP |
95
+ | `screenshot.capture_full_page` | Yes | CDP | capture | `screenshot.partial` | Full document screenshot; fails coherently above Chrome capture limits |
96
+ | `artifact.read` | No | daemon | raw | None | Read one bounded chunk from an owner-scoped artifact handle |
97
+ | `artifact.delete` | No | daemon | raw | None | Delete an owner-scoped artifact handle |
98
+ | `patch.apply_styles` | Yes | - | patch | `patch.styles` | Reversible CSS patch; `verify` returns computed result |
99
+ | `patch.apply_dom` | Yes | - | patch | `patch.dom` | Reversible text, attribute, add/remove/toggle-class mutation; `verify` returns result |
100
+ | `patch.list` | Yes | - | patch | `patch.dom` | Active rollback records in the current document |
101
+ | `patch.rollback` | Yes | - | patch | `patch.dom` | Revert one active patch |
102
+ | `patch.commit_session_baseline` | Yes | - | patch | `patch.dom` | Keep current mutations and discard their rollback history |
103
+ | `performance.get_metrics` | Yes | CDP | performance | `performance.read` | Chrome performance counters |
104
+ | `cdp.get_document` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools document tree |
105
+ | `cdp.get_dom_snapshot` | Yes | CDP | cdp | `cdp.dom_snapshot` | DevTools DOM snapshot; optional bounded `computedStyles` property names |
106
+ | `cdp.get_box_model` | Yes | CDP | cdp | `cdp.box_model` | DevTools-backed element geometry |
107
+ | `cdp.get_computed_styles_for_node` | Yes | CDP | cdp | `cdp.styles` | DevTools-backed computed styles |
108
+ | `cdp.dispatch_key_event` | Yes | CDP | cdp | `cdp.input` | DevTools keyDown/keyUp without foreground focus |
109
+
110
+ `No*` means HAR export issues no CDP command itself, but its source capture must
111
+ already be armed and continues to hold debugger ownership until explicitly
112
+ stopped.
99
113
 
100
114
  ## CLI
101
115
 
@@ -106,7 +120,7 @@ bbx call --tab 123 <method> '{...}' # explicit tab target inside enabled
106
120
  bbx batch '[{"method":"...","params":{}}]' # parallel calls
107
121
  ```
108
122
 
109
- **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `cdp-press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `scroll`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
123
+ **Convenience shortcuts:** `access-request`, `dom-query`, `describe`, `text`, `styles`, `box`, `click`, `focus`, `type`, `press-key`, `cdp-press-key`, `patch-style`, `patch-text`, `patches`, `rollback`, `screenshot`, `har`, `eval`, `console`, `wait`, `find`, `find-role`, `html`, `hover`, `navigate`, `storage`, `tab-create`, `tab-close`, `page-text`, `network`, `a11y-tree`, `perf`, `scroll`, `resize`, `reload`, `back`, `forward`, `attrs`, `matched-rules`
110
124
 
111
125
  Newer bridge methods such as `input.scroll_into_view` and `screenshot.capture_full_page` currently use the raw path: `bbx call <method> '{...}'`.
112
126
 
@@ -114,13 +128,16 @@ Newer bridge methods such as `input.scroll_into_view` and `screenshot.capture_fu
114
128
 
115
129
  ### access.request
116
130
 
117
- Request Browser Bridge access for the focused browser window. Surfaces an Enable prompt in the extension popup or side panel so the user can grant access. Does not require an existing session.
131
+ Request Browser Bridge access for the focused browser window. Surfaces an Enable prompt in the extension popup or side panel with the reported source, bounded operation intent, tab title, and sanitized origin. Does not require existing access.
118
132
 
119
133
  ```bash
120
134
  bbx access-request
121
- bbx call access.request
135
+ bbx access-request inspect
136
+ bbx call access.request '{"intent":"capture"}'
122
137
  ```
123
138
 
139
+ `intent` is optional and defaults to `general`. Accepted values are `inspect`, `interact`, `capture`, `navigate`, `debugger`, and `general`; arbitrary prompt text is rejected. The source and intent are reported context, while the title and origin are resolved and sanitized by the extension.
140
+
124
141
  If a tab-bound call returns `ACCESS_DENIED`, it also surfaces the Enable prompt automatically - so explicit `access.request` is optional but useful for proactive setup.
125
142
 
126
143
  If access is already pending for a window, do not call `access.request` again. Ask the user to click `Enable` for the requested window and wait for confirmation before continuing.
@@ -151,17 +168,32 @@ bbx call page.get_console '{"level":"error","limit":20,"clear":true}'
151
168
 
152
169
  Responses include `dropped` when older buffered entries were discarded on noisy pages.
153
170
 
171
+ ### page.handle_dialog
172
+
173
+ Inspecting is read-only; accepting or dismissing must be explicit. `promptText` is valid only with `action: "accept"`. An optional `expectedDialogId` checks that the current observation still matches immediately before dispatch, but Chrome's `Page.handleJavaScriptDialog` command cannot atomically bind to that identifier. A successful mutation therefore reports `commandDispatched: true` and `atomicDialogBinding: false`; it does not prove which application follow-up occurred.
174
+
175
+ ```bash
176
+ bbx call page.handle_dialog '{"action":"inspect"}'
177
+ bbx call page.handle_dialog '{"action":"accept","expectedDialogId":"00000000-0000-4000-8000-000000000000:1","promptText":"value"}' # replace with inspected dialogId
178
+ bbx call page.handle_dialog '{"action":"dismiss","expectedDialogId":"00000000-0000-4000-8000-000000000000:1"}' # replace with inspected dialogId
179
+ ```
180
+
181
+ `DIALOG_NOT_OPEN` means no current dialog was observable. `DIALOG_ACTION_CONFLICT` means the observation changed before or around dispatch; inspect again and never automatically repeat the mutating action. Dialog messages and prompt defaults are bounded and excluded from persisted action logs.
182
+
154
183
  ### page.wait_for_load_state
155
184
 
156
- Block until the tab reaches `complete` status. Useful after `input.click` on a navigation link.
185
+ Wait for truthful Chrome tab `complete` status, a URL condition, or both. URL waits check the current URL first, then observe tab updates plus `pushState`, `replaceState`, `popstate`, and hash changes. Results include `finalUrl`, `urlMatch`, `elapsedMs`, and `observedNavigationKind`; this method does not call DOMContentLoaded behavior `networkidle`.
157
186
 
158
187
  ```bash
159
188
  bbx call page.wait_for_load_state '{"timeoutMs":10000}'
189
+ bbx call page.wait_for_load_state '{"url":"/dashboard","urlMatch":"contains","waitForLoad":false}'
160
190
  ```
161
191
 
192
+ `urlMatch: "regex"` accepts at most 256 characters and only a bounded linear subset: literals, escapes, anchors, dots, and character classes. Grouping, alternation, quantifiers, and backreferences are rejected.
193
+
162
194
  ### page.get_storage
163
195
 
164
- Read `localStorage` or `sessionStorage` entries. Values truncated at 500 chars each.
196
+ Read bounded `localStorage` or `sessionStorage` key/presence metadata. Values are never returned.
165
197
 
166
198
  ```bash
167
199
  bbx storage # all localStorage
@@ -169,6 +201,17 @@ bbx storage session token,user # specific sessionStorage keys
169
201
  bbx call page.get_storage '{"type":"session","keys":["token"]}'
170
202
  ```
171
203
 
204
+ ### sensitive.read
205
+
206
+ Deliberately read one exact Web Storage value after discovering its key. The
207
+ value bypasses token truncation, is never batched or automatically retried, and
208
+ is returned whole or rejected whole. Every attempt creates a Sensitive access
209
+ Recent Activity warning without retaining the key or value.
210
+
211
+ ```bash
212
+ bbx call sensitive.read '{"source":"session_storage","key":"token"}'
213
+ ```
214
+
172
215
  ### dom.query
173
216
 
174
217
  Run a bounded breadth-first DOM summary rooted at a selector or existing ref. Returns `{nodes, revision, truncated, registrySize}` and may also include `_registryPruned: true` when the element registry evicted older refs.
@@ -180,6 +223,23 @@ bbx call dom.query '{"selector":"main","maxNodes":10,"attributeAllowlist":["clas
180
223
 
181
224
  If `_registryPruned` is true, refresh previously cached refs before reusing them.
182
225
 
226
+ ### dom.baseline.create, compare, describe, and release
227
+
228
+ Use an explicit semantic baseline when an action must be verified against a known before-state without returning two full DOM snapshots. Baselines retain bounded structured summaries in extension memory only; they do not add attributes, scripts, or markers to the application page.
229
+
230
+ ```bash
231
+ bbx call dom.baseline.create '{"selector":"main","maxNodes":100,"maxDepth":8,"textBudget":160,"attributeAllowlist":["data-testid"]}'
232
+ bbx call dom.baseline.compare '{"baselineId":"baseline_...","maxChanges":50}'
233
+ bbx call dom.baseline.describe '{"baselineId":"baseline_..."}'
234
+ bbx call dom.baseline.release '{"baselineId":"baseline_..."}'
235
+ ```
236
+
237
+ The create result is a destination-bound opaque handle plus creation/expiry timestamps, window/tab/frame/document/selector/representation scope, effective capture options, and snapshot counts. It never returns retained nodes. `compare` reports exact `added`, `removed`, `changed`, `moved`, `unchanged`, and total counts while bounding returned examples with `maxChanges`; ambiguity and omitted evidence are explicit. `describe` returns the exact descriptor. `release` is idempotent.
238
+
239
+ Creation and release are stateful and excluded from read-only batch. Compare and describe do not mutate the baseline or extend its five-minute expiry, so they are batch-safe. Navigation, document replacement, tab/window cleanup, access disable, destination disconnect, expiry, quota eviction, or explicit release makes the handle unavailable. Navigation and document replacement return `DOM_BASELINE_INVALIDATED`; missing, expired, released, or evicted handles return `DOM_BASELINE_NOT_FOUND`.
240
+
241
+ Capture defaults to stable semantic fields and safe attributes. Form values and event-handler attributes are never retained, URL credentials/fragments/query values are removed, opaque URL payloads are redacted, and caller-provided attribute names are canonicalized and bounded. Use a narrow selector for dialogs, forms, lists, or application regions.
242
+
183
243
  ### dom.wait_for
184
244
 
185
245
  Wait for a DOM condition using MutationObserver + 250 ms polling fallback. A text-only wait searches all elements. `hidden` succeeds when no matching visible element remains, including when it is absent. Returns `{found, elementRef, duration}`.
@@ -220,9 +280,25 @@ bbx html el_abc123
220
280
  bbx call dom.get_html '{"elementRef":"el_abc123","outer":true,"maxLength":2000}'
221
281
  ```
222
282
 
283
+ ### styles.get_computed and styles.get_matched_rules
284
+
285
+ When `properties` is omitted, `styles.get_computed` returns exactly `display`, `position`, `width`, `height`, and `color`. Pass an explicit list for any other properties. `styles.get_matched_rules` currently returns only `{elementRef, classes, inlineStyle}`; it does not inspect stylesheet rules, specificity, or cascade order.
286
+
287
+ ### Actionable input targets
288
+
289
+ Input calls preserve an explicit `elementRef`. For selectors, the first actionable match wins; if it is not actionable, Browser Bridge evaluates at most 25 matches and chooses only a uniquely better candidate. It scrolls the selected target if needed and rechecks visibility, disabled/inert state, rendered bounds, and pointer hit testing before dispatch. Failures use `ELEMENT_NOT_FOUND`, `ELEMENT_NOT_ACTIONABLE`, `ELEMENT_OBSCURED`, or `ELEMENT_AMBIGUOUS` with bounded details.
290
+
291
+ Successful targeted click, focus, type, fill, press-key, checked-state, option-selection, hover, and drag results include `resolution` (`strategy`, candidate/evaluated counts, scrolling, hit test, and recovery fields) and `execution` (`requestedMode`, `actualMode`, `fallbackReason`, `debuggerUsed`, and coordinates). `cdp.dispatch_key_event`/MCP `cdp_press_key` and `input.scroll_into_view` use separate response contracts. `executionMode` is the dispatch path and accepts only `dom` or `cdp`, defaulting to `dom` for compatibility. CDP is supported only by click, hover, drag, type, and fill; unsupported combinations return `INPUT_UNSUPPORTED` instead of falling back silently.
292
+
293
+ `input.fill.mode` is separate: `auto`, `setter`, and `keystrokes` choose the DOM fill strategy. `mode: "auto"` may fall back from the setter to keystrokes and reports the used `mode`; it does not select CDP. With `executionMode: "cdp"`, fill uses native clear/text dispatch and reports `mode: "cdp"`.
294
+
295
+ Stale refs still fail by default. `recoverStale: true` permits one strict recovery attempt only within the current document and unchanged URL, based on a strong unique descriptor such as test ID, ID, role/name, label, or href plus semantic context. Recovery evaluates at most 100 same-tag candidates. If more exist, it returns `ELEMENT_AMBIGUOUS` with `reason: "scan_incomplete"` because uniqueness is not provable, even when the evaluated prefix contains one match. Ambiguous, weak, or changed-URL recovery also fails. Recovered results identify old/new refs and matched fields.
296
+
297
+ Browser input dispatch is not a generic application-state assertion. Follow each action with the cheapest relevant `dom.wait_for`, `page.wait_for_load_state`, `dom.describe`, or other structured read.
298
+
223
299
  ### input.hover
224
300
 
225
- Trigger CSS `:hover` state by dispatching `mouseenter`, `mouseover`, `mousemove`. Optional `duration` to hold hover before auto-releasing.
301
+ Trigger hover with DOM events by default, or a native pointer move with `executionMode: "cdp"`. Optional `duration` waits before returning; it does not promise a persistent hover after the call.
226
302
 
227
303
  ```bash
228
304
  bbx hover el_abc123
@@ -231,7 +307,7 @@ bbx call input.hover '{"target":{"elementRef":"el_abc123"},"duration":1000}'
231
307
 
232
308
  ### input.drag
233
309
 
234
- Full drag-and-drop sequence: `mousedown → dragstart → drag → dragenter → dragover → drop → dragend → mouseup`. Accepts source target, destination target, and optional pixel offsets.
310
+ The DOM path dispatches `mousedown → dragstart → drag → dragenter → dragover → drop → dragend → mouseup`. The CDP path performs a bounded interpolated native pointer drag and guarantees a release attempt after failure. Both accept source, destination, and optional destination offsets.
235
311
 
236
312
  ```bash
237
313
  bbx call input.drag '{"source":{"elementRef":"el_src"},"destination":{"elementRef":"el_dst"}}'
@@ -285,11 +361,22 @@ bbx page-text 8000
285
361
  bbx call page.get_text '{"textBudget":2000}'
286
362
  ```
287
363
 
364
+ ### page.extract_content
365
+
366
+ Extract article-like or documentation content without returning source HTML to the agent. The Node client reads a bounded HTML snapshot through the existing bridge, runs pinned Mozilla Readability locally, then falls back to the largest `article`, `main`, or `[role="main"]` subtree and finally sanitized body text. Scripts, styles, hidden regions, and form controls are excluded before extraction.
367
+
368
+ ```bash
369
+ bbx call page.extract_content '{"format":"text"}'
370
+ bbx call page.extract_content '{"format":"markdown","selector":"main","textBudget":8000}'
371
+ ```
372
+
373
+ Parameters: `format` (`text` default or `markdown`), optional `selector`, `includeMetadata` (default `true`), `consistency` (`best_effort` default or `settled`), `textBudget` (default 8000), and `settleTimeoutMs` (default 2000, maximum 10000). Settled extraction requires two identical bounded snapshots at least 100 ms apart; it does not claim network idle or a frozen page. Results report source, original length, truncation, omitted characters, bounded metadata, and settlement timeout state. An oversized snapshot fails coherently with guidance to narrow `selector` rather than returning partial semantic content.
374
+
288
375
  ### page.get_network
289
376
 
290
377
  The fetch/XHR interceptor is installed on the first call. Before reproducing a network issue, call `page.get_network` with `clear: true`, trigger the action, then read the buffer again without `clear` so the relevant request is not missed or erased.
291
378
 
292
- Read intercepted fetch/XHR requests. The interceptor is auto-installed on first call (via MAIN world script). Returns `{entries, count}` sorted newest-first.
379
+ The default `source: "fetch-xhr"` path auto-installs MAIN-world instrumentation on first call and returns completed entries in capture order (oldest to newest among the retained/limited entries). Prime with `clear: true`, reproduce, then read without clearing.
293
380
 
294
381
  ```bash
295
382
  bbx network
@@ -297,20 +384,73 @@ bbx network 50
297
384
  bbx call page.get_network '{"limit":20,"clear":true}'
298
385
  ```
299
386
 
300
- Each entry: `{method, url, status, duration, initiator}`. Responses include `dropped` when older buffered entries were discarded.
387
+ Each fetch/XHR entry is `{method, url, status, duration, type, ts, size}`. Responses also include `count`, `total`, `filteredTotal`, `source`, `captureState: "instrumented"`, `dropped`, and limit truncation metadata.
388
+
389
+ For optional all-resource metadata, use the explicit debugger-backed CDP lifecycle. Start clears prior state and holds debugger ownership until stop (or the 10-minute safety expiry); read does not retroactively capture events:
390
+
391
+ ```bash
392
+ bbx call page.get_network '{"source":"cdp","capture":"start"}'
393
+ # reproduce the activity
394
+ bbx call page.get_network '{"source":"cdp","capture":"read","limit":50}'
395
+ bbx call page.get_network '{"source":"cdp","capture":"stop"}'
396
+ ```
397
+
398
+ `capture: "clear"` clears completed/in-flight entries while leaving an armed capture running. CDP entries contain `requestId`, redacted `url`, `method`, `resourceType`, `status`, `mimeType`, `protocol`, cache flags, bounded redirect summary, `failureReason`, `duration`, and `timestamp`. Result lifecycle fields include `armed`, `armedDuringCapture`, `captureState`, `ownershipHeld`, `startedAt`, `inflight`, `dropped`, and `abandoned`.
399
+
400
+ CDP URL output removes credentials and fragments, replaces every query value with `[redacted]`, and summarizes data/blob URLs. Request/response bodies, cookies, authorization values, and complete headers are not returned. This mode captures all observed resource classes and is materially more expensive and intrusive than fetch/XHR instrumentation; always stop it when finished.
401
+
402
+ ### network.export_har
403
+
404
+ Export metadata-only HAR 1.2 from a CDP capture that is currently armed. The
405
+ method is a read and does not attach, start, stop, clear, or otherwise change the
406
+ capture. Use this exact sequence:
407
+
408
+ ```bash
409
+ bbx call page.get_network '{"source":"cdp","capture":"start"}'
410
+ # reproduce the activity
411
+ bbx har --limit 100 --url-pattern /api/ reproduction.har
412
+ bbx call page.get_network '{"source":"cdp","capture":"stop"}'
413
+ ```
414
+
415
+ Parameters are `limit` (1-200, default 50), `urlPattern` (at most 2048
416
+ characters; substring match on sanitized URLs), and `delivery` (`auto` default,
417
+ `inline`, or `artifact`). Count and inline byte/token limits retain the newest
418
+ matching entries and remove complete oldest entries; fields are not truncated to
419
+ fit. `auto` uses an artifact above 256 KiB, while explicit `artifact` always
420
+ returns one. Artifacts last five minutes, are readable/deletable only by their
421
+ requesting client, use private daemon files, and expose no browser-host path.
422
+
423
+ HAR URLs preserve schemes, hosts, paths, and query names, remove credentials and
424
+ fragments, and replace query values with `[redacted]`. `queryString`, headers,
425
+ and cookies are empty; bodies and authorization data are absent. Unsupported
426
+ header/body/content sizes and send/wait/receive timing phases are `-1`.
427
+ `entry.time` is observed total duration. Redirects are separate observed entries;
428
+ `_bbx` reports resource type, failure reason, and memory/disk/prefetch cache and
429
+ service-worker observations. Result counters expose completed-entry loss as
430
+ `dropped`, discarded unfinished activity as `abandoned`, and requests still
431
+ unfinished and omitted from the HAR as `inflight`.
432
+
433
+ MCP uses `browser_page` with `action: "har"`. `bbx har` accepts the same limit,
434
+ URL pattern, and delivery choices, then uses one client connection to download
435
+ owner-scoped chunks, verify byte length/SHA-256 and HAR shape, delete the
436
+ artifact, and atomically write the output on the CLI host.
301
437
 
302
438
  ### dom.get_accessibility_tree
303
439
 
304
- Retrieve the page's accessibility tree via CDP `Accessibility.getFullAXTree`. Each node is simplified to: `role`, `name`, `description`, `value`, `focused`, `required`, `checked`, `disabled`, `interactive`, `childIds`. Use `maxNodes` and `maxDepth` to control size.
440
+ Retrieve a depth-limited accessibility tree via CDP. Full reads use `Accessibility.getFullAXTree`; an optional unique `selector` resolves through CDP DOM methods and uses `Accessibility.getPartialAXTree`, retaining only the selected subtree and required ancestor chain. Missing and ambiguous selectors return typed errors instead of selecting an arbitrary candidate. Nodes include `role`, `name`, `description`, `value`, state fields, `interactive`, `semanticInteractive`, `focusable`, `focusableAndEnabled`, `ignored`, and `childIds`. `interactive` is semantic/focusability metadata, not current pointer actionability.
305
441
 
306
442
  This is debugger-backed. Prefer `dom.find_by_role`, `dom.find_by_text`, and targeted `dom.query`/`dom.describe` first.
307
443
 
308
444
  ```bash
309
445
  bbx a11y-tree
310
446
  bbx a11y-tree 50 3
311
- bbx call dom.get_accessibility_tree '{"maxNodes":100,"maxDepth":5}'
447
+ bbx call dom.get_accessibility_tree '{"maxNodes":100,"maxDepth":5,"compact":true}'
448
+ bbx call dom.get_accessibility_tree '{"maxNodes":100,"maxDepth":6,"interactiveOnly":true}'
449
+ bbx call dom.get_accessibility_tree '{"selector":"[role=dialog]","maxNodes":50,"compact":true}'
312
450
  ```
313
451
 
452
+ Filtering occurs before `maxNodes`. Compact mode drops ignored, decorative, and empty nodes while reconnecting retained descendants; interactive-only keeps non-ignored semantic interactive roles. Because CDP applies `maxDepth` before Browser Bridge receives the tree, every result truthfully reports `truncated: true`, `partialTopology: true`, depth metadata, missing-child counts, and a continuation hint. No AX node is written into the page DOM or converted directly into an actionable element ref; use role/name with `dom.find_by_role` before input.
453
+
314
454
  ### viewport.resize
315
455
 
316
456
  Set the browser viewport to specific dimensions using CDP device emulation. Pass `reset: true` to clear the override.
@@ -325,7 +465,9 @@ bbx call viewport.resize '{"reset":true}'
325
465
 
326
466
  ### performance.get_metrics
327
467
 
328
- Read Chrome performance counters via CDP `Performance.getMetrics`. Returns a flat `{metrics}` object with keys like `JSHeapUsedSize`, `LayoutCount`, `TaskDuration`, etc.
468
+ Read a browser-maintained point sample of raw Chrome counters via CDP `Performance.getMetrics`. The additive result is `{metrics, measurement}`, where `measurement` identifies the CDP source, `timeTicks` domain, sample timestamp, point-sample observation, and `webVitals: "not_measured"`. Metric names, availability, and units are defined by Chrome/CDP and can vary by browser version.
469
+
470
+ BBX does not establish a navigation observation window and does not measure LCP, CLS, or INP. Do not interpret the returned counters as Web Vitals or page-load measurements.
329
471
 
330
472
  Debugger-backed. Use after lighter reads fail to explain a performance symptom.
331
473
 
@@ -334,16 +476,42 @@ bbx perf
334
476
  bbx call performance.get_metrics
335
477
  ```
336
478
 
337
- ### screenshot.capture_full_page
479
+ ### screenshot.capture_\*
338
480
 
339
- Capture a full-document screenshot beyond the current viewport. Use only when element or tight region captures cannot express the issue. Chrome capture limits still apply on very large pages.
481
+ Capture a complete element, a tight viewport region, or a full document. All methods accept `format: "png" | "jpeg" | "webp"`, `scale` from 0.1 to 4, and `delivery: "inline" | "artifact" | "auto"`; JPEG and WebP additionally accept integer `quality` from 0 to 100. Results include matching `format`, `mimeType`, encoded `byteLength`, actual `dimensions`, `complete`, `clipped`, and `delivery` metadata.
340
482
 
341
- This raw call returns base64 JSON. Prefer `bbx screenshot <ref> [outPath]` when one element is enough.
483
+ Element capture uses full page-coordinate bounds with CDP capture beyond the viewport. If Browser Bridge cannot guarantee the entire element, or if an element/page exceeds Chrome capture limits, it returns a typed failure rather than a viewport-clipped image labeled complete. Use full-page capture only when element or tight region captures cannot express the issue.
484
+
485
+ Raw calls default to inline base64 for compatibility. `auto` keeps small captures inline and returns a short-lived, daemon-owned opaque artifact handle for large captures; explicit `inline` preserves MCP image content when model vision is needed. Artifact handles expose no browser-host path and support owner-scoped bounded `artifact.read` and `artifact.delete` calls. Prefer `bbx screenshot [--format png|jpeg|webp] [--quality 0-100] <ref> [outPath]` when one element is enough; the CLI downloads, verifies, deletes, and atomically writes the artifact on the CLI host.
486
+
487
+ Artifact handles expire after five minutes and can be read or deleted only by
488
+ the requesting client. The daemon creates a private artifact directory and
489
+ owner-only files on POSIX hosts, removes artifacts on expiry/teardown, and never
490
+ returns its browser-host filesystem path.
342
491
 
343
492
  ```bash
344
- bbx call screenshot.capture_full_page '{}'
493
+ bbx call screenshot.capture_full_page '{"format":"jpeg","quality":75,"delivery":"auto","scale":0.75}'
345
494
  ```
346
495
 
496
+ ## Recovery Telemetry
497
+
498
+ Recovery telemetry is process-local and uses fixed five-second buckets over a
499
+ five-minute window. Three failures for the same hidden group within 60 seconds
500
+ set `activeLoop` for that category. Public summaries always use only these fixed
501
+ categories: `automatic_mcp_retry`, `stale_ref_recovery`, `debugger_reattach`,
502
+ `content_script_reinjection`, `native_host_reconnect`, and `request_outcome`.
503
+ They contain attempts/successes/failures/pending, bounded rates, saturation,
504
+ last-event timestamps, and loop flags, but no page data, URL, selector, payload,
505
+ error text, method name, tab identifier, or internal group name.
506
+
507
+ `health.ping` includes daemon and available routed-extension summaries;
508
+ `daemon.metrics` includes daemon scope. `bbx doctor` combines both, marks active
509
+ loops as issues, and emits fixed guidance. Stop repeated attempts: re-query stale
510
+ refs, close competing debuggers, reload extension/page after reinjection
511
+ failures, restart for native-host reconnect failures, stop automatic MCP retry
512
+ loops, or inspect the first typed error for request failures. Extension data
513
+ resets with the service worker and daemon data resets with the daemon.
514
+
347
515
  ## Request Envelope
348
516
 
349
517
  ```json
@@ -363,11 +531,22 @@ bbx call screenshot.capture_full_page '{}'
363
531
  | `ACCESS_DENIED` | Turn on Browser Bridge for the target window | `retry: false` |
364
532
  | `TAB_MISMATCH` | Explicit `tabId` is missing, closed, or outside enabled window | `retry: false`, use `tabs.list` |
365
533
  | `ELEMENT_STALE` | Re-query DOM for fresh `elementRef` | `retry: false`, use `dom.query` |
534
+ | `ELEMENT_AMBIGUOUS` | Narrow selector or use an explicit ref | `retry: false`, use `dom.query` |
535
+ | `ELEMENT_NOT_ACTIONABLE` | Inspect hidden/disabled/inert/zero-size target | `retry: false`, use `dom.describe` |
536
+ | `ELEMENT_OBSCURED` | Inspect the element blocking the target point | `retry: false`, use `layout.hit_test` |
537
+ | `ELEMENT_NOT_FOUND` | Correct or narrow the selector | `retry: false`, use `dom.query` |
538
+ | `DOM_BASELINE_NOT_FOUND` | Handle expired, released, evicted, or does not exist | `retry: false`, create a new baseline |
539
+ | `DOM_BASELINE_INVALIDATED` | Navigation, document, or access scope changed | `retry: false`, create a new baseline |
540
+ | `DOM_BASELINE_QUOTA_EXCEEDED` | Snapshot or retained-state quota was exceeded | `retry: false`, narrow selector or release |
541
+ | `INPUT_UNSUPPORTED` | Use DOM execution or a CDP-supported input method | `retry: false` |
542
+ | `INPUT_INVALID_TARGET` | Choose a control compatible with the input | `retry: false`, use `dom.describe` |
543
+ | `INPUT_FOCUS_CHANGED` | Inspect focus handlers; do not replay native text blindly | `retry: false`, use `dom.describe` |
544
+ | `DIALOG_NOT_OPEN` | Trigger or inspect the current dialog | `retry: false` |
545
+ | `DIALOG_ACTION_CONFLICT` | Inspect again; never auto-repeat a dialog mutation | `retry: false` |
366
546
  | `CONTENT_SCRIPT_UNAVAILABLE` | Page is restricted (chrome://, extensions, etc.) | `retry: false` |
367
547
  | `NATIVE_HOST_UNAVAILABLE` | Check daemon: `bbx status` | `retry: false` |
368
548
  | `EXTENSION_DISCONNECTED` | Extension not connected to daemon | `retry: true` after 3 s, check `health.ping` |
369
549
  | `TIMEOUT` | Wait/evaluate exceeded `timeoutMs` | `retry: true` after 1 s |
370
- | `RATE_LIMITED` | Too many requests | `retry: true` after 2 s |
371
550
 
372
551
  Timeout on content-script request → use narrower `dom.query` or CDP fallback.
373
552
  Timeout on navigation → increase `timeoutMs`, set `waitForLoad:false`, or check `page.get_state`.