@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
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Weavetab — intelligence/patternCompiler.ts
3
+ *
4
+ * Compiles raw browser session trails (action sequences) into persistent,
5
+ * reusable site workflow patterns stored under ~/.weavetab-system/memory/sites/.
6
+ *
7
+ * Design invariants:
8
+ * - One file per origin (sanitized hostname). Never creates per-path or per-run files.
9
+ * - Pattern names are caller-supplied (snake_case). Duplicate calls for the same
10
+ * name on the same origin UPDATE the existing pattern rather than creating a new file.
11
+ * - Volatile / unstable selectors (auto-generated classes, nth-child paths) are
12
+ * stripped and replaced with semantic anchors where possible.
13
+ * - Never reads or writes to ~/.weavetab/ (user config space).
14
+ */
15
+ import { readFile, writeFile } from "fs/promises";
16
+ import { join } from "path";
17
+ import fs from "fs";
18
+ import { getSystemMemorySitesDir } from "../config/loader.js";
19
+ import { logDebug } from "../audit/logger.js";
20
+ // ─── Volatile selector detection ────────────────────────────────────────────
21
+ const VOLATILE_PATTERNS = [
22
+ /\[class\*="[a-z0-9]+-[a-z0-9]+"\]/, // CSS-module hash attribute selectors
23
+ /:nth-child\(\d+\)/, // positional pseudo-selectors
24
+ /\.\w{8,}[-_]\w{4,}/, // hashed Tailwind / CSS-module class combos
25
+ /div > div > div > /, // deep anonymous structural chains (3+ levels)
26
+ ];
27
+ function isVolatileSelector(sel) {
28
+ return VOLATILE_PATTERNS.some((rx) => rx.test(sel));
29
+ }
30
+ function stabilizeSelector(sel) {
31
+ if (!sel)
32
+ return undefined;
33
+ if (isVolatileSelector(sel)) {
34
+ logDebug(`[PatternCompiler] Dropping volatile selector: ${sel}`);
35
+ return undefined;
36
+ }
37
+ return sel;
38
+ }
39
+ // ─── File path helpers ───────────────────────────────────────────────────────
40
+ function originToFilename(origin) {
41
+ return origin
42
+ .replace(/^https?:\/\//, "")
43
+ .replace(/[^a-zA-Z0-9.-]/g, "_");
44
+ }
45
+ function siteProfilePath(origin) {
46
+ const sitesDir = getSystemMemorySitesDir();
47
+ return join(sitesDir, `${originToFilename(origin)}.json`);
48
+ }
49
+ // ─── In-process LRU cache ───────────────────────────────────────────────────
50
+ const profileCache = new Map();
51
+ const CACHE_MAX = 100;
52
+ function cacheSet(origin, profile) {
53
+ if (profileCache.size >= CACHE_MAX && !profileCache.has(origin)) {
54
+ const firstKey = profileCache.keys().next().value;
55
+ if (firstKey !== undefined)
56
+ profileCache.delete(firstKey);
57
+ }
58
+ profileCache.set(origin, profile);
59
+ }
60
+ export function clearSiteProfileCache() {
61
+ profileCache.clear();
62
+ }
63
+ // ─── Public API ──────────────────────────────────────────────────────────────
64
+ /**
65
+ * Load a site profile from disk. Returns null if the site has never been profiled.
66
+ * Hits in-process cache first — no redundant disk reads within a session.
67
+ */
68
+ export async function getSiteProfile(origin) {
69
+ if (profileCache.has(origin))
70
+ return profileCache.get(origin);
71
+ const filePath = siteProfilePath(origin);
72
+ if (!fs.existsSync(filePath))
73
+ return null;
74
+ try {
75
+ const raw = await readFile(filePath, "utf-8");
76
+ const profile = JSON.parse(raw);
77
+ cacheSet(origin, profile);
78
+ return profile;
79
+ }
80
+ catch (e) {
81
+ logDebug(`[PatternCompiler] Failed to parse site profile for ${origin}: ${e}`);
82
+ return null;
83
+ }
84
+ }
85
+ /**
86
+ * Persist a site profile. Always deep-merges into existing data on disk — never
87
+ * blindly overwrites — so concurrent tool calls don't clobber each other.
88
+ */
89
+ export async function saveSiteProfile(profile) {
90
+ const filePath = siteProfilePath(profile.origin);
91
+ let base = null;
92
+ if (fs.existsSync(filePath)) {
93
+ try {
94
+ base = JSON.parse(await readFile(filePath, "utf-8"));
95
+ }
96
+ catch { /* treat corrupt file as empty */ }
97
+ }
98
+ const merged = {
99
+ ...base,
100
+ ...profile,
101
+ origin: profile.origin,
102
+ patterns: { ...(base?.patterns ?? {}), ...profile.patterns },
103
+ quirks: { ...(base?.quirks ?? {}), ...profile.quirks },
104
+ visitCount: profile.visitCount ?? base?.visitCount ?? 0,
105
+ updatedAt: new Date().toISOString(),
106
+ };
107
+ cacheSet(profile.origin, merged);
108
+ await writeFile(filePath, JSON.stringify(merged, null, 2), "utf-8");
109
+ logDebug(`[PatternCompiler] Saved site profile → ${filePath}`);
110
+ }
111
+ /**
112
+ * Learn or update a named workflow pattern for a given origin.
113
+ * - Duplicate name = merge/update, never duplicate file.
114
+ * - Volatile selectors in steps are dropped to keep patterns stable.
115
+ * - Anti-spam: skips disk write when content is identical to existing.
116
+ */
117
+ export async function learnPattern(origin, name, incoming) {
118
+ const profile = (await getSiteProfile(origin)) ?? {
119
+ origin,
120
+ quirks: {},
121
+ patterns: {},
122
+ visitCount: 0,
123
+ updatedAt: new Date().toISOString(),
124
+ };
125
+ const existing = profile.patterns[name] ?? {
126
+ name,
127
+ steps: [],
128
+ reliability: 1.0,
129
+ strikes: 0,
130
+ last_verified: new Date().toISOString(),
131
+ total_executions: 0,
132
+ successful_executions: 0,
133
+ };
134
+ const stabilizedSteps = (incoming.steps ?? existing.steps).map((step) => ({
135
+ ...step,
136
+ target: step.target ? stabilizeSelector(step.target) : undefined,
137
+ }));
138
+ const hasChanges = (!profile.patterns[name] ||
139
+ (incoming.steps !== undefined && JSON.stringify(stabilizedSteps) !== JSON.stringify(existing.steps)) ||
140
+ (incoming.success_indicator !== undefined && incoming.success_indicator !== existing.success_indicator) ||
141
+ (incoming.entry !== undefined && incoming.entry !== existing.entry));
142
+ if (hasChanges) {
143
+ const updated = {
144
+ ...existing,
145
+ ...incoming,
146
+ name,
147
+ steps: stabilizedSteps,
148
+ last_verified: new Date().toISOString(),
149
+ };
150
+ await saveSiteProfile({
151
+ ...profile,
152
+ patterns: { ...profile.patterns, [name]: updated },
153
+ });
154
+ logDebug(`[PatternCompiler] Pattern "${name}" learned/updated for ${origin}`);
155
+ return updated;
156
+ }
157
+ else {
158
+ logDebug(`[PatternCompiler] Pattern "${name}" unchanged — skipping write`);
159
+ return existing;
160
+ }
161
+ }
162
+ /**
163
+ * Record the outcome of executing a named pattern.
164
+ * Updates reliability via EWMA (α=0.15) and tracks consecutive strikes.
165
+ * 3 strikes → reliability will naturally fall below 0.4, agent should re-explore.
166
+ */
167
+ export async function recordPatternExecution(origin, patternName, success, durationMs) {
168
+ const profile = await getSiteProfile(origin);
169
+ if (!profile)
170
+ return null;
171
+ const pattern = profile.patterns[patternName];
172
+ if (!pattern)
173
+ return null;
174
+ const alpha = 0.15;
175
+ const newReliability = Math.round((0.85 * pattern.reliability + alpha * (success ? 1 : 0)) * 1000) / 1000;
176
+ const updated = {
177
+ ...pattern,
178
+ reliability: newReliability,
179
+ strikes: success ? 0 : pattern.strikes + 1,
180
+ last_verified: success ? new Date().toISOString() : pattern.last_verified,
181
+ total_executions: pattern.total_executions + 1,
182
+ successful_executions: success ? pattern.successful_executions + 1 : pattern.successful_executions,
183
+ avg_duration_ms: durationMs !== undefined
184
+ ? Math.round(pattern.avg_duration_ms !== undefined
185
+ ? 0.8 * pattern.avg_duration_ms + 0.2 * durationMs
186
+ : durationMs)
187
+ : pattern.avg_duration_ms,
188
+ };
189
+ if (updated.strikes >= 3) {
190
+ logDebug(`[PatternCompiler] ⚠ Pattern "${patternName}" on ${origin} hit 3 strikes (reliability: ${updated.reliability}) — re-learning recommended`);
191
+ }
192
+ await saveSiteProfile({
193
+ ...profile,
194
+ patterns: { ...profile.patterns, [patternName]: updated },
195
+ });
196
+ return updated;
197
+ }
198
+ /**
199
+ * Record or update a site quirk (cookie banner, rate limit, modal popup, etc.)
200
+ * Only writes if the stored data differs from incoming — prevents spam writes.
201
+ */
202
+ export async function recordQuirk(origin, quirkKey, quirkData) {
203
+ const profile = (await getSiteProfile(origin)) ?? {
204
+ origin,
205
+ quirks: {},
206
+ patterns: {},
207
+ visitCount: 0,
208
+ updatedAt: new Date().toISOString(),
209
+ };
210
+ const existing = profile.quirks[quirkKey];
211
+ if (JSON.stringify(existing) === JSON.stringify(quirkData)) {
212
+ logDebug(`[PatternCompiler] Quirk "${quirkKey}" unchanged — skipping write`);
213
+ return;
214
+ }
215
+ await saveSiteProfile({
216
+ ...profile,
217
+ quirks: { ...profile.quirks, [quirkKey]: quirkData },
218
+ });
219
+ logDebug(`[PatternCompiler] Recorded quirk "${quirkKey}" for ${origin}`);
220
+ }
221
+ /**
222
+ * Returns the reliability score (0–1) of a named pattern, or -1 if unknown.
223
+ */
224
+ export async function getPatternReliability(origin, patternName) {
225
+ const profile = await getSiteProfile(origin);
226
+ if (!profile)
227
+ return -1;
228
+ return profile.patterns[patternName]?.reliability ?? -1;
229
+ }
230
+ /**
231
+ * Formats a compact site profile hint string for agent system prompt injection.
232
+ * Called at session start when navigating to a known origin.
233
+ */
234
+ export function formatSiteProfileHint(profile) {
235
+ const lines = [`[SiteProfile] ${profile.origin}`];
236
+ if (profile.framework)
237
+ lines.push(` framework: ${profile.framework}`);
238
+ if (profile.captcha_frequency && profile.captcha_frequency !== "none") {
239
+ lines.push(` captcha_frequency: ${profile.captcha_frequency}`);
240
+ }
241
+ const patternNames = Object.keys(profile.patterns);
242
+ if (patternNames.length > 0) {
243
+ lines.push(" known_patterns:");
244
+ for (const name of patternNames) {
245
+ const p = profile.patterns[name];
246
+ const flag = p.strikes >= 3
247
+ ? " [RE-LEARN NEEDED]"
248
+ : p.reliability >= 0.8
249
+ ? " [fast-path eligible]"
250
+ : "";
251
+ lines.push(` - ${name}: reliability=${p.reliability.toFixed(2)}, steps=${p.steps.length}${flag}`);
252
+ }
253
+ }
254
+ const quirkKeys = Object.keys(profile.quirks).filter((k) => profile.quirks[k] !== undefined && profile.quirks[k] !== null);
255
+ if (quirkKeys.length > 0) {
256
+ lines.push(` quirks: ${quirkKeys.join(", ")}`);
257
+ }
258
+ return lines.join("\n");
259
+ }
@@ -34,6 +34,8 @@ export interface DomainProfile {
34
34
  updatedAt: string;
35
35
  visitCount: number;
36
36
  }
37
+ export declare function clearMemoryCache(): void;
38
+ export declare function getDomainFilePath(hostname: string): string;
37
39
  export declare function loadDomainProfile(hostname: string): Promise<DomainProfile | null>;
38
40
  export declare function saveDomainProfile(hostname: string, updates: Partial<DomainProfile>): Promise<DomainProfile>;
39
41
  /**
@@ -1,20 +1,30 @@
1
1
  import { readFile, writeFile } from "fs/promises";
2
2
  import { join } from "path";
3
- import { GLOBAL_CONFIG_DIR } from "../config/loader.js";
3
+ import { GLOBAL_CONFIG_DIR, getSystemDir } from "../config/loader.js";
4
4
  import { logDebug } from "../audit/logger.js";
5
5
  import fs from "fs";
6
6
  function domainFile(hostname) {
7
7
  const safe = hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
8
- const memoryDir = join(GLOBAL_CONFIG_DIR, "memory");
9
- if (!fs.existsSync(memoryDir))
10
- fs.mkdirSync(memoryDir, { recursive: true });
11
- return join(memoryDir, `${safe}.json`);
8
+ const memoryDir = getSystemDir("memory");
9
+ const systemPath = join(memoryDir, `${safe}.json`);
10
+ // Backward compatibility: migrate legacy ~/.weavetab/memory/<safe>.json if it exists
11
+ const legacyPath = join(GLOBAL_CONFIG_DIR, "memory", `${safe}.json`);
12
+ if (!fs.existsSync(systemPath) && fs.existsSync(legacyPath)) {
13
+ try {
14
+ fs.copyFileSync(legacyPath, systemPath);
15
+ }
16
+ catch { /* ignore migration error */ }
17
+ }
18
+ return systemPath;
12
19
  }
13
- // memoryCache is capped at 200 DomainProfile entries (one per unique hostname).
14
- // When the cap is hit, the oldest entry is evicted (insertion-order LRU) so the
15
- // cache can't grow without bound over a long session.
16
20
  const memoryCache = new Map();
17
21
  const MEMORY_CACHE_MAX = 200;
22
+ export function clearMemoryCache() {
23
+ memoryCache.clear();
24
+ }
25
+ export function getDomainFilePath(hostname) {
26
+ return domainFile(hostname);
27
+ }
18
28
  function memoryCacheSet(hostname, profile) {
19
29
  if (memoryCache.size >= MEMORY_CACHE_MAX && !memoryCache.has(hostname)) {
20
30
  // Evict the oldest (first) entry
@@ -1,5 +1,5 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
- import { getConfigHeadless, getConfigVisuals } from "../config/loader.js";
2
+ import { getConfigHeadless } from "../config/loader.js";
3
3
  function sleep(ms) {
4
4
  return new Promise(r => setTimeout(r, ms));
5
5
  }
@@ -378,7 +378,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
378
378
  return;
379
379
  }
380
380
  wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
381
- if (label && distance >= 150 && getConfigVisuals(config) !== false) {
381
+ if (label && distance >= 150) {
382
382
  wtThought(session, `moving to ${label.slice(0, 40)}...`);
383
383
  await sleep(60 + Math.random() * 80);
384
384
  }
@@ -1,5 +1,5 @@
1
1
  import { logDebug } from "../audit/logger.js";
2
- import { getConfigVisuals, getConfigDevMode } from "../config/loader.js";
2
+ import { getConfigDevMode } from "../config/loader.js";
3
3
  // ─── Minimal Overlay Script ───────────────────────────────────────────────────
4
4
  export function getMinimalOverlayScript() {
5
5
  return `(function(){
@@ -67,7 +67,6 @@ export function getMinimalOverlayScript() {
67
67
 
68
68
  window.__wt_setConfig = function(c) {
69
69
  if (c && c.devMode) window.__wt_devMode = true;
70
- if (c && c.visuals !== undefined) window.__wt_showVisuals = !!c.visuals;
71
70
  };
72
71
 
73
72
  window.__wt_status = function() { return JSON.stringify({ rootInjected: true }); };
@@ -121,7 +120,6 @@ function buildConfigExpr(config) {
121
120
  return `
122
121
  if(window.__wt_setConfig)window.__wt_setConfig(${JSON.stringify({
123
122
  ...config,
124
- visuals: getConfigVisuals(config) !== false,
125
123
  })});
126
124
  window.postMessage({type:'Weavetab-msg-in',data:${JSON.stringify(msg)}},'*');
127
125
  `;
@@ -154,7 +152,7 @@ export async function ensureVisualLayer(session, config, client) {
154
152
  returnByValue: true
155
153
  });
156
154
  if (checkResult?.value) {
157
- logDebug(`[Weavetab] [OK] Minimal overlay injected successfully`);
155
+ logDebug(`[Weavetab] overlay injected`);
158
156
  }
159
157
  else {
160
158
  logDebug(`[Weavetab] [!] Minimal overlay injection failed - element not found`);
@@ -173,7 +171,7 @@ export async function setupOverlay(session, config) {
173
171
  await ensureVisualLayer(session, config);
174
172
  }
175
173
  export async function setFaviconState(_session, _state) {
176
- // Handled robustly via extension content.js
174
+ // handled via extension content.js
177
175
  }
178
176
  export async function removeTitleBadge(_session) {
179
177
  // No-op: title dot prefix removed in favor of favicon indicators
@@ -0,0 +1,40 @@
1
+ import type CDP from "chrome-remote-interface";
2
+ import type { WeavetabPermissions } from "./interface.js";
3
+ export interface IPluginExtensionBridge {
4
+ send(session: CDP.Client, message: object): Promise<boolean>;
5
+ setHudState(session: CDP.Client, state: {
6
+ action?: string;
7
+ badge?: string;
8
+ badgeColor?: string;
9
+ groupState?: string;
10
+ }): Promise<void>;
11
+ showThought(session: CDP.Client, text: string, options?: {
12
+ durationMs?: number;
13
+ }): Promise<void>;
14
+ clearThought(session: CDP.Client): Promise<void>;
15
+ injectStyles(session: CDP.Client, css: string): Promise<void>;
16
+ injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
17
+ hideOverlay(session: CDP.Client): Promise<void>;
18
+ restoreOverlay(session: CDP.Client): Promise<void>;
19
+ }
20
+ export declare class PluginExtensionBridge implements IPluginExtensionBridge {
21
+ readonly pluginName: string;
22
+ private readonly permissions?;
23
+ constructor(pluginName: string, permissions?: WeavetabPermissions | undefined);
24
+ private checkPermission;
25
+ send(session: CDP.Client, message: object): Promise<boolean>;
26
+ setHudState(session: CDP.Client, state: {
27
+ action?: string;
28
+ badge?: string;
29
+ badgeColor?: string;
30
+ groupState?: string;
31
+ }): Promise<void>;
32
+ showThought(session: CDP.Client, text: string, options?: {
33
+ durationMs?: number;
34
+ }): Promise<void>;
35
+ clearThought(session: CDP.Client): Promise<void>;
36
+ injectStyles(session: CDP.Client, css: string): Promise<void>;
37
+ injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
38
+ hideOverlay(session: CDP.Client): Promise<void>;
39
+ restoreOverlay(session: CDP.Client): Promise<void>;
40
+ }
@@ -0,0 +1,158 @@
1
+ import { sendToExtension, hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
2
+ import { logDebug } from "../audit/logger.js";
3
+ export class PluginExtensionBridge {
4
+ pluginName;
5
+ permissions;
6
+ constructor(pluginName, permissions) {
7
+ this.pluginName = pluginName;
8
+ this.permissions = permissions;
9
+ }
10
+ checkPermission(category) {
11
+ if (!this.permissions || !this.permissions.extension)
12
+ return true; // Default permissive if not restricted
13
+ return this.permissions.extension[category] !== false;
14
+ }
15
+ async send(session, message) {
16
+ if (!session)
17
+ return false;
18
+ return await sendToExtension(session, {
19
+ ...message,
20
+ plugin: this.pluginName,
21
+ });
22
+ }
23
+ async setHudState(session, state) {
24
+ if (!this.checkPermission("hud")) {
25
+ logDebug(`[Weavetab PluginExtension] Plugin "${this.pluginName}" lacks permissions.extension.hud`);
26
+ return;
27
+ }
28
+ await this.send(session, {
29
+ type: "wt-hud-update",
30
+ action: state.action,
31
+ badge: state.badge,
32
+ badgeColor: state.badgeColor,
33
+ groupState: state.groupState,
34
+ timestamp: Date.now(),
35
+ });
36
+ }
37
+ async showThought(session, text, options) {
38
+ if (!this.checkPermission("hud"))
39
+ return;
40
+ if (!session || !session.Runtime)
41
+ return;
42
+ const duration = options?.durationMs || 4000;
43
+ const safeText = JSON.stringify(text);
44
+ try {
45
+ await session.Runtime.evaluate({
46
+ expression: `(function() {
47
+ var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
48
+ if (existing) existing.remove();
49
+
50
+ var bubble = document.createElement('div');
51
+ bubble.id = '__wt_plugin_thought_${this.pluginName}';
52
+ bubble.className = 'wt-bubble';
53
+ bubble.setAttribute('data-wt-internal', 'true');
54
+ bubble.style.cssText = [
55
+ 'position: fixed',
56
+ 'bottom: 24px',
57
+ 'right: 24px',
58
+ 'background: linear-gradient(135deg, rgba(17, 24, 39, 0.95), rgba(30, 41, 59, 0.95))',
59
+ 'color: #f8fafc',
60
+ 'padding: 12px 18px',
61
+ 'border-radius: 12px',
62
+ 'font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
63
+ 'font-size: 13px',
64
+ 'font-weight: 500',
65
+ 'box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)',
66
+ 'z-index: 2147483647',
67
+ 'backdrop-filter: blur(8px)',
68
+ 'pointer-events: none',
69
+ 'animation: wtThoughtFade 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
70
+ 'display: flex',
71
+ 'align-items: center',
72
+ 'gap: 8px'
73
+ ].join(';');
74
+
75
+ var icon = document.createElement('span');
76
+ icon.textContent = '✨';
77
+ var textSpan = document.createElement('span');
78
+ textSpan.textContent = ${safeText};
79
+
80
+ bubble.appendChild(icon);
81
+ bubble.appendChild(textSpan);
82
+ document.body.appendChild(bubble);
83
+
84
+ setTimeout(function() {
85
+ if (bubble && bubble.parentNode) {
86
+ bubble.style.opacity = '0';
87
+ bubble.style.transition = 'opacity 0.3s ease';
88
+ setTimeout(function() { bubble.remove(); }, 300);
89
+ }
90
+ }, ${duration});
91
+ })()`,
92
+ returnByValue: false,
93
+ });
94
+ }
95
+ catch (e) {
96
+ logDebug(`[Weavetab PluginExtension] Failed to show thought:`, e);
97
+ }
98
+ }
99
+ async clearThought(session) {
100
+ if (!session || !session.Runtime)
101
+ return;
102
+ try {
103
+ await session.Runtime.evaluate({
104
+ expression: `(function() {
105
+ var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
106
+ if (existing) existing.remove();
107
+ })()`,
108
+ returnByValue: false,
109
+ });
110
+ }
111
+ catch { /* ignore */ }
112
+ }
113
+ async injectStyles(session, css) {
114
+ if (!this.checkPermission("styles")) {
115
+ throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.styles`);
116
+ }
117
+ if (!session || !session.Runtime)
118
+ return;
119
+ const safeCss = JSON.stringify(css);
120
+ await session.Runtime.evaluate({
121
+ expression: `(function() {
122
+ var styleId = '__wt_plugin_styles_${this.pluginName}';
123
+ var style = document.getElementById(styleId) || document.createElement('style');
124
+ style.id = styleId;
125
+ style.textContent = ${safeCss};
126
+ if (!style.parentNode) document.head.appendChild(style);
127
+ })()`,
128
+ returnByValue: false,
129
+ });
130
+ }
131
+ async injectScript(session, js) {
132
+ if (!this.checkPermission("scripts")) {
133
+ throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.scripts`);
134
+ }
135
+ if (!session || !session.Runtime)
136
+ throw new Error("No active CDP session available");
137
+ const res = await session.Runtime.evaluate({
138
+ expression: js,
139
+ returnByValue: true,
140
+ awaitPromise: true,
141
+ });
142
+ return res.result?.value ?? res;
143
+ }
144
+ async hideOverlay(session) {
145
+ if (!this.checkPermission("overlay"))
146
+ return;
147
+ if (!session)
148
+ return;
149
+ await hideWeaveTabOverlay(session);
150
+ }
151
+ async restoreOverlay(session) {
152
+ if (!this.checkPermission("overlay"))
153
+ return;
154
+ if (!session)
155
+ return;
156
+ await restoreWeaveTabOverlay(session);
157
+ }
158
+ }