@browserbridge/bbx 1.7.7 → 1.8.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 (34) hide show
  1. package/PRIVACY.md +111 -0
  2. package/README.md +13 -5
  3. package/RESPONSIBLE_USE.md +16 -0
  4. package/SECURITY.md +52 -0
  5. package/package.json +12 -6
  6. package/packages/agent-client/src/cli-args.js +24 -0
  7. package/packages/agent-client/src/cli.js +46 -52
  8. package/packages/agent-client/src/client.js +140 -5
  9. package/packages/agent-client/src/command-registry.js +56 -53
  10. package/packages/agent-client/src/content-extract.js +198 -0
  11. package/packages/agent-client/src/remotes.js +1 -1
  12. package/packages/agent-client/src/runtime.js +768 -35
  13. package/packages/agent-client/src/types.ts +131 -6
  14. package/packages/mcp-server/src/guidance.js +1 -1
  15. package/packages/mcp-server/src/handlers-capture.js +41 -5
  16. package/packages/mcp-server/src/handlers-dom.js +8 -2
  17. package/packages/mcp-server/src/handlers-navigation.js +2 -1
  18. package/packages/mcp-server/src/handlers-page.js +41 -4
  19. package/packages/mcp-server/src/handlers-utils.js +67 -7
  20. package/packages/mcp-server/src/handlers.js +4 -4
  21. package/packages/mcp-server/src/server.js +102 -6
  22. package/packages/native-host/src/daemon.js +158 -6
  23. package/packages/native-host/src/native-host.js +3 -1
  24. package/packages/protocol/src/defaults.js +3 -0
  25. package/packages/protocol/src/errors.js +54 -1
  26. package/packages/protocol/src/protocol.js +297 -1
  27. package/packages/protocol/src/registry.js +66 -35
  28. package/packages/protocol/src/summary.js +87 -5
  29. package/packages/protocol/src/types.ts +234 -0
  30. package/skills/browser-bridge/SKILL.md +34 -13
  31. package/skills/browser-bridge/references/interaction.md +55 -11
  32. package/skills/browser-bridge/references/patch-workflow.md +2 -0
  33. package/skills/browser-bridge/references/protocol.md +157 -82
  34. package/skills/browser-bridge/references/token-efficiency.md +19 -13
@@ -1,5 +1,15 @@
1
1
  export type BridgeRequestSource = 'cli' | 'mcp';
2
2
 
3
+ export type AccessIntent = 'inspect' | 'interact' | 'capture' | 'navigate' | 'debugger' | 'general';
4
+
5
+ export interface AccessRequestParams {
6
+ intent?: AccessIntent;
7
+ }
8
+
9
+ export interface NormalizedAccessRequestParams extends BridgeParams {
10
+ intent: AccessIntent;
11
+ }
12
+
3
13
  export type Capability =
4
14
  | 'page.read'
5
15
  | 'page.evaluate'
@@ -25,6 +35,15 @@ export type ErrorCode =
25
35
  | 'ACCESS_DENIED'
26
36
  | 'TAB_MISMATCH'
27
37
  | 'ELEMENT_STALE'
38
+ | 'ELEMENT_AMBIGUOUS'
39
+ | 'ELEMENT_NOT_ACTIONABLE'
40
+ | 'ELEMENT_OBSCURED'
41
+ | 'ELEMENT_NOT_FOUND'
42
+ | 'INPUT_UNSUPPORTED'
43
+ | 'INPUT_INVALID_TARGET'
44
+ | 'INPUT_FOCUS_CHANGED'
45
+ | 'DIALOG_NOT_OPEN'
46
+ | 'DIALOG_ACTION_CONFLICT'
28
47
  | 'RESULT_TRUNCATED'
29
48
  | 'RATE_LIMITED'
30
49
  | 'INTERNAL_ERROR'
@@ -45,9 +64,11 @@ export type BridgeMethod =
45
64
  | 'page.get_state'
46
65
  | 'page.evaluate'
47
66
  | 'page.get_console'
67
+ | 'page.handle_dialog'
48
68
  | 'page.wait_for_load_state'
49
69
  | 'page.get_storage'
50
70
  | 'page.get_text'
71
+ | 'page.extract_content'
51
72
  | 'page.get_network'
52
73
  | 'network.intercept.add'
53
74
  | 'network.intercept.remove'
@@ -228,6 +249,29 @@ export interface InputTarget {
228
249
  selector?: string;
229
250
  }
230
251
 
252
+ export type InputExecutionMode = 'dom' | 'cdp';
253
+
254
+ export interface InputResolutionMetadata {
255
+ strategy: 'elementRef' | 'selector-first' | 'selector-ranked' | 'stale-recovery';
256
+ candidateCount: number;
257
+ evaluatedCount: number;
258
+ scrolled: boolean;
259
+ hitTest: 'target' | 'descendant' | 'none' | 'not-required';
260
+ recovered: boolean;
261
+ oldRef?: string;
262
+ newRef?: string;
263
+ matchedFields?: string[];
264
+ confidenceBasis?: string;
265
+ }
266
+
267
+ export interface InputExecutionMetadata {
268
+ requestedMode: InputExecutionMode;
269
+ actualMode: InputExecutionMode;
270
+ fallbackReason: null;
271
+ debuggerUsed: boolean;
272
+ targetCoordinates?: { x: number; y: number };
273
+ }
274
+
231
275
  export interface InputActionParams {
232
276
  target?: InputTarget;
233
277
  button?: 'left' | 'middle' | 'right';
@@ -239,6 +283,8 @@ export interface InputActionParams {
239
283
  submit?: boolean;
240
284
  key?: string;
241
285
  modifiers?: string[];
286
+ executionMode?: InputExecutionMode;
287
+ recoverStale?: boolean;
242
288
  }
243
289
 
244
290
  export interface NormalizedInputAction extends BridgeParams {
@@ -252,6 +298,8 @@ export interface NormalizedInputAction extends BridgeParams {
252
298
  submit: boolean;
253
299
  key: string;
254
300
  modifiers: string[];
301
+ executionMode: InputExecutionMode;
302
+ recoverStale: boolean;
255
303
  }
256
304
 
257
305
  export interface CdpDispatchKeyEventParams {
@@ -277,11 +325,15 @@ export interface NormalizedCdpNodeIdParams extends BridgeParams {
277
325
  export interface CheckedActionParams {
278
326
  target?: InputTarget;
279
327
  checked?: boolean;
328
+ executionMode?: InputExecutionMode;
329
+ recoverStale?: boolean;
280
330
  }
281
331
 
282
332
  export interface NormalizedCheckedAction extends BridgeParams {
283
333
  target: InputTarget;
284
334
  checked: boolean;
335
+ executionMode: InputExecutionMode;
336
+ recoverStale: boolean;
285
337
  }
286
338
 
287
339
  export interface SelectActionParams {
@@ -289,6 +341,8 @@ export interface SelectActionParams {
289
341
  values?: string[];
290
342
  labels?: string[];
291
343
  indexes?: number[];
344
+ executionMode?: InputExecutionMode;
345
+ recoverStale?: boolean;
292
346
  }
293
347
 
294
348
  export interface NormalizedSelectAction extends BridgeParams {
@@ -296,6 +350,8 @@ export interface NormalizedSelectAction extends BridgeParams {
296
350
  values: string[];
297
351
  labels: string[];
298
352
  indexes: number[];
353
+ executionMode: InputExecutionMode;
354
+ recoverStale: boolean;
299
355
  }
300
356
 
301
357
  export interface ViewportActionParams {
@@ -417,6 +473,21 @@ export interface ConsoleParams {
417
473
  limit?: number;
418
474
  }
419
475
 
476
+ export type DialogAction = 'inspect' | 'accept' | 'dismiss';
477
+
478
+ export interface HandleDialogParams {
479
+ action?: DialogAction;
480
+ promptText?: string;
481
+ /** Optional stale-decision check performed immediately before CDP dispatch. */
482
+ expectedDialogId?: string;
483
+ }
484
+
485
+ export interface NormalizedHandleDialogParams extends BridgeParams {
486
+ action: DialogAction;
487
+ promptText: string | null;
488
+ expectedDialogId: string | null;
489
+ }
490
+
420
491
  export interface NormalizedConsoleParams extends BridgeParams {
421
492
  level: string;
422
493
  clear: boolean;
@@ -483,12 +554,16 @@ export interface HoverParams {
483
554
  target?: InputTarget;
484
555
  duration?: number;
485
556
  modifiers?: string[];
557
+ executionMode?: InputExecutionMode;
558
+ recoverStale?: boolean;
486
559
  }
487
560
 
488
561
  export interface NormalizedHoverParams extends BridgeParams {
489
562
  target: InputTarget;
490
563
  duration: number;
491
564
  modifiers: string[];
565
+ executionMode: InputExecutionMode;
566
+ recoverStale: boolean;
492
567
  }
493
568
 
494
569
  export interface DragParams {
@@ -496,6 +571,8 @@ export interface DragParams {
496
571
  destination?: InputTarget;
497
572
  offsetX?: number;
498
573
  offsetY?: number;
574
+ executionMode?: InputExecutionMode;
575
+ recoverStale?: boolean;
499
576
  }
500
577
 
501
578
  export interface NormalizedDragParams extends BridgeParams {
@@ -503,6 +580,8 @@ export interface NormalizedDragParams extends BridgeParams {
503
580
  destination: InputTarget;
504
581
  offsetX: number;
505
582
  offsetY: number;
583
+ executionMode: InputExecutionMode;
584
+ recoverStale: boolean;
506
585
  }
507
586
 
508
587
  export interface StorageParams {
@@ -515,14 +594,75 @@ export interface NormalizedStorageParams extends BridgeParams {
515
594
  keys: string[] | null;
516
595
  }
517
596
 
597
+ export type ExtractContentFormat = 'text' | 'markdown';
598
+ export type ExtractContentConsistency = 'best_effort' | 'settled';
599
+ export type ExtractContentSource = 'readability' | 'semantic-root' | 'body';
600
+ export type ScreenshotFormat = 'png' | 'jpeg' | 'webp';
601
+
602
+ export interface ExtractContentParams {
603
+ format?: ExtractContentFormat;
604
+ selector?: string;
605
+ includeMetadata?: boolean;
606
+ consistency?: ExtractContentConsistency;
607
+ textBudget?: number;
608
+ settleTimeoutMs?: number;
609
+ }
610
+
611
+ export interface NormalizedExtractContentParams extends BridgeParams {
612
+ format: ExtractContentFormat;
613
+ selector: string | null;
614
+ includeMetadata: boolean;
615
+ consistency: ExtractContentConsistency;
616
+ textBudget: number;
617
+ settleTimeoutMs: number;
618
+ }
619
+
620
+ export interface ScreenshotParams extends BridgeParams {
621
+ format?: ScreenshotFormat;
622
+ quality?: number;
623
+ }
624
+
625
+ export interface NormalizedScreenshotParams extends BridgeParams {
626
+ format: ScreenshotFormat;
627
+ quality: number | null;
628
+ }
629
+
630
+ export interface ScreenshotResult {
631
+ image: string;
632
+ mimeType: `image/${ScreenshotFormat}`;
633
+ format: ScreenshotFormat;
634
+ rect: { x: number; y: number; width: number; height: number; scale: number };
635
+ complete: boolean;
636
+ clipped: boolean;
637
+ }
638
+
639
+ export interface ExtractContentResult {
640
+ format: ExtractContentFormat;
641
+ content: string;
642
+ title?: string;
643
+ byline?: string;
644
+ excerpt?: string;
645
+ siteName?: string;
646
+ source: ExtractContentSource;
647
+ root?: { tag: string; selector?: string };
648
+ length: number;
649
+ truncated: boolean;
650
+ omitted: number;
651
+ settlement?: { requested: boolean; quietMs: number; timedOut: boolean };
652
+ }
653
+
518
654
  export interface WaitForLoadStateParams {
519
655
  waitForLoad?: boolean;
520
656
  timeoutMs?: number;
657
+ url?: string;
658
+ urlMatch?: 'exact' | 'contains' | 'regex';
521
659
  }
522
660
 
523
661
  export interface NormalizedWaitForLoadStateParams extends BridgeParams {
524
662
  waitForLoad: boolean;
525
663
  timeoutMs: number;
664
+ url: string | null;
665
+ urlMatch: 'exact' | 'contains' | 'regex' | null;
526
666
  }
527
667
 
528
668
  export interface TabCreateParams {
@@ -544,25 +684,119 @@ export interface NormalizedTabCloseParams extends BridgeParams {
544
684
  }
545
685
 
546
686
  export interface AccessibilityTreeParams {
687
+ selector?: string;
547
688
  maxDepth?: number;
548
689
  maxNodes?: number;
690
+ compact?: boolean;
691
+ interactiveOnly?: boolean;
549
692
  }
550
693
 
551
694
  export interface NormalizedAccessibilityTreeParams extends BridgeParams {
695
+ selector: string | null;
552
696
  maxDepth: number;
553
697
  maxNodes: number;
698
+ compact: boolean;
699
+ interactiveOnly: boolean;
554
700
  }
555
701
 
702
+ export interface AccessibilityTreeNode {
703
+ nodeId: string;
704
+ role: string;
705
+ name: string;
706
+ description: string;
707
+ value: string;
708
+ focused: boolean;
709
+ required: boolean;
710
+ checked: 'true' | 'false' | 'mixed' | null;
711
+ disabled: boolean;
712
+ interactive: boolean;
713
+ semanticInteractive: boolean;
714
+ focusable: boolean;
715
+ focusableAndEnabled: boolean;
716
+ ignored: boolean;
717
+ childIds: string[];
718
+ }
719
+
720
+ export interface AccessibilityTreeResult {
721
+ nodes: AccessibilityTreeNode[];
722
+ rootIds: string[];
723
+ count: number;
724
+ total: number;
725
+ rawTotal: number;
726
+ source: 'cdp-accessibility';
727
+ compact: boolean;
728
+ interactiveOnly: boolean;
729
+ truncated: boolean;
730
+ truncation: {
731
+ reason: 'maxNodes' | 'maxDepth';
732
+ reasons: Array<'maxNodes' | 'maxDepth'>;
733
+ maxNodes: number;
734
+ maxDepth: number;
735
+ omitted: number;
736
+ missingChildCount: number;
737
+ partialTopology: true;
738
+ };
739
+ continuationHint: string;
740
+ }
741
+
742
+ export type NetworkSource = 'fetch-xhr' | 'cdp';
743
+ export type NetworkCaptureAction = 'read' | 'start' | 'clear' | 'stop';
744
+
556
745
  export interface NetworkParams {
557
746
  clear?: boolean;
558
747
  limit?: number;
559
748
  urlPattern?: string;
749
+ source?: NetworkSource;
750
+ capture?: NetworkCaptureAction;
560
751
  }
561
752
 
562
753
  export interface NormalizedNetworkParams extends BridgeParams {
563
754
  clear: boolean;
564
755
  limit: number;
565
756
  urlPattern: string | null;
757
+ source: NetworkSource;
758
+ capture: NetworkCaptureAction;
759
+ }
760
+
761
+ export interface CdpNetworkEntry {
762
+ requestId: string;
763
+ url: string;
764
+ method: string;
765
+ resourceType: string;
766
+ status: number;
767
+ mimeType: string;
768
+ protocol: string;
769
+ fromCache: boolean;
770
+ fromDiskCache: boolean;
771
+ fromServiceWorker: boolean;
772
+ fromPrefetchCache: boolean;
773
+ redirect: {
774
+ count: number;
775
+ hops: Array<{ url: string; status: number }>;
776
+ truncated: boolean;
777
+ };
778
+ failureReason: string;
779
+ duration: number;
780
+ timestamp: number;
781
+ }
782
+
783
+ export interface NetworkResult {
784
+ entries: Array<CdpNetworkEntry | Record<string, unknown>>;
785
+ count: number;
786
+ total: number;
787
+ filteredTotal: number;
788
+ dropped: number;
789
+ abandoned: number;
790
+ source: NetworkSource;
791
+ capture: NetworkCaptureAction | null;
792
+ armed: boolean;
793
+ armedDuringCapture: boolean;
794
+ captureState: 'armed' | 'stop_failed' | 'stopped' | 'instrumented';
795
+ ownershipHeld: boolean;
796
+ startedAt: number | null;
797
+ inflight: number;
798
+ truncated: boolean;
799
+ truncation: { reason: 'limit' | null; limit: number; omitted: number };
566
800
  }
567
801
 
568
802
  export type NetworkInterceptAction = 'fulfill' | 'continue' | 'block';
@@ -13,13 +13,13 @@ Permission prompts are controlled by the host agent, not by Browser Bridge. In p
13
13
 
14
14
  Skill name: `browser-bridge` (also known as `bbx`). In GitHub Copilot, invoke as `/browser-bridge`. `bbx` is the CLI command used throughout this skill.
15
15
  When the runtime supports subagents, delegate bridge inspection to a smaller, lower-cost worker and return only concise findings to the parent.
16
- For open-ended investigation, start with structured reads (`page.get_state`, `dom.query`, `page.get_text`, `styles.get_computed`, and `bbx batch` for CLI or `browser_batch` for MCP) and escalate to screenshots or debugger-backed methods only when structured evidence is insufficient. `browser_call` accepts one protocol method at a time; `batch` is not a valid `browser_call` method.
16
+ For open-ended investigation, start with structured reads (`page.get_state`, `dom.query`, `page.get_text`, `page.extract_content`, `styles.get_computed`, and `bbx batch` for CLI or `browser_batch` for MCP) and escalate to screenshots or debugger-backed methods only when structured evidence is insufficient. `browser_call` accepts one protocol method at a time; `batch` is not a valid `browser_call` method.
17
17
 
18
18
  ## CLI
19
19
 
20
20
  ```bash
21
21
  bbx status # daemon + extension health
22
- bbx doctor # install/access readiness
22
+ bbx doctor # consolidated local diagnostics (does not probe remotes)
23
23
  bbx access-request # ask user to enable access for the focused window
24
24
  bbx restart # start/restart the daemon and running MCP servers
25
25
  bbx call <method> '{...}' # any RPC method (raw output)
@@ -58,7 +58,7 @@ bbx a11y-tree [maxNodes] [maxDepth] # accessibility tree
58
58
  ```bash
59
59
  bbx eval [--await] <expression> # JS eval (--await for promises, - for stdin)
60
60
  bbx console [level] # console output
61
- bbx network [limit] # network requests
61
+ bbx network [limit] # recent fetch/XHR requests
62
62
  bbx intercept add <pattern> [--respond <body>] [--status <code>] [--block] # intercept matching requests
63
63
  bbx intercept list|remove <id>|clear # manage interception rules
64
64
  bbx page-text [budget] # full page text
@@ -70,6 +70,8 @@ bbx back # navigate back
70
70
  bbx forward # navigate forward
71
71
  bbx scroll <top> [left] # scroll viewport
72
72
  bbx resize <width> <height> # resize viewport
73
+ bbx call page.wait_for_load_state '{"url":"/dashboard","urlMatch":"contains","waitForLoad":false}'
74
+ bbx call page.handle_dialog '{"action":"inspect"}' # inspect current JS dialog; never auto-accept
73
75
  ```
74
76
 
75
77
  ### Interact & Patch
@@ -83,11 +85,12 @@ bbx press-key <key> [ref] # send key event
83
85
  bbx cdp-press-key --tab <id> Escape # CDP key event without foreground focus
84
86
  bbx hover <ref> # hover over element
85
87
  bbx call input.scroll_into_view '{"target":{"elementRef":"el_123"}}' # ensure target is visible
88
+ bbx call input.click '{"target":{"elementRef":"el_123"},"executionMode":"cdp"}' # optional native click
86
89
  bbx patch-style <ref> prop=val... # apply style patch
87
90
  bbx patch-text <ref> <text...> # apply text patch
88
91
  bbx patches # list active patches
89
92
  bbx rollback <patchId> # rollback a patch
90
- bbx screenshot <ref> [outPath] # capture partial element screenshot
93
+ bbx screenshot [--format png|jpeg|webp] [--quality 0-100] <ref> [outPath] # complete element capture
91
94
  bbx call screenshot.capture_full_page '{}' # raw base64; avoid unless document context matters
92
95
  ```
93
96
 
@@ -104,6 +107,8 @@ bbx dom-query "#app" --remote <name>
104
107
 
105
108
  In MCP mode, pass `destinationId` on any browser tool call instead; `browser_status` lists all destinations with reachability. Only use remotes the user has configured; local remains the default.
106
109
 
110
+ `bbx doctor` is intentionally local-only. It reports how many remotes are configured, but marks their status `not_probed_local_only` and credentials `unverified`; use `bbx remote test <name>` when the user wants a remote probe.
111
+
107
112
  ## Access Flow
108
113
 
109
114
  Browser Bridge access is window-scoped. The user turns it on once for the current browser window in the popup or side panel.
@@ -132,6 +137,12 @@ After access is enabled:
132
137
  | ------------------------- | -------- | ----------------------------------------------------------------------------------------- |
133
138
  | `ACCESS_DENIED` | No | Failed call already surfaced an `Enable` cue; ask user to click `Enable`, then retry once |
134
139
  | `ELEMENT_STALE` | No | Re-query with `dom.query` or `dom.find_by_text` to get a fresh ref |
140
+ | `ELEMENT_AMBIGUOUS` | No | Narrow the selector or use an explicit `elementRef`; do not guess |
141
+ | `ELEMENT_NOT_ACTIONABLE` | No | Inspect hidden/disabled/inert state with `dom.describe` |
142
+ | `ELEMENT_OBSCURED` | No | Inspect bounded blocker details or use `layout.hit_test` |
143
+ | `INPUT_FOCUS_CHANGED` | No | Inspect focus handlers; do not replay native text automatically |
144
+ | `DIALOG_NOT_OPEN` | No | Trigger or inspect the dialog again |
145
+ | `DIALOG_ACTION_CONFLICT` | No | Inspect current dialog state; never auto-repeat accept/dismiss |
135
146
  | `TAB_MISMATCH` | No | Tab closed or not found - use `tabs.list` to find an available tab |
136
147
  | `TIMEOUT` | Once | Retry once; if still failing, simplify (smaller `maxNodes`, narrower selector) |
137
148
  | `RATE_LIMITED` | After 2s | Back off 2 seconds, then retry |
@@ -157,14 +168,18 @@ Error responses now include a machine-readable `error.recovery` field with `retr
157
168
  9. **Batch reads** - combine independent reads with `bbx batch` in CLI mode or `browser_batch` in MCP mode; do not pass `batch` as a `browser_call` method.
158
169
  10. **Avoid debugger first** - prefer DOM/content-script methods (`dom.*`, `styles.*`, `layout.get_box_model`, `page.get_console`, `page.get_text`, `page.get_storage`, `page.get_network`) before any debugger-backed method. Escalate to CDP only when those cannot answer the question.
159
170
  11. **Evaluate only when needed** - `page.evaluate` is powerful but debugger-backed; use it only when DOM, storage, console, network, or text reads cannot expose the needed state.
160
- 12. **Debugger-backed methods are last resort** - treat `page.evaluate`, `dom.get_accessibility_tree`, `viewport.resize`, `performance.get_metrics`, `screenshot.capture_*`, and all `cdp.*` methods as escalation steps because they attach `chrome.debugger`.
171
+ 12. **Debugger-backed methods are last resort** - treat `page.evaluate`, `page.handle_dialog`, `dom.get_accessibility_tree`, `page.get_network` with `source: 'cdp'`, input with `executionMode: 'cdp'`, `viewport.resize`, `performance.get_metrics`, `screenshot.capture_*`, and all `cdp.*` methods as escalation steps because they attach `chrome.debugger`.
161
172
  13. **Wait after change** - after editing source, wait for expected new text, a selector matching the changed attribute, or a detach/attach remount; waiting for `attached` on a selector that already exists does not prove HMR ran. Use `page.wait_for_load_state` for navigation, not HMR.
162
- 14. **Prime event buffers** - before reproducing a console or network issue, call `page.get_console` and `page.get_network` once with `clear: true`; then reproduce the event and read each buffer without clearing it.
173
+ 14. **Prime event buffers** - before reproducing a console or fetch/XHR issue, call `page.get_console` and default `page.get_network` once with `clear: true`; then reproduce and read without clearing. CDP all-resource capture instead requires explicit `start`, reproduce, `read`, and `stop` calls.
163
174
  15. **Semantic finding** - use `dom.find_by_text` / `dom.find_by_role` when you know the label but not the selector.
164
- 16. **Text extraction** - use `page.get_text` for full page text instead of `dom.query` on body.
175
+ 16. **Text extraction** - use `page.extract_content` for articles/documentation, or `page.get_text` for all visible UI text, instead of `dom.query` on body.
165
176
  17. **Network monitoring** - use `page.get_network` to inspect API calls; auto-installs interceptor.
166
177
  18. **Accessibility tree only when necessary** - `dom.get_accessibility_tree` is debugger-backed; use it when semantic structure cannot be inferred from DOM queries and role/text search.
167
178
  19. **Tailwind-aware** - when `page.get_state` returns `hints.tailwind: true`, load `references/tailwind.md`; avoid selecting by utility classes, prefer `find_by_text`/`find_by_role`; `dom.query` auto-escapes `[]` brackets.
179
+ 20. **Verify after input** - a dispatched DOM or CDP event does not prove application state changed. Follow it with the cheapest relevant wait or structured read.
180
+ 21. **Do not guess input targets** - for targeted click, focus, type, fill, press-key, checked-state, option-selection, hover, and drag calls, honor actionability errors and returned `resolution` metadata. `cdp_press_key` and `scroll_into_view` use separate contracts. `executionMode` is only `dom` or `cdp`; `input.fill.mode: auto` is a separate DOM setter/keystroke strategy.
181
+ 22. **Stale recovery stays opt-in** - prefer re-querying. Use `recoverStale: true` once only when the same document/URL and a strong unique semantic descriptor should still identify the target; inspect `recovered`, old/new refs, and matched fields. A scan with more than 100 same-tag candidates fails as `ELEMENT_AMBIGUOUS`/`scan_incomplete` because uniqueness is not provable.
182
+ 23. **Dialogs are explicit** - inspect first, then accept or dismiss only when intended. `expectedDialogId` is a pre-dispatch stale-decision check, not an atomic CDP binding; never auto-repeat `DIALOG_ACTION_CONFLICT`.
168
183
 
169
184
  ## Token Budget Quick Rules
170
185
 
@@ -172,7 +187,7 @@ Error responses now include a machine-readable `error.recovery` field with `retr
172
187
  2. **Use `attributeAllowlist`** - filter irrelevant attributes (e.g. `['class', 'href', 'data-testid']`)
173
188
  3. **Batch independent reads** - combine into a single `bbx batch` / `browser_batch` call
174
189
  4. **Refresh refs after pruning** - if `dom.query` returns `_registryPruned: true`, old refs may have been evicted; re-query before reusing them
175
- 5. **Watch overflow counters** - `page.get_console` and `page.get_network` return `dropped` when hot pages overflow the 200-entry buffers
190
+ 5. **Watch overflow counters** - `page.get_console` and `page.get_network` return `dropped` when hot pages overflow the 200-entry buffers; CDP network also reports `abandoned` and capture lifecycle state
176
191
 
177
192
  ## Investigate Workflow
178
193
 
@@ -235,7 +250,8 @@ bbx page-text 2000 # extract page content
235
250
  | Inspect | `dom.query`, `dom.describe`, `dom.get_html`, `styles.get_computed`, `layout.get_box_model` |
236
251
  | Find | `dom.find_by_text`, `dom.find_by_role`, `dom.wait_for`, `dom.get_accessibility_tree` |
237
252
  | Page State | `page.get_console`, `page.get_storage`, `page.get_text`, `page.wait_for_load_state`, `page.evaluate` (debugger-backed) |
238
- | Network | `page.get_network`, `network.intercept.add/remove/list/clear` (debugger-backed; rules are in-memory and drop if the debugger detaches - verify with `list`) |
253
+ | Dialogs | `page.handle_dialog` (debugger-backed; explicit inspect/accept/dismiss only) |
254
+ | Network | `page.get_network` (fetch/XHR default; optional CDP lifecycle), `network.intercept.add/remove/list/clear` (debugger-backed) |
239
255
  | Interact | `input.click`, `input.type`, `input.fill`, `input.focus`, `input.press_key`, `cdp.dispatch_key_event`, `input.hover`, `input.drag`, `input.scroll_into_view` |
240
256
  | Tabs | `tabs.list` (preferred), `tabs.create` (avoid unless necessary), `tabs.close`, `tabs.activate` |
241
257
  | Patch | `patch.apply_styles`, `patch.apply_dom`, `patch.rollback` |
@@ -273,8 +289,8 @@ page.get_state → page.get_console(clear: true) + page.get_network(clear: true)
273
289
 
274
290
  ```
275
291
  dom.find_by_role('button', 'Login') → input.click
292
+ → page.wait_for_load_state({url: '/dashboard', urlMatch: 'contains', waitForLoad: false})
276
293
  → dom.wait_for('.dashboard', {state: 'visible', timeoutMs: 10000})
277
- → page.get_state (verify URL changed)
278
294
  → page.get_console (check for errors)
279
295
  → dom.query('.dashboard', {maxNodes: 15}) (inspect result)
280
296
  ```
@@ -291,6 +307,8 @@ dom.find_by_role('button', 'Login') → input.click
291
307
 
292
308
  `bbx a11y-tree` and `dom.get_accessibility_tree` are sensitive to `maxDepth` and `maxNodes`. Shallow runs can undercount interactive nodes on real pages, so widen those limits before treating a low interactive count as a bug.
293
309
 
310
+ For control discovery, prefer `compact: true` or `interactiveOnly: true`, then use role/name with `dom.find_by_role` to obtain an input ref. AX `interactive` means semantic/focusable, not currently visible, unobscured, or otherwise actionable; AX results are depth-limited partial topology and include truncation metadata even when `maxNodes` was not reached.
311
+
294
312
  > **MCP mode:** If Browser Bridge is connected via MCP (tools named `browser_dom`, `browser_capture`, etc.), use the MCP tools directly - do not shell out to `bbx`. The MCP tools map 1:1 to CLI capabilities. In prompts, `BB MCP` and `Browser Bridge MCP` both work. Do not treat `bbx-mcp` as a skill alias.
295
313
  >
296
314
  > For open-ended MCP inspection tasks, prefer `browser_investigate` first. It is read-only, designed for cheaper delegated investigation, and falls back to a deterministic sequence when the client cannot delegate. Escalate to `browser_capture` only when the structured investigation is not enough.
@@ -306,7 +324,7 @@ Every CLI shortcut command produces consistent `{ok, summary, evidence}` JSON. U
306
324
  ## CLI Raw Params Gotchas
307
325
 
308
326
  - Use `selector`, not `scope`, to narrow `dom.find_by_text` and `dom.find_by_role`.
309
- - Wrap interaction targets as `target: { elementRef }` or `target: { selector }`; `viewport.scroll` also uses the `target` wrapper for element scrolling. **Do not pass `ref` or `elementRef` at the top level** -- interaction methods require the nested `target` wrapper:
327
+ - Wrap targeted DOM input as `target: { elementRef }` or `target: { selector }`; `input.press_key` may omit the target to use the active element, `input.scroll_into_view` also accepts `target`, and `cdp.dispatch_key_event` targets the tab. **Do not pass `ref` or `elementRef` at the top level**:
310
328
  ```bash
311
329
  # CORRECT
312
330
  bbx call input.click '{"target":{"elementRef":"el_xxx"}}'
@@ -316,6 +334,9 @@ Every CLI shortcut command produces consistent `{ok, summary, evidence}` JSON. U
316
334
  bbx call input.click '{"elementRef":"el_xxx"}'
317
335
  ```
318
336
  - `input.drag` uses `source`, `destination`, and optional destination offsets `offsetX` / `offsetY`.
337
+ - `executionMode` accepts `dom` or `cdp` and defaults to `dom`; CDP supports only click, hover, drag, type, and fill. `input.fill.mode` (`auto`, `setter`, `keystrokes`) is a separate DOM strategy.
338
+ - `page.get_network` defaults to low-cost fetch/XHR instrumentation. All-resource CDP capture must be armed before the activity with `source: "cdp", capture: "start"`, read later, and explicitly stopped.
339
+ - `page.handle_dialog` mutations are not atomically bound to `expectedDialogId`; treat `commandDispatched` as dispatch evidence and verify follow-up state.
319
340
  - Raw `screenshot.capture_region` and `screenshot.capture_full_page` return base64 JSON; prefer `bbx screenshot <ref> [outPath]` when one element is enough.
320
341
 
321
342
  ## Response Shapes
@@ -324,7 +345,7 @@ The summarizer auto-detects response types and produces concise summaries:
324
345
 
325
346
  Shortcut commands intentionally expose only the common case. Use `bbx call <method> '{...}'` when you need method-specific fields that are not surfaced by a shortcut, such as `tabs.create.active`.
326
347
 
327
- `dom.query` results include `registrySize` and may include `_registryPruned: true` after element-ref eviction. `page.get_console` and `page.get_network` include `dropped` when older buffered entries were discarded.
348
+ `dom.query` results include `registrySize` and may include `_registryPruned: true` after element-ref eviction. `page.get_console` and `page.get_network` include `dropped` when older buffered entries were discarded. Targeted click, focus, type, fill, press-key, checked-state, option-selection, hover, and drag results include bounded `resolution` and `execution` metadata; `cdp_press_key` and `scroll_into_view` use separate contracts.
328
349
 
329
350
  | Response Type | Detection | Summary Format |
330
351
  | ---------------- | -------------------------------- | ------------------------------------------------------------ |
@@ -338,7 +359,7 @@ Shortcut commands intentionally expose only the common case. Use `bbx call <meth
338
359
  | Element describe | `result.tag + elementRef + bbox` | `Element tag#id: text.` |
339
360
  | Computed styles | `result.properties + elementRef` | `Computed N style(s) for ref.` |
340
361
  | Box model | `result.content + border` | `Box model: W×H at (x, y).` |
341
- | Network | `entries[0].type=fetch/xhr` | `Network: N requests.` |
362
+ | Network | `result.source + entries` | `Network: N requests.` |
342
363
  | Console | `entries` (no type field) | `Console: N entries.` |
343
364
  | Logs | `entries[0].at + method` | `Log: N entries.` |
344
365
  | Patch apply | `result.patchId` | `Patch id applied.` |