@weavetab/mcp 2.5.0-beta.2 → 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 (113) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +6 -6
  3. package/LICENSE +1 -1
  4. package/README.md +158 -961
  5. package/SECURITY.md +33 -22
  6. package/TROUBLESHOOTING.md +2 -2
  7. package/dist/cdp/bridge.js +2 -1
  8. package/dist/cdp/connector.d.ts +2 -0
  9. package/dist/cdp/connector.js +8 -4
  10. package/dist/cdp/helpers.d.ts +4 -0
  11. package/dist/cdp/helpers.js +181 -0
  12. package/dist/cdp/omni_broker.d.ts +1 -1
  13. package/dist/cdp/omni_broker.js +1 -1
  14. package/dist/cdp/walker.d.ts +25 -1
  15. package/dist/cdp/walker.js +304 -7
  16. package/dist/cli/plugin.js +24 -17
  17. package/dist/cli/updater.d.ts +1 -1
  18. package/dist/cli/updater.js +27 -7
  19. package/dist/config/loader.d.ts +8 -0
  20. package/dist/config/loader.js +41 -2
  21. package/dist/desktop/discovery.d.ts +44 -0
  22. package/dist/desktop/discovery.js +364 -0
  23. package/dist/index.js +95 -15
  24. package/dist/intelligence/marks.d.ts +36 -0
  25. package/dist/intelligence/marks.js +30 -0
  26. package/dist/intelligence/patternCompiler.d.ts +118 -0
  27. package/dist/intelligence/patternCompiler.js +259 -0
  28. package/dist/intelligence/sessionMemory.d.ts +2 -0
  29. package/dist/intelligence/sessionMemory.js +18 -8
  30. package/dist/overlay/injector.js +2 -2
  31. package/dist/plugin/interface.d.ts +76 -2
  32. package/dist/plugin/loader.d.ts +15 -1
  33. package/dist/plugin/loader.js +217 -70
  34. package/dist/plugin/pipeline.d.ts +75 -0
  35. package/dist/plugin/pipeline.js +183 -0
  36. package/dist/plugin/registry.js +3 -2
  37. package/dist/security/secrets.d.ts +1 -0
  38. package/dist/security/secrets.js +13 -0
  39. package/dist/sensors/actionability.js +1 -2
  40. package/dist/sensors/guard.js +4 -10
  41. package/dist/sensors/mutations.js +12 -0
  42. package/dist/sensors/operation_monitor.js +1 -0
  43. package/dist/server.d.ts +1 -1
  44. package/dist/server.js +428 -156
  45. package/dist/state/agent.d.ts +1 -0
  46. package/dist/state/agent.js +3 -0
  47. package/dist/state/delta.d.ts +12 -4
  48. package/dist/state/delta.js +67 -14
  49. package/dist/state/session.d.ts +6 -0
  50. package/dist/state/session.js +27 -0
  51. package/dist/tools/browser_detect.d.ts +16 -3
  52. package/dist/tools/browser_detect.js +50 -5
  53. package/dist/tools/burst.d.ts +17 -8
  54. package/dist/tools/burst.js +236 -77
  55. package/dist/tools/checkpoint.d.ts +38 -0
  56. package/dist/tools/checkpoint.js +271 -0
  57. package/dist/tools/click.d.ts +1 -0
  58. package/dist/tools/click.js +74 -70
  59. package/dist/tools/design_extract.js +1 -1
  60. package/dist/tools/eval.js +1 -1
  61. package/dist/tools/fill.d.ts +20 -1
  62. package/dist/tools/fill.js +199 -40
  63. package/dist/tools/find.d.ts +16 -0
  64. package/dist/tools/find.js +67 -44
  65. package/dist/tools/github/index.d.ts +19 -4
  66. package/dist/tools/github/index.js +40 -4
  67. package/dist/tools/highlight.d.ts +6 -2
  68. package/dist/tools/highlight.js +158 -72
  69. package/dist/tools/inspect.js +1 -1
  70. package/dist/tools/key.js +65 -56
  71. package/dist/tools/navigate.js +29 -34
  72. package/dist/tools/pdf.js +21 -13
  73. package/dist/tools/pointer.d.ts +18 -3
  74. package/dist/tools/pointer.js +89 -1
  75. package/dist/tools/read.d.ts +28 -8
  76. package/dist/tools/read.js +230 -56
  77. package/dist/tools/scrape.js +67 -13
  78. package/dist/tools/screenshot.js +21 -16
  79. package/dist/tools/scroll.js +6 -11
  80. package/dist/tools/select.js +8 -2
  81. package/dist/tools/snapshot.js +3 -34
  82. package/dist/tools/tabs.js +14 -6
  83. package/dist/tools/thought.d.ts +2 -0
  84. package/dist/tools/thought.js +13 -14
  85. package/dist/tools/type.d.ts +2 -0
  86. package/dist/tools/type.js +107 -58
  87. package/dist/tools/type_secret.js +42 -76
  88. package/dist/tools/upload.js +1 -1
  89. package/dist/tools/video_compiler.d.ts +1 -1
  90. package/dist/tools/video_compiler.js +11 -12
  91. package/dist/version.d.ts +5 -0
  92. package/dist/version.js +5 -0
  93. package/docs/DOCUMENTATION.md +21 -32
  94. package/docs/PLUGINS.md +121 -0
  95. package/docs/TOOLS.md +56 -0
  96. package/docs/architecture/blind-injection.md +83 -20
  97. package/docs/architecture/overview.md +49 -49
  98. package/docs/configuration/folders.md +55 -54
  99. package/docs/desktop-automation/electron-apps.md +217 -162
  100. package/docs/get-started/introduction.md +60 -60
  101. package/docs/intelligence/site-profiles.md +151 -0
  102. package/docs/tools/tools.md +631 -293
  103. package/docs/troubleshooting/trouble-loop.md +31 -31
  104. package/ext-dist/background.js +10 -62
  105. package/ext-dist/content.js +333 -920
  106. package/ext-dist/hud/hud.js +9 -9
  107. package/ext-dist/manifest.json +2 -6
  108. package/ext-dist/styles.css +6 -1
  109. package/mcp.json +101 -67
  110. package/package.json +5 -6
  111. package/server.json +3 -3
  112. package/ext-dist/sidepanel/sidepanel.js +0 -25
  113. package/ext-dist/sidepanel.html +0 -547
@@ -1,3 +1,6 @@
1
+ import { getConfigAgentId } from "../config/loader.js";
2
+ import { SessionSecretTracker } from "../security/secrets.js";
3
+ import { sessionRegistry } from "../state/session.js";
1
4
  export async function browserScrape(session, options, _config) {
2
5
  const { query, type = "css", attributes = ["textContent"], multiple = true } = options;
3
6
  const res = await session.Runtime.evaluate({
@@ -34,14 +37,20 @@ export async function browserScrape(session, options, _config) {
34
37
  }
35
38
 
36
39
  const attrs = ${JSON.stringify(attributes)};
40
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
37
41
  nodes.forEach(node => {
38
42
  const item = {};
39
43
 
40
44
  // Secret containment: redact any element marked as sensitive.
41
- // data-wt-secret is set by browser_type_secret at injection time.
42
45
  const isSecret = node.hasAttribute && (
43
46
  node.hasAttribute('data-wt-secret') ||
44
- (node.tagName === 'INPUT' && node.type === 'password')
47
+ (node.tagName === 'INPUT' && (
48
+ node.type === 'password' ||
49
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
50
+ secretRegex.test(node.getAttribute('name') || '') ||
51
+ secretRegex.test(node.getAttribute('id') || '') ||
52
+ secretRegex.test(node.getAttribute('aria-label') || '')
53
+ ))
45
54
  );
46
55
 
47
56
  if (isSecret) {
@@ -53,6 +62,9 @@ export async function browserScrape(session, options, _config) {
53
62
  item[attr] = node[attr]?.trim() || '';
54
63
  } else if (attr === 'outerHTML') {
55
64
  item[attr] = node.outerHTML;
65
+ } else if (attr === 'value') {
66
+ // Read live DOM property (not HTML attribute) so getter overrides work
67
+ item[attr] = node.value !== undefined ? String(node.value) : '';
56
68
  } else {
57
69
  item[attr] = node.getAttribute(attr) || node[attr] || '';
58
70
  }
@@ -98,6 +110,7 @@ export async function browserScrape(session, options, _config) {
98
110
  const fullExpr = type === 'xpath'
99
111
  ? `(function(){
100
112
  var attrs = ${JSON.stringify(attributes)};
113
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
101
114
  var snap = document.evaluate(${JSON.stringify(query)}, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
102
115
  var limit = ${multiple} ? snap.snapshotLength : Math.min(snap.snapshotLength, 1);
103
116
  var results = [];
@@ -105,11 +118,25 @@ export async function browserScrape(session, options, _config) {
105
118
  var node = snap.snapshotItem(i);
106
119
  if (!node || node.nodeType !== 1) continue;
107
120
  var item = {};
108
- attrs.forEach(function(attr) {
109
- if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
110
- else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
111
- else { item[attr] = node.getAttribute ? (node.getAttribute(attr) || node[attr] || '') : ''; }
112
- });
121
+ var isSecret = node.hasAttribute && (
122
+ node.hasAttribute('data-wt-secret') ||
123
+ (node.tagName === 'INPUT' && (
124
+ node.type === 'password' ||
125
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
126
+ secretRegex.test(node.getAttribute('name') || '') ||
127
+ secretRegex.test(node.getAttribute('id') || '') ||
128
+ secretRegex.test(node.getAttribute('aria-label') || '')
129
+ ))
130
+ );
131
+ if (isSecret) {
132
+ attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
133
+ } else {
134
+ attrs.forEach(function(attr) {
135
+ if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
136
+ else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
137
+ else { item[attr] = node.getAttribute ? (node.getAttribute(attr) || node[attr] || '') : ''; }
138
+ });
139
+ }
113
140
  results.push(item);
114
141
  }
115
142
  return { data: results };
@@ -120,13 +147,28 @@ export async function browserScrape(session, options, _config) {
120
147
  try { nodes = Array.from(window.__Weavetab_shadowQuery_all(${JSON.stringify(query)})); } catch(_){}
121
148
  }
122
149
  var attrs = ${JSON.stringify(attributes)};
150
+ var secretRegex = /password|passcode|pin|passphrase|secret|token|auth.?key|access.?code|mot.?de.?passe|contrase[nñ]a|kennwort|senha/i;
123
151
  return { data: nodes.map(function(node) {
124
152
  var item = {};
125
- attrs.forEach(function(attr) {
126
- if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
127
- else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
128
- else { item[attr] = node.getAttribute(attr) || node[attr] || ''; }
129
- });
153
+ var isSecret = node.hasAttribute && (
154
+ node.hasAttribute('data-wt-secret') ||
155
+ (node.tagName === 'INPUT' && (
156
+ node.type === 'password' ||
157
+ secretRegex.test(node.getAttribute('autocomplete') || '') ||
158
+ secretRegex.test(node.getAttribute('name') || '') ||
159
+ secretRegex.test(node.getAttribute('id') || '') ||
160
+ secretRegex.test(node.getAttribute('aria-label') || '')
161
+ ))
162
+ );
163
+ if (isSecret) {
164
+ attrs.forEach(function(attr) { item[attr] = '[REDACTED]'; });
165
+ } else {
166
+ attrs.forEach(function(attr) {
167
+ if (attr === 'textContent' || attr === 'innerText') { item[attr] = (node[attr] || '').trim(); }
168
+ else if (attr === 'outerHTML') { item[attr] = node.outerHTML; }
169
+ else { item[attr] = node.getAttribute(attr) || node[attr] || ''; }
170
+ });
171
+ }
130
172
  return item;
131
173
  })};
132
174
  })()`;
@@ -136,5 +178,17 @@ export async function browserScrape(session, options, _config) {
136
178
  }
137
179
  }
138
180
  }
139
- return { success: true, data: val?.data || [], message: `Extracted ${val?.data?.length || 0} elements` };
181
+ let targetId = "default";
182
+ const active = sessionRegistry.getActive(getConfigAgentId(_config));
183
+ if (active)
184
+ targetId = active.targetId;
185
+ const rawData = (val?.data || []);
186
+ const sanitizedData = rawData.map(item => {
187
+ const sanitizedItem = {};
188
+ for (const [k, v] of Object.entries(item)) {
189
+ sanitizedItem[k] = typeof v === 'string' ? SessionSecretTracker.redact(targetId, v) : v;
190
+ }
191
+ return sanitizedItem;
192
+ });
193
+ return { success: true, data: sanitizedData, message: `Extracted ${sanitizedData.length} elements` };
140
194
  }
@@ -1,4 +1,4 @@
1
- import { hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
1
+ import { hideWeaveTabOverlay, restoreWeaveTabOverlay, maskSecretFieldsForCapture, restoreSecretFieldsAfterCapture } from "../cdp/helpers.js";
2
2
  export async function browserScreenshot(session, options = {}, _config) {
3
3
  const format = options.format ?? "png";
4
4
  const quality = format === "jpeg" ? (options.quality ?? 90) : undefined;
@@ -42,33 +42,38 @@ export async function browserScreenshot(session, options = {}, _config) {
42
42
  if (clip)
43
43
  params.clip = clip;
44
44
  }
45
- // ── AI-blind: hide Weavetab overlay elements before screenshot ──────────────
45
+ // ── AI-blind: hide Weavetab overlay elements and mask secrets before screenshot ─
46
46
  // The chat panel, ghost cursor, thought bubble, HUD iframe, and all scripted
47
- // UI are FOR THE USER only. The AI must never see or interact with them.
48
- // hideOverlay defaults to true — only set to false if explicitly requested.
47
+ // UI are FOR THE USER only. Secret values are masked to prevent visual leaks.
49
48
  const shouldHide = options.hideOverlay !== false;
50
49
  if (shouldHide) {
51
50
  await hideWeaveTabOverlay(session);
52
51
  }
52
+ await maskSecretFieldsForCapture(session);
53
53
  let data;
54
54
  try {
55
- const res = await session.Page.captureScreenshot(params);
56
- data = res.data;
57
- }
58
- catch (e) {
59
- if (params.captureBeyondViewport) {
60
- params.captureBeyondViewport = false;
61
- delete params.clip;
55
+ try {
62
56
  const res = await session.Page.captureScreenshot(params);
63
57
  data = res.data;
64
58
  }
65
- else {
66
- throw e;
59
+ catch (e) {
60
+ if (params.captureBeyondViewport) {
61
+ params.captureBeyondViewport = false;
62
+ delete params.clip;
63
+ const res = await session.Page.captureScreenshot(params);
64
+ data = res.data;
65
+ }
66
+ else {
67
+ throw e;
68
+ }
67
69
  }
68
70
  }
69
- // Restore overlay visibility immediately after capture
70
- if (shouldHide) {
71
- await restoreWeaveTabOverlay(session);
71
+ finally {
72
+ // Restore secret fields and overlay visibility immediately after capture
73
+ await restoreSecretFieldsAfterCapture(session);
74
+ if (shouldHide) {
75
+ await restoreWeaveTabOverlay(session);
76
+ }
72
77
  }
73
78
  // ─────────────────────────────────────────────────────────────────────────────
74
79
  const width = clip ? clip.width : vp.w;
@@ -5,8 +5,7 @@
5
5
  */
6
6
  import { buildActionMap, findBestMatch, safeSelector } from "../cdp/walker.js";
7
7
  import { getCachedActionMap, cacheActionMap } from "../state/cache.js";
8
- import { computeDelta, storeSnapshot } from "../state/delta.js";
9
- import { waitForDOMStable } from "../sensors/guard.js";
8
+ import { storeSnapshot } from "../state/delta.js";
10
9
  import { sessionRegistry } from "../state/session.js";
11
10
  import { logAction } from "../audit/logger.js";
12
11
  import { getConfigAgentId } from "../config/loader.js";
@@ -175,22 +174,18 @@ export async function browserScroll(session, args, config) {
175
174
  x: vp.x, y: vp.y,
176
175
  deltaX: chunkX, deltaY: chunkY
177
176
  });
178
- await sleep(profile.baseDelay + Math.random() * 8);
177
+ await sleep(profile.baseDelay + Math.random() * 4);
179
178
  }
180
179
  }
181
- await waitForDOMStable(session, 80, 600).catch(() => { });
182
- await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } });
183
- const nextMap = await buildActionMap(session, { lite: true }, targetId);
184
- cacheActionMap(targetId, nextMap);
185
- const diff = computeDelta(targetId, nextMap.url, nextMap.elements);
186
- const changesCount = diff ? diff.added.length + diff.removed.length + diff.changed.length : 0;
180
+ await sleep(40);
181
+ await sendToExtension(session, { type: 'Weavetab_STATE', hud: { action: '' } }).catch(() => { });
187
182
  logAction("browser_scroll", direction, `${distance}px`);
188
183
  return {
189
184
  success: true,
190
185
  scrolled: id ?? label ?? "window",
191
186
  direction,
192
187
  distance,
193
- new_visible_count: nextMap.elements.length,
194
- dom_changed: changesCount > 0,
188
+ new_visible_count: 0,
189
+ dom_changed: true,
195
190
  };
196
191
  }
@@ -9,6 +9,12 @@ async function getSelector(session, options, config) {
9
9
  return options.selector;
10
10
  if (options.ref || options.id) {
11
11
  const ref = (options.ref || options.id);
12
+ if (ref.startsWith("w:")) {
13
+ return `[data-wt-id="${ref.slice(2)}"]`;
14
+ }
15
+ if (ref.startsWith("#") || ref.startsWith(".") || ref.startsWith("[")) {
16
+ return ref;
17
+ }
12
18
  let targetId = "default";
13
19
  const active = sessionRegistry.getActive(getConfigAgentId(config));
14
20
  if (active)
@@ -20,13 +26,13 @@ async function getSelector(session, options, config) {
20
26
  map = await buildActionMap(session, { lite: true }, targetId);
21
27
  cacheActionMap(targetId, map);
22
28
  }
23
- const element = map.elements.find(el => el.id === ref || el.id.endsWith(ref.startsWith("w:") ? ref.slice(2) : ref));
29
+ const element = map.elements.find(el => el.id === ref || el.label === ref || el.id.endsWith(ref.startsWith("w:") ? ref.slice(2) : ref));
24
30
  if (element) {
25
31
  const sel = safeSelector(element);
26
32
  if (sel)
27
33
  return sel;
28
34
  }
29
- throw new Error(`Could not resolve element for ref/id: ${ref}`);
35
+ return `[name="${ref}"], #${ref}, [aria-label="${ref}"]`;
30
36
  }
31
37
  if (options.nodeId) {
32
38
  const { nodeIds } = await session.DOM.pushNodesByBackendIdsToFrontend({ backendNodeIds: [options.nodeId] });
@@ -1,7 +1,7 @@
1
1
  import fs from "fs/promises";
2
2
  import path from "path";
3
3
  import { getOutputDir, getConfigAgentId } from "../config/loader.js";
4
- import { hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
4
+ import { hideWeaveTabOverlay, restoreWeaveTabOverlay, maskSecretFieldsForCapture, restoreSecretFieldsAfterCapture } from "../cdp/helpers.js";
5
5
  import { clearTabCache } from "../state/cache.js";
6
6
  import { clearSnapshot } from "../state/delta.js";
7
7
  import { sessionRegistry } from "../state/session.js";
@@ -28,22 +28,7 @@ export async function browserSnapshot(session, options, config) {
28
28
  }
29
29
  // ── Blank secret field values before capture ──────────────────────────
30
30
  // Prevents credential values from being embedded in the MHTML archive.
31
- // data-wt-secret is set by browser_type_secret at injection time.
32
- await session.Runtime.evaluate({
33
- expression: `(function() {
34
- var els = document.querySelectorAll('[data-wt-secret], input[type="password"]');
35
- for (var i = 0; i < els.length; i++) {
36
- var el = els[i];
37
- el.__wt_snap_backup = el.value;
38
- try { el.value = ''; } catch(e) {}
39
- if (el.isContentEditable) {
40
- el.__wt_snap_text_backup = el.textContent;
41
- try { el.textContent = ''; } catch(e) {}
42
- }
43
- }
44
- })()`,
45
- returnByValue: false,
46
- }).catch(() => { });
31
+ await maskSecretFieldsForCapture(session);
47
32
  let snapshotData;
48
33
  try {
49
34
  const res = await session.Page.captureSnapshot({ format: "mhtml" });
@@ -51,26 +36,10 @@ export async function browserSnapshot(session, options, config) {
51
36
  }
52
37
  finally {
53
38
  // ── Restore secret field values after capture (always runs) ──────────
39
+ await restoreSecretFieldsAfterCapture(session);
54
40
  if (shouldHide) {
55
41
  await restoreWeaveTabOverlay(session);
56
42
  }
57
- await session.Runtime.evaluate({
58
- expression: `(function() {
59
- var els = document.querySelectorAll('[data-wt-secret], input[type="password"]');
60
- for (var i = 0; i < els.length; i++) {
61
- var el = els[i];
62
- if (el.__wt_snap_backup !== undefined) {
63
- try { el.value = el.__wt_snap_backup; } catch(e) {}
64
- delete el.__wt_snap_backup;
65
- }
66
- if (el.__wt_snap_text_backup !== undefined) {
67
- try { el.textContent = el.__wt_snap_text_backup; } catch(e) {}
68
- delete el.__wt_snap_text_backup;
69
- }
70
- }
71
- })()`,
72
- returnByValue: false,
73
- }).catch(() => { });
74
43
  }
75
44
  await fs.mkdir(path.dirname(filePath), { recursive: true });
76
45
  await fs.writeFile(filePath, snapshotData, "utf8");
@@ -111,16 +111,24 @@ async function enforceMaxTabs(config) {
111
111
  }
112
112
  // ─── Tools ────────────────────────────────────────────────────────────────────
113
113
  export async function WeavetabOpen(url, config, reuse = true, appName) {
114
- const port = resolvePort(config, appName);
114
+ // Infer appName from active session if omitted
115
+ let resolvedAppName = appName;
116
+ if (!resolvedAppName) {
117
+ const lastApp = sessionRegistry.getLastActiveApp();
118
+ if (lastApp) {
119
+ resolvedAppName = lastApp;
120
+ }
121
+ }
122
+ const port = resolvePort(config, resolvedAppName);
115
123
  // Tab reuse: if same URL is already open, focus it instead of duplicating
116
124
  if (reuse && url) {
117
125
  try {
118
- const targets = await getTabList(getConfigAgentId(config), appName);
126
+ const targets = await getTabList(getConfigAgentId(config), resolvedAppName);
119
127
  const existing = targets.find((t) => t.url === url);
120
128
  if (existing) {
121
- const client = await connectToTab(existing.id, undefined, undefined, undefined, appName);
129
+ const client = await connectToTab(existing.id, undefined, undefined, undefined, resolvedAppName);
122
130
  await client.session.Target.activateTarget({ targetId: existing.id });
123
- sessionRegistry.setActive(existing.id, getConfigAgentId(config), appName);
131
+ sessionRegistry.setActive(existing.id, getConfigAgentId(config), resolvedAppName);
124
132
  recordTabActivity(existing.id);
125
133
  logAction("browser_tabs", url, `reused existing tab ${existing.id}`);
126
134
  return { success: true, tabId: existing.id, url, reused: true };
@@ -128,10 +136,10 @@ export async function WeavetabOpen(url, config, reuse = true, appName) {
128
136
  }
129
137
  catch { /* fall through to open new */ }
130
138
  }
131
- if (!appName) {
139
+ if (!resolvedAppName) {
132
140
  await enforceMaxTabs(config);
133
141
  }
134
- const bctxId = appName ? undefined : sessionRegistry.getBrowserContextIdForAgent(getConfigAgentId(config));
142
+ const bctxId = resolvedAppName ? undefined : sessionRegistry.getBrowserContextIdForAgent(getConfigAgentId(config));
135
143
  const metaSession = await CDP({ host: "127.0.0.1", port });
136
144
  const { targetId } = await metaSession.Target.createTarget({ url, browserContextId: bctxId });
137
145
  await metaSession.close();
@@ -2,6 +2,8 @@ import CDP from "chrome-remote-interface";
2
2
  export interface ThoughtOptions {
3
3
  action: "feedback" | "question";
4
4
  message: string;
5
+ anchor?: string;
6
+ style?: "observation" | "decision" | "warning" | "question";
5
7
  input?: "sentence" | "paragraph";
6
8
  timeout?: number;
7
9
  }
@@ -7,32 +7,31 @@ function sleep(ms) {
7
7
  return new Promise(r => setTimeout(r, ms));
8
8
  }
9
9
  export async function browserThought(session, args) {
10
- const { action, message, input, timeout } = args;
10
+ const { action, message, input, timeout, anchor, style } = args;
11
11
  logAction("browser_thought", action, message);
12
12
  if (action === "feedback") {
13
- await sendToExtension(session, {
14
- type: 'Weavetab_STATE',
15
- hud: { action: message },
16
- stickyFeedback: true,
17
- }).catch(() => { });
18
- // Forward to sidepanel thought stream
13
+ // Forward thought event to extension
19
14
  await sendToExtension(session, {
20
15
  type: 'WT_THOUGHT',
21
16
  text: message,
22
17
  kind: 'thought',
18
+ anchor,
19
+ style: style || 'observation',
20
+ action: 'feedback',
23
21
  }).catch(() => { });
24
22
  return { success: true };
25
23
  }
26
24
  if (action === "question") {
27
25
  const QUESTION_TIMEOUT_MS = Math.min((timeout || 20), 20) * 1000;
28
26
  await sendToExtension(session, {
29
- type: 'Weavetab_STATE',
30
- hud: { action: message },
31
- stickyFeedback: true,
32
- ask: {
33
- input: input || "sentence",
34
- timeout: Math.min((timeout || 20), 20),
35
- },
27
+ type: 'WT_THOUGHT',
28
+ text: message,
29
+ kind: 'question',
30
+ anchor,
31
+ style: style || 'question',
32
+ action: 'question',
33
+ input: input || "sentence",
34
+ timeout: Math.min((timeout || 20), 20),
36
35
  }).catch(() => { });
37
36
  const start = Date.now();
38
37
  while (Date.now() - start < QUESTION_TIMEOUT_MS + 5000) {
@@ -48,6 +48,8 @@ export declare function detectFramework(session: CDP.Client): Promise<'react' |
48
48
  export declare function frameworkAwareType(session: CDP.Client, objectId: string, text: string, framework: 'react' | 'vue' | 'angular' | 'vanilla'): Promise<boolean>;
49
49
  export declare function weaveType(session: CDP.Client, args: {
50
50
  id?: string;
51
+ target?: string;
52
+ selector?: string;
51
53
  label?: string;
52
54
  intent?: string;
53
55
  backendNodeId?: number;