@jobshimo/browser-link 0.29.0 → 0.31.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 (51) hide show
  1. package/dist/activity/extract.js +1 -0
  2. package/dist/activity/extract.js.map +1 -1
  3. package/dist/agent-instructions/content.js +12 -0
  4. package/dist/agent-instructions/content.js.map +1 -1
  5. package/dist/bridge/dispatch.js +5 -3
  6. package/dist/bridge/dispatch.js.map +1 -1
  7. package/dist/cdp/expect.d.ts +95 -0
  8. package/dist/cdp/expect.js +197 -0
  9. package/dist/cdp/expect.js.map +1 -0
  10. package/dist/cdp/flow.d.ts +7 -0
  11. package/dist/cdp/flow.js +4 -47
  12. package/dist/cdp/flow.js.map +1 -1
  13. package/dist/cdp/inpage/builders.d.ts +39 -0
  14. package/dist/cdp/inpage/builders.js +136 -0
  15. package/dist/cdp/inpage/builders.js.map +1 -1
  16. package/dist/cdp/screenshot.d.ts +109 -0
  17. package/dist/cdp/screenshot.js +189 -0
  18. package/dist/cdp/screenshot.js.map +1 -0
  19. package/dist/cdp/support.js +1 -0
  20. package/dist/cdp/support.js.map +1 -1
  21. package/dist/cdp/transport.js +43 -1
  22. package/dist/cdp/transport.js.map +1 -1
  23. package/dist/commands/about.js +2 -0
  24. package/dist/commands/about.js.map +1 -1
  25. package/dist/extension/background.js +53 -1
  26. package/dist/extension/background.js.map +1 -1
  27. package/dist/extension/expect.d.ts +95 -0
  28. package/dist/extension/expect.js +203 -0
  29. package/dist/extension/expect.js.map +1 -0
  30. package/dist/extension/flow.d.ts +7 -0
  31. package/dist/extension/flow.js +4 -51
  32. package/dist/extension/flow.js.map +1 -1
  33. package/dist/extension/inpage/builders.d.ts +39 -0
  34. package/dist/extension/inpage/builders.js +136 -0
  35. package/dist/extension/inpage/builders.js.map +1 -1
  36. package/dist/extension/manifest.json +1 -1
  37. package/dist/extension/screenshot.d.ts +113 -0
  38. package/dist/extension/screenshot.js +193 -0
  39. package/dist/extension/screenshot.js.map +1 -0
  40. package/dist/permissions.js +6 -0
  41. package/dist/permissions.js.map +1 -1
  42. package/dist/tools/browser-definitions.js +105 -3
  43. package/dist/tools/browser-definitions.js.map +1 -1
  44. package/dist/tools/browser-dispatch.js +129 -9
  45. package/dist/tools/browser-dispatch.js.map +1 -1
  46. package/dist/tools/responses.d.ts +14 -2
  47. package/dist/tools/responses.js +21 -0
  48. package/dist/tools/responses.js.map +1 -1
  49. package/dist/tools/server-instructions.js +18 -0
  50. package/dist/tools/server-instructions.js.map +1 -1
  51. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "browser-link",
4
- "version": "0.29.0",
4
+ "version": "0.31.0",
5
5
  "description": "Bridge between Chrome and an MCP client (Claude Code, OpenCode, GitHub Copilot CLI, …). Per-tab manual activation.",
6
6
  "permissions": [
7
7
  "debugger",
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `browser.screenshot` planning and capture, shared by the extension
3
+ * (`chrome.debugger`) and — as a verbatim copy at
4
+ * `packages/server/src/cdp/screenshot.ts`, guarded by `cdp/drift.test.ts` —
5
+ * the cdp-direct transport. Both speak raw CDP, so the logic is identical;
6
+ * it only needs a `send(method, params)` primitive and an element-rect
7
+ * resolver, which makes it unit-testable without Chrome.
8
+ *
9
+ * Coordinates: `Page.captureScreenshot`'s `clip` is in CSS pixels of the
10
+ * page (document) space; `scale` multiplies it into output pixels.
11
+ */
12
+ export type ScreenshotMode = 'viewport' | 'full_page' | 'element';
13
+ export type ScreenshotFormat = 'png' | 'jpeg';
14
+ export declare const DEFAULT_MAX_DIMENSION = 8000;
15
+ /** Below this scale the text in a page is unreadable; crop instead. */
16
+ export declare const MIN_SCALE = 0.25;
17
+ /** Cap on the decoded image size returned to the agent. */
18
+ export declare const MAX_IMAGE_BYTES: number;
19
+ export declare const DEFAULT_JPEG_QUALITY = 80;
20
+ export interface Size {
21
+ width: number;
22
+ height: number;
23
+ }
24
+ export interface Rect extends Size {
25
+ x: number;
26
+ y: number;
27
+ }
28
+ export interface LayoutMetrics {
29
+ cssContentSize?: Size;
30
+ contentSize?: Size;
31
+ cssVisualViewport?: {
32
+ pageX: number;
33
+ pageY: number;
34
+ clientWidth: number;
35
+ clientHeight: number;
36
+ };
37
+ visualViewport?: {
38
+ pageX: number;
39
+ pageY: number;
40
+ clientWidth: number;
41
+ clientHeight: number;
42
+ };
43
+ /** Layout viewport in device pixels / CSS pixels — their ratio is the DPR. */
44
+ layoutViewport?: {
45
+ clientWidth: number;
46
+ };
47
+ cssLayoutViewport?: {
48
+ clientWidth: number;
49
+ };
50
+ }
51
+ export interface ScreenshotPlan {
52
+ clip: Rect & {
53
+ scale: number;
54
+ };
55
+ /** Output size in pixels. */
56
+ width: number;
57
+ height: number;
58
+ scale: number;
59
+ /** Device pixels per CSS pixel; the capture is clip x scale x dpr pixels. */
60
+ dpr: number;
61
+ truncated: boolean;
62
+ }
63
+ /**
64
+ * Device pixels per CSS pixel, derived from the layout metrics (no extra
65
+ * in-page round trip): layout-viewport width ratio, else content-size
66
+ * ratio, else 1.
67
+ */
68
+ export declare function dprFromMetrics(m: LayoutMetrics): number;
69
+ export declare function planClip(mode: ScreenshotMode, metrics: LayoutMetrics, elementRect: Rect | undefined, maxDimension: number): ScreenshotPlan;
70
+ /** Decoded byte length of a base64 string. */
71
+ export declare function base64ByteLength(b64: string): number;
72
+ export interface Attempt {
73
+ format: ScreenshotFormat;
74
+ scale: number;
75
+ quality: number | undefined;
76
+ }
77
+ /**
78
+ * What to try after an image came back over the byte cap: PNG → JPEG at the
79
+ * same scale, then JPEG at a scale reduced in proportion to the overshoot.
80
+ * `null` when nothing smaller is worth trying.
81
+ */
82
+ export declare function nextOversizeAttempt(prev: Attempt, bytes: number, cap: number): Attempt | null;
83
+ export type ElementRectOutcome = ({
84
+ ok: true;
85
+ } & Rect) | {
86
+ ok: false;
87
+ reason: 'invalid-selector' | 'not-found';
88
+ error?: string;
89
+ };
90
+ export interface ScreenshotDeps {
91
+ send: (method: string, params?: Record<string, unknown>) => Promise<unknown>;
92
+ resolveElementRect: (selector: string) => Promise<ElementRectOutcome>;
93
+ }
94
+ export interface ScreenshotOptions {
95
+ mode?: ScreenshotMode;
96
+ selector?: string;
97
+ format?: ScreenshotFormat;
98
+ quality?: number;
99
+ maxDimension?: number;
100
+ }
101
+ export interface ScreenshotResult {
102
+ ok: true;
103
+ mode: ScreenshotMode;
104
+ format: ScreenshotFormat;
105
+ width: number;
106
+ height: number;
107
+ scale: number;
108
+ bytes: number;
109
+ truncated?: true;
110
+ size_reduced?: true;
111
+ image_b64: string;
112
+ }
113
+ export declare function runScreenshot(deps: ScreenshotDeps, opts: ScreenshotOptions): Promise<ScreenshotResult>;
@@ -0,0 +1,193 @@
1
+ /**
2
+ * `browser.screenshot` planning and capture, shared by the extension
3
+ * (`chrome.debugger`) and — as a verbatim copy at
4
+ * `packages/server/src/cdp/screenshot.ts`, guarded by `cdp/drift.test.ts` —
5
+ * the cdp-direct transport. Both speak raw CDP, so the logic is identical;
6
+ * it only needs a `send(method, params)` primitive and an element-rect
7
+ * resolver, which makes it unit-testable without Chrome.
8
+ *
9
+ * Coordinates: `Page.captureScreenshot`'s `clip` is in CSS pixels of the
10
+ * page (document) space; `scale` multiplies it into output pixels.
11
+ */
12
+ export const DEFAULT_MAX_DIMENSION = 8000;
13
+ /** Below this scale the text in a page is unreadable; crop instead. */
14
+ export const MIN_SCALE = 0.25;
15
+ /** Hard floor when shrinking to satisfy the byte cap. */
16
+ const ABSOLUTE_MIN_SCALE = 0.1;
17
+ /** Cap on the decoded image size returned to the agent. */
18
+ export const MAX_IMAGE_BYTES = 8 * 1024 * 1024;
19
+ export const DEFAULT_JPEG_QUALITY = 80;
20
+ const MAX_ATTEMPTS = 4;
21
+ /**
22
+ * Device pixels per CSS pixel, derived from the layout metrics (no extra
23
+ * in-page round trip): layout-viewport width ratio, else content-size
24
+ * ratio, else 1.
25
+ */
26
+ export function dprFromMetrics(m) {
27
+ const pairs = [
28
+ [m.layoutViewport?.clientWidth, m.cssLayoutViewport?.clientWidth],
29
+ [m.contentSize?.width, m.cssContentSize?.width],
30
+ [m.visualViewport?.clientWidth, m.cssVisualViewport?.clientWidth],
31
+ ];
32
+ for (const [device, css] of pairs) {
33
+ if (positive(device) && positive(css))
34
+ return device / css;
35
+ }
36
+ return 1;
37
+ }
38
+ function positive(n) {
39
+ return typeof n === 'number' && Number.isFinite(n) && n > 0;
40
+ }
41
+ export function planClip(mode, metrics, elementRect, maxDimension) {
42
+ const dpr = dprFromMetrics(metrics);
43
+ let region;
44
+ if (mode === 'element') {
45
+ if (!elementRect)
46
+ throw new Error('screenshot: element rect missing for element mode');
47
+ region = { ...elementRect };
48
+ }
49
+ else if (mode === 'viewport') {
50
+ // Device-pixel fallback values are converted to the CSS pixels clip uses.
51
+ const css = metrics.cssVisualViewport;
52
+ const dev = metrics.visualViewport;
53
+ if (css) {
54
+ region = { x: css.pageX, y: css.pageY, width: css.clientWidth, height: css.clientHeight };
55
+ }
56
+ else if (dev) {
57
+ region = {
58
+ x: dev.pageX / dpr,
59
+ y: dev.pageY / dpr,
60
+ width: dev.clientWidth / dpr,
61
+ height: dev.clientHeight / dpr,
62
+ };
63
+ }
64
+ else {
65
+ throw new Error('screenshot: layout metrics have no viewport');
66
+ }
67
+ }
68
+ else {
69
+ const size = metrics.cssContentSize
70
+ ? metrics.cssContentSize
71
+ : metrics.contentSize
72
+ ? { width: metrics.contentSize.width / dpr, height: metrics.contentSize.height / dpr }
73
+ : undefined;
74
+ if (!size)
75
+ throw new Error('screenshot: layout metrics have no content size');
76
+ region = { x: 0, y: 0, width: size.width, height: size.height };
77
+ }
78
+ if (!positive(region.width) || !positive(region.height)) {
79
+ throw new Error(`screenshot: ${mode} region is zero-sized — nothing to capture`);
80
+ }
81
+ // The output is clip x scale x dpr DEVICE pixels, so max_dimension bounds
82
+ // the longest side in device pixels.
83
+ let scale = Math.min(1, maxDimension / (Math.max(region.width, region.height) * dpr));
84
+ let truncated = false;
85
+ if (scale < MIN_SCALE) {
86
+ scale = MIN_SCALE;
87
+ const limit = maxDimension / (MIN_SCALE * dpr);
88
+ if (region.width > limit)
89
+ region.width = limit;
90
+ if (region.height > limit)
91
+ region.height = limit;
92
+ truncated = true;
93
+ }
94
+ return {
95
+ clip: { ...region, scale },
96
+ width: Math.max(1, Math.round(region.width * scale * dpr)),
97
+ height: Math.max(1, Math.round(region.height * scale * dpr)),
98
+ scale,
99
+ dpr,
100
+ truncated,
101
+ };
102
+ }
103
+ /** Decoded byte length of a base64 string. */
104
+ export function base64ByteLength(b64) {
105
+ if (b64.length === 0)
106
+ return 0;
107
+ let padding = 0;
108
+ if (b64.endsWith('=='))
109
+ padding = 2;
110
+ else if (b64.endsWith('='))
111
+ padding = 1;
112
+ return Math.floor((b64.length * 3) / 4) - padding;
113
+ }
114
+ /**
115
+ * What to try after an image came back over the byte cap: PNG → JPEG at the
116
+ * same scale, then JPEG at a scale reduced in proportion to the overshoot.
117
+ * `null` when nothing smaller is worth trying.
118
+ */
119
+ export function nextOversizeAttempt(prev, bytes, cap) {
120
+ if (prev.format === 'png') {
121
+ return { format: 'jpeg', scale: prev.scale, quality: prev.quality ?? DEFAULT_JPEG_QUALITY };
122
+ }
123
+ if (prev.scale <= ABSOLUTE_MIN_SCALE)
124
+ return null;
125
+ const next = Math.max(ABSOLUTE_MIN_SCALE, prev.scale * Math.sqrt(cap / bytes) * 0.85);
126
+ return { format: 'jpeg', scale: next, quality: prev.quality ?? DEFAULT_JPEG_QUALITY };
127
+ }
128
+ export async function runScreenshot(deps, opts) {
129
+ const mode = opts.mode ?? 'viewport';
130
+ const maxDimension = opts.maxDimension ?? DEFAULT_MAX_DIMENSION;
131
+ let elementRect;
132
+ if (mode === 'element') {
133
+ if (!opts.selector)
134
+ throw new Error('screenshot: selector is required for element mode');
135
+ const r = await deps.resolveElementRect(opts.selector);
136
+ if (!r.ok) {
137
+ if (r.reason === 'invalid-selector') {
138
+ throw new Error(`Invalid selector "${opts.selector}": ${r.error ?? 'syntax error'}`);
139
+ }
140
+ throw new Error(`Element not found: ${opts.selector}`);
141
+ }
142
+ elementRect = { x: r.x, y: r.y, width: r.width, height: r.height };
143
+ }
144
+ // Always read the metrics: besides the page size they carry the DPR the
145
+ // output dimensions depend on.
146
+ const metrics = (await deps.send('Page.getLayoutMetrics'));
147
+ const plan = planClip(mode, metrics, elementRect, maxDimension);
148
+ let attempt = {
149
+ format: opts.format ?? 'png',
150
+ scale: plan.scale,
151
+ quality: opts.quality,
152
+ };
153
+ let reduced = false;
154
+ let lastBytes = 0;
155
+ for (let i = 0; i < MAX_ATTEMPTS; i++) {
156
+ const params = {
157
+ format: attempt.format,
158
+ captureBeyondViewport: mode !== 'viewport',
159
+ clip: { ...plan.clip, scale: attempt.scale },
160
+ };
161
+ if (attempt.format === 'jpeg' && attempt.quality !== undefined) {
162
+ params.quality = attempt.quality;
163
+ }
164
+ const res = (await deps.send('Page.captureScreenshot', params));
165
+ const bytes = base64ByteLength(res.data);
166
+ lastBytes = bytes;
167
+ if (bytes <= MAX_IMAGE_BYTES) {
168
+ const result = {
169
+ ok: true,
170
+ mode,
171
+ format: attempt.format,
172
+ width: Math.max(1, Math.round(plan.clip.width * attempt.scale * plan.dpr)),
173
+ height: Math.max(1, Math.round(plan.clip.height * attempt.scale * plan.dpr)),
174
+ scale: attempt.scale,
175
+ bytes,
176
+ image_b64: res.data,
177
+ };
178
+ if (plan.truncated)
179
+ result.truncated = true;
180
+ if (reduced)
181
+ result.size_reduced = true;
182
+ return result;
183
+ }
184
+ const next = nextOversizeAttempt(attempt, bytes, MAX_IMAGE_BYTES);
185
+ if (!next)
186
+ break;
187
+ attempt = next;
188
+ reduced = true;
189
+ }
190
+ const mb = (n) => (n / (1024 * 1024)).toFixed(1);
191
+ throw new Error(`screenshot: image is ${mb(lastBytes)} MB, which exceeds the ${mb(MAX_IMAGE_BYTES)} MB cap even after reducing quality and scale — use mode "element", a smaller max_dimension, or jpeg`);
192
+ }
193
+ //# sourceMappingURL=screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"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"}
@@ -53,6 +53,12 @@ export const TOOL_CATALOGUE = [
53
53
  category: 'read',
54
54
  summary: 'Screenshot a <canvas> as PNG/JPEG — for Qt-WASM, WebGL and other DOMless surfaces',
55
55
  },
56
+ {
57
+ name: 'browser.screenshot',
58
+ family: 'bridge',
59
+ category: 'read',
60
+ summary: 'Screenshot the viewport, the FULL page or one element as PNG/JPEG',
61
+ },
56
62
  {
57
63
  name: 'browser.console',
58
64
  family: 'bridge',
@@ -1 +1 @@
1
- {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,40 @@ const SETTLE_SCHEMA_PROPERTIES = {
29
29
  description: 'Overall cap on the settle wait in ms, in case the page never goes fully quiet (polling animations, live tickers, spinners). Default 2000, hard ceiling 10000. Ignored when settle_ms is 0.',
30
30
  },
31
31
  };
32
+ /**
33
+ * Shared `expect` schema for click / type / press and the matching
34
+ * browser.flow steps. Defined once so the wording cannot drift.
35
+ */
36
+ const EXPECT_SCHEMA = {
37
+ type: 'object',
38
+ description: 'Assert the EFFECT of the action, not just that the page went quiet. After the action dispatches (and settles), the page is polled until every listed condition holds or `timeout_ms` elapses. Unmet: ok:false with `expectation not met: <which> after <ms>ms` plus the settle info (inside a browser.flow it fails that step fail-fast like any other failure). Met: the result carries `expect: { met: true, elapsed_ms }`. Provide at least one condition; all given conditions must hold. appears/disappears conditions are TRANSITIONS from the state sampled right before the action; one that already held beforehand fails immediately.',
39
+ properties: {
40
+ url_changed: {
41
+ type: 'boolean',
42
+ description: 'location.href differs from its value captured right before the action was dispatched.',
43
+ },
44
+ selector_appears: {
45
+ type: 'string',
46
+ description: 'A VISIBLE element matching this CSS selector exists (pierces open Shadow DOM and same-origin iframes, like click/find).',
47
+ },
48
+ selector_disappears: {
49
+ type: 'string',
50
+ description: 'No VISIBLE element matching this CSS selector remains (same deep search).',
51
+ },
52
+ text_appears: {
53
+ type: 'string',
54
+ description: 'This text is visible somewhere on the page (case-insensitive, whitespace-collapsed, deep search).',
55
+ },
56
+ timeout_ms: {
57
+ type: 'number',
58
+ minimum: 1,
59
+ maximum: 10000,
60
+ description: 'Expectation window in ms. Default 3000, hard ceiling 10000 (out-of-range is rejected).',
61
+ },
62
+ },
63
+ additionalProperties: false,
64
+ };
65
+ const EXPECT_GOTCHA = 'Settle proves QUIET, not EFFECT: a click whose handler never fired returns ok:true with settle { mutation_count: 0, settled: true }, indistinguishable from success until a later step fails. When you are counting on a specific effect (navigation, a dialog opening/closing, a result appearing), pass `expect` — e.g. { url_changed: true } or { selector_appears: "[role=dialog]" } — so a no-op action fails HERE with `expectation not met` instead of costing a timeout later. `expect` asserts a TRANSITION: the page is sampled right before the action, `selector_appears` / `text_appears` must be absent beforehand and present afterwards, `selector_disappears` must be present beforehand and gone afterwards, and a condition that already held before the action fails immediately (e.g. `selector_appears "[role=dialog]" was already present before the action`) because it cannot prove the action did anything. `url_changed` compares against the URL sampled right before THIS action, so a navigation still pending from a PREVIOUS step can satisfy it: when chaining navigations, confirm the earlier one landed (an `expect` on that step or a browser.wait_for) first. Prefer `expect` over a following browser.wait_for for the action\'s own consequence; it also works over cdp-direct tabs.';
32
66
  export const BROWSER_TOOL_DEFINITIONS = [
33
67
  {
34
68
  name: 'browser.list_tabs',
@@ -313,6 +347,62 @@ export const BROWSER_TOOL_DEFINITIONS = [
313
347
  example: 'browser.canvas_screenshot({ tab_id: "tab_1" }) // first visible canvas, full size, PNG',
314
348
  },
315
349
  },
350
+ {
351
+ name: 'browser.screenshot',
352
+ description: 'Take a real screenshot of a tab and return it as an MCP image (plus a JSON text block with the metadata). `mode: "viewport"` (default) captures what is visible now; `mode: "full_page"` captures the WHOLE document from top to bottom in one image, without scrolling or stitching; `mode: "element"` captures one element (pass `selector`; the same deep Shadow DOM / same-origin iframe search as click/find, scrolled into view first). Unlike `browser.canvas_screenshot` this renders the page as the browser paints it, so it works on any page, not just a `<canvas>`. Returns `{ ok, mode, format, width, height, scale, bytes, truncated? , size_reduced? }` plus the image.',
353
+ inputSchema: {
354
+ type: 'object',
355
+ properties: {
356
+ tab_id: { type: 'string' },
357
+ mode: {
358
+ type: 'string',
359
+ enum: ['viewport', 'full_page', 'element'],
360
+ description: 'What to capture. "viewport" (default): the visible area. "full_page": the entire scrollable document. "element": the element matched by `selector`.',
361
+ },
362
+ selector: {
363
+ type: 'string',
364
+ description: 'CSS selector of the element to capture. Required for mode "element"; rejected for the other modes.',
365
+ },
366
+ format: {
367
+ type: 'string',
368
+ enum: ['png', 'jpeg'],
369
+ description: 'Output format. PNG (default) is lossless; JPEG is much smaller for photo-heavy pages. If a PNG exceeds the ~8 MB cap the tool retries as JPEG automatically (reported in `format` and `size_reduced`).',
370
+ },
371
+ quality: {
372
+ type: 'integer',
373
+ minimum: 0,
374
+ maximum: 100,
375
+ description: 'JPEG quality 0-100 (default 80 when JPEG is used). Ignored for PNG.',
376
+ },
377
+ max_dimension: {
378
+ type: 'integer',
379
+ minimum: 100,
380
+ maximum: 16000,
381
+ description: 'Longest side of the output image in pixels (default 8000). Larger regions are scaled down to fit; pages so tall that the scale would drop below 0.25 are cropped instead and flagged `truncated: true`.',
382
+ },
383
+ },
384
+ required: ['tab_id'],
385
+ additionalProperties: false,
386
+ },
387
+ doc: {
388
+ purpose: 'Let the agent SEE a page: the current viewport, the entire page in one image, or a single element — for visual checks, layout bugs and "does it look right" questions.',
389
+ when_to_use: [
390
+ 'To verify a layout or styling change visually, or when the user says the page "looks wrong" / "se ve mal".',
391
+ 'To capture a whole long page (landing page, article, report) without scrolling: `mode: "full_page"`.',
392
+ 'To look at one component (a card, a chart, a dialog) at full fidelity without the surrounding noise: `mode: "element"`.',
393
+ ],
394
+ gotchas: [
395
+ 'Images cost tokens. Prefer "element" or a smaller `max_dimension` when you only need to inspect one area; use JPEG for photo-heavy pages.',
396
+ 'Very tall pages are scaled down to fit `max_dimension`; below a 0.25 scale the page is cropped from the top and `truncated: true` is set — use "element" for the rest.',
397
+ 'The result is capped at ~8 MB. A PNG over the cap is retried as JPEG, then at a lower scale; if nothing fits the call fails with the measured size.',
398
+ 'Lazy-loaded content that only renders on scroll is NOT triggered by a full-page capture; scroll the page (e.g. `browser.evaluate`) first if images below the fold are blank.',
399
+ '"element" mode scrolls the element into view first (which can change the page scroll position) and clips to its border box. Content inside cross-origin iframes or closed shadow roots is painted in the image but cannot be targeted by `selector`.',
400
+ 'Known limitation: an "element" larger than the visible area of its iframe or scrollable/overflow container is only painted as far as that container shows it — the rest of the element is blank in the image. Capture the container instead, or use "full_page" when the element is not inside a nested scroller.',
401
+ 'This is a read tool: it does not claim the tab. For a `<canvas>` whose pixels you want exactly as drawn, `browser.canvas_screenshot` is still the right tool.',
402
+ ],
403
+ example: 'browser.screenshot({ tab_id: "tab_1", mode: "full_page", format: "jpeg", quality: 70 })',
404
+ },
405
+ },
316
406
  {
317
407
  name: 'browser.console',
318
408
  description: 'Return recent console messages (log, info, warn, error) from the connected tab since it was attached. Rolling buffer (last 200).',
@@ -608,6 +698,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
608
698
  description: 'Skip the occlusion guard and dispatch the click even if another element currently covers the click point. Escape hatch for cases where the "covering" element is intentional (e.g. a transparent hit-target layer) or the guard produces a false positive. Default false.',
609
699
  },
610
700
  ...SETTLE_SCHEMA_PROPERTIES,
701
+ expect: EXPECT_SCHEMA,
611
702
  },
612
703
  required: ['tab_id', 'selector'],
613
704
  additionalProperties: false,
@@ -625,7 +716,9 @@ export const BROWSER_TOOL_DEFINITIONS = [
625
716
  'CLOSED shadow roots (attachShadow({mode:"closed"})) are unreachable — there is no CDP-level workaround. Cross-origin iframes are also unreachable (same-origin policy).',
626
717
  'The result carries a compact `settle` object (`{ settled, duration_ms, mutation_count, url_changed?, focus_moved?, reason? }`) unless `settle_ms:0`. `settled:false` means the page never went quiet within `settle_timeout_ms` — not necessarily an error, some pages never stop mutating (tickers, spinners). When the settle wait itself could not run to completion, `settled:false` comes with `reason`: "context-destroyed" (the action navigated the page, destroying the observer\'s execution context — the action still succeeded, and this is itself a strong navigation signal) or "settle-error" (any other settle-side failure). With settle on, a separate browser.wait_for right after a click is usually unnecessary.',
627
718
  'The error message distinguishes a malformed selector ("Invalid selector …") from a well-formed selector that matches nothing ("Element not found …") — a syntax error means fix the selector string itself, not retry against a fresher snapshot.',
719
+ EXPECT_GOTCHA,
628
720
  ],
721
+ example: 'browser.click({ tab_id: "tab_1", selector: "a.next-lesson-link", expect: { url_changed: true, timeout_ms: 5000 } })',
629
722
  },
630
723
  },
631
724
  {
@@ -639,6 +732,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
639
732
  text: { type: 'string' },
640
733
  clear: { type: 'boolean', default: false },
641
734
  ...SETTLE_SCHEMA_PROPERTIES,
735
+ expect: EXPECT_SCHEMA,
642
736
  },
643
737
  required: ['tab_id', 'selector', 'text'],
644
738
  additionalProperties: false,
@@ -654,6 +748,8 @@ export const BROWSER_TOOL_DEFINITIONS = [
654
748
  'For Enter/Tab/Escape/arrow keys after typing (submitting a form, confirming an autocomplete suggestion), use browser.press — dispatchEvent-based synthetic KeyboardEvent via browser.evaluate is NOT trusted input and many rich editors and autocompletes ignore it.',
655
749
  'The result carries a compact `settle` object (same shape as browser.click) unless `settle_ms:0`.',
656
750
  'Same error distinction as browser.click: "Invalid selector …" (malformed CSS) vs "Element not found …" (well-formed, no match).',
751
+ 'Optional `expect` asserts the effect of typing, e.g. { selector_appears: ".autocomplete-option" }. ' +
752
+ EXPECT_GOTCHA,
657
753
  ],
658
754
  },
659
755
  },
@@ -678,6 +774,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
678
774
  description: 'Optional. Focus this element (resolved via the same deep Shadow DOM / iframe search as browser.click) before dispatching the key. Omit to send the key to the currently focused element.',
679
775
  },
680
776
  ...SETTLE_SCHEMA_PROPERTIES,
777
+ expect: EXPECT_SCHEMA,
681
778
  },
682
779
  required: ['tab_id', 'key'],
683
780
  additionalProperties: false,
@@ -695,8 +792,10 @@ export const BROWSER_TOOL_DEFINITIONS = [
695
792
  'Named keys are case-insensitive ("arrowup" works); single printable characters are case-SENSITIVE. Shift IS auto-added for characters that require it on a US layout — uppercase letters, shifted punctuation like "@" or "{" — so to type an uppercase letter just pass key:"A". Do NOT pass key:"a" with modifiers:["Shift"] expecting an "A": that inserts a lowercase "a" with shiftKey reported as held, a combination no physical keyboard produces.',
696
793
  'CLOSED shadow roots and cross-origin iframes are unreachable for the optional `selector`, same as browser.click.',
697
794
  'The result carries a compact `settle` object (same shape as browser.click) unless `settle_ms:0`.',
795
+ 'Optional `expect` asserts the effect of the key, e.g. Enter submitting a form: { url_changed: true }. ' +
796
+ EXPECT_GOTCHA,
698
797
  ],
699
- example: 'browser.press({ tab_id: "tab_1", key: "Enter" })',
798
+ example: 'browser.press({ tab_id: "tab_1", key: "Enter", expect: { selector_appears: ".results" } })',
700
799
  },
701
800
  },
702
801
  {
@@ -756,7 +855,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
756
855
  },
757
856
  {
758
857
  name: 'browser.flow',
759
- description: 'Run a declarative sequence of steps (find/click/type/press/wait_for/drag/sleep/repeat) in ONE round trip instead of one MCP call per step. Steps execute strictly in order and STOP at the first failure (fail-fast) — there is no branching. A `find` step\'s resolved selector becomes the IMPLICIT TARGET for the very next click/type/press step that omits its own `selector` (an explicit `selector` on that step always overrides it and leaves the implicit target untouched for a later step). `type` and `press` steps that end up with no selector at all — no explicit one, no implicit one — act on whatever currently has focus, same as standalone browser.press; only `click` requires a resolved target and fails the flow if none is available. A `drag` step runs a full drag-and-drop gesture with the SAME params as standalone browser.drag (each endpoint a CSS selector or a viewport x/y coordinate pair, plus duration_ms/hold options) — its endpoints are ALWAYS explicit: it neither consumes nor sets the implicit target, which survives a drag step untouched for a later click/type/press. Drag steps work on extension tabs only: on a cdp: tab_id the flow fails fast at the drag step with the same "not supported over cdp-direct" error standalone browser.drag returns there. `click`/`type`/`press` default `settle_ms` to 150 exactly like the standalone tools, unless a step overrides it. Example (GIF picker): `{ find: { text: "GIF", role: "button" } }, { click: {} }, { wait_for: { selector: "[data-testid=gif-search]", condition: "visible" } }, { type: { selector: "[data-testid=gif-search]", text: "shrek" } }, { press: { key: "Enter" } }` — 5 steps, 1 round trip. On success returns `{ ok: true, steps_completed, results }` where each entry is `{selector}` for a find step, `{ok, settle?}` for an action/wait_for step, or `{ok, drag_mode}` for a drag step (`drag_mode: "html5"|"pointer"` — the same silently-failing-drop diagnostic standalone browser.drag returns) — a click step\'s entry additionally carries `hit_element` exactly like standalone browser.click: present only when the dispatched click landed on a different element than the resolved target, omitted when the click hit the target itself. On failure returns `{ ok: false, failed_step, step_kind, error, steps_completed, recovery_snapshot }` — `error` is the same message the standalone tool would have returned for that failure, and `recovery_snapshot` is a focused `browser.snapshot({only_interactive:true, max_interactive:40})` of the page as it stands, so you see where the flow stopped without a follow-up call. Every response also carries `flow_id`, the opaque id of this run. A flow can additionally come back `{ ok: true, stopped_by: "cancelled", steps_completed, results }`: the HUMAN stopped it from the extension popup (or, on a cdp: tab, revoked the cdp-direct grant). That is a success carrying the partial work that really happened — do NOT relaunch it, and do NOT treat it as a transient failure to retry; report what completed and ask the user what they want instead. `evaluate` and `navigate` are intentionally NOT flow steps in v1 — arbitrary JS inside a flow raises permission-model questions this tool does not solve yet, and a navigation mid-flow can tear down the very document later steps expect to act on; use standalone browser.navigate before a flow, or browser.evaluate after one.',
858
+ description: 'Run a declarative sequence of steps (find/click/type/press/wait_for/drag/sleep/repeat) in ONE round trip instead of one MCP call per step. Steps execute strictly in order and STOP at the first failure (fail-fast) — there is no branching. A `find` step\'s resolved selector becomes the IMPLICIT TARGET for the very next click/type/press step that omits its own `selector` (an explicit `selector` on that step always overrides it and leaves the implicit target untouched for a later step). `type` and `press` steps that end up with no selector at all — no explicit one, no implicit one — act on whatever currently has focus, same as standalone browser.press; only `click` requires a resolved target and fails the flow if none is available. A `drag` step runs a full drag-and-drop gesture with the SAME params as standalone browser.drag (each endpoint a CSS selector or a viewport x/y coordinate pair, plus duration_ms/hold options) — its endpoints are ALWAYS explicit: it neither consumes nor sets the implicit target, which survives a drag step untouched for a later click/type/press. Drag steps work on extension tabs only: on a cdp: tab_id the flow fails fast at the drag step with the same "not supported over cdp-direct" error standalone browser.drag returns there. `click`/`type`/`press` default `settle_ms` to 150 exactly like the standalone tools, unless a step overrides it. They also accept the standalone `expect` (`url_changed` / `selector_appears` / `selector_disappears` / `text_appears` / `timeout_ms`): settle proves QUIET, not EFFECT, so put `expect` on the step whose effect the next steps depend on, and a click that did nothing fails THAT step (`expectation not met: ... after Nms`, with the usual recovery_snapshot) instead of timing out several steps later. Example: `{ click: { selector: "a.next-lesson-link", expect: { url_changed: true } } }`. Example (GIF picker): `{ find: { text: "GIF", role: "button" } }, { click: {} }, { wait_for: { selector: "[data-testid=gif-search]", condition: "visible" } }, { type: { selector: "[data-testid=gif-search]", text: "shrek" } }, { press: { key: "Enter" } }` — 5 steps, 1 round trip. On success returns `{ ok: true, steps_completed, results }` where each entry is `{selector}` for a find step, `{ok, settle?, expect?}` for an action/wait_for step (`expect: { met: true, elapsed_ms }` appears only on a step that set `expect`), or `{ok, drag_mode}` for a drag step (`drag_mode: "html5"|"pointer"` — the same silently-failing-drop diagnostic standalone browser.drag returns) — a click step\'s entry additionally carries `hit_element` exactly like standalone browser.click: present only when the dispatched click landed on a different element than the resolved target, omitted when the click hit the target itself. On failure returns `{ ok: false, failed_step, step_kind, error, steps_completed, recovery_snapshot }` — `error` is the same message the standalone tool would have returned for that failure, and `recovery_snapshot` is a focused `browser.snapshot({only_interactive:true, max_interactive:40})` of the page as it stands, so you see where the flow stopped without a follow-up call. Every response also carries `flow_id`, the opaque id of this run. A flow can additionally come back `{ ok: true, stopped_by: "cancelled", steps_completed, results }`: the HUMAN stopped it from the extension popup (or, on a cdp: tab, revoked the cdp-direct grant). That is a success carrying the partial work that really happened — do NOT relaunch it, and do NOT treat it as a transient failure to retry; report what completed and ask the user what they want instead. `evaluate` and `navigate` are intentionally NOT flow steps in v1 — arbitrary JS inside a flow raises permission-model questions this tool does not solve yet, and a navigation mid-flow can tear down the very document later steps expect to act on; use standalone browser.navigate before a flow, or browser.evaluate after one.',
760
859
  inputSchema: {
761
860
  type: 'object',
762
861
  properties: {
@@ -802,6 +901,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
802
901
  },
803
902
  force: { type: 'boolean' },
804
903
  settle_ms: { type: 'number' },
904
+ expect: EXPECT_SCHEMA,
805
905
  },
806
906
  additionalProperties: false,
807
907
  },
@@ -822,6 +922,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
822
922
  text: { type: 'string' },
823
923
  clear: { type: 'boolean' },
824
924
  settle_ms: { type: 'number' },
925
+ expect: EXPECT_SCHEMA,
825
926
  },
826
927
  required: ['text'],
827
928
  additionalProperties: false,
@@ -846,6 +947,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
846
947
  description: 'Optional — omit to use the implicit target, or to send the key to whatever currently has focus.',
847
948
  },
848
949
  settle_ms: { type: 'number' },
950
+ expect: EXPECT_SCHEMA,
849
951
  },
850
952
  required: ['key'],
851
953
  additionalProperties: false,
@@ -1007,7 +1109,7 @@ export const BROWSER_TOOL_DEFINITIONS = [
1007
1109
  "A `drag` step's endpoints are ALWAYS explicit (selector or viewport coords) — drag neither consumes nor sets the find implicit target, and a pending implicit target survives a drag step for a later click/type/press. Both endpoints must be visible in the viewport at once, same as standalone browser.drag.",
1008
1110
  'Drag steps are extension-transport only: a flow containing a drag step run on a cdp: tab fails fast at that step with the same "not supported over cdp-direct" error standalone browser.drag returns — the earlier steps HAVE already executed by then.',
1009
1111
  'A `sleep` step is NOT the same as `settle_ms`. settle_ms is a quiet-PERIOD wait that returns as soon as the page stops mutating, so on an already-idle page it collapses to nothing — it can never throttle anything. `sleep` is a fixed pause that is always spent in full, and it counts its full ms against the flow budget. Out-of-range ms (outside 1..30000) is REJECTED, never clamped: silently halving a throttle would hit a rate-limited backend at twice the intended rate. A sleep step names no element, so it neither consumes nor sets the find implicit target.',
1010
- 'Worst-case time budget, hard ceiling 60 seconds: each wait_for step contributes its (clamped, max 30s) timeout_ms, each action step its settle ceiling (~2.5s; ~0.5s with settle_ms:0), each drag step its duration_ms (default 1.5s, clamped at 60s) plus any holds (clamped at 10s each), each sleep step its full ms, plus fixed overhead. A flow whose worst case exceeds 60s is REJECTED up front with the computed budget — reduce wait_for timeout_ms or sleep.ms values, or split the flow. A full 20-step flow of action steps with default settle fits comfortably.',
1112
+ 'Worst-case time budget, hard ceiling 60 seconds: each wait_for step contributes its (clamped, max 30s) timeout_ms, each action step its settle ceiling (~2.5s; ~0.5s with settle_ms:0) plus its `expect` window when set (timeout_ms, default 3s, max 10s), each drag step its duration_ms (default 1.5s, clamped at 60s) plus any holds (clamped at 10s each), each sleep step its full ms, plus fixed overhead. A flow whose worst case exceeds 60s is REJECTED up front with the computed budget — reduce wait_for timeout_ms or sleep.ms values, or split the flow. A full 20-step flow of action steps with default settle fits comfortably.',
1011
1113
  'evaluate and navigate steps are NOT supported in v1 — see the tool description for why. Navigate before the flow; evaluate after it.',
1012
1114
  '`repeat` is BOUNDED by construction: `max_iterations` is required, repeats do not nest, and inner steps count against the same 20-step ceiling. That is what keeps the worst case computable — the budget is max_iterations × (inner steps + delay_ms + a while_found probe), and a repeat that projects over 60s is rejected up front like any other flow. Lower max_iterations, shorten the body, or drop delay_ms.',
1013
1115
  'A repeat iteration starts with a FRESH implicit target, and none escapes: a `find` inside the body cannot feed a click after the repeat, and a `find` before the repeat cannot feed one inside it. Put the find inside the body.',