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,327 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import {
3
- createCancellationRegistry,
4
- createProgressEmitter,
5
- calculateBudgetStatus,
6
- CancellationError,
7
- } from "./index.js";
8
- import type { Logger } from "../logger.js";
9
- import type { ClientCapabilities } from "../types/capabilities.js";
10
-
11
- describe("progress", () => {
12
- let mockLogger: Logger;
13
-
14
- beforeEach(() => {
15
- mockLogger = {
16
- debug: vi.fn(),
17
- info: vi.fn(),
18
- warn: vi.fn(),
19
- error: vi.fn(),
20
- setLevel: vi.fn(),
21
- withCorrelation: vi.fn().mockReturnThis(),
22
- } as unknown as Logger;
23
- });
24
-
25
- describe("CancellationRegistry", () => {
26
- it("registers and tracks requests", () => {
27
- const registry = createCancellationRegistry(mockLogger);
28
-
29
- registry.register("request-1");
30
- expect(registry.isCancelled("request-1")).toBe(false);
31
- });
32
-
33
- it("marks requests as cancelled", () => {
34
- const registry = createCancellationRegistry(mockLogger);
35
-
36
- registry.register("request-1");
37
- registry.cancel("request-1");
38
- expect(registry.isCancelled("request-1")).toBe(true);
39
- });
40
-
41
- it("does not cancel unregistered requests", () => {
42
- const registry = createCancellationRegistry(mockLogger);
43
-
44
- registry.cancel("unknown-request");
45
- expect(registry.isCancelled("unknown-request")).toBe(false);
46
- });
47
-
48
- it("unregisters requests and clears cancellation status", () => {
49
- const registry = createCancellationRegistry(mockLogger);
50
-
51
- registry.register("request-1");
52
- registry.cancel("request-1");
53
- registry.unregister("request-1");
54
- expect(registry.isCancelled("request-1")).toBe(false);
55
- });
56
-
57
- it("checkCancelled throws for cancelled requests", () => {
58
- const registry = createCancellationRegistry(mockLogger);
59
-
60
- registry.register("request-1");
61
- registry.cancel("request-1");
62
-
63
- expect(() => registry.checkCancelled("request-1")).toThrow(
64
- CancellationError
65
- );
66
- });
67
-
68
- it("checkCancelled does not throw for active requests", () => {
69
- const registry = createCancellationRegistry(mockLogger);
70
-
71
- registry.register("request-1");
72
-
73
- expect(() => registry.checkCancelled("request-1")).not.toThrow();
74
- });
75
-
76
- it("handles multiple concurrent requests", () => {
77
- const registry = createCancellationRegistry(mockLogger);
78
-
79
- registry.register("request-1");
80
- registry.register("request-2");
81
- registry.register("request-3");
82
-
83
- registry.cancel("request-2");
84
-
85
- expect(registry.isCancelled("request-1")).toBe(false);
86
- expect(registry.isCancelled("request-2")).toBe(true);
87
- expect(registry.isCancelled("request-3")).toBe(false);
88
- });
89
- });
90
-
91
- describe("CancellationError", () => {
92
- it("has correct name and message", () => {
93
- const error = new CancellationError("request-123");
94
-
95
- expect(error.name).toBe("CancellationError");
96
- expect(error.message).toBe("Request request-123 was cancelled");
97
- expect(error.requestId).toBe("request-123");
98
- });
99
-
100
- it("is instanceof Error", () => {
101
- const error = new CancellationError("request-123");
102
- expect(error instanceof Error).toBe(true);
103
- });
104
- });
105
-
106
- describe("ProgressEmitter", () => {
107
- it("emits progress notifications when capability is available", async () => {
108
- const sendNotification = vi.fn().mockResolvedValue(undefined);
109
- const capabilities: ClientCapabilities = {
110
- sampling: true,
111
- elicitation: false,
112
- logging: false,
113
- progress: true,
114
- resourcesListChanged: false,
115
- resourcesSubscribe: false,
116
- protocolVersion: "2025-06-18",
117
- };
118
-
119
- const emitter = createProgressEmitter(
120
- sendNotification,
121
- capabilities,
122
- mockLogger
123
- );
124
-
125
- emitter.emit({
126
- progressToken: "token-1",
127
- progress: 5,
128
- total: 10,
129
- message: "Processing step 5",
130
- });
131
-
132
- expect(sendNotification).toHaveBeenCalledWith("notifications/progress", {
133
- progressToken: "token-1",
134
- progress: 5,
135
- total: 10,
136
- });
137
- });
138
-
139
- it("skips notification when capability is not available", () => {
140
- const sendNotification = vi.fn().mockResolvedValue(undefined);
141
- const capabilities: ClientCapabilities = {
142
- sampling: false,
143
- elicitation: false,
144
- logging: false,
145
- progress: false,
146
- resourcesListChanged: false,
147
- resourcesSubscribe: false,
148
- protocolVersion: "2025-06-18",
149
- };
150
-
151
- const emitter = createProgressEmitter(
152
- sendNotification,
153
- capabilities,
154
- mockLogger
155
- );
156
-
157
- emitter.emit({
158
- progressToken: "token-1",
159
- progress: 5,
160
- total: 10,
161
- });
162
-
163
- expect(sendNotification).not.toHaveBeenCalled();
164
- expect(mockLogger.debug).toHaveBeenCalledWith(
165
- "Progress notification skipped (not supported)",
166
- expect.any(Object)
167
- );
168
- });
169
-
170
- it("reports hasProgress correctly", () => {
171
- const sendNotification = vi.fn().mockResolvedValue(undefined);
172
-
173
- const withProgress = createProgressEmitter(
174
- sendNotification,
175
- { progress: true } as ClientCapabilities,
176
- mockLogger
177
- );
178
-
179
- const withoutProgress = createProgressEmitter(
180
- sendNotification,
181
- { progress: false } as ClientCapabilities,
182
- mockLogger
183
- );
184
-
185
- expect(withProgress.hasProgress()).toBe(true);
186
- expect(withoutProgress.hasProgress()).toBe(false);
187
- });
188
-
189
- it("handles notification failures gracefully", async () => {
190
- const sendNotification = vi
191
- .fn()
192
- .mockRejectedValue(new Error("Network error"));
193
- const capabilities: ClientCapabilities = {
194
- sampling: true,
195
- elicitation: false,
196
- logging: false,
197
- progress: true,
198
- resourcesListChanged: false,
199
- resourcesSubscribe: false,
200
- protocolVersion: "2025-06-18",
201
- };
202
-
203
- const emitter = createProgressEmitter(
204
- sendNotification,
205
- capabilities,
206
- mockLogger
207
- );
208
-
209
- // Should not throw
210
- emitter.emit({
211
- progressToken: "token-1",
212
- progress: 5,
213
- total: 10,
214
- });
215
-
216
- // Wait for async error handling
217
- await new Promise((resolve) => setTimeout(resolve, 10));
218
-
219
- expect(mockLogger.warn).toHaveBeenCalledWith(
220
- "Failed to emit progress notification",
221
- expect.objectContaining({ error: "Network error" })
222
- );
223
- });
224
- });
225
-
226
- describe("calculateBudgetStatus", () => {
227
- it("calculates remaining budget correctly", () => {
228
- const now = Date.now();
229
- const startTime = now - 5 * 60 * 1000; // 5 minutes ago
230
-
231
- const status = calculateBudgetStatus({
232
- stepsUsed: 20,
233
- maxSteps: 50,
234
- startTime,
235
- maxMinutes: 30,
236
- pagesVisited: 5,
237
- maxPages: 20,
238
- });
239
-
240
- expect(status.stepsUsed).toBe(20);
241
- expect(status.stepsRemaining).toBe(30);
242
- expect(status.minutesUsed).toBeCloseTo(5, 0);
243
- expect(status.minutesRemaining).toBeCloseTo(25, 0);
244
- expect(status.pagesVisited).toBe(5);
245
- expect(status.pagesRemaining).toBe(15);
246
- expect(status.isExhausted).toBe(false);
247
- });
248
-
249
- it("detects exhausted steps budget", () => {
250
- const status = calculateBudgetStatus({
251
- stepsUsed: 50,
252
- maxSteps: 50,
253
- startTime: Date.now(),
254
- maxMinutes: 30,
255
- pagesVisited: 5,
256
- maxPages: 20,
257
- });
258
-
259
- expect(status.isExhausted).toBe(true);
260
- expect(status.exhaustedReason).toBe("maxSteps limit reached");
261
- expect(status.stepsRemaining).toBe(0);
262
- });
263
-
264
- it("detects exhausted time budget", () => {
265
- const startTime = Date.now() - 35 * 60 * 1000; // 35 minutes ago
266
-
267
- const status = calculateBudgetStatus({
268
- stepsUsed: 20,
269
- maxSteps: 50,
270
- startTime,
271
- maxMinutes: 30,
272
- pagesVisited: 5,
273
- maxPages: 20,
274
- });
275
-
276
- expect(status.isExhausted).toBe(true);
277
- expect(status.exhaustedReason).toBe("maxMinutes timeout reached");
278
- expect(status.minutesRemaining).toBe(0);
279
- });
280
-
281
- it("detects exhausted pages budget", () => {
282
- const status = calculateBudgetStatus({
283
- stepsUsed: 20,
284
- maxSteps: 50,
285
- startTime: Date.now(),
286
- maxMinutes: 30,
287
- pagesVisited: 20,
288
- maxPages: 20,
289
- });
290
-
291
- expect(status.isExhausted).toBe(true);
292
- expect(status.exhaustedReason).toBe("maxPages limit reached");
293
- expect(status.pagesRemaining).toBe(0);
294
- });
295
-
296
- it("prevents negative remaining values", () => {
297
- const status = calculateBudgetStatus({
298
- stepsUsed: 100,
299
- maxSteps: 50,
300
- startTime: Date.now() - 60 * 60 * 1000, // 1 hour ago
301
- maxMinutes: 30,
302
- pagesVisited: 50,
303
- maxPages: 20,
304
- });
305
-
306
- expect(status.stepsRemaining).toBe(0);
307
- expect(status.minutesRemaining).toBe(0);
308
- expect(status.pagesRemaining).toBe(0);
309
- });
310
-
311
- it("rounds minutes to one decimal place", () => {
312
- const startTime = Date.now() - 7.333 * 60 * 1000;
313
-
314
- const status = calculateBudgetStatus({
315
- stepsUsed: 0,
316
- maxSteps: 50,
317
- startTime,
318
- maxMinutes: 30,
319
- pagesVisited: 0,
320
- maxPages: 20,
321
- });
322
-
323
- // Should be rounded to one decimal
324
- expect(status.minutesUsed.toString()).toMatch(/^\d+\.?\d?$/);
325
- });
326
- });
327
- });
@@ -1,170 +0,0 @@
1
- import type { Logger } from "../logger.js";
2
- import type { ClientCapabilities } from "../types/capabilities.js";
3
- import type { ProgressInfo, CancelledCallback } from "./types.js";
4
-
5
- export * from "./types.js";
6
-
7
- export interface CancellationRegistry {
8
- register(requestId: string): void;
9
- unregister(requestId: string): void;
10
- isCancelled(requestId: string): boolean;
11
- cancel(requestId: string): void;
12
- checkCancelled(requestId: string): void;
13
- }
14
-
15
- export interface ProgressEmitter {
16
- emit(info: ProgressInfo): void;
17
- hasProgress(): boolean;
18
- }
19
-
20
- export function createCancellationRegistry(
21
- logger: Logger
22
- ): CancellationRegistry {
23
- const cancelledRequests = new Set<string>();
24
- const activeRequests = new Set<string>();
25
-
26
- return {
27
- register(requestId: string): void {
28
- activeRequests.add(requestId);
29
- logger.debug("Request registered for cancellation tracking", {
30
- requestId,
31
- });
32
- },
33
-
34
- unregister(requestId: string): void {
35
- activeRequests.delete(requestId);
36
- cancelledRequests.delete(requestId);
37
- logger.debug("Request unregistered from cancellation tracking", {
38
- requestId,
39
- });
40
- },
41
-
42
- isCancelled(requestId: string): boolean {
43
- return cancelledRequests.has(requestId);
44
- },
45
-
46
- cancel(requestId: string): void {
47
- if (activeRequests.has(requestId)) {
48
- cancelledRequests.add(requestId);
49
- logger.info("Request marked as cancelled", { requestId });
50
- }
51
- },
52
-
53
- checkCancelled(requestId: string): void {
54
- if (cancelledRequests.has(requestId)) {
55
- throw new CancellationError(requestId);
56
- }
57
- },
58
- };
59
- }
60
-
61
- export class CancellationError extends Error {
62
- public readonly requestId: string;
63
-
64
- constructor(requestId: string) {
65
- super(`Request ${requestId} was cancelled`);
66
- this.name = "CancellationError";
67
- this.requestId = requestId;
68
- }
69
- }
70
-
71
- export function createProgressEmitter(
72
- sendNotification: (method: string, params: unknown) => Promise<void>,
73
- capabilities: ClientCapabilities,
74
- logger: Logger
75
- ): ProgressEmitter {
76
- const hasProgressCapability = capabilities.progress;
77
-
78
- return {
79
- emit(info: ProgressInfo): void {
80
- if (!hasProgressCapability) {
81
- logger.debug("Progress notification skipped (not supported)", {
82
- token: info.progressToken,
83
- progress: info.progress,
84
- });
85
- return;
86
- }
87
-
88
- const notification = {
89
- progressToken: info.progressToken,
90
- progress: info.progress,
91
- total: info.total,
92
- };
93
-
94
- logger.debug("Emitting progress notification", {
95
- ...notification,
96
- message: info.message,
97
- budgetStatus: info.budgetStatus,
98
- });
99
-
100
- sendNotification("notifications/progress", notification).catch(
101
- (error) => {
102
- logger.warn("Failed to emit progress notification", {
103
- error: error instanceof Error ? error.message : "Unknown error",
104
- });
105
- }
106
- );
107
- },
108
-
109
- hasProgress(): boolean {
110
- return hasProgressCapability;
111
- },
112
- };
113
- }
114
-
115
- export function calculateBudgetStatus(params: {
116
- stepsUsed: number;
117
- maxSteps: number;
118
- startTime: number;
119
- maxMinutes: number;
120
- pagesVisited: number;
121
- maxPages: number;
122
- }): {
123
- stepsUsed: number;
124
- stepsRemaining: number;
125
- minutesUsed: number;
126
- minutesRemaining: number;
127
- pagesVisited: number;
128
- pagesRemaining: number;
129
- isExhausted: boolean;
130
- exhaustedReason?: string;
131
- } {
132
- const {
133
- stepsUsed,
134
- maxSteps,
135
- startTime,
136
- maxMinutes,
137
- pagesVisited,
138
- maxPages,
139
- } = params;
140
-
141
- const minutesUsed = (Date.now() - startTime) / 60000;
142
- const stepsRemaining = maxSteps - stepsUsed;
143
- const minutesRemaining = maxMinutes - minutesUsed;
144
- const pagesRemaining = maxPages - pagesVisited;
145
-
146
- let isExhausted = false;
147
- let exhaustedReason: string | undefined;
148
-
149
- if (stepsRemaining <= 0) {
150
- isExhausted = true;
151
- exhaustedReason = "maxSteps limit reached";
152
- } else if (minutesRemaining <= 0) {
153
- isExhausted = true;
154
- exhaustedReason = "maxMinutes timeout reached";
155
- } else if (pagesRemaining <= 0) {
156
- isExhausted = true;
157
- exhaustedReason = "maxPages limit reached";
158
- }
159
-
160
- return {
161
- stepsUsed,
162
- stepsRemaining: Math.max(0, stepsRemaining),
163
- minutesUsed: Math.round(minutesUsed * 10) / 10,
164
- minutesRemaining: Math.max(0, Math.round(minutesRemaining * 10) / 10),
165
- pagesVisited,
166
- pagesRemaining: Math.max(0, pagesRemaining),
167
- isExhausted,
168
- exhaustedReason,
169
- };
170
- }
@@ -1,25 +0,0 @@
1
- export interface ProgressInfo {
2
- progressToken: string | number;
3
- progress: number;
4
- total?: number;
5
- message?: string;
6
- budgetStatus?: {
7
- stepsUsed: number;
8
- stepsRemaining: number;
9
- minutesUsed: number;
10
- minutesRemaining: number;
11
- pagesVisited: number;
12
- pagesRemaining: number;
13
- };
14
- }
15
-
16
- export interface ProgressNotification {
17
- method: "notifications/progress";
18
- params: {
19
- progressToken: string | number;
20
- progress: number;
21
- total?: number;
22
- };
23
- }
24
-
25
- export type CancelledCallback = (requestId: string) => void;