@weavetab/mcp 2.5.0-beta.2 → 2.5.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +216 -631
- package/README.md +153 -1074
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +2 -2
- package/dist/cdp/bridge.js +2 -1
- package/dist/cdp/connector.d.ts +2 -0
- package/dist/cdp/connector.js +10 -6
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/omni_broker.d.ts +1 -1
- package/dist/cdp/omni_broker.js +1 -1
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +304 -7
- package/dist/cli/plugin.d.ts +1 -0
- package/dist/cli/plugin.js +81 -18
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +9 -1
- package/dist/config/loader.js +42 -3
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +137 -21
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/injector.js +2 -2
- package/dist/plugin/interface.d.ts +88 -5
- package/dist/plugin/loader.d.ts +12 -1
- package/dist/plugin/loader.js +280 -70
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.d.ts +2 -1
- package/dist/plugin/registry.js +24 -14
- package/dist/security/secrets.d.ts +7 -0
- package/dist/security/secrets.js +37 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +453 -178
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +6 -0
- package/dist/state/session.js +27 -0
- package/dist/tools/browser_detect.d.ts +16 -3
- package/dist/tools/browser_detect.js +52 -7
- package/dist/tools/burst.d.ts +17 -8
- package/dist/tools/burst.js +236 -77
- package/dist/tools/captcha.js +1 -1
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +74 -70
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +34 -39
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.js +15 -7
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +107 -58
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +37 -41
- package/docs/PLUGINS.md +121 -0
- package/docs/TOOLS.md +54 -0
- package/docs/architecture/blind-injection.md +83 -20
- package/docs/architecture/overview.md +49 -49
- package/docs/architecture/security.md +8 -7
- package/docs/architecture/transport.md +2 -2
- package/docs/cli/cli-commands.md +7 -1
- package/docs/cli/cli-flags.md +15 -7
- package/docs/cli/cli-scripting.md +6 -3
- package/docs/configuration/config-locations.md +3 -4
- package/docs/configuration/env-variables.md +4 -4
- package/docs/configuration/folders.md +55 -54
- package/docs/desktop-automation/electron-apps.md +214 -162
- package/docs/get-started/introduction.md +60 -60
- package/docs/get-started/setup-wizard.md +7 -6
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/tools/tools.md +631 -293
- package/docs/troubleshooting/trouble-browser.md +1 -1
- package/docs/troubleshooting/trouble-loop.md +31 -31
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -920
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/icon.png +0 -0
- package/mcp.json +87 -71
- package/package.json +118 -119
- package/server.json +5 -5
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- 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 =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
package/dist/overlay/injector.js
CHANGED
|
@@ -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]
|
|
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
|
-
//
|
|
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
|
|
@@ -57,22 +57,36 @@ export interface WeavetabPermissions {
|
|
|
57
57
|
reason?: string;
|
|
58
58
|
[key: string]: any;
|
|
59
59
|
};
|
|
60
|
+
/** @deprecated use `filesystem` */
|
|
60
61
|
fs?: {
|
|
61
62
|
read?: string[];
|
|
62
63
|
write?: string[];
|
|
63
64
|
reason?: string;
|
|
64
65
|
};
|
|
66
|
+
filesystem?: {
|
|
67
|
+
read?: string[];
|
|
68
|
+
write?: string[];
|
|
69
|
+
reason?: string;
|
|
70
|
+
};
|
|
65
71
|
tools?: {
|
|
66
|
-
/**
|
|
72
|
+
/** Must be true for the plugin to register any new MCP tools */
|
|
73
|
+
add?: boolean;
|
|
74
|
+
/** Explicit list of built-in tool names this plugin may override */
|
|
67
75
|
override?: string[];
|
|
68
|
-
/**
|
|
76
|
+
/** Explicit list of built-in tool names this plugin may remove */
|
|
69
77
|
remove?: string[];
|
|
70
|
-
|
|
78
|
+
reason?: string;
|
|
79
|
+
};
|
|
80
|
+
hooks?: {
|
|
81
|
+
onBeforeAction?: boolean;
|
|
82
|
+
onAfterAction?: boolean;
|
|
83
|
+
onDOMMutation?: boolean;
|
|
84
|
+
onPageNavigate?: boolean;
|
|
71
85
|
reason?: string;
|
|
72
86
|
};
|
|
73
87
|
[key: string]: any;
|
|
74
88
|
}
|
|
75
|
-
export type WeavetabManifestPermissions = WeavetabPermissions;
|
|
89
|
+
export type WeavetabManifestPermissions = WeavetabPermissions | string[];
|
|
76
90
|
export interface WeavetabManifestAuthor {
|
|
77
91
|
name: string;
|
|
78
92
|
role?: string;
|
|
@@ -96,6 +110,8 @@ export interface WeavetabManifest {
|
|
|
96
110
|
version: string;
|
|
97
111
|
/** Short summary of what this plugin provides */
|
|
98
112
|
description?: string;
|
|
113
|
+
/** Entry point file relative to the plugin root (e.g., 'dist/index.js') */
|
|
114
|
+
entry?: string;
|
|
99
115
|
/** Single author or multiple authors with roles */
|
|
100
116
|
author?: string | WeavetabManifestAuthor | (string | WeavetabManifestAuthor)[];
|
|
101
117
|
/** Array of authors */
|
|
@@ -119,7 +135,7 @@ export interface WeavetabManifest {
|
|
|
119
135
|
/** Default configuration template */
|
|
120
136
|
defaultConfig?: Record<string, any>;
|
|
121
137
|
/** Required permissions with security rationale */
|
|
122
|
-
permissions?:
|
|
138
|
+
permissions?: WeavetabManifestPermissions;
|
|
123
139
|
/** Open source license */
|
|
124
140
|
license?: string;
|
|
125
141
|
[key: string]: any;
|
|
@@ -132,7 +148,57 @@ export interface WeavetabPluginTool<TArgs = any> {
|
|
|
132
148
|
parameters?: Record<string, any>;
|
|
133
149
|
handler: (args: TArgs, session?: any, config?: any) => Promise<any>;
|
|
134
150
|
}
|
|
151
|
+
export interface DOMMutationDelta {
|
|
152
|
+
type?: "cdp_mutation" | "action_delta" | "page_update" | "dom_diff" | "rich_delta" | string;
|
|
153
|
+
timestamp?: number;
|
|
154
|
+
url?: string;
|
|
155
|
+
added?: Array<{
|
|
156
|
+
id?: string;
|
|
157
|
+
role?: string;
|
|
158
|
+
label?: string;
|
|
159
|
+
tag?: string;
|
|
160
|
+
text?: string;
|
|
161
|
+
backendNodeId?: number;
|
|
162
|
+
[key: string]: any;
|
|
163
|
+
}>;
|
|
164
|
+
removed?: Array<string | {
|
|
165
|
+
backendNodeId?: number;
|
|
166
|
+
tag?: string;
|
|
167
|
+
text?: string;
|
|
168
|
+
[key: string]: any;
|
|
169
|
+
}>;
|
|
170
|
+
changed?: Array<{
|
|
171
|
+
ref?: string;
|
|
172
|
+
role?: string;
|
|
173
|
+
label?: string;
|
|
174
|
+
attribute?: string;
|
|
175
|
+
oldValue?: any;
|
|
176
|
+
newValue?: any;
|
|
177
|
+
changes?: Record<string, {
|
|
178
|
+
from: any;
|
|
179
|
+
to: any;
|
|
180
|
+
}>;
|
|
181
|
+
actionable?: boolean;
|
|
182
|
+
[key: string]: any;
|
|
183
|
+
}>;
|
|
184
|
+
significantChange?: boolean;
|
|
185
|
+
stable_count?: number;
|
|
186
|
+
iframes?: any[];
|
|
187
|
+
[key: string]: any;
|
|
188
|
+
}
|
|
189
|
+
export type BeforeActionHook = (tool: string, args: Record<string, any>, session?: any) => Promise<Record<string, any> | {
|
|
190
|
+
__blocked: boolean;
|
|
191
|
+
reason?: string;
|
|
192
|
+
} | void> | Record<string, any> | {
|
|
193
|
+
__blocked: boolean;
|
|
194
|
+
reason?: string;
|
|
195
|
+
} | void;
|
|
196
|
+
export type AfterActionHook = (tool: string, result: any, session?: any, args?: Record<string, any>) => Promise<any> | any;
|
|
197
|
+
export type DOMMutationHook = (delta: DOMMutationDelta, session?: any) => Promise<void> | void;
|
|
135
198
|
export interface PluginHooksContext {
|
|
199
|
+
onBeforeAction: (handler: BeforeActionHook) => void;
|
|
200
|
+
onAfterAction: (handler: AfterActionHook) => void;
|
|
201
|
+
onDOMMutation: (handler: DOMMutationHook) => void;
|
|
136
202
|
beforeToolCall: (handler: (toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void) => void;
|
|
137
203
|
afterToolCall: (handler: (toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void) => void;
|
|
138
204
|
onPageNavigate?: (handler: (url: string, session?: any) => Promise<void> | void) => void;
|
|
@@ -144,6 +210,13 @@ export interface PluginMcpContext {
|
|
|
144
210
|
unregisterTool?: (toolName: string) => void;
|
|
145
211
|
[key: string]: any;
|
|
146
212
|
}
|
|
213
|
+
export interface PluginMemoryContext {
|
|
214
|
+
getSiteProfile(origin: string): Promise<any>;
|
|
215
|
+
learnPattern(origin: string, name: string, pattern: any): Promise<any>;
|
|
216
|
+
recordExecution(origin: string, name: string, success: boolean, durationMs?: number): Promise<any>;
|
|
217
|
+
getReliability(origin: string, patternName: string): Promise<number>;
|
|
218
|
+
recordQuirk(origin: string, quirkKey: string, quirkData: any): Promise<void>;
|
|
219
|
+
}
|
|
147
220
|
export interface PluginContext {
|
|
148
221
|
name: string;
|
|
149
222
|
version?: string;
|
|
@@ -153,6 +226,7 @@ export interface PluginContext {
|
|
|
153
226
|
extension: IPluginExtensionBridge;
|
|
154
227
|
mcp: PluginMcpContext;
|
|
155
228
|
hooks: PluginHooksContext;
|
|
229
|
+
memory?: PluginMemoryContext;
|
|
156
230
|
logger: {
|
|
157
231
|
info: (...args: any[]) => void;
|
|
158
232
|
warn: (...args: any[]) => void;
|
|
@@ -169,6 +243,10 @@ export interface WeavetabPluginDefinition {
|
|
|
169
243
|
overrides?: WeavetabToolOverride[];
|
|
170
244
|
removeTools?: string[];
|
|
171
245
|
defaultConfig?: Record<string, any>;
|
|
246
|
+
permissions?: WeavetabPermissions;
|
|
247
|
+
onBeforeAction?: BeforeActionHook;
|
|
248
|
+
onAfterAction?: AfterActionHook;
|
|
249
|
+
onDOMMutation?: DOMMutationHook;
|
|
172
250
|
onLoad?: (ctx: PluginContext) => Promise<void> | void;
|
|
173
251
|
onUnload?: (ctx: PluginContext) => Promise<void> | void;
|
|
174
252
|
}
|
|
@@ -176,6 +254,7 @@ export interface WeavetabPlugin {
|
|
|
176
254
|
name: string;
|
|
177
255
|
version: string;
|
|
178
256
|
description?: string;
|
|
257
|
+
permissions?: WeavetabPermissions;
|
|
179
258
|
/** Add new tools */
|
|
180
259
|
tools?: WeavetabToolDefinition[] | WeavetabPluginTool[];
|
|
181
260
|
/** Override or wrap existing tools */
|
|
@@ -189,8 +268,12 @@ export interface WeavetabPlugin {
|
|
|
189
268
|
hooks?: {
|
|
190
269
|
beforeToolCall: Array<(toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void>;
|
|
191
270
|
afterToolCall: Array<(toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void>;
|
|
271
|
+
onDOMMutation: Array<DOMMutationHook>;
|
|
192
272
|
onPageNavigate: Array<(url: string, session?: any) => Promise<void> | void>;
|
|
193
273
|
};
|
|
274
|
+
onBeforeAction?: BeforeActionHook;
|
|
275
|
+
onAfterAction?: AfterActionHook;
|
|
276
|
+
onDOMMutation?: DOMMutationHook;
|
|
194
277
|
onLoad?: (ctx: PluginContext) => Promise<void> | void;
|
|
195
278
|
onUnload?: (ctx: PluginContext) => Promise<void> | void;
|
|
196
279
|
}
|
package/dist/plugin/loader.d.ts
CHANGED
|
@@ -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,11 @@ 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
|
+
export declare function buildPluginContext(plugin: WeavetabPlugin, storageManager: PluginStorageManager, extensionBridge: PluginExtensionBridge, pluginConfig: Record<string, any>, hooks: {
|
|
25
|
+
beforeToolCall: Array<(toolName: string, args: Record<string, any>, session?: any) => Promise<any> | void>;
|
|
26
|
+
afterToolCall: Array<(toolName: string, args: Record<string, any>, result: any, session?: any) => Promise<any> | void>;
|
|
27
|
+
onPageNavigate: Array<(url: string, session?: any) => Promise<void> | void>;
|
|
28
|
+
}): PluginContext;
|
|
18
29
|
export declare function discoverPlugins(pluginDir?: string): WeavetabPlugin[];
|
|
19
30
|
/**
|
|
20
31
|
* Loads a specific plugin by name from node_modules.
|