@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
@@ -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
@@ -152,7 +152,7 @@ export async function ensureVisualLayer(session, config, client) {
152
152
  returnByValue: true
153
153
  });
154
154
  if (checkResult?.value) {
155
- logDebug(`[Weavetab] [OK] Minimal overlay injected successfully`);
155
+ logDebug(`[Weavetab] overlay injected`);
156
156
  }
157
157
  else {
158
158
  logDebug(`[Weavetab] [!] Minimal overlay injection failed - element not found`);
@@ -171,7 +171,7 @@ export async function setupOverlay(session, config) {
171
171
  await ensureVisualLayer(session, config);
172
172
  }
173
173
  export async function setFaviconState(_session, _state) {
174
- // Handled robustly via extension content.js
174
+ // handled via extension content.js
175
175
  }
176
176
  export async function removeTitleBadge(_session) {
177
177
  // No-op: title dot prefix removed in favor of favicon indicators
@@ -62,6 +62,11 @@ export interface WeavetabPermissions {
62
62
  write?: string[];
63
63
  reason?: string;
64
64
  };
65
+ filesystem?: {
66
+ read?: string[];
67
+ write?: string[];
68
+ reason?: string;
69
+ };
65
70
  tools?: {
66
71
  /** List of built-in tool names this plugin is permitted to override */
67
72
  override?: string[];
@@ -72,7 +77,7 @@ export interface WeavetabPermissions {
72
77
  };
73
78
  [key: string]: any;
74
79
  }
75
- export type WeavetabManifestPermissions = WeavetabPermissions;
80
+ export type WeavetabManifestPermissions = WeavetabPermissions | string[];
76
81
  export interface WeavetabManifestAuthor {
77
82
  name: string;
78
83
  role?: string;
@@ -96,6 +101,8 @@ export interface WeavetabManifest {
96
101
  version: string;
97
102
  /** Short summary of what this plugin provides */
98
103
  description?: string;
104
+ /** Entry point file relative to the plugin root (e.g., 'dist/index.js') */
105
+ entry?: string;
99
106
  /** Single author or multiple authors with roles */
100
107
  author?: string | WeavetabManifestAuthor | (string | WeavetabManifestAuthor)[];
101
108
  /** Array of authors */
@@ -114,12 +121,14 @@ export interface WeavetabManifest {
114
121
  engines?: Record<string, string>;
115
122
  /** Supported environments */
116
123
  environments?: string[];
124
+ /** Whether the plugin should be lazy-loaded on demand */
125
+ lazy?: boolean;
117
126
  /** Tools exposed by this plugin to the MCP server */
118
127
  tools?: WeavetabManifestTool[];
119
128
  /** Default configuration template */
120
129
  defaultConfig?: Record<string, any>;
121
130
  /** Required permissions with security rationale */
122
- permissions?: WeavetabPermissions;
131
+ permissions?: WeavetabManifestPermissions;
123
132
  /** Open source license */
124
133
  license?: string;
125
134
  [key: string]: any;
@@ -132,7 +141,57 @@ export interface WeavetabPluginTool<TArgs = any> {
132
141
  parameters?: Record<string, any>;
133
142
  handler: (args: TArgs, session?: any, config?: any) => Promise<any>;
134
143
  }
144
+ export interface DOMMutationDelta {
145
+ type?: "cdp_mutation" | "action_delta" | "page_update" | "dom_diff" | "rich_delta" | string;
146
+ timestamp?: number;
147
+ url?: string;
148
+ added?: Array<{
149
+ id?: string;
150
+ role?: string;
151
+ label?: string;
152
+ tag?: string;
153
+ text?: string;
154
+ backendNodeId?: number;
155
+ [key: string]: any;
156
+ }>;
157
+ removed?: Array<string | {
158
+ backendNodeId?: number;
159
+ tag?: string;
160
+ text?: string;
161
+ [key: string]: any;
162
+ }>;
163
+ changed?: Array<{
164
+ ref?: string;
165
+ role?: string;
166
+ label?: string;
167
+ attribute?: string;
168
+ oldValue?: any;
169
+ newValue?: any;
170
+ changes?: Record<string, {
171
+ from: any;
172
+ to: any;
173
+ }>;
174
+ actionable?: boolean;
175
+ [key: string]: any;
176
+ }>;
177
+ significantChange?: boolean;
178
+ stable_count?: number;
179
+ iframes?: any[];
180
+ [key: string]: any;
181
+ }
182
+ export type BeforeActionHook = (tool: string, args: Record<string, any>, session?: any) => Promise<Record<string, any> | {
183
+ __blocked: boolean;
184
+ reason?: string;
185
+ } | void> | Record<string, any> | {
186
+ __blocked: boolean;
187
+ reason?: string;
188
+ } | void;
189
+ export type AfterActionHook = (tool: string, result: any, session?: any, args?: Record<string, any>) => Promise<any> | any;
190
+ export type DOMMutationHook = (delta: DOMMutationDelta, session?: any) => Promise<void> | void;
135
191
  export interface PluginHooksContext {
192
+ onBeforeAction: (handler: BeforeActionHook) => void;
193
+ onAfterAction: (handler: AfterActionHook) => void;
194
+ onDOMMutation: (handler: DOMMutationHook) => void;
136
195
  beforeToolCall: (handler: (toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void) => void;
137
196
  afterToolCall: (handler: (toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void) => void;
138
197
  onPageNavigate?: (handler: (url: string, session?: any) => Promise<void> | void) => void;
@@ -144,6 +203,13 @@ export interface PluginMcpContext {
144
203
  unregisterTool?: (toolName: string) => void;
145
204
  [key: string]: any;
146
205
  }
206
+ export interface PluginMemoryContext {
207
+ getSiteProfile(origin: string): Promise<any>;
208
+ learnPattern(origin: string, name: string, pattern: any): Promise<any>;
209
+ recordExecution(origin: string, name: string, success: boolean, durationMs?: number): Promise<any>;
210
+ getReliability(origin: string, patternName: string): Promise<number>;
211
+ recordQuirk(origin: string, quirkKey: string, quirkData: any): Promise<void>;
212
+ }
147
213
  export interface PluginContext {
148
214
  name: string;
149
215
  version?: string;
@@ -153,6 +219,7 @@ export interface PluginContext {
153
219
  extension: IPluginExtensionBridge;
154
220
  mcp: PluginMcpContext;
155
221
  hooks: PluginHooksContext;
222
+ memory?: PluginMemoryContext;
156
223
  logger: {
157
224
  info: (...args: any[]) => void;
158
225
  warn: (...args: any[]) => void;
@@ -169,6 +236,9 @@ export interface WeavetabPluginDefinition {
169
236
  overrides?: WeavetabToolOverride[];
170
237
  removeTools?: string[];
171
238
  defaultConfig?: Record<string, any>;
239
+ onBeforeAction?: BeforeActionHook;
240
+ onAfterAction?: AfterActionHook;
241
+ onDOMMutation?: DOMMutationHook;
172
242
  onLoad?: (ctx: PluginContext) => Promise<void> | void;
173
243
  onUnload?: (ctx: PluginContext) => Promise<void> | void;
174
244
  }
@@ -189,8 +259,12 @@ export interface WeavetabPlugin {
189
259
  hooks?: {
190
260
  beforeToolCall: Array<(toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void>;
191
261
  afterToolCall: Array<(toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void>;
262
+ onDOMMutation: Array<DOMMutationHook>;
192
263
  onPageNavigate: Array<(url: string, session?: any) => Promise<void> | void>;
193
264
  };
265
+ onBeforeAction?: BeforeActionHook;
266
+ onAfterAction?: AfterActionHook;
267
+ onDOMMutation?: DOMMutationHook;
194
268
  onLoad?: (ctx: PluginContext) => Promise<void> | void;
195
269
  onUnload?: (ctx: PluginContext) => Promise<void> | void;
196
270
  }
@@ -1,4 +1,6 @@
1
- import type { WeavetabPlugin, WeavetabManifest } from "./interface.js";
1
+ import type { WeavetabPlugin, WeavetabManifest, WeavetabPermissions, PluginContext } from "./interface.js";
2
+ import { PluginStorageManager } from "./storage.js";
3
+ import { PluginExtensionBridge } from "./extension_bridge.js";
2
4
  export interface InstalledPluginInfo {
3
5
  packageName: string;
4
6
  dir: string;
@@ -6,6 +8,10 @@ export interface InstalledPluginInfo {
6
8
  pkgJson?: Record<string, any>;
7
9
  hasValidManifest: boolean;
8
10
  }
11
+ /**
12
+ * Normalizes permissions format across legacy arrays and object aliases (filesystem vs fs).
13
+ */
14
+ export declare function normalizePermissions(perms?: any): WeavetabPermissions;
9
15
  /**
10
16
  * Reads the manifest (weavetab.json) and package.json for an installed package
11
17
  * without executing its runtime code (safe for listing/inspection).
@@ -15,6 +21,14 @@ export declare function inspectInstalledPackage(pkgDir: string): InstalledPlugin
15
21
  * Scan node_modules and return metadata for all installed Weavetab plugins.
16
22
  */
17
23
  export declare function listInstalledPlugins(pluginDir?: string): InstalledPluginInfo[];
24
+ /**
25
+ * Builds the canonical rich PluginContext for a loaded plugin.
26
+ */
27
+ export declare function buildPluginContext(plugin: WeavetabPlugin, storageManager: PluginStorageManager, extensionBridge: PluginExtensionBridge, pluginConfig: Record<string, any>, hooks: {
28
+ beforeToolCall: Array<(toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void>;
29
+ afterToolCall: Array<(toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void>;
30
+ onPageNavigate: Array<(url: string, session?: any) => Promise<void> | void>;
31
+ }): PluginContext;
18
32
  export declare function discoverPlugins(pluginDir?: string): WeavetabPlugin[];
19
33
  /**
20
34
  * Loads a specific plugin by name from node_modules.