@weavetab/mcp 2.5.0-beta.1 → 2.5.0-beta.4

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 (156) hide show
  1. package/AGENTS.md +2 -1
  2. package/ARCHITECTURE.md +6 -6
  3. package/LICENSE +1 -1
  4. package/README.md +164 -802
  5. package/SECURITY.md +33 -22
  6. package/TROUBLESHOOTING.md +3 -3
  7. package/dist/cdp/bridge.d.ts +10 -0
  8. package/dist/cdp/bridge.js +285 -78
  9. package/dist/cdp/connector.d.ts +7 -3
  10. package/dist/cdp/connector.js +196 -75
  11. package/dist/cdp/helpers.d.ts +4 -0
  12. package/dist/cdp/helpers.js +181 -0
  13. package/dist/cdp/lock.d.ts +9 -6
  14. package/dist/cdp/lock.js +72 -59
  15. package/dist/cdp/omni_broker.d.ts +2 -1
  16. package/dist/cdp/omni_broker.js +13 -7
  17. package/dist/cdp/profiles.d.ts +10 -0
  18. package/dist/cdp/profiles.js +65 -0
  19. package/dist/cdp/walker.d.ts +25 -1
  20. package/dist/cdp/walker.js +354 -7
  21. package/dist/cli/plugin.d.ts +0 -2
  22. package/dist/cli/plugin.js +51 -696
  23. package/dist/cli/updater.d.ts +1 -1
  24. package/dist/cli/updater.js +27 -7
  25. package/dist/config/loader.d.ts +33 -6
  26. package/dist/config/loader.js +174 -22
  27. package/dist/desktop/discovery.d.ts +44 -0
  28. package/dist/desktop/discovery.js +364 -0
  29. package/dist/index.js +140 -64
  30. package/dist/intelligence/marks.d.ts +36 -0
  31. package/dist/intelligence/marks.js +30 -0
  32. package/dist/intelligence/patternCompiler.d.ts +118 -0
  33. package/dist/intelligence/patternCompiler.js +259 -0
  34. package/dist/intelligence/sessionMemory.d.ts +2 -0
  35. package/dist/intelligence/sessionMemory.js +18 -8
  36. package/dist/overlay/ghost.js +2 -2
  37. package/dist/overlay/injector.js +3 -5
  38. package/dist/plugin/extension_bridge.d.ts +40 -0
  39. package/dist/plugin/extension_bridge.js +158 -0
  40. package/dist/plugin/interface.d.ts +151 -15
  41. package/dist/plugin/loader.d.ts +19 -1
  42. package/dist/plugin/loader.js +331 -63
  43. package/dist/plugin/pipeline.d.ts +75 -0
  44. package/dist/plugin/pipeline.js +183 -0
  45. package/dist/plugin/registry.js +12 -1
  46. package/dist/plugin/storage.d.ts +51 -0
  47. package/dist/plugin/storage.js +164 -0
  48. package/dist/security/secrets.d.ts +1 -0
  49. package/dist/security/secrets.js +13 -0
  50. package/dist/sensors/actionability.js +1 -2
  51. package/dist/sensors/guard.js +4 -10
  52. package/dist/sensors/mutations.js +12 -0
  53. package/dist/sensors/operation_monitor.js +1 -0
  54. package/dist/server.d.ts +1 -1
  55. package/dist/server.js +600 -236
  56. package/dist/state/agent.d.ts +1 -0
  57. package/dist/state/agent.js +3 -0
  58. package/dist/state/delta.d.ts +12 -4
  59. package/dist/state/delta.js +67 -14
  60. package/dist/state/session.d.ts +8 -2
  61. package/dist/state/session.js +60 -13
  62. package/dist/tools/browser_detect.d.ts +16 -2
  63. package/dist/tools/browser_detect.js +54 -6
  64. package/dist/tools/burst.d.ts +17 -7
  65. package/dist/tools/burst.js +240 -83
  66. package/dist/tools/checkpoint.d.ts +38 -0
  67. package/dist/tools/checkpoint.js +271 -0
  68. package/dist/tools/click.d.ts +1 -0
  69. package/dist/tools/click.js +97 -98
  70. package/dist/tools/clipboard.js +31 -24
  71. package/dist/tools/design_extract.js +1 -1
  72. package/dist/tools/eval.js +1 -1
  73. package/dist/tools/fill.d.ts +20 -1
  74. package/dist/tools/fill.js +199 -40
  75. package/dist/tools/find.d.ts +16 -0
  76. package/dist/tools/find.js +67 -44
  77. package/dist/tools/github/index.d.ts +19 -4
  78. package/dist/tools/github/index.js +40 -4
  79. package/dist/tools/highlight.d.ts +6 -2
  80. package/dist/tools/highlight.js +158 -72
  81. package/dist/tools/inspect.js +1 -1
  82. package/dist/tools/key.js +65 -56
  83. package/dist/tools/navigate.d.ts +1 -1
  84. package/dist/tools/navigate.js +57 -44
  85. package/dist/tools/pdf.js +21 -13
  86. package/dist/tools/pointer.d.ts +18 -3
  87. package/dist/tools/pointer.js +89 -1
  88. package/dist/tools/read.d.ts +28 -8
  89. package/dist/tools/read.js +230 -56
  90. package/dist/tools/scrape.js +67 -13
  91. package/dist/tools/screenshot.js +21 -16
  92. package/dist/tools/scroll.js +6 -11
  93. package/dist/tools/select.js +8 -2
  94. package/dist/tools/snapshot.js +3 -34
  95. package/dist/tools/tabs.d.ts +10 -10
  96. package/dist/tools/tabs.js +55 -39
  97. package/dist/tools/thought.d.ts +2 -0
  98. package/dist/tools/thought.js +13 -14
  99. package/dist/tools/type.d.ts +2 -0
  100. package/dist/tools/type.js +109 -60
  101. package/dist/tools/type_secret.js +42 -76
  102. package/dist/tools/upload.js +1 -1
  103. package/dist/tools/video_compiler.d.ts +1 -1
  104. package/dist/tools/video_compiler.js +11 -12
  105. package/dist/version.d.ts +5 -0
  106. package/dist/version.js +5 -0
  107. package/docs/DOCUMENTATION.md +82 -36
  108. package/docs/PLUGINS.md +82 -499
  109. package/docs/TOOLS.md +51 -99
  110. package/docs/architecture/blind-injection.md +83 -0
  111. package/docs/architecture/dual-injection.md +32 -0
  112. package/docs/architecture/overview.md +49 -0
  113. package/docs/architecture/security.md +35 -0
  114. package/docs/architecture/transport.md +37 -0
  115. package/docs/captcha-stealth/captcha-solver.md +75 -0
  116. package/docs/captcha-stealth/stealth-physics.md +60 -0
  117. package/docs/cli/cli-commands.md +44 -0
  118. package/docs/cli/cli-flags.md +20 -0
  119. package/docs/cli/cli-introduction.md +20 -0
  120. package/docs/cli/cli-scripting.md +32 -0
  121. package/docs/cli/g-ide/ide-antigravity.md +24 -0
  122. package/docs/cli/g-ide/ide-claude-code.md +24 -0
  123. package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
  124. package/docs/cli/g-ide/ide-cursor.md +31 -0
  125. package/docs/cli/g-ide/ide-generic.md +34 -0
  126. package/docs/cli/g-ide/ide-opencode.md +24 -0
  127. package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
  128. package/docs/cli/g-ide/ide-windsurf.md +24 -0
  129. package/docs/configuration/config-locations.md +24 -0
  130. package/docs/configuration/configuration.md +73 -0
  131. package/docs/configuration/env-variables.md +19 -0
  132. package/docs/configuration/folders.md +55 -0
  133. package/docs/desktop-automation/electron-apps.md +217 -0
  134. package/docs/get-started/installation.md +47 -0
  135. package/docs/get-started/introduction.md +60 -0
  136. package/docs/get-started/quickstart.md +54 -0
  137. package/docs/get-started/setup-wizard.md +26 -0
  138. package/docs/intelligence/site-profiles.md +151 -0
  139. package/docs/skills-ecosystem/skills-overview.md +35 -0
  140. package/docs/skills-ecosystem/skills-recipes.md +36 -0
  141. package/docs/tools/tools.md +631 -0
  142. package/docs/troubleshooting/trouble-browser.md +27 -0
  143. package/docs/troubleshooting/trouble-config.md +20 -0
  144. package/docs/troubleshooting/trouble-loop.md +31 -0
  145. package/ext-dist/background.js +10 -62
  146. package/ext-dist/content.js +333 -921
  147. package/ext-dist/hud/hud.js +9 -9
  148. package/ext-dist/manifest.json +2 -6
  149. package/ext-dist/styles.css +6 -1
  150. package/mcp.json +108 -72
  151. package/package.json +14 -9
  152. package/server.json +3 -3
  153. package/docs/DEV_MODE.md +0 -68
  154. package/docs/dev_plugin.md +0 -310
  155. package/ext-dist/sidepanel/sidepanel.js +0 -25
  156. package/ext-dist/sidepanel.html +0 -547
@@ -12,7 +12,7 @@ import { resetFallbackIdCounter, clearShadowDomCache, clearAXTreeCache } from ".
12
12
  import { clearTabCache } from "../state/cache.js";
13
13
  import { bumpVisitCount } from "../intelligence/sessionMemory.js";
14
14
  import { sessionRegistry } from "../state/session.js";
15
- export async function weaveNavigate(session, url, config, targetId = "default", force = false, fast = false) {
15
+ export async function weaveNavigate(session, url, config, targetId = "default", force = false, fast = false, appName) {
16
16
  logDebug(`[weaveNavigate] Attempting navigation to: ${url}`);
17
17
  let parsed;
18
18
  if (url.startsWith('Weavetab://')) {
@@ -40,16 +40,35 @@ export async function weaveNavigate(session, url, config, targetId = "default",
40
40
  return { success: false, error_code: "INVALID_URL", message: `[!] Invalid URL: ${url}` };
41
41
  }
42
42
  }
43
- // Allow http(s), file (for local HTML debugging), and chrome-extension (handled below).
44
- // Block script-execution protocols (javascript:, data:) that bypass the domain policy.
43
+ const active = sessionRegistry.getActive(config.agentId, appName);
44
+ const isElectron = !!(appName || active?.isElectron);
45
+ // Allowed web and desktop application protocols.
46
+ // Block script-execution protocols (javascript:, data:) that bypass safety policies.
45
47
  const proto = parsed.protocol.toLowerCase();
46
- if (proto !== "http:" && proto !== "https:" && proto !== "file:" && proto !== "chrome-extension:") {
47
- return { success: false, error_code: "BLOCKED_PROTOCOL", message: `[!] Blocked protocol '${proto}'. Only http, https, and file are allowed.` };
48
+ const allowedProtocols = new Set([
49
+ "http:",
50
+ "https:",
51
+ "file:",
52
+ "chrome-extension:",
53
+ "app:",
54
+ "vscode-webview:",
55
+ "vscode-file:",
56
+ "discord:",
57
+ "slack:",
58
+ "devtools:",
59
+ "chrome:",
60
+ "about:",
61
+ ]);
62
+ if (!allowedProtocols.has(proto)) {
63
+ return { success: false, error_code: "BLOCKED_PROTOCOL", message: `[!] Blocked protocol '${proto}'. Only web, file, and desktop app protocols are allowed.` };
48
64
  }
49
- const domainCheck = checkDomain(parsed.href, config);
50
- logDebug(`[weaveNavigate] Domain check for ${parsed.hostname}: ${JSON.stringify(domainCheck)}`);
51
- if (!domainCheck.allowed) {
52
- return { success: false, error_code: "DOMAIN_BLOCKED", message: `[!] Weave blocked: ${domainCheck.reason}` };
65
+ // Domain policy check only applies to standard web navigations
66
+ if (!isElectron && (proto === "http:" || proto === "https:")) {
67
+ const domainCheck = checkDomain(parsed.href, config);
68
+ logDebug(`[weaveNavigate] Domain check for ${parsed.hostname}: ${JSON.stringify(domainCheck)}`);
69
+ if (!domainCheck.allowed) {
70
+ return { success: false, error_code: "DOMAIN_BLOCKED", message: `[!] Weave blocked: ${domainCheck.reason}` };
71
+ }
53
72
  }
54
73
  // Extension pages: open in new tab instead of navigating current tab
55
74
  // (chrome-extension:// pages use a different process context — Page.navigate
@@ -88,7 +107,6 @@ export async function weaveNavigate(session, url, config, targetId = "default",
88
107
  };
89
108
  }
90
109
  // Resolve active tab id
91
- const active = sessionRegistry.getActive(config.agentId);
92
110
  const resolvedTargetId = active?.targetId ?? targetId;
93
111
  // 1. Anchor Tab Protection
94
112
  let isAnchor = false;
@@ -97,7 +115,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
97
115
  expression: `new Promise((resolve) => {
98
116
  let done = false;
99
117
  const finish = (val) => { if (!done) { done = true; resolve(val); } };
100
- setTimeout(() => finish(false), 500);
118
+ setTimeout(() => finish(false), 30);
101
119
  try {
102
120
  const browserAPI = typeof browser !== 'undefined' ? browser : (typeof chrome !== 'undefined' ? chrome : null);
103
121
  if (browserAPI && browserAPI.runtime && browserAPI.runtime.id) {
@@ -111,7 +129,7 @@ export async function weaveNavigate(session, url, config, targetId = "default",
111
129
  })`,
112
130
  awaitPromise: true,
113
131
  returnByValue: true
114
- });
132
+ }).catch(() => null);
115
133
  isAnchor = res?.result?.value === true;
116
134
  }
117
135
  catch (e) {
@@ -133,50 +151,45 @@ export async function weaveNavigate(session, url, config, targetId = "default",
133
151
  sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: `navigating to ${parsed.hostname}...` } }).catch(() => { });
134
152
  sendToExtension(session, { type: 'Weavetab_STATE', cursor: { x: 10, y: 10 }, state: 'thinking' }).catch(() => { });
135
153
  await session.Page.navigate({ url: parsed.href });
136
- // Proper navigation wait — DOM stability after load
154
+ // Fast navigation wait
137
155
  try {
138
156
  await awaitNavigation(session, config.navigationTimeout || 30000);
139
157
  }
140
158
  catch (navErr) {
141
159
  logDebug(`[weaveNavigate] Navigation timeout: ${navErr}`);
142
- // Continue — page may be partially loaded
143
160
  }
144
- // Clear delta snapshot — next weave_read will be a fresh full read
161
+ // Clear delta snapshot & caches
145
162
  clearSnapshot(resolvedTargetId);
146
163
  resetLoopCounter("navigation");
147
- clearToolCallWindow(); // prevents cross-page STUCK_LOOP bricks
164
+ clearToolCallWindow();
148
165
  resetFallbackIdCounter();
149
166
  clearShadowDomCache(resolvedTargetId);
150
167
  clearAXTreeCache(resolvedTargetId);
151
168
  clearTabCache(resolvedTargetId);
152
- // Dismiss common cookie banners / popups inline (popups.ts was removed)
153
- await session.Runtime.evaluate({
154
- expression: `
155
- (function(){
156
- var selectors = [
157
- '[id*="cookie"] button','[class*="cookie"] button',
158
- '[id*="consent"] button','[class*="consent"] button',
159
- '[id*="gdpr"] button','[class*="gdpr"] button',
160
- '[aria-label*="Accept"]','[aria-label*="Agree"]',
161
- 'button[data-cookiebanner]'
162
- ];
163
- for(var i=0;i<selectors.length;i++){
164
- var el=document.querySelector(selectors[i]);
165
- if(el){try{el.click();}catch(e){}break;}
166
- }
167
- })()
168
- `
169
- }).catch(() => { });
170
- const { frameTree } = await session.Page.getFrameTree();
171
- const finalUrl = frameTree.frame.url;
172
- let title = parsed.hostname;
173
- try {
174
- const titleRes = await session.Runtime.evaluate({ expression: 'document.title', returnByValue: true });
175
- if (titleRes?.result?.value) {
176
- title = titleRes.result.value;
177
- }
178
- }
179
- catch { }
169
+ // Parallelize post-navigation metadata & cookie banner dismissal
170
+ const [frameRes, titleRes] = await Promise.all([
171
+ session.Page.getFrameTree().catch(() => ({ frameTree: { frame: { url: parsed.href } } })),
172
+ session.Runtime.evaluate({ expression: 'document.title', returnByValue: true }).catch(() => null),
173
+ session.Runtime.evaluate({
174
+ expression: `
175
+ (function(){
176
+ var selectors = [
177
+ '[id*="cookie"] button','[class*="cookie"] button',
178
+ '[id*="consent"] button','[class*="consent"] button',
179
+ '[id*="gdpr"] button','[class*="gdpr"] button',
180
+ '[aria-label*="Accept"]','[aria-label*="Agree"]',
181
+ 'button[data-cookiebanner]'
182
+ ];
183
+ for(var i=0;i<selectors.length;i++){
184
+ var el=document.querySelector(selectors[i]);
185
+ if(el){try{el.click();}catch(e){}break;}
186
+ }
187
+ })()
188
+ `
189
+ }).catch(() => { })
190
+ ]);
191
+ const finalUrl = frameRes?.frameTree?.frame?.url || parsed.href;
192
+ const title = titleRes?.result?.value || parsed.hostname;
180
193
  // Keep session registry in sync with the new URL
181
194
  if (active)
182
195
  active.url = finalUrl;
package/dist/tools/pdf.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getOutputDir } from "../config/loader.js";
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
4
+ import { hideWeaveTabOverlay, restoreWeaveTabOverlay, maskSecretFieldsForCapture, restoreSecretFieldsAfterCapture } from "../cdp/helpers.js";
5
5
  export async function browserPdf(session, options, config) {
6
6
  // Always hide the overlay before generating PDF (agent must never see it).
7
7
  // Set hideOverlay: false explicitly to keep overlays visible.
@@ -9,18 +9,26 @@ export async function browserPdf(session, options, config) {
9
9
  if (shouldHide) {
10
10
  await hideWeaveTabOverlay(session);
11
11
  }
12
- const { data } = await session.Page.printToPDF({
13
- landscape: options.landscape ?? false,
14
- scale: options.scale ?? 1.0,
15
- pageRanges: options.pageRanges ?? "",
16
- marginTop: 0.4, // 1cm ish
17
- marginBottom: 0.4,
18
- marginLeft: 0.4,
19
- marginRight: 0.4,
20
- printBackground: true
21
- });
22
- if (shouldHide) {
23
- await restoreWeaveTabOverlay(session);
12
+ await maskSecretFieldsForCapture(session);
13
+ let data;
14
+ try {
15
+ const pdfRes = await session.Page.printToPDF({
16
+ landscape: options.landscape ?? false,
17
+ scale: options.scale ?? 1.0,
18
+ pageRanges: options.pageRanges ?? "",
19
+ marginTop: 0.4, // 1cm ish
20
+ marginBottom: 0.4,
21
+ marginLeft: 0.4,
22
+ marginRight: 0.4,
23
+ printBackground: true
24
+ });
25
+ data = pdfRes.data;
26
+ }
27
+ finally {
28
+ await restoreSecretFieldsAfterCapture(session);
29
+ if (shouldHide) {
30
+ await restoreWeaveTabOverlay(session);
31
+ }
24
32
  }
25
33
  // Use configured PDF directory or default
26
34
  const pdfsDir = getOutputDir("pdfs", config);
@@ -19,8 +19,18 @@ export interface PointerResult {
19
19
  url: string;
20
20
  dom_changed?: boolean;
21
21
  }
22
- export declare function browserPointer(session: CDP.Client, args: {
23
- action: "click" | "double_click" | "hover" | "drag_and_drop";
22
+ export interface PointerStep {
23
+ action?: "hover" | "click" | "double_click";
24
+ ref?: string;
25
+ selector?: string;
26
+ target?: string;
27
+ label?: string;
28
+ x?: number;
29
+ y?: number;
30
+ holdMs?: number;
31
+ }
32
+ export interface PointerArgs {
33
+ action?: "click" | "double_click" | "hover" | "drag_and_drop" | "hover_chain";
24
34
  ref?: string;
25
35
  selector?: string;
26
36
  target?: string;
@@ -32,4 +42,9 @@ export declare function browserPointer(session: CDP.Client, args: {
32
42
  end_y?: number;
33
43
  waitForChild?: string;
34
44
  holdMs?: number;
35
- }, config: Config): Promise<PointerResult>;
45
+ hover_chain?: Array<string | PointerStep>;
46
+ chain?: Array<string | PointerStep>;
47
+ clickFinal?: boolean;
48
+ [key: string]: unknown;
49
+ }
50
+ export declare function browserPointer(session: CDP.Client, args: PointerArgs, config: Config): Promise<PointerResult>;
@@ -117,7 +117,95 @@ async function resolveRefToCoords(session, targetId, ref, currentUrl) {
117
117
  return null;
118
118
  }
119
119
  export async function browserPointer(session, args, config) {
120
- let { action, ref, selector, label, x, y, end_ref, end_x, end_y, waitForChild, holdMs = 300 } = args;
120
+ let { action = "click", ref, selector, label, x, y, end_ref, end_x, end_y, waitForChild, holdMs = 200 } = args;
121
+ // ── HOVER CHAIN / SUBMENU NAVIGATION (Photopea, Figma, Photoshop, Flyout Menus) ──
122
+ const rawChain = args.hover_chain || args.chain;
123
+ if (action === "hover_chain" || (rawChain && rawChain.length > 0)) {
124
+ const chainItems = (rawChain || []).map((step, idx, arr) => {
125
+ if (typeof step === "string") {
126
+ const isLast = idx === arr.length - 1;
127
+ return {
128
+ action: isLast && args.clickFinal !== false ? "click" : "hover",
129
+ ref: step,
130
+ label: step,
131
+ target: step
132
+ };
133
+ }
134
+ return step;
135
+ });
136
+ const startMs = Date.now();
137
+ const { Page, Input } = session;
138
+ let targetId = "default";
139
+ const active = sessionRegistry.getActive(getConfigAgentId(config));
140
+ if (active)
141
+ targetId = active.targetId;
142
+ let lastX = 0, lastY = 0;
143
+ for (let i = 0; i < chainItems.length; i++) {
144
+ const step = chainItems[i];
145
+ const stepTarget = step.ref || step.selector || step.target || step.label;
146
+ let stepCoords = null;
147
+ if (step.x !== undefined && step.y !== undefined) {
148
+ stepCoords = { x: step.x, y: step.y, selector: step.selector || '' };
149
+ }
150
+ else if (stepTarget) {
151
+ stepCoords = await resolveRefToCoords(session, targetId, stepTarget).catch(() => null);
152
+ if (!stepCoords) {
153
+ // Submenu might have rendered after previous hover; quick retry with fresh map
154
+ await new Promise(r => setTimeout(r, 80));
155
+ await buildActionMap(session, { lite: true }, targetId).then(m => cacheActionMap(targetId, m)).catch(() => { });
156
+ stepCoords = await resolveRefToCoords(session, targetId, stepTarget).catch(() => null);
157
+ }
158
+ }
159
+ if (!stepCoords) {
160
+ throw new Error(`[browser_pointer hover_chain] Could not resolve coordinates for chain step [${i}]: '${stepTarget}'`);
161
+ }
162
+ lastX = stepCoords.x;
163
+ lastY = stepCoords.y;
164
+ // Move pointer smoothly to element
165
+ await ghostMove(session, stepCoords.x, stepCoords.y, config, step.label, "fast");
166
+ await Input.dispatchMouseEvent({ type: "mouseMoved", x: stepCoords.x, y: stepCoords.y, pointerType: "mouse" });
167
+ // Dispatch mouseenter/mouseover events for dynamic flyout/hover menus
168
+ if (stepCoords.selector) {
169
+ await session.Runtime.evaluate({
170
+ expression: `(function(sel, cx, cy) {
171
+ const el = document.querySelector(sel) || (window.__Weavetab_shadowQuery ? window.__Weavetab_shadowQuery(sel) : null);
172
+ if (!el) return;
173
+ el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: false, cancelable: true, clientX: cx, clientY: cy }));
174
+ el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true, cancelable: true, clientX: cx, clientY: cy }));
175
+ el.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, cancelable: true, clientX: cx, clientY: cy }));
176
+ })(${JSON.stringify(stepCoords.selector)}, ${stepCoords.x}, ${stepCoords.y})`,
177
+ returnByValue: false
178
+ }).catch(() => { });
179
+ }
180
+ const stepHold = step.holdMs ?? (i === chainItems.length - 1 ? 40 : 100);
181
+ await new Promise(r => setTimeout(r, stepHold));
182
+ // If action is click (e.g. final step in chain)
183
+ if (step.action === "click") {
184
+ await Input.dispatchMouseEvent({ type: "mousePressed", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 1, pointerType: "mouse" });
185
+ await new Promise(r => setTimeout(r, 40));
186
+ await Input.dispatchMouseEvent({ type: "mouseReleased", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 1, pointerType: "mouse" });
187
+ }
188
+ else if (step.action === "double_click") {
189
+ await Input.dispatchMouseEvent({ type: "mousePressed", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 1, pointerType: "mouse" });
190
+ await Input.dispatchMouseEvent({ type: "mouseReleased", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 1, pointerType: "mouse" });
191
+ await new Promise(r => setTimeout(r, 60));
192
+ await Input.dispatchMouseEvent({ type: "mousePressed", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 2, pointerType: "mouse" });
193
+ await Input.dispatchMouseEvent({ type: "mouseReleased", x: stepCoords.x, y: stepCoords.y, button: "left", clickCount: 2, pointerType: "mouse" });
194
+ }
195
+ }
196
+ const execMs = Date.now() - startMs;
197
+ const { frameTree: postFrameTree } = await Page.getFrameTree().catch(() => ({ frameTree: { frame: { url: '' } } }));
198
+ return {
199
+ success: true,
200
+ action: "hover_chain",
201
+ x: lastX,
202
+ y: lastY,
203
+ execution_ms: execMs,
204
+ tab_id: targetId,
205
+ url: postFrameTree.frame.url,
206
+ dom_changed: true,
207
+ };
208
+ }
121
209
  if (selector && !ref) {
122
210
  ref = selector;
123
211
  }
@@ -4,7 +4,25 @@
4
4
  * Weavetab by fy2ne
5
5
  */
6
6
  import CDP from "chrome-remote-interface";
7
+ import { type MarkEntry } from "../intelligence/marks.js";
7
8
  import type { Config } from "../config/loader.js";
9
+ export interface VisualMark {
10
+ mark: number;
11
+ x: number;
12
+ y: number;
13
+ width: number;
14
+ height: number;
15
+ ref?: string;
16
+ label?: string;
17
+ type?: MarkEntry["type"];
18
+ }
19
+ export interface BrowserMapResult {
20
+ text: string;
21
+ /** Base64 JPEG of the page annotated with numbered SoM badges. Only present in visual mode. */
22
+ imageBase64?: string;
23
+ /** Structured mark list — numbers match badges visible on the screenshot. */
24
+ marks?: VisualMark[];
25
+ }
8
26
  export interface ReadOptions {
9
27
  lite?: boolean;
10
28
  full?: boolean;
@@ -14,17 +32,19 @@ export interface ReadOptions {
14
32
  delta?: boolean;
15
33
  intent?: string;
16
34
  ax_tree?: boolean;
17
- /** Debug: draws colored element borders via extension. The screen glow (blue edge fade) is the standard visual indicator, shown automatically during reading states. */
35
+ /** Debug: draws colored element borders via extension. */
18
36
  show_overlays?: boolean;
19
- /** Force deep shadow DOM traversal (crosses all shadow boundaries to find elements). Auto-enabled when flat query returns sparse results. */
37
+ /** Force deep shadow DOM traversal. */
20
38
  deep?: boolean;
39
+ /** Semantic DOM pruning. Defaults to true to drop non-interactive noise and SVGs. */
40
+ prune?: boolean;
21
41
  /**
22
- * Run one-shot semantic DOM pruning before returning elements.
23
- * Drops hidden elements with no interactive descendants and no known toggle-trigger.
24
- * Replaces SVG icons with [SVG Icon: label] tokens.
25
- * NOTE: distinct from delta — delta diffs across time, prune compresses the current snapshot once.
42
+ * Visual grounding mode (Set-of-Marks).
43
+ * - true → always inject numbered badges + screenshot.
44
+ * - "auto" → activate only when DOM interactive count < 5 (canvas/WebGL pages).
45
+ * Use browser_click({ mark: N }) to interact with numbered marks.
26
46
  */
27
- prune?: boolean;
47
+ visual?: boolean | "auto";
28
48
  }
29
- export declare function browserMap(session: CDP.Client, options: ReadOptions | undefined, targetId: string | undefined, config: Config): Promise<string>;
49
+ export declare function browserMap(session: CDP.Client, options: ReadOptions | undefined, targetId: string | undefined, config: Config): Promise<string | BrowserMapResult>;
30
50
  export { browserMap as weaveRead };