@weavetab/mcp 2.5.0-beta.5 → 2.5.1

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 (160) hide show
  1. package/AGENTS.md +49 -49
  2. package/ARCHITECTURE.md +590 -590
  3. package/README.md +216 -100
  4. package/SECURITY.md +207 -207
  5. package/TROUBLESHOOTING.md +373 -365
  6. package/dist/anticaptcha/detector.js +395 -395
  7. package/dist/anticaptcha/solver.js +130 -130
  8. package/dist/cdp/bridge.js +73 -73
  9. package/dist/cdp/connector.js +46 -22
  10. package/dist/cdp/extension.js +34 -34
  11. package/dist/cdp/helpers.js +174 -174
  12. package/dist/cdp/pruner.js +195 -195
  13. package/dist/cdp/ref_cache.js +66 -66
  14. package/dist/cdp/security.js +10 -10
  15. package/dist/cdp/verify.js +57 -57
  16. package/dist/cdp/walker.js +563 -525
  17. package/dist/cli/plugin.js +26 -3
  18. package/dist/config/loader.d.ts +0 -3
  19. package/dist/config/loader.js +0 -9
  20. package/dist/index.js +0 -4
  21. package/dist/intelligence/intent.js +81 -81
  22. package/dist/intelligence/sessionMemory.js +2 -2
  23. package/dist/overlay/ghost.js +22 -22
  24. package/dist/overlay/injector.js +89 -89
  25. package/dist/plugin/extension_bridge.js +54 -54
  26. package/dist/plugin/interface.d.ts +7 -0
  27. package/dist/plugin/loader.js +3 -4
  28. package/dist/plugin/schema.d.ts +11 -0
  29. package/dist/plugin/schema.js +59 -0
  30. package/dist/security/guard.js +18 -18
  31. package/dist/sensors/actionability.js +167 -167
  32. package/dist/sensors/guard.js +29 -29
  33. package/dist/sensors/network.js +15 -17
  34. package/dist/server.d.ts +1 -1
  35. package/dist/server.js +173 -144
  36. package/dist/state/delta.js +6 -7
  37. package/dist/tools/automation.js +85 -85
  38. package/dist/tools/canvas.js +48 -25
  39. package/dist/tools/checkpoint.js +94 -35
  40. package/dist/tools/click.js +52 -41
  41. package/dist/tools/clipboard.js +80 -74
  42. package/dist/tools/design_extract.js +441 -441
  43. package/dist/tools/dialog.js +13 -13
  44. package/dist/tools/emulation.d.ts +2 -0
  45. package/dist/tools/emulation.js +7 -0
  46. package/dist/tools/fill.js +344 -347
  47. package/dist/tools/find.js +78 -52
  48. package/dist/tools/highlight.js +67 -67
  49. package/dist/tools/inspect.js +54 -25
  50. package/dist/tools/key.js +42 -35
  51. package/dist/tools/navigate.d.ts +1 -1
  52. package/dist/tools/navigate.js +35 -35
  53. package/dist/tools/office/parse.js +62 -18
  54. package/dist/tools/pdf.d.ts +9 -0
  55. package/dist/tools/pdf.js +7 -4
  56. package/dist/tools/performance.d.ts +11 -4
  57. package/dist/tools/performance.js +94 -42
  58. package/dist/tools/performance_test.d.ts +2 -2
  59. package/dist/tools/performance_test.js +69 -69
  60. package/dist/tools/pointer.js +116 -116
  61. package/dist/tools/read.js +99 -67
  62. package/dist/tools/recording.js +9 -0
  63. package/dist/tools/scrape.js +146 -146
  64. package/dist/tools/screenshot.d.ts +2 -0
  65. package/dist/tools/screenshot.js +46 -4
  66. package/dist/tools/scroll.d.ts +3 -1
  67. package/dist/tools/scroll.js +13 -15
  68. package/dist/tools/select.d.ts +3 -0
  69. package/dist/tools/select.js +131 -124
  70. package/dist/tools/snapshot.js +8 -6
  71. package/dist/tools/storage.d.ts +5 -1
  72. package/dist/tools/storage.js +16 -0
  73. package/dist/tools/tabs.d.ts +7 -5
  74. package/dist/tools/tabs.js +79 -26
  75. package/dist/tools/thought.js +8 -8
  76. package/dist/tools/type.js +274 -274
  77. package/dist/tools/type_secret.js +23 -23
  78. package/dist/tools/upload.js +3 -3
  79. package/dist/tools/video_compiler.js +9 -0
  80. package/dist/tools/wait.js +35 -35
  81. package/dist/version.d.ts +1 -1
  82. package/dist/version.js +1 -1
  83. package/docs/DOCUMENTATION.md +583 -585
  84. package/docs/PLUGINS.md +139 -121
  85. package/docs/TOOLS.md +3 -4
  86. package/docs/architecture/blind-injection.md +83 -83
  87. package/docs/architecture/overview.md +49 -49
  88. package/docs/configuration/configuration.md +39 -27
  89. package/docs/configuration/folders.md +62 -55
  90. package/docs/desktop-automation/electron-apps.md +225 -214
  91. package/docs/get-started/installation.md +1 -1
  92. package/docs/get-started/introduction.md +60 -60
  93. package/docs/get-started/quickstart.md +2 -2
  94. package/docs/get-started/setup-wizard.md +1 -1
  95. package/docs/intelligence/site-profiles.md +151 -151
  96. package/docs/tools/tools.md +582 -631
  97. package/docs/troubleshooting/trouble-loop.md +31 -31
  98. package/ext-dist/background.js +415 -415
  99. package/ext-dist/content.js +2527 -2527
  100. package/ext-dist/dashboard.html +583 -583
  101. package/ext-dist/hud/hud.js +3 -3
  102. package/ext-dist/hud/index.html +20 -20
  103. package/ext-dist/manifest.json +73 -73
  104. package/ext-dist/styles.css +223 -223
  105. package/mcp.json +796 -805
  106. package/package.json +8 -8
  107. package/server.json +23 -23
  108. package/smithery.yaml +7 -11
  109. package/dist/cdp/walker.test.d.ts +0 -2
  110. package/dist/cdp/walker.test.js +0 -172
  111. package/dist/intelligence/sessionMemory.test.d.ts +0 -2
  112. package/dist/intelligence/sessionMemory.test.js +0 -62
  113. package/dist/intelligence/trail.test.d.ts +0 -2
  114. package/dist/intelligence/trail.test.js +0 -110
  115. package/dist/state/cache.test.d.ts +0 -2
  116. package/dist/state/cache.test.js +0 -76
  117. package/dist/state/delta.test.d.ts +0 -2
  118. package/dist/state/delta.test.js +0 -279
  119. package/dist/state/groups.d.ts +0 -15
  120. package/dist/state/groups.js +0 -80
  121. package/dist/tools/accessibility.d.ts +0 -14
  122. package/dist/tools/accessibility.js +0 -32
  123. package/dist/tools/ask.d.ts +0 -14
  124. package/dist/tools/ask.js +0 -49
  125. package/dist/tools/click_and_wait.d.ts +0 -33
  126. package/dist/tools/click_and_wait.js +0 -149
  127. package/dist/tools/drag.d.ts +0 -15
  128. package/dist/tools/drag.js +0 -29
  129. package/dist/tools/geo.d.ts +0 -19
  130. package/dist/tools/geo.js +0 -29
  131. package/dist/tools/github/analyze.d.ts +0 -4
  132. package/dist/tools/github/analyze.js +0 -113
  133. package/dist/tools/github/cache.d.ts +0 -21
  134. package/dist/tools/github/cache.js +0 -28
  135. package/dist/tools/github/index.d.ts +0 -19
  136. package/dist/tools/github/index.js +0 -40
  137. package/dist/tools/github/issues.d.ts +0 -9
  138. package/dist/tools/github/issues.js +0 -88
  139. package/dist/tools/github/pr.d.ts +0 -5
  140. package/dist/tools/github/pr.js +0 -88
  141. package/dist/tools/github/rateLimiter.d.ts +0 -4
  142. package/dist/tools/github/rateLimiter.js +0 -46
  143. package/dist/tools/github/read.d.ts +0 -34
  144. package/dist/tools/github/read.js +0 -74
  145. package/dist/tools/github/repoContext.d.ts +0 -20
  146. package/dist/tools/github/repoContext.js +0 -129
  147. package/dist/tools/hover.d.ts +0 -19
  148. package/dist/tools/hover.js +0 -57
  149. package/dist/tools/integration.test.d.ts +0 -2
  150. package/dist/tools/integration.test.js +0 -96
  151. package/dist/tools/macro_compiler.d.ts +0 -7
  152. package/dist/tools/macro_compiler.js +0 -82
  153. package/dist/tools/mouse.d.ts +0 -21
  154. package/dist/tools/mouse.js +0 -62
  155. package/dist/tools/response.d.ts +0 -129
  156. package/dist/tools/response.js +0 -5
  157. package/dist/tools/wait_for.d.ts +0 -23
  158. package/dist/tools/wait_for.js +0 -30
  159. package/dist/tools/windows.d.ts +0 -22
  160. package/dist/tools/windows.js +0 -57
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@weavetab/mcp",
3
- "version": "2.5.0-beta.5",
4
- "description": "Direct Layer 0 interface for AI agents across web and desktop applications. No WebDriver. No cloud. Pure control.",
3
+ "version": "2.5.1",
4
+ "description": "The open automation engine for AI agents. Control live browsers and desktop apps locally with instant execution.",
5
5
  "author": {
6
6
  "name": "fy2ne",
7
7
  "email": "hello@fy2ne.me",
8
8
  "url": "https://fy2ne.me"
9
9
  },
10
10
  "license": "AGPL-3.0-only",
11
- "homepage": "https://github.com/weavetab/mcp",
11
+ "homepage": "https://github.com/Weavetab/mcp",
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/weavetab/mcp.git"
14
+ "url": "https://github.com/Weavetab/mcp.git"
15
15
  },
16
16
  "bugs": {
17
- "url": "https://github.com/weavetab/mcp/issues"
17
+ "url": "https://github.com/Weavetab/mcp/issues"
18
18
  },
19
19
  "keywords": [
20
20
  "mcp server",
@@ -47,7 +47,7 @@
47
47
  "publishConfig": {
48
48
  "access": "public"
49
49
  },
50
- "mcpName": "io.github.Weavetab/mcp",
50
+ "mcpName": "dev.weavetab/mcp",
51
51
  "files": [
52
52
  "dist/",
53
53
  "!dist/**/*.map",
@@ -68,8 +68,8 @@
68
68
  "README.md"
69
69
  ],
70
70
  "scripts": {
71
- "build": "npm run build:ext && npm run build:hud && tsc",
72
- "build:hud": "esbuild ext-dist/hud/src/index.ts --bundle --minify --outfile=ext-dist/hud/hud.js --format=iife",
71
+ "build": "node scripts/build.js",
72
+ "build:hud": "node scripts/build-hud.js",
73
73
  "build:ext": "node scripts/build-ext.js",
74
74
  "docs:generate": "node scripts/generate-docs.js",
75
75
  "verify:versions": "node scripts/verify-versions.js",
package/server.json CHANGED
@@ -1,23 +1,23 @@
1
- {
2
- "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
- "name": "io.github.Weavetab/mcp",
4
- "title": "Weavetab MCP",
5
- "description": "Give AI agents full control over live browsers and apps to navigate, click, extract, and automate.",
6
- "repository": {
7
- "url": "https://github.com/Weavetab/mcp",
8
- "source": "github"
9
- },
10
- "version": "2.5.0-beta.5",
11
- "packages": [
12
- {
13
- "registryType": "npm",
14
- "registryBaseUrl": "https://registry.npmjs.org",
15
- "identifier": "@weavetab/mcp",
16
- "version": "2.5.0-beta.5",
17
- "transport": {
18
- "type": "stdio"
19
- },
20
- "environmentVariables": []
21
- }
22
- ]
23
- }
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "dev.weavetab/mcp",
4
+ "title": "Weavetab",
5
+ "description": "Open automation engine for AI agents. Control live browsers and desktop apps locally.",
6
+ "repository": {
7
+ "url": "https://github.com/Weavetab/mcp",
8
+ "source": "github"
9
+ },
10
+ "version": "2.5.1",
11
+ "packages": [
12
+ {
13
+ "registryType": "npm",
14
+ "registryBaseUrl": "https://registry.npmjs.org",
15
+ "identifier": "@weavetab/mcp",
16
+ "version": "2.5.1",
17
+ "transport": {
18
+ "type": "stdio"
19
+ },
20
+ "environmentVariables": []
21
+ }
22
+ ]
23
+ }
package/smithery.yaml CHANGED
@@ -1,11 +1,7 @@
1
- # Smithery.ai Universal MCP Configuration
2
- # Works automatically across Cursor, Claude Desktop, Windsurf, Continue.dev, Roo Code & Smithery CLI
3
-
4
- startCommand:
5
- type: stdio
6
- config:
7
- command: node
8
- args:
9
- - dist/index.js
10
- env:
11
- NODE_ENV: production
1
+ startCommand:
2
+ type: stdio
3
+ config:
4
+ command: npx
5
+ args:
6
+ - -y
7
+ - "@weavetab/mcp@latest"
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=walker.test.d.ts.map
@@ -1,172 +0,0 @@
1
- /**
2
- * walker.test.ts — Phase 1.1
3
- * Unit tests for walker.ts utility functions (no CDP session needed).
4
- * buildActionMap and buildAXTreeMap require live browser — not tested here.
5
- */
6
- import { test } from "node:test";
7
- import assert from "node:assert/strict";
8
- import { toRefEntries, findAllMatches, findBestMatch, toMarkdownTable, safeSelector, clearViewportCache, setShadowDomCache, clearShadowDomCache, clearAXTreeCache, resetFallbackIdCounter as resetIdCounter, } from "./walker.js";
9
- function makeEl(overrides = {}) {
10
- return {
11
- id: "w:1",
12
- role: "button",
13
- label: "Submit",
14
- group: "form",
15
- fingerprint: "button:submit:form:1",
16
- depth: 0,
17
- ...overrides,
18
- };
19
- }
20
- test("toRefEntries: converts ActionElements to RefEntries", () => {
21
- const els = [
22
- makeEl({ id: "w:1", label: "Sign In", role: "button", type: "submit", fingerprint: "a" }),
23
- makeEl({ id: "w:2", label: "Email", role: "textbox", type: "email", fingerprint: "b" }),
24
- ];
25
- const refs = toRefEntries(els);
26
- assert.equal(refs.length, 2);
27
- assert.equal(refs[0].id, "w:1");
28
- assert.equal(refs[0].label, "Sign In");
29
- assert.equal(refs[0].type, "submit");
30
- assert.equal(refs[0].disabled, false);
31
- assert.equal(refs[1].id, "w:2");
32
- });
33
- test("toRefEntries: empty array returns empty", () => {
34
- assert.deepEqual(toRefEntries([]), []);
35
- });
36
- test("findAllMatches: exact label match", () => {
37
- const els = [
38
- makeEl({ id: "w:1", label: "Sign In" }),
39
- makeEl({ id: "w:2", label: "Sign Out" }),
40
- makeEl({ id: "w:3", label: "Email" }),
41
- ];
42
- const matches = findAllMatches(els, "Sign In");
43
- assert.equal(matches.length, 1);
44
- assert.equal(matches[0].id, "w:1");
45
- });
46
- test("findAllMatches: partial label match", () => {
47
- const els = [
48
- makeEl({ id: "w:1", label: "Sign In" }),
49
- makeEl({ id: "w:2", label: "Sign Out" }),
50
- makeEl({ id: "w:3", label: "Email" }),
51
- ];
52
- const matches = findAllMatches(els, "Sign");
53
- assert.equal(matches.length, 2);
54
- assert.ok(matches.find(m => m.id === "w:1"));
55
- assert.ok(matches.find(m => m.id === "w:2"));
56
- });
57
- test("findAllMatches: case insensitive", () => {
58
- const els = [makeEl({ id: "w:1", label: "SIGN IN" })];
59
- const matches = findAllMatches(els, "sign in");
60
- assert.equal(matches.length, 1);
61
- });
62
- test("findAllMatches: placeholder match", () => {
63
- const els = [makeEl({ id: "w:1", label: "", placeholder: "Enter name" })];
64
- const matches = findAllMatches(els, "Enter name");
65
- assert.equal(matches.length, 1);
66
- assert.equal(matches[0].id, "w:1");
67
- });
68
- test("findAllMatches: empty query returns empty", () => {
69
- const els = [makeEl({ id: "w:1", label: "Submit" })];
70
- assert.deepEqual(findAllMatches(els, ""), []);
71
- assert.deepEqual(findAllMatches(els, " "), []);
72
- assert.deepEqual(findAllMatches(els, "\t"), []);
73
- });
74
- test("findAllMatches: fuzzy multi-word match", () => {
75
- const els = [
76
- makeEl({ id: "w:1", label: "Create New Account" }),
77
- makeEl({ id: "w:2", label: "Create Post" }),
78
- ];
79
- const matches = findAllMatches(els, "Create Account");
80
- assert.equal(matches.length, 1);
81
- assert.equal(matches[0].id, "w:1");
82
- });
83
- test("findAllMatches: returns unique elements only", () => {
84
- const els = [makeEl({ id: "w:1", label: "Submit" })];
85
- // label and type both match "Submit"
86
- const matches = findAllMatches(els, "Submit");
87
- assert.equal(matches.length, 1);
88
- });
89
- test("findBestMatch: returns first match", () => {
90
- const els = [
91
- makeEl({ id: "w:1", label: "Sign In" }),
92
- makeEl({ id: "w:2", label: "Sign Out" }),
93
- ];
94
- const best = findBestMatch(els, "Sign");
95
- assert.ok(best);
96
- assert.equal(best.id, "w:1");
97
- });
98
- test("findBestMatch: undefined on no match", () => {
99
- assert.equal(findBestMatch([], "nonexistent"), undefined);
100
- });
101
- test("toMarkdownTable: basic formatting", () => {
102
- const els = [
103
- makeEl({ id: "w:1", label: "Submit", role: "button", depth: 0 }),
104
- makeEl({ id: "w:2", label: "Email", role: "textbox", depth: 0 }),
105
- ];
106
- const md = toMarkdownTable(els, "https://example.com", "Test Page");
107
- assert.ok(md.includes("https://example.com"));
108
- assert.ok(md.includes("Test Page"));
109
- assert.ok(md.includes("Submit"));
110
- assert.ok(md.includes("Email"));
111
- assert.ok(md.includes("(Btn)"));
112
- assert.ok(md.includes("(Input)"));
113
- });
114
- test("toMarkdownTable: respects depth indentation", () => {
115
- const els = [
116
- makeEl({ id: "w:1", label: "Root", role: "button", depth: 0 }),
117
- makeEl({ id: "w:2", label: "Child", role: "textbox", depth: 1 }),
118
- ];
119
- const md = toMarkdownTable(els, "https://x.com", "X");
120
- // child should be indented
121
- const lines = md.split("\n");
122
- const childLine = lines.find(l => l.includes("Child"));
123
- assert.ok(childLine, "Child line should exist");
124
- assert.ok(childLine.startsWith(" "), "Child should be indented");
125
- });
126
- test("toMarkdownTable: strips non-interactive elements without label", () => {
127
- const els = [
128
- makeEl({ id: "", label: "", role: "generic", depth: 0 }),
129
- makeEl({ id: "w:1", label: "Real", role: "button", depth: 0 }),
130
- ];
131
- const md = toMarkdownTable(els, "https://x.com", "X");
132
- assert.ok(!md.includes("generic"), "Empty generic should be stripped");
133
- assert.ok(md.includes("Real"));
134
- });
135
- test("safeSelector: returns selector when present", () => {
136
- const el = makeEl({ selector: "#submit-btn" });
137
- assert.equal(safeSelector(el), "#submit-btn");
138
- });
139
- test("safeSelector: returns undefined when no selector", () => {
140
- assert.equal(safeSelector(makeEl()), undefined);
141
- });
142
- test("safeSelector: returns undefined on undefined input", () => {
143
- assert.equal(safeSelector(undefined), undefined);
144
- });
145
- test("clearViewportCache: no-op smoke test", () => {
146
- clearViewportCache("test-target");
147
- assert.ok(true, "clearViewportCache should not throw");
148
- });
149
- test("resetFallbackIdCounter: no-op smoke test", () => {
150
- resetIdCounter();
151
- assert.ok(true, "resetFallbackIdCounter should not throw");
152
- });
153
- test("shadowDomCache: set and clear", () => {
154
- setShadowDomCache("target-a", true);
155
- setShadowDomCache("target-b", false);
156
- clearShadowDomCache("target-a");
157
- clearShadowDomCache("target-b");
158
- assert.ok(true, "shadowDomCache should not throw");
159
- });
160
- test("clearAXTreeCache: no-op smoke test", () => {
161
- clearAXTreeCache("any-target");
162
- assert.ok(true, "clearAXTreeCache should not throw");
163
- });
164
- test("findAllMatches: type match fallback", () => {
165
- const els = [
166
- makeEl({ id: "w:1", label: "", type: "checkbox" }),
167
- makeEl({ id: "w:2", label: "Checkbox", type: "text" }),
168
- ];
169
- const matches = findAllMatches(els, "checkbox");
170
- assert.equal(matches.length, 2);
171
- });
172
- //# sourceMappingURL=walker.test.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=sessionMemory.test.d.ts.map
@@ -1,62 +0,0 @@
1
- /**
2
- * sessionMemory.test.ts — Phase 1.1
3
- * Integrated into node:test runner (npm test / npm run test:unit).
4
- * Previously a manual runTests() script; ported to node:test format.
5
- */
6
- import { test, before, after } from "node:test";
7
- import assert from "node:assert/strict";
8
- import fs from "fs";
9
- import path from "path";
10
- import { getBestSelectorForIntent, logMemorySuccess, logMemoryFailure, loadDomainProfile, } from "./sessionMemory.js";
11
- import { getSystemDir } from "../config/loader.js";
12
- const hostname = "test.weavetab.dev";
13
- let profilePath;
14
- before(() => {
15
- const memoryDir = getSystemDir("memory");
16
- profilePath = path.join(memoryDir, `${hostname}.json`);
17
- if (fs.existsSync(profilePath))
18
- fs.unlinkSync(profilePath);
19
- });
20
- after(() => {
21
- if (fs.existsSync(profilePath))
22
- fs.unlinkSync(profilePath);
23
- });
24
- test("1: Initial success log — selector is immediately retrievable", async () => {
25
- await logMemorySuccess(hostname, "Click Login", ".login-btn", { role: "button", tag: "button" });
26
- const sel = await getBestSelectorForIntent(hostname, "Click Login");
27
- assert.equal(sel?.selector, ".login-btn", `Expected .login-btn, got ${sel?.selector}`);
28
- });
29
- test("2: Dead selector (first strike) — score drops below threshold", async () => {
30
- // Simulate visit count increment
31
- const profile = await loadDomainProfile(hostname);
32
- profile.visitCount++;
33
- fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
34
- await logMemoryFailure(hostname, "Click Login", ".login-btn", "DEAD_SELECTOR");
35
- const sel = await getBestSelectorForIntent(hostname, "Click Login");
36
- assert.equal(sel, undefined, "Expected undefined — score dropped below 0.5 threshold after one failure");
37
- });
38
- test("3: Dead selector recovery — new successful selector wins", async () => {
39
- await logMemorySuccess(hostname, "Click Login", "#login-new", { role: "button", tag: "button" });
40
- const sel = await getBestSelectorForIntent(hostname, "Click Login");
41
- assert.equal(sel?.selector, "#login-new", `Expected #login-new, got ${sel?.selector}`);
42
- });
43
- test("4: Decay check — both old and new memory entries exist in profile", async () => {
44
- const profile = await loadDomainProfile(hostname);
45
- const oldMem = profile.memories.find(m => m.selector === ".login-btn");
46
- const newMem = profile.memories.find(m => m.selector === "#login-new");
47
- assert.ok(oldMem !== undefined, "Expected old memory (.login-btn) to still exist");
48
- assert.ok(newMem !== undefined, "Expected new memory (#login-new) to exist");
49
- });
50
- test("5: Negative memory expiration — both selectors decay below threshold after many visits", async () => {
51
- const profile = await loadDomainProfile(hostname);
52
- const inFailed = profile.failedSelectors.find(f => f.selector === ".login-btn");
53
- assert.ok(inFailed !== undefined, "Expected .login-btn in failedSelectors after DEAD_SELECTOR strike");
54
- // Advance visits far past expiration window (expiration = visitCount + 10).
55
- // After +15 visits, DecayFactor drops to ~0.25, pulling #login-new below 0.5 threshold too.
56
- profile.visitCount += 15;
57
- fs.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
58
- // Both selectors are now expired / decayed — correct behavior is a miss (force DOM re-resolution)
59
- const sel = await getBestSelectorForIntent(hostname, "Click Login");
60
- assert.equal(sel, undefined, "Expected undefined after extreme visit-count decay — selectors should re-confirm via DOM");
61
- });
62
- //# sourceMappingURL=sessionMemory.test.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=trail.test.d.ts.map
@@ -1,110 +0,0 @@
1
- /**
2
- * trail.test.ts — Phase 1.1
3
- * Unit tests for the Phase 0.1 fix: resetLoopCounter must NOT wipe the trail,
4
- * loop detection must respect timestamp-based reset boundaries.
5
- */
6
- import { test } from "node:test";
7
- import assert from "node:assert/strict";
8
- import { recordAction, getTrail, clearTrail, getLoopDiagnostics, detectLoop, resetLoopCounter, resetLoopCounterForRef, } from "./trail.js";
9
- // Helper: record N consecutive failures for a given tool+ref
10
- function recordFailures(tool, ref, count) {
11
- for (let i = 0; i < count; i++) {
12
- recordAction({
13
- timestamp: new Date().toISOString(),
14
- tool,
15
- input: { ref },
16
- result: { success: false, stuck: true },
17
- });
18
- }
19
- }
20
- // Helper: record one success
21
- function recordSuccess(tool, ref) {
22
- recordAction({
23
- timestamp: new Date().toISOString(),
24
- tool,
25
- input: { ref },
26
- result: { success: true, stuck: false },
27
- });
28
- }
29
- test("trail: recordAction appends entries", () => {
30
- clearTrail();
31
- recordSuccess("browser_click", "w:1");
32
- assert.equal(getTrail().length, 1);
33
- clearTrail();
34
- });
35
- test("loop: no warning below threshold (< 5 strikes)", () => {
36
- clearTrail();
37
- recordFailures("browser_click", "w:99", 4);
38
- const diag = getLoopDiagnostics("browser_click", "w:99");
39
- assert.equal(diag.severity, "none", `Expected none at 4 strikes, got ${diag.severity}`);
40
- clearTrail();
41
- });
42
- test("loop: warning fires at ≥ 5 strikes (half of LOOP_LIMIT=10)", () => {
43
- clearTrail();
44
- recordFailures("browser_click", "w:99", 5);
45
- const diag = getLoopDiagnostics("browser_click", "w:99");
46
- assert.equal(diag.severity, "warning", `Expected warning at 5 strikes, got ${diag.severity}`);
47
- clearTrail();
48
- });
49
- test("loop: critical fires at ≥ 10 strikes", () => {
50
- clearTrail();
51
- recordFailures("browser_click", "w:99", 10);
52
- const diag = getLoopDiagnostics("browser_click", "w:99");
53
- assert.equal(diag.severity, "critical", `Expected critical at 10 strikes, got ${diag.severity}`);
54
- clearTrail();
55
- });
56
- test("loop: success resets the consecutive strike count", () => {
57
- clearTrail();
58
- recordFailures("browser_click", "w:5", 6);
59
- recordSuccess("browser_click", "w:5"); // interrupts the consecutive run
60
- const diag = getLoopDiagnostics("browser_click", "w:5");
61
- assert.equal(diag.strikes, 0, `Expected 0 strikes after success, got ${diag.strikes}`);
62
- clearTrail();
63
- });
64
- test("Phase 0.1: resetLoopCounter preserves trail history", () => {
65
- clearTrail();
66
- recordSuccess("browser_click", "w:1");
67
- recordSuccess("browser_type", "w:2");
68
- const countBefore = getTrail().length;
69
- assert.equal(countBefore, 2);
70
- resetLoopCounter("test");
71
- // Trail must still have the same entries — history is preserved
72
- const countAfter = getTrail().length;
73
- assert.equal(countAfter, 2, `Trail should still have 2 entries after resetLoopCounter, got ${countAfter}`);
74
- clearTrail();
75
- });
76
- test("Phase 0.1: resetLoopCounter clears strikes without wiping trail", () => {
77
- clearTrail();
78
- recordFailures("browser_click", "w:7", 10);
79
- const before = getLoopDiagnostics("browser_click", "w:7");
80
- assert.equal(before.severity, "critical");
81
- resetLoopCounter("test");
82
- // New failures recorded AFTER reset should count fresh
83
- recordFailures("browser_click", "w:7", 2);
84
- const after = getLoopDiagnostics("browser_click", "w:7");
85
- // Trail still has 12 entries but only 2 are after the reset boundary
86
- assert.equal(after.strikes, 2, `Expected 2 strikes post-reset, got ${after.strikes}`);
87
- assert.equal(after.severity, "none", `Expected none at 2 strikes post-reset, got ${after.severity}`);
88
- clearTrail();
89
- });
90
- test("Phase 0.1: resetLoopCounterForRef only affects that ref", () => {
91
- clearTrail();
92
- recordFailures("browser_click", "w:A", 10); // critical
93
- recordFailures("browser_click", "w:B", 10); // critical
94
- resetLoopCounterForRef("browser_click", "w:A");
95
- const diagA = getLoopDiagnostics("browser_click", "w:A");
96
- const diagB = getLoopDiagnostics("browser_click", "w:B");
97
- assert.equal(diagA.strikes, 0, `w:A should have 0 strikes after per-ref reset, got ${diagA.strikes}`);
98
- assert.equal(diagB.severity, "critical", `w:B should still be critical, got ${diagB.severity}`);
99
- clearTrail();
100
- });
101
- test("Phase 0.1: detectLoop returns false after resetLoopCounter", () => {
102
- clearTrail();
103
- recordFailures("browser_click", "w:X", 10);
104
- assert.equal(detectLoop("browser_click", "w:X"), true, "Expected loop before reset");
105
- resetLoopCounter("test");
106
- recordFailures("browser_click", "w:X", 3); // not enough for loop after reset
107
- assert.equal(detectLoop("browser_click", "w:X"), false, "Expected no loop after reset with only 3 new strikes");
108
- clearTrail();
109
- });
110
- //# sourceMappingURL=trail.test.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=cache.test.d.ts.map
@@ -1,76 +0,0 @@
1
- /**
2
- * cache.test.ts — Phase 1.1
3
- * Unit tests for action map and box model caching with mutation-based TTL invalidation.
4
- */
5
- import { test } from "node:test";
6
- import assert from "node:assert/strict";
7
- import { getCachedActionMap, cacheActionMap, cacheBoxModel, getCachedBoxModel, cacheNode, getCachedNode, clearTabCache, } from "./cache.js";
8
- test("cache: basic action map storage and retrieval", () => {
9
- const targetId = "tab-1";
10
- const mockMap = {
11
- url: "https://example.com",
12
- elements: [],
13
- };
14
- clearTabCache(targetId);
15
- assert.equal(getCachedActionMap(targetId), null, "Initial cache miss");
16
- cacheActionMap(targetId, mockMap);
17
- const retrieved = getCachedActionMap(targetId);
18
- assert.ok(retrieved !== null, "Cache hit after storage");
19
- assert.equal(retrieved?.url, "https://example.com");
20
- clearTabCache(targetId);
21
- });
22
- test("cache: URL validation prevents stale cache", () => {
23
- const targetId = "tab-2";
24
- const mockMap = {
25
- url: "https://example.com",
26
- elements: [],
27
- };
28
- cacheActionMap(targetId, mockMap);
29
- // Same URL → cache hit
30
- assert.ok(getCachedActionMap(targetId, "https://example.com") !== null);
31
- // Different URL → cache miss
32
- assert.equal(getCachedActionMap(targetId, "https://other.com"), null);
33
- clearTabCache(targetId);
34
- });
35
- test("cache: box model storage and retrieval", () => {
36
- const targetId = "tab-3";
37
- const backendNodeId = 12345;
38
- const mockBoxModel = { model: { content: [0, 0, 100, 100] } };
39
- // First cache an action map to initialize the state
40
- cacheActionMap(targetId, { url: "https://example.com", elements: [] });
41
- cacheBoxModel(targetId, backendNodeId, mockBoxModel);
42
- const retrieved = getCachedBoxModel(targetId, backendNodeId);
43
- assert.ok(retrieved !== null, "Box model cached");
44
- assert.deepEqual(retrieved, mockBoxModel);
45
- clearTabCache(targetId);
46
- });
47
- test("cache: node storage and retrieval", () => {
48
- const targetId = "tab-4";
49
- const backendNodeId = 67890;
50
- const mockNode = { nodeId: 67890, nodeName: "DIV" };
51
- // First cache an action map to initialize the state
52
- cacheActionMap(targetId, { url: "https://example.com", elements: [] });
53
- cacheNode(targetId, backendNodeId, mockNode);
54
- const retrieved = getCachedNode(targetId, backendNodeId);
55
- assert.ok(retrieved !== null, "Node cached");
56
- assert.deepEqual(retrieved, mockNode);
57
- clearTabCache(targetId);
58
- });
59
- test("cache: clearTabCache removes all cached data", () => {
60
- const targetId = "tab-5";
61
- const mockMap = { url: "https://example.com", elements: [] };
62
- const mockBoxModel = { model: { content: [0, 0, 100, 100] } };
63
- cacheActionMap(targetId, mockMap);
64
- cacheBoxModel(targetId, 12345, mockBoxModel);
65
- assert.ok(getCachedActionMap(targetId) !== null);
66
- assert.ok(getCachedBoxModel(targetId, 12345) !== null);
67
- clearTabCache(targetId);
68
- assert.equal(getCachedActionMap(targetId), null);
69
- assert.equal(getCachedBoxModel(targetId, 12345), null);
70
- });
71
- test("cache: missing cache returns null without error", () => {
72
- assert.equal(getCachedActionMap("nonexistent-tab"), null);
73
- assert.equal(getCachedBoxModel("nonexistent-tab", 999), null);
74
- assert.equal(getCachedNode("nonexistent-tab", 999), null);
75
- });
76
- //# sourceMappingURL=cache.test.js.map
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=delta.test.d.ts.map