@fusengine/browser-mcp 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -156,6 +156,11 @@ Key agentic patterns:
156
156
  checkouts are visible and actionable.
157
157
  - **`browser_wait_for`** — wait on a condition (`text` / `selector` / `gone` / `urlContains`),
158
158
  not a fixed delay.
159
+ - **`browser_snapshot` `selectors:true`** — each element also gets a **durable CSS `selector`**
160
+ (finder-style: prefers `data-testid`/stable id/semantic class, rejects generated hashes,
161
+ validated unique). Cache it to act on later visits via `browser_click target=<selector>`
162
+ without re-snapshotting. The act tools also **remember the winning locator strategy per
163
+ site** (under the output dir) and replay it first on repeat actions.
159
164
  - **`browser_collect`** — exhaust a **virtualized / infinite-scroll** list (hotel/flight
160
165
  results, feeds): auto-detects the scroll container, scrolls incrementally and **dedups
161
166
  rows by stable key** until the list ends. Returns the full set of rows (text / url /
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Robust CSS selector generation for the action cache. A `@medv/finder`-style
3
+ * walk builds the shortest unique path, preferring stable hooks (data-testid ›
4
+ * stable id › semantic class › nth-of-type) and rejecting generated tokens
5
+ * (hashes, css-in-js, long digit runs). The browser-side defs are embedded into
6
+ * the snapshot script; `isStableToken` mirrors the filter for unit testing.
7
+ * @module extraction/selector
8
+ */
9
+ /** A token (id/class) is unstable if it looks machine-generated. */
10
+ export declare function isStableToken(value: string | null | undefined): boolean;
11
+ /**
12
+ * Browser-side definitions injected into snapshot scripts: defines `genSelector`
13
+ * (returns a unique selector within the element's root, or null). Relative to
14
+ * the element's root node, so light-DOM selectors work with `page.locator`;
15
+ * shadow-scoped selectors are best-effort.
16
+ */
17
+ export declare const SELECTOR_DEFS = "\n const UNSTABLE = /([0-9a-f]{5,}|\\d{3,}|^(?:css|sc|jss|emotion|Mui|chakra|styled))/i;\n const stableTok = (v) => !!v && v.length >= 3 && !UNSTABLE.test(v);\n const uniqSel = (root, sel) => { try { return root.querySelectorAll(sel).length === 1; } catch (e) { return false; } };\n const genSelector = (el) => {\n const root = el.getRootNode();\n const parts = [];\n let node = el;\n while (node && node.nodeType === 1 && node !== root) {\n let seg = '';\n for (const a of ['data-testid','data-test','data-cy','data-qa']) {\n const v = node.getAttribute && node.getAttribute(a);\n if (v) { seg = '[' + a + '=\"' + v + '\"]'; break; }\n }\n if (!seg && node.id && stableTok(node.id)) seg = '#' + CSS.escape(node.id);\n if (!seg) {\n const cls = (node.classList ? [...node.classList] : []).filter(stableTok).slice(0, 2);\n const tag = node.tagName.toLowerCase();\n if (cls.length) seg = tag + cls.map((c) => '.' + CSS.escape(c)).join('');\n else {\n const sib = node.parentNode ? [...node.parentNode.children].filter((c) => c.tagName === node.tagName) : [node];\n seg = tag + (sib.length > 1 ? ':nth-of-type(' + (sib.indexOf(node) + 1) + ')' : '');\n }\n }\n parts.unshift(seg);\n const sel = parts.join(' > ');\n if (uniqSel(root, sel)) return sel;\n node = node.parentElement || (node.parentNode && node.parentNode.host);\n }\n const full = parts.join(' > ');\n return uniqSel(root, full) ? full : null;\n };";
18
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/extraction/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGvE;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,qhDA+BrB,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Robust CSS selector generation for the action cache. A `@medv/finder`-style
3
+ * walk builds the shortest unique path, preferring stable hooks (data-testid ›
4
+ * stable id › semantic class › nth-of-type) and rejecting generated tokens
5
+ * (hashes, css-in-js, long digit runs). The browser-side defs are embedded into
6
+ * the snapshot script; `isStableToken` mirrors the filter for unit testing.
7
+ * @module extraction/selector
8
+ */
9
+ /** A token (id/class) is unstable if it looks machine-generated. */
10
+ export function isStableToken(value) {
11
+ if (!value || value.length < 3)
12
+ return false;
13
+ return !/([0-9a-f]{5,}|\d{3,}|^(?:css|sc|jss|emotion|Mui|chakra|styled))/i.test(value);
14
+ }
15
+ /**
16
+ * Browser-side definitions injected into snapshot scripts: defines `genSelector`
17
+ * (returns a unique selector within the element's root, or null). Relative to
18
+ * the element's root node, so light-DOM selectors work with `page.locator`;
19
+ * shadow-scoped selectors are best-effort.
20
+ */
21
+ export const SELECTOR_DEFS = `
22
+ const UNSTABLE = /([0-9a-f]{5,}|\\d{3,}|^(?:css|sc|jss|emotion|Mui|chakra|styled))/i;
23
+ const stableTok = (v) => !!v && v.length >= 3 && !UNSTABLE.test(v);
24
+ const uniqSel = (root, sel) => { try { return root.querySelectorAll(sel).length === 1; } catch (e) { return false; } };
25
+ const genSelector = (el) => {
26
+ const root = el.getRootNode();
27
+ const parts = [];
28
+ let node = el;
29
+ while (node && node.nodeType === 1 && node !== root) {
30
+ let seg = '';
31
+ for (const a of ['data-testid','data-test','data-cy','data-qa']) {
32
+ const v = node.getAttribute && node.getAttribute(a);
33
+ if (v) { seg = '[' + a + '="' + v + '"]'; break; }
34
+ }
35
+ if (!seg && node.id && stableTok(node.id)) seg = '#' + CSS.escape(node.id);
36
+ if (!seg) {
37
+ const cls = (node.classList ? [...node.classList] : []).filter(stableTok).slice(0, 2);
38
+ const tag = node.tagName.toLowerCase();
39
+ if (cls.length) seg = tag + cls.map((c) => '.' + CSS.escape(c)).join('');
40
+ else {
41
+ const sib = node.parentNode ? [...node.parentNode.children].filter((c) => c.tagName === node.tagName) : [node];
42
+ seg = tag + (sib.length > 1 ? ':nth-of-type(' + (sib.indexOf(node) + 1) + ')' : '');
43
+ }
44
+ }
45
+ parts.unshift(seg);
46
+ const sel = parts.join(' > ');
47
+ if (uniqSel(root, sel)) return sel;
48
+ node = node.parentElement || (node.parentNode && node.parentNode.host);
49
+ }
50
+ const full = parts.join(' > ');
51
+ return uniqSel(root, full) ? full : null;
52
+ };`;
53
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/extraction/selector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAC,KAAgC;IAC5D,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,CAAC,kEAAkE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACzF,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA+BxB,CAAC"}
@@ -14,5 +14,5 @@ export declare const REF_ATTRIBUTE = "data-fuse-ref";
14
14
  * the Node side rewrites `index` to a global counter and adds the frame-scoped
15
15
  * `ref`. Capped at 200 elements per frame.
16
16
  */
17
- export declare const SNAPSHOT_SCRIPT = "() => {\n const SEL = 'button,a,input,select,textarea,[role=button],[role=combobox],[role=checkbox],[role=radio],[role=switch],[role=tab],[role=menuitem],[role=option],[contenteditable=true]';\n const obscured = (el, r) => {\n const cx = r.x + r.width / 2, cy = r.y + r.height / 2;\n if (r.width === 0 || cx < 0 || cy < 0 || cx > innerWidth || cy > innerHeight) return false;\n const top = document.elementFromPoint(cx, cy);\n return !!top && top !== el && !el.contains(top) && !top.contains(el);\n };\n const out = [];\n const describe = (el, index) => {\n const r = el.getBoundingClientRect();\n const val = typeof el.value === 'string' ? el.value : null;\n const isCheck = el.type === 'checkbox' || el.type === 'radio';\n return {\n index,\n tag: el.tagName.toLowerCase(),\n text: (el.innerText || el.getAttribute('aria-label') || el.getAttribute('placeholder') || '').trim().slice(0, 120),\n role: el.getAttribute('role'), id: el.id || null, name: el.getAttribute('name'),\n type: el.getAttribute('type'), href: el.getAttribute('href'),\n value: val ? val.slice(0, 120) : null, placeholder: el.getAttribute('placeholder'),\n disabled: !!el.disabled || el.getAttribute('aria-disabled') === 'true',\n checked: isCheck ? !!el.checked : undefined,\n options: el.tagName === 'SELECT' ? [...el.options].slice(0, 12).map((o) => o.label || o.value) : undefined,\n ariaExpanded: el.getAttribute('aria-expanded'), ariaControls: el.getAttribute('aria-controls'),\n visible: r.width > 0 && r.height > 0, obscured: obscured(el, r),\n box: {x: Math.round(r.x), y: Math.round(r.y), width: Math.round(r.width), height: Math.round(r.height)}\n };\n };\n const visit = (root) => {\n if (out.length >= 200) return;\n for (const el of root.querySelectorAll(SEL)) {\n if (out.length >= 200) return;\n const i = out.length;\n el.setAttribute('data-fuse-ref', String(i));\n out.push(describe(el, i));\n }\n for (const host of root.querySelectorAll('*')) {\n if (host.shadowRoot) visit(host.shadowRoot);\n }\n };\n visit(document);\n return out;\n}";
17
+ export declare const SNAPSHOT_SCRIPT = "(arg) => {\n const SEL = 'button,a,input,select,textarea,[role=button],[role=combobox],[role=checkbox],[role=radio],[role=switch],[role=tab],[role=menuitem],[role=option],[contenteditable=true]';\n const wantSel = !!(arg && arg.selectors);\n const UNSTABLE = /([0-9a-f]{5,}|\\d{3,}|^(?:css|sc|jss|emotion|Mui|chakra|styled))/i;\n const stableTok = (v) => !!v && v.length >= 3 && !UNSTABLE.test(v);\n const uniqSel = (root, sel) => { try { return root.querySelectorAll(sel).length === 1; } catch (e) { return false; } };\n const genSelector = (el) => {\n const root = el.getRootNode();\n const parts = [];\n let node = el;\n while (node && node.nodeType === 1 && node !== root) {\n let seg = '';\n for (const a of ['data-testid','data-test','data-cy','data-qa']) {\n const v = node.getAttribute && node.getAttribute(a);\n if (v) { seg = '[' + a + '=\"' + v + '\"]'; break; }\n }\n if (!seg && node.id && stableTok(node.id)) seg = '#' + CSS.escape(node.id);\n if (!seg) {\n const cls = (node.classList ? [...node.classList] : []).filter(stableTok).slice(0, 2);\n const tag = node.tagName.toLowerCase();\n if (cls.length) seg = tag + cls.map((c) => '.' + CSS.escape(c)).join('');\n else {\n const sib = node.parentNode ? [...node.parentNode.children].filter((c) => c.tagName === node.tagName) : [node];\n seg = tag + (sib.length > 1 ? ':nth-of-type(' + (sib.indexOf(node) + 1) + ')' : '');\n }\n }\n parts.unshift(seg);\n const sel = parts.join(' > ');\n if (uniqSel(root, sel)) return sel;\n node = node.parentElement || (node.parentNode && node.parentNode.host);\n }\n const full = parts.join(' > ');\n return uniqSel(root, full) ? full : null;\n };\n const obscured = (el, r) => {\n const cx = r.x + r.width / 2, cy = r.y + r.height / 2;\n if (r.width === 0 || cx < 0 || cy < 0 || cx > innerWidth || cy > innerHeight) return false;\n const top = document.elementFromPoint(cx, cy);\n return !!top && top !== el && !el.contains(top) && !top.contains(el);\n };\n const out = [];\n const describe = (el, index) => {\n const r = el.getBoundingClientRect();\n const val = typeof el.value === 'string' ? el.value : null;\n const isCheck = el.type === 'checkbox' || el.type === 'radio';\n return {\n index,\n tag: el.tagName.toLowerCase(),\n text: (el.innerText || el.getAttribute('aria-label') || el.getAttribute('placeholder') || '').trim().slice(0, 120),\n role: el.getAttribute('role'), id: el.id || null, name: el.getAttribute('name'),\n type: el.getAttribute('type'), href: el.getAttribute('href'),\n value: val ? val.slice(0, 120) : null, placeholder: el.getAttribute('placeholder'),\n disabled: !!el.disabled || el.getAttribute('aria-disabled') === 'true',\n checked: isCheck ? !!el.checked : undefined,\n options: el.tagName === 'SELECT' ? [...el.options].slice(0, 12).map((o) => o.label || o.value) : undefined,\n ariaExpanded: el.getAttribute('aria-expanded'), ariaControls: el.getAttribute('aria-controls'),\n visible: r.width > 0 && r.height > 0, obscured: obscured(el, r),\n selector: wantSel ? genSelector(el) : undefined,\n box: {x: Math.round(r.x), y: Math.round(r.y), width: Math.round(r.width), height: Math.round(r.height)}\n };\n };\n const visit = (root) => {\n if (out.length >= 200) return;\n for (const el of root.querySelectorAll(SEL)) {\n if (out.length >= 200) return;\n const i = out.length;\n el.setAttribute('data-fuse-ref', String(i));\n out.push(describe(el, i));\n }\n for (const host of root.querySelectorAll('*')) {\n if (host.shadowRoot) visit(host.shadowRoot);\n }\n };\n visit(document);\n return out;\n}";
18
18
  //# sourceMappingURL=snapshot-walk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-walk.d.ts","sourceRoot":"","sources":["../../src/extraction/snapshot-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6EAA6E;AAC7E,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAK7C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,0nEA0C1B,CAAC"}
1
+ {"version":3,"file":"snapshot-walk.d.ts","sourceRoot":"","sources":["../../src/extraction/snapshot-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,6EAA6E;AAC7E,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAK7C;;;;;GAKG;AACH,eAAO,MAAM,eAAe,kvHA4C1B,CAAC"}
@@ -6,6 +6,7 @@
6
6
  * Closed shadow roots are inaccessible by browser SOP and stay opaque.
7
7
  * @module extraction/snapshot-walk
8
8
  */
9
+ import { SELECTOR_DEFS } from "./selector.js";
9
10
  /** Attribute injected on each interactive element to anchor a stable ref. */
10
11
  export const REF_ATTRIBUTE = "data-fuse-ref";
11
12
  const SELECTOR = "button,a,input,select,textarea,[role=button],[role=combobox],[role=checkbox],[role=radio],[role=switch],[role=tab],[role=menuitem],[role=option],[contenteditable=true]";
@@ -15,8 +16,9 @@ const SELECTOR = "button,a,input,select,textarea,[role=button],[role=combobox],[
15
16
  * the Node side rewrites `index` to a global counter and adds the frame-scoped
16
17
  * `ref`. Capped at 200 elements per frame.
17
18
  */
18
- export const SNAPSHOT_SCRIPT = `() => {
19
+ export const SNAPSHOT_SCRIPT = `(arg) => {
19
20
  const SEL = '${SELECTOR}';
21
+ const wantSel = !!(arg && arg.selectors);${SELECTOR_DEFS}
20
22
  const obscured = (el, r) => {
21
23
  const cx = r.x + r.width / 2, cy = r.y + r.height / 2;
22
24
  if (r.width === 0 || cx < 0 || cy < 0 || cx > innerWidth || cy > innerHeight) return false;
@@ -40,6 +42,7 @@ export const SNAPSHOT_SCRIPT = `() => {
40
42
  options: el.tagName === 'SELECT' ? [...el.options].slice(0, 12).map((o) => o.label || o.value) : undefined,
41
43
  ariaExpanded: el.getAttribute('aria-expanded'), ariaControls: el.getAttribute('aria-controls'),
42
44
  visible: r.width > 0 && r.height > 0, obscured: obscured(el, r),
45
+ selector: wantSel ? genSelector(el) : undefined,
43
46
  box: {x: Math.round(r.x), y: Math.round(r.y), width: Math.round(r.width), height: Math.round(r.height)}
44
47
  };
45
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-walk.js","sourceRoot":"","sources":["../../src/extraction/snapshot-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,6EAA6E;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAE7C,MAAM,QAAQ,GACZ,yKAAyK,CAAC;AAE5K;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;iBACd,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAgCA,aAAa;;;;;;;;;EASpC,CAAC"}
1
+ {"version":3,"file":"snapshot-walk.js","sourceRoot":"","sources":["../../src/extraction/snapshot-walk.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,6EAA6E;AAC7E,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAE7C,MAAM,QAAQ,GACZ,yKAAyK,CAAC;AAE5K;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;iBACd,QAAQ;6CACoB,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yBAiCjC,aAAa;;;;;;;;;EASpC,CAAC"}
@@ -16,5 +16,5 @@ export { REF_ATTRIBUTE } from "./snapshot-walk.js";
16
16
  * Detached frames and frames that reject evaluation (e.g. mid-navigation) are
17
17
  * skipped rather than aborting the whole snapshot.
18
18
  */
19
- export declare function captureSnapshot(page: Page): Promise<InteractiveElement[]>;
19
+ export declare function captureSnapshot(page: Page, selectors?: boolean): Promise<InteractiveElement[]>;
20
20
  //# sourceMappingURL=snapshot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/extraction/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAItE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKnD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAsB/E"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/extraction/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAItE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAKnD;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAuBlG"}
@@ -1,4 +1,4 @@
1
- import { evalScript } from "../lib/evaluate.js";
1
+ import { evalScriptArg } from "../lib/evaluate.js";
2
2
  import { SNAPSHOT_SCRIPT } from "./snapshot-walk.js";
3
3
  export { REF_ATTRIBUTE } from "./snapshot-walk.js";
4
4
  /** Soft cap on total elements across all frames, to bound output size. */
@@ -9,9 +9,10 @@ const MAX_ELEMENTS = 400;
9
9
  * Detached frames and frames that reject evaluation (e.g. mid-navigation) are
10
10
  * skipped rather than aborting the whole snapshot.
11
11
  */
12
- export async function captureSnapshot(page) {
12
+ export async function captureSnapshot(page, selectors = false) {
13
13
  const frames = page.frames();
14
14
  const all = [];
15
+ const arg = { selectors };
15
16
  let global = 0;
16
17
  for (let f = 0; f < frames.length && all.length < MAX_ELEMENTS; f++) {
17
18
  const frame = frames[f];
@@ -19,7 +20,7 @@ export async function captureSnapshot(page) {
19
20
  continue;
20
21
  let local;
21
22
  try {
22
- local = await evalScript(frame, SNAPSHOT_SCRIPT);
23
+ local = await evalScriptArg(frame, SNAPSHOT_SCRIPT, arg);
23
24
  }
24
25
  catch {
25
26
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/extraction/snapshot.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,0EAA0E;AAC1E,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAU;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE;YAAE,SAAS;QAC3C,IAAI,KAA2B,CAAC;QAChC,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,UAAU,CAAuB,KAAK,EAAE,eAAe,CAAC,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,MAAM,IAAI,YAAY;gBAAE,MAAM;YACtC,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACzD,IAAI,CAAC,GAAG,CAAC;gBAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;YACxB,EAAE,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC;YACpB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/extraction/snapshot.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,0EAA0E;AAC1E,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAU,EAAE,SAAS,GAAG,KAAK;IACjE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAyB,EAAE,CAAC;IACrC,MAAM,GAAG,GAAG,EAAE,SAAS,EAAE,CAAC;IAC1B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACxB,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,EAAE;YAAE,SAAS;QAC3C,IAAI,KAA2B,CAAC;QAChC,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,aAAa,CAAmC,KAAK,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC;QAC7F,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;YACvB,IAAI,GAAG,CAAC,MAAM,IAAI,YAAY;gBAAE,MAAM;YACtC,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACzD,IAAI,CAAC,GAAG,CAAC;gBAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;YACxB,EAAE,CAAC,KAAK,GAAG,MAAM,EAAE,CAAC;YACpB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -62,6 +62,8 @@ export interface InteractiveElement {
62
62
  ariaExpanded?: string | null;
63
63
  ariaControls?: string | null;
64
64
  obscured?: boolean;
65
+ /** Robust, reusable CSS selector (only when snapshot is asked for `selectors`). */
66
+ selector?: string | null;
65
67
  }
66
68
  /** A row harvested from a (possibly virtualized) list via scroll-collect. */
67
69
  export interface CollectedItem {
@@ -1 +1 @@
1
- {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/interfaces/extraction.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,yCAAyC;AACzC,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,MAAM;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAED,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"extraction.d.ts","sourceRoot":"","sources":["../../src/interfaces/extraction.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,yCAAyC;AACzC,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACtD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,SAAS,EAAE,UAAU,GAAG,IAAI,CAAC;CAC9B;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;CACd;AAED,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CAClB;AAED,8DAA8D;AAC9D,MAAM,WAAW,MAAM;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,mBAAmB,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAC5C;AAED,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../../src/server/tools/act.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAwB/D,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAuDlF"}
1
+ {"version":3,"file":"act.d.ts","sourceRoot":"","sources":["../../../src/server/tools/act.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA4B/D,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CAuDlF"}
@@ -1,12 +1,14 @@
1
1
  import { z } from "zod";
2
2
  import { performAction } from "../../actions/perform.js";
3
+ import { runWithMemory } from "../../state/action-memory.js";
3
4
  import { jsonResult } from "../result.js";
4
5
  import { withSession } from "./with-session.js";
5
6
  function actTool(server, sessions, name, description, inputSchema, build) {
6
7
  server.registerTool(name, { title: name, description, inputSchema }, async (args) => {
7
8
  const a = args;
8
9
  return withSession(sessions, String(a.sessionId), async (s) => {
9
- const result = await performAction(s.page, build(a), s.config.humanMode);
10
+ const action = build(a);
11
+ const result = await runWithMemory(s.config.siteMemoryDir, s.page, action, (act) => performAction(s.page, act, s.config.humanMode));
10
12
  return jsonResult({ result, url: s.page.url() });
11
13
  });
12
14
  });
@@ -1 +1 @@
1
- {"version":3,"file":"act.js","sourceRoot":"","sources":["../../../src/server/tools/act.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"act.js","sourceRoot":"","sources":["../../../src/server/tools/act.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/tools/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAmB/D,qDAAqD;AACrD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CA8CvF"}
1
+ {"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/tools/snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA2B/D,qDAAqD;AACrD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI,CA8CvF"}
@@ -5,11 +5,12 @@ import { smartClick } from "../../actions/smart-click.js";
5
5
  import { smartFill } from "../../actions/smart-fill.js";
6
6
  import { captureSnapshot } from "../../extraction/snapshot.js";
7
7
  import { diffSnapshots } from "../../extraction/snapshot-diff.js";
8
+ import { runWithMemory } from "../../state/action-memory.js";
8
9
  import { errorResult, jsonResult } from "../result.js";
9
10
  import { withSession } from "./with-session.js";
10
11
  const KIND = z.enum(["click", "fill", "select", "pick"]);
11
- /** Run the chosen action (by ref or text fallback). */
12
- async function runAct(page, a, human) {
12
+ /** Run the chosen action (by ref or text fallback), with site-memory assist. */
13
+ async function runAct(page, a, human, dir) {
13
14
  const kind = a.kind;
14
15
  const value = a.value ? String(a.value) : "";
15
16
  const option = a.option ? String(a.option) : "";
@@ -17,22 +18,24 @@ async function runAct(page, a, human) {
17
18
  return actByRef(page, a.ref, kind, value, option);
18
19
  if (typeof a.target !== "string")
19
20
  return null;
21
+ const target = a.target;
20
22
  if (kind === "pick")
21
- return pickAutocomplete(page, page.locator(a.target).first(), value, option);
22
- return kind === "fill"
23
- ? smartFill(page, a.target, value, "", human)
24
- : smartClick(page, a.target, "", human);
23
+ return pickAutocomplete(page, page.locator(target).first(), value, option);
24
+ return runWithMemory(dir, page, { type: kind, target }, (act) => {
25
+ const pref = String(act.preferredStrategy ?? "");
26
+ return kind === "fill" ? smartFill(page, target, value, pref, human) : smartClick(page, target, pref, human);
27
+ });
25
28
  }
26
29
  /** Register `browser_snapshot` and `browser_act`. */
27
30
  export function registerSnapshotTools(server, sessions) {
28
31
  server.registerTool("browser_snapshot", {
29
32
  title: "Snapshot",
30
- description: "Return the indexed interactive elements of the live page, including those inside open Shadow DOM and iframes (same- and cross-origin). Use each element's `ref` (e.g. \"12\" or \"3:4\" for a sub-frame) with browser_act for deterministic targeting.",
31
- inputSchema: { sessionId: z.string() },
33
+ description: "Return the indexed interactive elements of the live page, including those inside open Shadow DOM and iframes (same- and cross-origin). Use each element's `ref` (e.g. \"12\" or \"3:4\" for a sub-frame) with browser_act for deterministic targeting. Pass `selectors:true` to also get a durable CSS `selector` per element (cacheable to act later without re-snapshotting).",
34
+ inputSchema: { sessionId: z.string(), selectors: z.boolean().optional() },
32
35
  }, async (args) => {
33
36
  const a = args;
34
37
  return withSession(sessions, String(a.sessionId), async (s) => {
35
- const elements = await captureSnapshot(s.page);
38
+ const elements = await captureSnapshot(s.page, a.selectors === true);
36
39
  return jsonResult({ url: s.page.url(), count: elements.length, elements });
37
40
  });
38
41
  });
@@ -52,7 +55,7 @@ export function registerSnapshotTools(server, sessions) {
52
55
  return withSession(sessions, String(a.sessionId), async (s) => {
53
56
  const before = await captureSnapshot(s.page);
54
57
  const urlBefore = s.page.url();
55
- const result = await runAct(s.page, a, s.config.humanMode);
58
+ const result = await runAct(s.page, a, s.config.humanMode, s.config.siteMemoryDir);
56
59
  if (!result)
57
60
  return errorResult("browser_act requires either `ref` or `target`");
58
61
  const after = await captureSnapshot(s.page);
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/server/tools/snapshot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../../src/server/tools/snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Wire per-site memory into a single action execution: inject the remembered
3
+ * winning `preferredStrategy` before running, and persist the winner on success.
4
+ * No-op for non click/fill actions (they have no memory key). Used by the MCP
5
+ * act tools, which otherwise would never consult site memory.
6
+ * @module state/action-memory
7
+ */
8
+ import type { Page } from "playwright";
9
+ import type { ActionResult } from "../interfaces/types.js";
10
+ /** A loose action with the fields site memory keys on. */
11
+ type Action = Record<string, unknown> & {
12
+ type: string;
13
+ target?: string;
14
+ };
15
+ /**
16
+ * Run `exec(action)` with site-memory assist: prefill `preferredStrategy` from
17
+ * memory, then persist the winning strategy if the action succeeds. When `dir`
18
+ * is empty memory is skipped.
19
+ */
20
+ export declare function runWithMemory(dir: string, page: Page, action: Action, exec: (a: Action) => Promise<ActionResult>): Promise<ActionResult>;
21
+ export {};
22
+ //# sourceMappingURL=action-memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-memory.d.ts","sourceRoot":"","sources":["../../src/state/action-memory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG3D,0DAA0D;AAC1D,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,GACzC,OAAO,CAAC,YAAY,CAAC,CAQvB"}
@@ -0,0 +1,19 @@
1
+ import { loadSiteMemory, rememberActionStrategy, rememberedAction } from "./site-memory.js";
2
+ /**
3
+ * Run `exec(action)` with site-memory assist: prefill `preferredStrategy` from
4
+ * memory, then persist the winning strategy if the action succeeds. When `dir`
5
+ * is empty memory is skipped.
6
+ */
7
+ export async function runWithMemory(dir, page, action, exec) {
8
+ if (!dir)
9
+ return exec(action);
10
+ const url = page.url();
11
+ const remembered = rememberedAction(loadSiteMemory(dir, url), action);
12
+ if (remembered && !action.preferredStrategy)
13
+ action.preferredStrategy = remembered.strategy;
14
+ const result = await exec(action);
15
+ if (result.ok)
16
+ rememberActionStrategy(dir, url, action, result);
17
+ return result;
18
+ }
19
+ //# sourceMappingURL=action-memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action-memory.js","sourceRoot":"","sources":["../../src/state/action-memory.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAK5F;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAW,EACX,IAAU,EACV,MAAc,EACd,IAA0C;IAE1C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,gBAAgB,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;IACtE,IAAI,UAAU,IAAI,CAAC,MAAM,CAAC,iBAAiB;QAAE,MAAM,CAAC,iBAAiB,GAAG,UAAU,CAAC,QAAQ,CAAC;IAC5F,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,MAAM,CAAC,EAAE;QAAE,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAChE,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusengine/browser-mcp",
3
- "version": "0.1.16",
3
+ "version": "0.1.17",
4
4
  "description": "MCP server + CLI giving AI agents a real, stealth browser (Patchright/Playwright) — per-country identity, self-healing actions, snapshots, multi-step plans, structured extraction, CDP attach.",
5
5
  "license": "MIT",
6
6
  "author": "Fusengine",
@@ -54,7 +54,7 @@
54
54
  "build": "tsc -p tsconfig.json",
55
55
  "typecheck": "tsc -p tsconfig.json --noEmit",
56
56
  "test": "bun test tests/unit",
57
- "test:integration": "node --test --import tsx tests/integration/mcp.test.ts tests/integration/probe.test.ts tests/integration/snapshot.test.ts tests/integration/snapshot-frames.test.ts tests/integration/collect.test.ts tests/integration/run.test.ts tests/integration/extract-schema.test.ts",
57
+ "test:integration": "node --test --import tsx tests/integration/mcp.test.ts tests/integration/probe.test.ts tests/integration/snapshot.test.ts tests/integration/snapshot-frames.test.ts tests/integration/collect.test.ts tests/integration/selectors.test.ts tests/integration/run.test.ts tests/integration/extract-schema.test.ts",
58
58
  "browsers": "patchright install chromium",
59
59
  "mcp": "node --import tsx src/bin/mcp.ts",
60
60
  "cli": "node --import tsx src/bin/cli.ts"