pi-harness-runtime 1.1.440 → 1.1.441

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 (349) hide show
  1. package/harness/e2e/auto-test-loop.ts +2 -0
  2. package/harness/e2e/browser-scrapers/base-browser-scraper.ts +2 -0
  3. package/harness/e2e/browser-scrapers/chrome-profile-manager.ts +2 -0
  4. package/harness/e2e/browser-scrapers/codex-browser-scraper.ts +2 -0
  5. package/harness/e2e/tools-detector.ts +2 -0
  6. package/harness/key-loader.ts +2 -0
  7. package/harness/langchain/checkpointer.ts +2 -0
  8. package/harness/langchain/cron-cli.ts +2 -0
  9. package/harness/langchain/graph.ts +2 -0
  10. package/harness/notification-center.ts +2 -0
  11. package/harness/secret-provider.ts +2 -0
  12. package/index.ts +2 -0
  13. package/package.json +7 -1
  14. package/packages/a2a-adapter/package.json +1 -1
  15. package/packages/agent-policy/dist/extension.d.ts.map +1 -1
  16. package/packages/agent-policy/dist/extension.js +2 -0
  17. package/packages/agent-policy/dist/extension.js.map +1 -1
  18. package/packages/agent-policy/package.json +1 -1
  19. package/packages/agent-policy/src/extension.ts +2 -0
  20. package/packages/architecture-generator/package.json +1 -1
  21. package/packages/auth/dist/minimax-browser-auth.d.ts.map +1 -1
  22. package/packages/auth/dist/minimax-browser-auth.js +23 -1
  23. package/packages/auth/dist/minimax-browser-auth.js.map +1 -1
  24. package/packages/auth/package.json +1 -1
  25. package/packages/auth/src/minimax-browser-auth.ts +30 -5
  26. package/packages/autonomous-refactor/package.json +1 -1
  27. package/packages/autonomous-runtime/dist/inbox.d.ts.map +1 -1
  28. package/packages/autonomous-runtime/dist/inbox.js +2 -0
  29. package/packages/autonomous-runtime/dist/inbox.js.map +1 -1
  30. package/packages/autonomous-runtime/package.json +1 -1
  31. package/packages/autonomous-runtime/src/inbox.ts +2 -0
  32. package/packages/cache-strategy/package.json +1 -1
  33. package/packages/capability-registry/package.json +1 -1
  34. package/packages/checkpoint/package.json +1 -1
  35. package/packages/cli-plugin-sdk/package.json +1 -1
  36. package/packages/clipboard/dist/src/clipboard.d.ts.map +1 -1
  37. package/packages/clipboard/dist/src/clipboard.js +2 -0
  38. package/packages/clipboard/dist/src/clipboard.js.map +1 -1
  39. package/packages/clipboard/dist/src/copy-sync.d.ts.map +1 -1
  40. package/packages/clipboard/dist/src/copy-sync.js +2 -0
  41. package/packages/clipboard/dist/src/copy-sync.js.map +1 -1
  42. package/packages/clipboard/dist/src/gist-relay.d.ts.map +1 -1
  43. package/packages/clipboard/dist/src/gist-relay.js +2 -0
  44. package/packages/clipboard/dist/src/gist-relay.js.map +1 -1
  45. package/packages/clipboard/package.json +1 -1
  46. package/packages/clipboard/src/clipboard.ts +2 -0
  47. package/packages/clipboard/src/copy-sync.ts +2 -0
  48. package/packages/clipboard/src/gist-relay.ts +2 -0
  49. package/packages/clipboard-plugin/package.json +1 -1
  50. package/packages/code-generation/package.json +1 -1
  51. package/packages/code-review/package.json +1 -1
  52. package/packages/codex-adapter/package.json +1 -1
  53. package/packages/codex-watcher/package.json +1 -1
  54. package/packages/config-capture/package.json +1 -1
  55. package/packages/connection-runtime/package.json +1 -1
  56. package/packages/context-compiler/package.json +1 -1
  57. package/packages/context-discovery/package.json +1 -1
  58. package/packages/context-manager/package.json +1 -1
  59. package/packages/contracts/dist/delegation-store.js +1 -4
  60. package/packages/contracts/dist/workspace-env.js +28 -1
  61. package/packages/contracts/package.json +1 -1
  62. package/packages/contracts/src/delegation-store.ts +9 -4
  63. package/packages/contracts/src/workspace-env.ts +29 -4
  64. package/packages/cookie-sanitizer/package.json +1 -1
  65. package/packages/cost-optimizer/package.json +1 -1
  66. package/packages/dependency-analyzer/package.json +1 -1
  67. package/packages/django-plugin/package.json +1 -1
  68. package/packages/doc-generator/package.json +1 -1
  69. package/packages/durable-review/package.json +1 -1
  70. package/packages/e2e-tester/package.json +1 -1
  71. package/packages/evaluation-engine/package.json +1 -1
  72. package/packages/evaluation-runner/package.json +1 -1
  73. package/packages/event-bus/package.json +1 -1
  74. package/packages/event-sink/package.json +1 -1
  75. package/packages/event-store/dist/src/store.d.ts.map +1 -1
  76. package/packages/event-store/dist/src/store.js +2 -0
  77. package/packages/event-store/dist/src/store.js.map +1 -1
  78. package/packages/event-store/package.json +1 -1
  79. package/packages/event-store/src/store.ts +3 -0
  80. package/packages/experience-replay/package.json +1 -1
  81. package/packages/feedback-collector/package.json +1 -1
  82. package/packages/file-copy-helper/package.json +1 -1
  83. package/packages/framework-detector/package.json +1 -1
  84. package/packages/framework-plugin-sdk/package.json +1 -1
  85. package/packages/frappe-plugin/package.json +1 -1
  86. package/packages/generic-web-plugin/dist/analyzer.d.ts.map +1 -1
  87. package/packages/generic-web-plugin/dist/analyzer.js +2 -0
  88. package/packages/generic-web-plugin/dist/analyzer.js.map +1 -1
  89. package/packages/generic-web-plugin/package.json +1 -1
  90. package/packages/generic-web-plugin/src/analyzer.ts +3 -0
  91. package/packages/health-monitor/package.json +1 -1
  92. package/packages/intent-analyzer/package.json +1 -1
  93. package/packages/jev-judge/package.json +1 -1
  94. package/packages/knowledge-graph/package.json +1 -1
  95. package/packages/knowledge-retrieval/dist/mcp-client.d.ts.map +1 -1
  96. package/packages/knowledge-retrieval/dist/mcp-client.js +0 -1
  97. package/packages/knowledge-retrieval/dist/mcp-client.js.map +1 -1
  98. package/packages/knowledge-retrieval/package.json +1 -4
  99. package/packages/knowledge-retrieval/src/mcp-client.ts +0 -1
  100. package/packages/laravel-plugin/package.json +1 -1
  101. package/packages/learning-engine/package.json +1 -1
  102. package/packages/logging/package.json +1 -1
  103. package/packages/mcp-adapter/dist/server.d.ts.map +1 -1
  104. package/packages/mcp-adapter/dist/server.js +2 -0
  105. package/packages/mcp-adapter/dist/server.js.map +1 -1
  106. package/packages/mcp-adapter/package.json +1 -1
  107. package/packages/mcp-adapter/src/server.ts +3 -0
  108. package/packages/memory-engine/package.json +1 -1
  109. package/packages/milestone-manager/package.json +1 -1
  110. package/packages/model-registry/package.json +1 -1
  111. package/packages/nextjs-plugin/package.json +1 -1
  112. package/packages/notification/package.json +1 -1
  113. package/packages/observability/package.json +1 -1
  114. package/packages/okf-indexer/package.json +1 -1
  115. package/packages/paperclip-integration/dist/index.d.ts.map +1 -1
  116. package/packages/paperclip-integration/dist/index.js +2 -0
  117. package/packages/paperclip-integration/dist/index.js.map +1 -1
  118. package/packages/paperclip-integration/package.json +1 -1
  119. package/packages/paperclip-integration/src/index.ts +2 -0
  120. package/packages/performance-optimizer/package.json +1 -1
  121. package/packages/permission-mode/package.json +1 -1
  122. package/packages/privilege-broker/dist/audit.d.ts.map +1 -1
  123. package/packages/privilege-broker/dist/audit.js +2 -0
  124. package/packages/privilege-broker/dist/audit.js.map +1 -1
  125. package/packages/privilege-broker/package.json +1 -1
  126. package/packages/privilege-broker/src/audit.ts +2 -0
  127. package/packages/project-analyzer/package.json +1 -1
  128. package/packages/project-bootstrap/package.json +1 -1
  129. package/packages/projection-engine/package.json +1 -1
  130. package/packages/prompt-compiler/dist/compiler.d.ts.map +1 -1
  131. package/packages/prompt-compiler/dist/compiler.js +2 -0
  132. package/packages/prompt-compiler/dist/compiler.js.map +1 -1
  133. package/packages/prompt-compiler/package.json +1 -1
  134. package/packages/prompt-compiler/src/compiler.ts +2 -0
  135. package/packages/prompt-versioning/package.json +1 -1
  136. package/packages/provider-adapter-sdk/package.json +1 -1
  137. package/packages/provider-router/package.json +1 -1
  138. package/packages/provider-selector/package.json +1 -1
  139. package/packages/providers/package.json +1 -1
  140. package/packages/qdrant-skills/package.json +1 -1
  141. package/packages/quota-manager/package.json +1 -1
  142. package/packages/rate-limiter/package.json +1 -1
  143. package/packages/react-vite-plugin/package.json +1 -1
  144. package/packages/relay/package.json +1 -1
  145. package/packages/release-manager/package.json +1 -1
  146. package/packages/requirement-compiler/package.json +1 -1
  147. package/packages/runtime/package.json +1 -1
  148. package/packages/scheduler/package.json +1 -1
  149. package/packages/scheduler-adapter/package.json +1 -1
  150. package/packages/session/package.json +1 -1
  151. package/packages/session-api/package.json +1 -1
  152. package/packages/session-export/package.json +1 -1
  153. package/packages/shared-context/package.json +1 -1
  154. package/packages/skill-mcp-client/dist/client.d.ts.map +1 -1
  155. package/packages/skill-mcp-client/dist/client.js +0 -1
  156. package/packages/skill-mcp-client/dist/client.js.map +1 -1
  157. package/packages/skill-mcp-client/package.json +1 -4
  158. package/packages/skill-mcp-client/src/client.ts +0 -1
  159. package/packages/skill-registry/package.json +1 -1
  160. package/packages/skills/dist/parser.d.ts.map +1 -1
  161. package/packages/skills/dist/parser.js +2 -0
  162. package/packages/skills/dist/parser.js.map +1 -1
  163. package/packages/skills/dist/skill-versioning.d.ts.map +1 -1
  164. package/packages/skills/dist/skill-versioning.js +2 -0
  165. package/packages/skills/dist/skill-versioning.js.map +1 -1
  166. package/packages/skills/package.json +1 -1
  167. package/packages/skills/src/parser.ts +3 -0
  168. package/packages/skills/src/skill-versioning.ts +2 -0
  169. package/packages/sprint-planner/package.json +1 -1
  170. package/packages/subscription-engine/package.json +1 -1
  171. package/packages/task-compiler/package.json +1 -1
  172. package/packages/tencentdb-memory/package.json +1 -1
  173. package/packages/tencentdb-sync/package.json +1 -1
  174. package/packages/test-data-generator/package.json +1 -1
  175. package/packages/test-generator/package.json +1 -1
  176. package/packages/todo-bd-sync/package.json +1 -1
  177. package/packages/token-estimation/package.json +1 -1
  178. package/packages/token-optimizer/package.json +1 -1
  179. package/packages/tooling-preference/package.json +1 -1
  180. package/packages/trajectory/package.json +1 -1
  181. package/packages/tui/package.json +1 -1
  182. package/packages/two-phase-review/package.json +1 -1
  183. package/packages/types/package.json +1 -1
  184. package/packages/voice-api/package.json +1 -1
  185. package/packages/webmcp-tester/dist/index.js +0 -2
  186. package/packages/webmcp-tester/package.json +1 -1
  187. package/packages/webmcp-tester/src/index.ts +0 -3
  188. package/packages/wiki/package.json +1 -1
  189. package/packages/workflow-events/package.json +1 -1
  190. package/packages/workspace-scanner/package.json +1 -1
  191. package/packages/worktree/package.json +1 -1
  192. package/packages/write-review/dist/trigger.d.ts.map +1 -1
  193. package/packages/write-review/dist/trigger.js +1 -2
  194. package/packages/write-review/dist/trigger.js.map +1 -1
  195. package/packages/write-review/package.json +1 -1
  196. package/packages/write-review/src/trigger.ts +1 -2
  197. package/scripts/lib/runtime-imports.ts +189 -3
  198. package/scripts/telegram-webhook-server.ts +2 -0
  199. package/scripts/verify-package-contents.ts +148 -1
  200. package/harness/e2e/tools-detector.test.ts +0 -155
  201. package/harness/glm-quota-countdown.test.ts +0 -323
  202. package/harness/langchain/agents.test.ts +0 -39
  203. package/harness/langchain/checkpointer.test.ts +0 -243
  204. package/harness/langchain/daemon.test.ts +0 -459
  205. package/harness/langchain/model-router.test.ts +0 -191
  206. package/harness/langchain/ping-pong-middleware.test.ts +0 -69
  207. package/harness/langchain/scoreboard.test.ts +0 -267
  208. package/harness/langchain/status-line.test.ts +0 -106
  209. package/harness/langchain/surge.test.ts +0 -467
  210. package/harness/langchain/widget.test.ts +0 -251
  211. package/harness/skill-sync.test.ts +0 -57
  212. package/harness/ssh-detach-interceptor.test.ts +0 -263
  213. package/harness/ssh-hang-recovery.test.ts +0 -86
  214. package/packages/a2a-adapter/test/a2a.test.ts +0 -151
  215. package/packages/agent-policy/test/interlock.test.ts +0 -101
  216. package/packages/agent-policy/test/policy-retention.test.ts +0 -380
  217. package/packages/agent-policy/test/policy.test.ts +0 -387
  218. package/packages/auth/test/auth.test.ts +0 -238
  219. package/packages/autonomous-refactor/test/refactor.test.ts +0 -202
  220. package/packages/autonomous-runtime/test/types.test.ts +0 -95
  221. package/packages/cache-strategy/test/cache.test.ts +0 -137
  222. package/packages/capability-registry/test/registry.test.ts +0 -204
  223. package/packages/checkpoint/test/engine.test.ts +0 -265
  224. package/packages/cli-plugin-sdk/test/cli.test.ts +0 -45
  225. package/packages/code-generation/test/generator.test.ts +0 -238
  226. package/packages/code-review/test/engine.test.ts +0 -302
  227. package/packages/codex-adapter/test/provider.test.ts +0 -142
  228. package/packages/codex-watcher/test/watch.test.ts +0 -453
  229. package/packages/context-compiler/test/context-compiler.test.ts +0 -746
  230. package/packages/context-manager/test/context.test.ts +0 -198
  231. package/packages/contracts/test/delegation.test.ts +0 -350
  232. package/packages/contracts/test/workspace-env.test.ts +0 -802
  233. package/packages/cookie-sanitizer/test/infer.test.ts +0 -110
  234. package/packages/cookie-sanitizer/test/normalize.test.ts +0 -113
  235. package/packages/cookie-sanitizer/test/parse.test.ts +0 -204
  236. package/packages/cookie-sanitizer/test/sync.test.ts +0 -237
  237. package/packages/cookie-sanitizer/test/watcher.test.ts +0 -27
  238. package/packages/cost-optimizer/test/cost-optimizer.test.ts +0 -438
  239. package/packages/dependency-analyzer/test/analyzer.test.ts +0 -49
  240. package/packages/django-plugin/test/analyzer.test.ts +0 -195
  241. package/packages/doc-generator/test/docs.test.ts +0 -168
  242. package/packages/durable-review/test/acceptance.test.ts +0 -357
  243. package/packages/durable-review/test/smoke.test.ts +0 -107
  244. package/packages/evaluation-engine/test/engine.test.ts +0 -277
  245. package/packages/evaluation-runner/test/runner.test.ts +0 -168
  246. package/packages/event-bus/test/bus.test.ts +0 -100
  247. package/packages/event-store/test/store.test.ts +0 -115
  248. package/packages/experience-replay/test/replay.test.ts +0 -267
  249. package/packages/feedback-collector/test/collector.test.ts +0 -142
  250. package/packages/framework-detector/test/detector.test.ts +0 -75
  251. package/packages/framework-plugin-sdk/test/manager.test.ts +0 -320
  252. package/packages/frappe-plugin/test/analyzer.test.ts +0 -250
  253. package/packages/generic-web-plugin/test/analyzer.test.ts +0 -134
  254. package/packages/health-monitor/test/health.test.ts +0 -231
  255. package/packages/intent-analyzer/test/analyzer.test.ts +0 -72
  256. package/packages/intent-analyzer/test/ping-pong.test.ts +0 -96
  257. package/packages/jev-judge/tests/auto-continue-context.test.ts +0 -280
  258. package/packages/jev-judge/tests/continuation-controller.test.ts +0 -1396
  259. package/packages/jev-judge/tests/jev-provider-chain.test.ts +0 -536
  260. package/packages/jev-judge/tests/outcome-application.test.ts +0 -132
  261. package/packages/jev-judge/tests/question-risk.test.ts +0 -129
  262. package/packages/jev-judge/tests/typesafe-provider.test.ts +0 -640
  263. package/packages/jev-judge/tests/validation-ledger.test.ts +0 -181
  264. package/packages/jev-judge/tests/validation-plan.test.ts +0 -170
  265. package/packages/jev-judge/tests/validation-tie-breaker.test.ts +0 -88
  266. package/packages/laravel-plugin/test/analyzer.test.ts +0 -220
  267. package/packages/learning-engine/test/engine.test.ts +0 -331
  268. package/packages/logging/test/buffer.test.ts +0 -44
  269. package/packages/logging/test/classification.test.ts +0 -137
  270. package/packages/logging/test/config.test.ts +0 -97
  271. package/packages/logging/test/fanout.test.ts +0 -186
  272. package/packages/logging/test/identity-transport.test.ts +0 -197
  273. package/packages/logging/test/incidents.test.ts +0 -404
  274. package/packages/logging/test/ingest-schema.test.ts +0 -184
  275. package/packages/logging/test/integration.test.ts +0 -140
  276. package/packages/logging/test/log-http.test.ts +0 -246
  277. package/packages/logging/test/redact.test.ts +0 -109
  278. package/packages/logging/test/sampler.test.ts +0 -57
  279. package/packages/logging/test/server.test.ts +0 -444
  280. package/packages/logging/test/shipper.test.ts +0 -152
  281. package/packages/logging/test/spool.test.ts +0 -96
  282. package/packages/mcp-adapter/test/server.test.ts +0 -91
  283. package/packages/memory-engine/test/engine.test.ts +0 -456
  284. package/packages/model-registry/test/registry.test.ts +0 -342
  285. package/packages/nextjs-plugin/test/analyzer.test.ts +0 -149
  286. package/packages/notification/test/approval-payload.test.ts +0 -269
  287. package/packages/notification/test/detail-format.test.ts +0 -106
  288. package/packages/notification/test/notification.test.ts +0 -375
  289. package/packages/notification/test/pending-question-lifecycle.test.ts +0 -195
  290. package/packages/notification/test/pending-question.test.ts +0 -331
  291. package/packages/notification/test/tooling-proposal-payload.test.ts +0 -212
  292. package/packages/observability/test/observability.test.ts +0 -1350
  293. package/packages/performance-optimizer/test/optimizer.test.ts +0 -267
  294. package/packages/project-analyzer/test/index.test.ts +0 -1719
  295. package/packages/prompt-compiler/test/prompt-compiler.test.ts +0 -813
  296. package/packages/prompt-versioning/test/versioning.test.ts +0 -112
  297. package/packages/provider-adapter-sdk/test/sdk.test.ts +0 -220
  298. package/packages/provider-router/test/provider-router.test.ts +0 -405
  299. package/packages/provider-selector/test/selector.test.ts +0 -192
  300. package/packages/providers/test/providers.test.ts +0 -225
  301. package/packages/qdrant-skills/test/collection-info.test.ts +0 -165
  302. package/packages/qdrant-skills/test/embedder-batch.test.ts +0 -184
  303. package/packages/qdrant-skills/test/embedder.test.ts +0 -97
  304. package/packages/quota-manager/test/quota.test.ts +0 -432
  305. package/packages/rate-limiter/test/limiter.test.ts +0 -128
  306. package/packages/react-vite-plugin/test/analyzer.test.ts +0 -139
  307. package/packages/relay/test/client.test.ts +0 -446
  308. package/packages/relay/test/relay-e2e.test.ts +0 -299
  309. package/packages/relay/test/relay-endpoint.test.ts +0 -278
  310. package/packages/release-manager/test/release.test.ts +0 -215
  311. package/packages/requirement-compiler/test/requirement-compiler.test.ts +0 -743
  312. package/packages/runtime/test/runtime-api.test.ts +0 -169
  313. package/packages/runtime/test/runtime.test.ts +0 -297
  314. package/packages/scheduler/test/scheduler.test.ts +0 -125
  315. package/packages/session/test/manager.test.ts +0 -233
  316. package/packages/shared-context/test/shared-context.test.ts +0 -114
  317. package/packages/skill-registry/test/registry.test.ts +0 -508
  318. package/packages/skills/test/init-skills.test.ts +0 -114
  319. package/packages/skills/test/remote-skills.test.ts +0 -360
  320. package/packages/skills/test/remote-source.test.ts +0 -248
  321. package/packages/skills/test/sanitize.test.ts +0 -250
  322. package/packages/skills/test/vector-indexing.test.ts +0 -205
  323. package/packages/skills/test/vector-schema.test.ts +0 -180
  324. package/packages/skills/test/vector-threshold.test.ts +0 -108
  325. package/packages/sprint-planner/test/planner.test.ts +0 -187
  326. package/packages/task-compiler/test/task-compiler.test.ts +0 -628
  327. package/packages/test-data-generator/test/generator.test.ts +0 -265
  328. package/packages/test-generator/test/generator.test.ts +0 -156
  329. package/packages/token-estimation/test/token-estimation.test.ts +0 -207
  330. package/packages/token-optimizer/test/optimizer.test.ts +0 -141
  331. package/packages/tooling-preference/tests/changed-files.test.ts +0 -124
  332. package/packages/tooling-preference/tests/detect.test.ts +0 -188
  333. package/packages/tooling-preference/tests/proposal.test.ts +0 -107
  334. package/packages/tooling-preference/tests/quality-check.test.ts +0 -272
  335. package/packages/tooling-preference/tests/registry.test.ts +0 -51
  336. package/packages/tooling-preference/tests/store.test.ts +0 -99
  337. package/packages/trajectory/test/store.test.ts +0 -250
  338. package/packages/tui/test/harness-footer.test.ts +0 -559
  339. package/packages/tui/test/harness-header.test.ts +0 -590
  340. package/packages/two-phase-review/test/classify.test.ts +0 -102
  341. package/packages/two-phase-review/test/helpers.ts +0 -72
  342. package/packages/two-phase-review/test/rules.test.ts +0 -150
  343. package/packages/two-phase-review/test/runtime.test.ts +0 -362
  344. package/packages/two-phase-review/test/scope.test.ts +0 -186
  345. package/packages/two-phase-review/test/signals.test.ts +0 -183
  346. package/packages/two-phase-review/test/state.test.ts +0 -201
  347. package/packages/wiki/test/basic.test.ts +0 -242
  348. package/packages/workspace-scanner/test/scanner.test.ts +0 -43
  349. package/packages/worktree/test/worktree.test.ts +0 -163
@@ -1,813 +0,0 @@
1
- /**
2
- * Prompt Compiler - Tests
3
- *
4
- * Tests cover all acceptance criteria from RFC-0041:
5
- * 1. Identical inputs produce identical prompt content and hash.
6
- * 2. Required sections are never removed by compaction.
7
- * 3. Provider formatting does not alter task semantics.
8
- * 4. Secrets are redacted before persistence.
9
- * 5. Invalid output contracts fail compilation.
10
- * 6. Continuation prompts do not repeat completed work.
11
- * 7. Unit tests cover all error codes.
12
- * 8. Tests pass without network access.
13
- */
14
-
15
- import assert from "node:assert";
16
- import { describe, it } from "node:test";
17
-
18
- import type { CompiledRequirement } from "@pi-harness/requirement-compiler";
19
- import type { CompiledTask } from "@pi-harness/task-compiler";
20
- import { compactToBudget, estimateTokens } from "../src/budget.js";
21
- // Import directly from source (TypeScript modules)
22
- import { compilePrompt, createSyncHasher } from "../src/compiler.js";
23
- import {
24
- deduplicateSections,
25
- normalizeForDeduplication,
26
- } from "../src/deduplicate.js";
27
- import { normalizeRequest } from "../src/normalize.js";
28
- import { renderForProvider } from "../src/render.js";
29
- import { buildSections } from "../src/section-builder.js";
30
- import { PromptCompileError } from "../src/types.js";
31
- import { PROVIDER_PROFILES } from "../src/types.js";
32
- import type { PromptCompileRequest } from "../src/types.js";
33
- import { validateSections, validateTokenBudget } from "../src/validate.js";
34
-
35
- // --- Test fixtures -------------------------------------------------------
36
-
37
- function makeRequest(
38
- overrides?: Partial<PromptCompileRequest>,
39
- ): PromptCompileRequest {
40
- const requirement: CompiledRequirement = {
41
- id: "REQ-001",
42
- title: "Build login feature",
43
- problemStatement: "Implement user login with JWT",
44
- goals: [{ id: "g1", description: "Build login" }],
45
- constraints: [
46
- { id: "c1", description: "Use HTTPS only", blocking: true },
47
- { id: "c2", description: "Passwords hashed with bcrypt", blocking: true },
48
- ],
49
- acceptanceCriteria: [
50
- { id: "ac1", outcome: ["User can log in with valid credentials"] },
51
- { id: "ac2", outcome: ["Invalid credentials show error"] },
52
- ],
53
- riskTags: [],
54
- };
55
-
56
- const task: CompiledTask = {
57
- id: "task-001",
58
- type: "implementation",
59
- title: "Implement login",
60
- objective: "Implement user login feature with JWT authentication",
61
- dependencies: [],
62
- priority: 1,
63
- fileOwnership: {
64
- taskId: "task-001",
65
- mode: "exclusive",
66
- include: ["/project/src/auth/**"],
67
- exclude: [],
68
- },
69
- filesInScope: [
70
- { path: "/project/src/auth/login.ts", description: "Login handler" },
71
- { path: "/project/src/auth/jwt.ts", description: "JWT utilities" },
72
- ],
73
- estimatedComplexity: 3 as const,
74
- expectedOutputs: [
75
- {
76
- kind: "file",
77
- description: "login.ts implementation",
78
- path: "/project/src/auth/login.ts",
79
- required: true,
80
- },
81
- { kind: "test_result", description: "login tests", required: true },
82
- ],
83
- prohibitedCommands: ["rm -rf /", "yarn build"],
84
- permittedCommands: ["tsc", "jest", "node"],
85
- requiredCapabilities: ["typescript", "node"],
86
- acceptanceCriteria: ["ac1", "ac2"],
87
- jobId: "JOB-001",
88
- };
89
-
90
- return {
91
- task,
92
- requirement,
93
- context: { entries: [] },
94
- provider: "codex",
95
- attempt: 1,
96
- ...overrides,
97
- };
98
- }
99
-
100
- function makeDeps(overrides?: {
101
- projectRules?: string[];
102
- redactor?: (content: string) => string;
103
- }) {
104
- const hasher = createSyncHasher();
105
- return {
106
- hasher,
107
- clock: { now: () => new Date("2026-01-01T00:00:00.000Z") },
108
- redactor: {
109
- redact: overrides?.redactor ?? ((s: string) => s),
110
- },
111
- projectRules: overrides?.projectRules ?? [
112
- "Use TypeScript strict mode",
113
- "Write unit tests for all public functions",
114
- ],
115
- };
116
- }
117
-
118
- // --- Test 1: Deterministic hash --------------------------------------
119
-
120
- describe("Deterministic hash", () => {
121
- it("identical inputs produce identical prompt content and hash", async () => {
122
- const request = makeRequest();
123
- const deps = makeDeps();
124
-
125
- const pkg1 = await compilePrompt(request, deps);
126
- const pkg2 = await compilePrompt(request, deps);
127
-
128
- assert.strictEqual(pkg1.hash, pkg2.hash, "Hash should be deterministic");
129
- assert.strictEqual(pkg1.system, pkg2.system, "System prompt should match");
130
- assert.strictEqual(pkg1.user, pkg2.user, "User prompt should match");
131
- assert.strictEqual(pkg1.taskId, pkg2.taskId);
132
- assert.strictEqual(pkg1.provider, pkg2.provider);
133
- assert.strictEqual(pkg1.version, "1");
134
- });
135
-
136
- it("different content produces different hash", async () => {
137
- const request1 = makeRequest();
138
- const request2 = makeRequest({
139
- task: {
140
- ...request1.task,
141
- objective: "Different objective",
142
- } as CompiledTask,
143
- });
144
- const deps = makeDeps();
145
-
146
- const pkg1 = await compilePrompt(request1, deps);
147
- const pkg2 = await compilePrompt(request2, deps);
148
-
149
- assert.notStrictEqual(
150
- pkg1.hash,
151
- pkg2.hash,
152
- "Different content should produce different hash",
153
- );
154
- });
155
-
156
- it("hash includes both system and user content", async () => {
157
- const request = makeRequest();
158
- const deps = makeDeps();
159
-
160
- const pkg = await compilePrompt(request, deps);
161
-
162
- assert.ok(pkg.hash.length > 0, "Hash should not be empty");
163
- assert.ok(
164
- pkg.hash === deps.hasher.sha256(`${pkg.system}\n${pkg.user}`),
165
- "Hash should equal SHA-256 of system+user",
166
- );
167
- });
168
- });
169
-
170
- // --- Test 2: Required sections never removed -----------------------------
171
-
172
- describe("Required sections survive compaction", () => {
173
- it("objective section is required and never compacted", async () => {
174
- const request = makeRequest({
175
- context: {
176
- entries: Array.from({ length: 50 }, (_, i) => ({
177
- id: `ctx-${i}`,
178
- content: `This is supplemental context entry number ${i}. It provides background information that could be safely removed if needed.`,
179
- priority: 7, // supplemental priority
180
- source: `file-${i}.md`,
181
- })),
182
- },
183
- });
184
- const deps = makeDeps();
185
-
186
- const pkg = await compilePrompt(request, deps);
187
-
188
- assert.ok(
189
- pkg.user.includes("Implement user login feature"),
190
- "Objective should be in user prompt",
191
- );
192
- assert.ok(
193
- pkg.user.includes("User can log in with valid credentials"),
194
- "Acceptance criteria should be preserved",
195
- );
196
- assert.ok(
197
- pkg.user.includes("/project/src/auth/login.ts"),
198
- "Files in scope should be preserved",
199
- );
200
- });
201
-
202
- it("project rules section is required", async () => {
203
- const request = makeRequest();
204
- const deps = makeDeps({
205
- projectRules: ["Use TypeScript", "No console.log"],
206
- });
207
-
208
- const pkg = await compilePrompt(request, deps);
209
-
210
- assert.ok(
211
- pkg.system.includes("TypeScript") || pkg.user.includes("TypeScript"),
212
- "Project rules should be in prompt",
213
- );
214
- });
215
-
216
- it("acceptance criteria are never removed", async () => {
217
- const request = makeRequest();
218
- const deps = makeDeps();
219
-
220
- const pkg = await compilePrompt(request, deps);
221
-
222
- const acSection = pkg.sections.find(
223
- (s) => s.kind === "acceptance_criteria",
224
- );
225
- assert.ok(acSection, "AC section should exist");
226
- assert.strictEqual(
227
- acSection?.required,
228
- true,
229
- "AC section should be required",
230
- );
231
- assert.ok(
232
- pkg.user.includes("User can log in with valid credentials"),
233
- "AC text should be in prompt",
234
- );
235
- });
236
- });
237
-
238
- // --- Test 3: Provider formatting ---------------------------------------
239
-
240
- describe("Provider formatting", () => {
241
- it("anthropic uses XML formatting", async () => {
242
- const request = makeRequest({ provider: "anthropic" });
243
- const deps = makeDeps();
244
-
245
- const pkg = await compilePrompt(request, deps);
246
- const profile = PROVIDER_PROFILES.anthropic;
247
-
248
- const sections = buildSections(
249
- normalizeRequest(request),
250
- deps.projectRules,
251
- );
252
- const rendered = renderForProvider(sections, profile);
253
-
254
- // Anthropic's rules go in system
255
- if (profile.supportsSystemPrompt) {
256
- assert.ok(
257
- rendered.system.length > 0 || rendered.user.length > 0,
258
- "Should have some output",
259
- );
260
- }
261
- });
262
-
263
- it("all supported providers produce valid output", async () => {
264
- const request = makeRequest();
265
- const providers: Array<
266
- "codex" | "minimax" | "glm" | "openai" | "anthropic" | "gemini"
267
- > = ["codex", "minimax", "glm", "openai", "anthropic", "gemini"];
268
-
269
- for (const provider of providers) {
270
- const req = makeRequest({ provider });
271
- const deps = makeDeps();
272
- const pkg = await compilePrompt(req, deps);
273
-
274
- assert.ok(
275
- pkg.system.length > 0 || pkg.user.length > 0,
276
- `Provider ${provider} should produce output`,
277
- );
278
- assert.strictEqual(pkg.provider, provider);
279
- }
280
- });
281
-
282
- it("different styles produce different formatting but same content", async () => {
283
- const request = makeRequest({ provider: "openai" });
284
- const deps = makeDeps();
285
-
286
- const pkg1 = await compilePrompt(request, deps);
287
- const req2 = makeRequest({ provider: "anthropic" });
288
- const pkg2 = await compilePrompt(req2, deps);
289
-
290
- // Both should contain the same key content
291
- assert.ok(
292
- pkg1.user.includes("Implement user login"),
293
- "OpenAI should have objective",
294
- );
295
- assert.ok(
296
- pkg2.user.includes("Implement user login"),
297
- "Anthropic should have objective",
298
- );
299
- });
300
- });
301
-
302
- // --- Test 4: Secret redaction -----------------------------------------
303
-
304
- describe("Secret redaction", () => {
305
- it("API keys are redacted before persistence", async () => {
306
- const request = makeRequest();
307
- const deps = makeDeps({
308
- redactor: (content: string) =>
309
- content
310
- .replace(/sk-[a-zA-Z0-9]{20,}/g, "[API_KEY_REDACTED]")
311
- .replace(/password\s*=\s*["'][^"']+["']/gi, 'password="[REDACTED]"'),
312
- });
313
-
314
- const secretRequest = makeRequest({
315
- task: {
316
- ...request.task,
317
- objective: "Use API key sk-1234567890abcdefghij for authentication",
318
- permittedCommands: [
319
- "curl -H 'Authorization: Bearer sk-abcdefghijklmnop12345' https://api.example.com",
320
- ],
321
- } as CompiledTask,
322
- });
323
-
324
- const pkg = await compilePrompt(secretRequest, deps);
325
-
326
- assert.ok(
327
- !pkg.user.includes("sk-1234567890abcdefghij"),
328
- "API key should be redacted",
329
- );
330
- assert.ok(
331
- !pkg.user.includes("sk-abcdefghijklmnop12345"),
332
- "Bearer token should be redacted",
333
- );
334
- });
335
-
336
- it("identity section preserves task ID", async () => {
337
- const request = makeRequest();
338
- const deps = makeDeps();
339
-
340
- const pkg = await compilePrompt(request, deps);
341
-
342
- assert.ok(pkg.user.includes("task-001"), "Task ID should be preserved");
343
- assert.ok(
344
- pkg.user.includes("REQ-001"),
345
- "Requirement ID should be preserved",
346
- );
347
- });
348
-
349
- it("hash is computed over redacted content", async () => {
350
- const request = makeRequest();
351
- const deps = makeDeps({
352
- redactor: (content: string) =>
353
- content.replace(/sk-[a-zA-Z0-9]+/g, "[REDACTED]"),
354
- });
355
-
356
- const pkg = await compilePrompt(request, deps);
357
-
358
- assert.ok(pkg.hash.length > 0, "Hash should be computed");
359
- assert.ok(!pkg.user.includes("sk-"), "No API keys in prompt");
360
- });
361
- });
362
-
363
- // --- Test 5: Error codes -----------------------------------------------
364
-
365
- describe("Error codes", () => {
366
- it("INVALID_TASK when task ID is empty", async () => {
367
- const base = makeRequest();
368
- const request = makeRequest({
369
- task: { ...base.task, id: "" },
370
- });
371
- const deps = makeDeps();
372
-
373
- await assert.rejects(
374
- async () => compilePrompt(request, deps),
375
- (err: unknown) => {
376
- if (err instanceof PromptCompileError) {
377
- assert.strictEqual(err.code, "INVALID_TASK");
378
- return true;
379
- }
380
- return false;
381
- },
382
- );
383
- });
384
-
385
- it("MISSING_OBJECTIVE when objective is empty", async () => {
386
- const base = makeRequest();
387
- const request = makeRequest({
388
- task: { ...base.task, objective: "" },
389
- });
390
- const deps = makeDeps();
391
-
392
- await assert.rejects(
393
- async () => compilePrompt(request, deps),
394
- (err: unknown) => {
395
- if (err instanceof PromptCompileError) {
396
- assert.strictEqual(err.code, "MISSING_OBJECTIVE");
397
- return true;
398
- }
399
- return false;
400
- },
401
- );
402
- });
403
-
404
- it("MISSING_OUTPUT_CONTRACT when no outputs defined", async () => {
405
- const base = makeRequest();
406
- const request = makeRequest({
407
- task: { ...base.task, expectedOutputs: [] },
408
- });
409
- const deps = makeDeps();
410
-
411
- await assert.rejects(
412
- async () => compilePrompt(request, deps),
413
- (err: unknown) => {
414
- if (err instanceof PromptCompileError) {
415
- assert.strictEqual(err.code, "MISSING_OUTPUT_CONTRACT");
416
- return true;
417
- }
418
- return false;
419
- },
420
- );
421
- });
422
-
423
- it("TOKEN_BUDGET_EXCEEDED when estimated tokens exceed budget", async () => {
424
- // Test validation directly: compactToBudget removes all compactable content,
425
- // so the full compile cannot produce TOKEN_BUDGET_EXCEEDED.
426
- // We test validateTokenBudget directly.
427
- const profile = PROVIDER_PROFILES.codex;
428
- const available = profile.maximumInputTokens - profile.reservedOutputTokens;
429
-
430
- assert.throws(
431
- () => validateTokenBudget(available + 1, profile),
432
- (err: unknown) => {
433
- if (err instanceof PromptCompileError) {
434
- assert.strictEqual(err.code, "TOKEN_BUDGET_EXCEEDED");
435
- return true;
436
- }
437
- return false;
438
- },
439
- );
440
- });
441
- it("POLICY_CONFLICT for prohibited tools", async () => {
442
- const base = makeRequest();
443
- const request = makeRequest({
444
- task: {
445
- ...base.task,
446
- permittedCommands: ["rm -rf /", "tsc", "jest"],
447
- },
448
- });
449
- const deps = makeDeps();
450
-
451
- await assert.rejects(
452
- async () => compilePrompt(request, deps),
453
- (err: unknown) => {
454
- if (err instanceof PromptCompileError) {
455
- assert.strictEqual(err.code, "POLICY_CONFLICT");
456
- return true;
457
- }
458
- return false;
459
- },
460
- );
461
- });
462
-
463
- it("INVALID_TASK for unknown provider", async () => {
464
- const request = makeRequest({ provider: "unknown-provider" as "codex" });
465
- const deps = makeDeps();
466
-
467
- await assert.rejects(
468
- async () => compilePrompt(request, deps),
469
- (err: unknown) => {
470
- if (err instanceof PromptCompileError) {
471
- assert.strictEqual(err.code, "INVALID_TASK");
472
- return true;
473
- }
474
- return false;
475
- },
476
- );
477
- });
478
- });
479
-
480
- // --- Test 6: Continuation without repetition ---------------------------
481
-
482
- describe("Continuation without repetition", () => {
483
- it("completed items are marked as done, not repeated", async () => {
484
- const request = makeRequest({
485
- continuation: {
486
- previousResponsePath: "/path/to/previous/response.json",
487
- completedItems: ["Step 1: Login form", "Step 2: JWT utility"],
488
- incompleteItems: ["Step 3: Error handling"],
489
- instruction: "continue_without_repeating",
490
- },
491
- });
492
- const deps = makeDeps();
493
-
494
- const pkg = await compilePrompt(request, deps);
495
-
496
- assert.ok(
497
- pkg.user.includes("Step 1: Login form") || pkg.user.includes("DONE"),
498
- "Completed items should be marked",
499
- );
500
- assert.ok(
501
- pkg.user.includes("Step 3: Error handling"),
502
- "Incomplete items should be included",
503
- );
504
- assert.ok(
505
- pkg.user.includes("Continue from where the previous attempt stopped"),
506
- "Continuation instruction should be present",
507
- );
508
- });
509
-
510
- it("continuation section has required=true so it survives compaction", async () => {
511
- const request = makeRequest({
512
- continuation: {
513
- previousResponsePath: "/tmp/response.json",
514
- completedItems: ["A", "B"],
515
- incompleteItems: ["C"],
516
- instruction: "continue_without_repeating",
517
- },
518
- });
519
- const deps = makeDeps();
520
-
521
- const pkg = await compilePrompt(request, deps);
522
-
523
- const contSection = pkg.sections.find(
524
- (s) => s.kind === "continuation_instructions",
525
- );
526
- assert.ok(contSection, "Continuation section should exist");
527
- assert.strictEqual(
528
- contSection?.required,
529
- true,
530
- "Continuation should be required",
531
- );
532
- });
533
-
534
- it("previous response path is referenced", async () => {
535
- const request = makeRequest({
536
- continuation: {
537
- previousResponsePath: "/tmp/attempt-1-response.json",
538
- completedItems: [],
539
- incompleteItems: ["Remaining work"],
540
- instruction: "continue_without_repeating",
541
- },
542
- });
543
- const deps = makeDeps();
544
-
545
- const pkg = await compilePrompt(request, deps);
546
-
547
- assert.ok(
548
- pkg.user.includes("attempt-1-response.json") ||
549
- pkg.sections.some((s) =>
550
- s.sourceRefs.some((ref) => ref.source.includes("attempt-1")),
551
- ),
552
- "Previous response path should be referenced",
553
- );
554
- });
555
- });
556
-
557
- // --- Test 7: Token budget ----------------------------------------------
558
-
559
- describe("Token budget", () => {
560
- it("estimateTokens returns 0 for empty string", () => {
561
- assert.strictEqual(estimateTokens(""), 0);
562
- assert.strictEqual(estimateTokens(" "), 0);
563
- });
564
-
565
- it("estimateTokens scales with content length", () => {
566
- const short = "hello";
567
- const long = "hello world this is a much longer string";
568
-
569
- assert.ok(estimateTokens(long) > estimateTokens(short));
570
- });
571
-
572
- it("compactToBudget preserves required sections", () => {
573
- const sections = [
574
- {
575
- id: "objective",
576
- kind: "objective" as const,
577
- content: "This is the critical objective",
578
- required: true,
579
- compactable: false,
580
- sourceRefs: [],
581
- },
582
- {
583
- id: "context-1",
584
- kind: "supplemental" as const,
585
- content: "This is supplemental content that can be removed",
586
- required: false,
587
- compactable: true,
588
- sourceRefs: [],
589
- },
590
- ];
591
-
592
- const result = compactToBudget(sections, 100, estimateTokens);
593
-
594
- const objectiveSection = result.sections.find((s) => s.id === "objective");
595
- assert.ok(
596
- objectiveSection?.content.includes("critical objective"),
597
- "Required section should be preserved",
598
- );
599
- });
600
-
601
- it("validateTokenBudget throws when exceeded", () => {
602
- const profile = PROVIDER_PROFILES.codex;
603
- const available = profile.maximumInputTokens - profile.reservedOutputTokens;
604
-
605
- assert.throws(
606
- () => validateTokenBudget(available + 1, profile),
607
- (err: unknown) => {
608
- if (err instanceof PromptCompileError) {
609
- assert.strictEqual(err.code, "TOKEN_BUDGET_EXCEEDED");
610
- return true;
611
- }
612
- return false;
613
- },
614
- );
615
- });
616
-
617
- it("validateTokenBudget passes when within budget", () => {
618
- const profile = PROVIDER_PROFILES.codex;
619
- const available = profile.maximumInputTokens - profile.reservedOutputTokens;
620
-
621
- assert.doesNotThrow(() => validateTokenBudget(available, profile));
622
- assert.doesNotThrow(() => validateTokenBudget(100, profile));
623
- });
624
- });
625
-
626
- // --- Test 8: Deduplication ---------------------------------------------
627
-
628
- describe("Deduplication", () => {
629
- it("normalizeForDeduplication strips markdown and lowercases", () => {
630
- assert.strictEqual(normalizeForDeduplication("# Header"), "header");
631
- assert.strictEqual(normalizeForDeduplication("**bold** text"), "bold text");
632
- assert.strictEqual(
633
- normalizeForDeduplication("[link](http://example.com)"),
634
- "link",
635
- );
636
- assert.strictEqual(
637
- normalizeForDeduplication(" spaces collapse "),
638
- "spaces collapse",
639
- );
640
- });
641
-
642
- it("duplicate lines are removed from compactable sections", () => {
643
- const sections = [
644
- {
645
- id: "context-1",
646
- kind: "relevant_context" as const,
647
- content:
648
- "Important note\nThis is repeated\nThis is repeated\nAnother line",
649
- required: false,
650
- compactable: true,
651
- sourceRefs: [],
652
- },
653
- ];
654
-
655
- const result = deduplicateSections(sections);
656
-
657
- assert.ok(
658
- result[0]?.content.split("\n").filter(Boolean).length < 4,
659
- "Duplicates should be removed",
660
- );
661
- });
662
-
663
- it("required sections are not deduplicated", () => {
664
- const sections = [
665
- {
666
- id: "objective",
667
- kind: "objective" as const,
668
- content: "Same objective",
669
- required: true,
670
- compactable: false,
671
- sourceRefs: [],
672
- },
673
- {
674
- id: "objective-2",
675
- kind: "objective" as const,
676
- content: "Same objective",
677
- required: true,
678
- compactable: false,
679
- sourceRefs: [],
680
- },
681
- ];
682
-
683
- const result = deduplicateSections(sections);
684
-
685
- // Both required sections should be preserved
686
- assert.strictEqual(
687
- result.length,
688
- 2,
689
- "Required sections should not be deduplicated",
690
- );
691
- });
692
- });
693
-
694
- // --- Test 9: Metadata completeness ------------------------------------
695
-
696
- describe("Metadata completeness", () => {
697
- it("PromptPackage includes token estimate and source refs", async () => {
698
- const request = makeRequest({
699
- context: {
700
- entries: [
701
- {
702
- id: "ctx-1",
703
- content: "Important context about the codebase",
704
- priority: 1,
705
- source: "repo/readme.md",
706
- },
707
- ],
708
- },
709
- });
710
- const deps = makeDeps();
711
-
712
- const pkg = await compilePrompt(request, deps);
713
-
714
- assert.ok(
715
- pkg.estimatedInputTokens > 0,
716
- "Token estimate should be positive",
717
- );
718
- assert.ok(pkg.sourceRefs.length > 0, "Should have source references");
719
- assert.strictEqual(pkg.version, "1");
720
- assert.strictEqual(pkg.createdAt, "2026-01-01T00:00:00.000Z");
721
- assert.ok(pkg.hash.length > 0, "Hash should be present");
722
- });
723
-
724
- it("sections are ordered deterministically", async () => {
725
- const request = makeRequest();
726
- const deps = makeDeps();
727
-
728
- const pkg = await compilePrompt(request, deps);
729
-
730
- const kinds = pkg.sections.map((s) => s.kind);
731
- // identity should come first
732
- assert.strictEqual(kinds[0], "identity");
733
- // objective should be early
734
- assert.ok(
735
- kinds.indexOf("objective") < kinds.indexOf("supplemental") ||
736
- !kinds.includes("supplemental"),
737
- );
738
- });
739
- });
740
-
741
- // --- Test 10: PromptCompileError -------------------------------------
742
-
743
- describe("PromptCompileError", () => {
744
- it("has correct name, code, and message", () => {
745
- const error = new PromptCompileError(
746
- "TOKEN_BUDGET_EXCEEDED",
747
- "Budget exceeded",
748
- { estimated: 1000, available: 500 },
749
- );
750
-
751
- assert.strictEqual(error.name, "PromptCompileError");
752
- assert.strictEqual(error.code, "TOKEN_BUDGET_EXCEEDED");
753
- assert.strictEqual(error.message, "Budget exceeded");
754
- assert.deepStrictEqual(error.details, { estimated: 1000, available: 500 });
755
- });
756
-
757
- it("is instance of Error", () => {
758
- const error = new PromptCompileError("INVALID_TASK", "Bad task");
759
- assert.ok(error instanceof Error);
760
- assert.ok(error instanceof PromptCompileError);
761
- });
762
- });
763
-
764
- // --- Test 11: normalizeRequest ----------------------------------------
765
-
766
- describe("normalizeRequest", () => {
767
- it("trims strings", () => {
768
- const req = makeRequest({
769
- task: { ...makeRequest().task, id: " task-001 " } as CompiledTask,
770
- });
771
- const normalized = normalizeRequest(req);
772
- assert.strictEqual(normalized.taskId, "task-001");
773
- });
774
-
775
- it("sorts context entries by priority", () => {
776
- const req = makeRequest({
777
- context: {
778
- entries: [
779
- { id: "a", content: "Low priority", priority: 5, source: "x" },
780
- { id: "b", content: "High priority", priority: 1, source: "y" },
781
- { id: "c", content: "Medium priority", priority: 3, source: "z" },
782
- ],
783
- },
784
- });
785
- const normalized = normalizeRequest(req);
786
-
787
- assert.strictEqual(normalized.contextEntries[0]?.id, "b");
788
- assert.strictEqual(normalized.contextEntries[1]?.id, "c");
789
- assert.strictEqual(normalized.contextEntries[2]?.id, "a");
790
- });
791
-
792
- it("removes duplicates from arrays", () => {
793
- const req = makeRequest();
794
- const withDupes = normalizeRequest({
795
- ...req,
796
- context: {
797
- entries: [
798
- { id: "a", content: "Content A", priority: 1, source: "x" },
799
- { id: "b", content: "Content A", priority: 1, source: "y" }, // duplicate content
800
- ],
801
- },
802
- });
803
-
804
- const uniqueContents = new Set(
805
- withDupes.contextEntries.map((e) => e.content),
806
- );
807
- assert.strictEqual(
808
- uniqueContents.size,
809
- 1,
810
- "Duplicate content should be removed",
811
- );
812
- });
813
- });