@oscharko-dev/keiko-server 0.2.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 (509) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/assistant-response.d.ts +6 -0
  3. package/dist/assistant-response.d.ts.map +1 -0
  4. package/dist/assistant-response.js +12 -0
  5. package/dist/browser.d.ts +11 -0
  6. package/dist/browser.d.ts.map +1 -0
  7. package/dist/browser.js +245 -0
  8. package/dist/chat-handlers.d.ts +48 -0
  9. package/dist/chat-handlers.d.ts.map +1 -0
  10. package/dist/chat-handlers.js +821 -0
  11. package/dist/chat-stream-handlers.d.ts +4 -0
  12. package/dist/chat-stream-handlers.d.ts.map +1 -0
  13. package/dist/chat-stream-handlers.js +136 -0
  14. package/dist/conversation-prompt.d.ts +8 -0
  15. package/dist/conversation-prompt.d.ts.map +1 -0
  16. package/dist/conversation-prompt.js +36 -0
  17. package/dist/conversation-validation.d.ts +26 -0
  18. package/dist/conversation-validation.d.ts.map +1 -0
  19. package/dist/conversation-validation.js +125 -0
  20. package/dist/credentialPersistence.d.ts +23 -0
  21. package/dist/credentialPersistence.d.ts.map +1 -0
  22. package/dist/credentialPersistence.js +93 -0
  23. package/dist/credentialVault.d.ts +30 -0
  24. package/dist/credentialVault.d.ts.map +1 -0
  25. package/dist/credentialVault.js +206 -0
  26. package/dist/csp.d.ts +3 -0
  27. package/dist/csp.d.ts.map +1 -0
  28. package/dist/csp.js +75 -0
  29. package/dist/deps.d.ts +78 -0
  30. package/dist/deps.d.ts.map +1 -0
  31. package/dist/deps.js +457 -0
  32. package/dist/editor/agentRoutes.d.ts +7 -0
  33. package/dist/editor/agentRoutes.d.ts.map +1 -0
  34. package/dist/editor/agentRoutes.js +197 -0
  35. package/dist/editor/assuredGateRunner.d.ts +36 -0
  36. package/dist/editor/assuredGateRunner.d.ts.map +1 -0
  37. package/dist/editor/assuredGateRunner.js +100 -0
  38. package/dist/editor/assuredPreFilter.d.ts +34 -0
  39. package/dist/editor/assuredPreFilter.d.ts.map +1 -0
  40. package/dist/editor/assuredPreFilter.js +134 -0
  41. package/dist/editor/assuredPreFilterRunner.d.ts +31 -0
  42. package/dist/editor/assuredPreFilterRunner.d.ts.map +1 -0
  43. package/dist/editor/assuredPreFilterRunner.js +312 -0
  44. package/dist/editor/builtinLanguageProviders.d.ts +6 -0
  45. package/dist/editor/builtinLanguageProviders.d.ts.map +1 -0
  46. package/dist/editor/builtinLanguageProviders.js +221 -0
  47. package/dist/editor/codingContext.d.ts +12 -0
  48. package/dist/editor/codingContext.d.ts.map +1 -0
  49. package/dist/editor/codingContext.js +121 -0
  50. package/dist/editor/codingContextEvidence.d.ts +7 -0
  51. package/dist/editor/codingContextEvidence.d.ts.map +1 -0
  52. package/dist/editor/codingContextEvidence.js +52 -0
  53. package/dist/editor/codingContextProviders.d.ts +36 -0
  54. package/dist/editor/codingContextProviders.d.ts.map +1 -0
  55. package/dist/editor/codingContextProviders.js +348 -0
  56. package/dist/editor/completionModelEvidence.d.ts +16 -0
  57. package/dist/editor/completionModelEvidence.d.ts.map +1 -0
  58. package/dist/editor/completionModelEvidence.js +50 -0
  59. package/dist/editor/completionRoutes.d.ts +37 -0
  60. package/dist/editor/completionRoutes.d.ts.map +1 -0
  61. package/dist/editor/completionRoutes.js +411 -0
  62. package/dist/editor/contextRoutes.d.ts +6 -0
  63. package/dist/editor/contextRoutes.d.ts.map +1 -0
  64. package/dist/editor/contextRoutes.js +411 -0
  65. package/dist/editor/disposableAssuredExecution.d.ts +22 -0
  66. package/dist/editor/disposableAssuredExecution.d.ts.map +1 -0
  67. package/dist/editor/disposableAssuredExecution.js +57 -0
  68. package/dist/editor/editorCompletionModel.d.ts +47 -0
  69. package/dist/editor/editorCompletionModel.d.ts.map +1 -0
  70. package/dist/editor/editorCompletionModel.js +156 -0
  71. package/dist/editor/editorInlineCompletionModel.d.ts +34 -0
  72. package/dist/editor/editorInlineCompletionModel.d.ts.map +1 -0
  73. package/dist/editor/editorInlineCompletionModel.js +112 -0
  74. package/dist/editor/editorModelTokenBudget.d.ts +46 -0
  75. package/dist/editor/editorModelTokenBudget.d.ts.map +1 -0
  76. package/dist/editor/editorModelTokenBudget.js +121 -0
  77. package/dist/editor/inlineCompletionRateLimiter.d.ts +19 -0
  78. package/dist/editor/inlineCompletionRateLimiter.d.ts.map +1 -0
  79. package/dist/editor/inlineCompletionRateLimiter.js +46 -0
  80. package/dist/editor/inlineCompletionRoutes.d.ts +26 -0
  81. package/dist/editor/inlineCompletionRoutes.d.ts.map +1 -0
  82. package/dist/editor/inlineCompletionRoutes.js +404 -0
  83. package/dist/editor/inlineCompletionTelemetryEvidence.d.ts +5 -0
  84. package/dist/editor/inlineCompletionTelemetryEvidence.d.ts.map +1 -0
  85. package/dist/editor/inlineCompletionTelemetryEvidence.js +42 -0
  86. package/dist/editor/languageCancellation.d.ts +19 -0
  87. package/dist/editor/languageCancellation.d.ts.map +1 -0
  88. package/dist/editor/languageCancellation.js +48 -0
  89. package/dist/editor/languageProvider.d.ts +39 -0
  90. package/dist/editor/languageProvider.d.ts.map +1 -0
  91. package/dist/editor/languageProvider.js +11 -0
  92. package/dist/editor/languageRoutes.d.ts +15 -0
  93. package/dist/editor/languageRoutes.d.ts.map +1 -0
  94. package/dist/editor/languageRoutes.js +106 -0
  95. package/dist/editor/languageSanitize.d.ts +8 -0
  96. package/dist/editor/languageSanitize.d.ts.map +1 -0
  97. package/dist/editor/languageSanitize.js +101 -0
  98. package/dist/editor/languageService.d.ts +36 -0
  99. package/dist/editor/languageService.d.ts.map +1 -0
  100. package/dist/editor/languageService.js +93 -0
  101. package/dist/editor/languageServiceHost.d.ts +14 -0
  102. package/dist/editor/languageServiceHost.d.ts.map +1 -0
  103. package/dist/editor/languageServiceHost.js +242 -0
  104. package/dist/editor/localKnowledgeRetrieval.d.ts +21 -0
  105. package/dist/editor/localKnowledgeRetrieval.d.ts.map +1 -0
  106. package/dist/editor/localKnowledgeRetrieval.js +44 -0
  107. package/dist/editor/patchApplyEvidence.d.ts +21 -0
  108. package/dist/editor/patchApplyEvidence.d.ts.map +1 -0
  109. package/dist/editor/patchApplyEvidence.js +87 -0
  110. package/dist/editor/patchApplyRoutes.d.ts +16 -0
  111. package/dist/editor/patchApplyRoutes.d.ts.map +1 -0
  112. package/dist/editor/patchApplyRoutes.js +307 -0
  113. package/dist/editor/postApplyVerification.d.ts +42 -0
  114. package/dist/editor/postApplyVerification.d.ts.map +1 -0
  115. package/dist/editor/postApplyVerification.js +177 -0
  116. package/dist/editor/testGenerationEvidence.d.ts +6 -0
  117. package/dist/editor/testGenerationEvidence.d.ts.map +1 -0
  118. package/dist/editor/testGenerationEvidence.js +72 -0
  119. package/dist/editor/testGenerationPatch.d.ts +10 -0
  120. package/dist/editor/testGenerationPatch.d.ts.map +1 -0
  121. package/dist/editor/testGenerationPatch.js +66 -0
  122. package/dist/editor/testGenerationRoutes.d.ts +21 -0
  123. package/dist/editor/testGenerationRoutes.d.ts.map +1 -0
  124. package/dist/editor/testGenerationRoutes.js +254 -0
  125. package/dist/editor/testGenerationRunner.d.ts +23 -0
  126. package/dist/editor/testGenerationRunner.d.ts.map +1 -0
  127. package/dist/editor/testGenerationRunner.js +120 -0
  128. package/dist/editor/textOffsets.d.ts +6 -0
  129. package/dist/editor/textOffsets.d.ts.map +1 -0
  130. package/dist/editor/textOffsets.js +82 -0
  131. package/dist/editor/typescriptLanguageProvider.d.ts +3 -0
  132. package/dist/editor/typescriptLanguageProvider.d.ts.map +1 -0
  133. package/dist/editor/typescriptLanguageProvider.js +217 -0
  134. package/dist/evidence.d.ts +28 -0
  135. package/dist/evidence.d.ts.map +1 -0
  136. package/dist/evidence.js +145 -0
  137. package/dist/files-deny.d.ts +3 -0
  138. package/dist/files-deny.d.ts.map +1 -0
  139. package/dist/files-deny.js +12 -0
  140. package/dist/files.d.ts +97 -0
  141. package/dist/files.d.ts.map +1 -0
  142. package/dist/files.js +733 -0
  143. package/dist/gateway-setup.d.ts +10 -0
  144. package/dist/gateway-setup.d.ts.map +1 -0
  145. package/dist/gateway-setup.js +896 -0
  146. package/dist/governed-workflow.d.ts +17 -0
  147. package/dist/governed-workflow.d.ts.map +1 -0
  148. package/dist/governed-workflow.js +147 -0
  149. package/dist/grounded-answer.d.ts +12 -0
  150. package/dist/grounded-answer.d.ts.map +1 -0
  151. package/dist/grounded-answer.js +69 -0
  152. package/dist/grounded-context-index.d.ts +25 -0
  153. package/dist/grounded-context-index.d.ts.map +1 -0
  154. package/dist/grounded-context-index.js +169 -0
  155. package/dist/grounded-document-evidence.d.ts +28 -0
  156. package/dist/grounded-document-evidence.d.ts.map +1 -0
  157. package/dist/grounded-document-evidence.js +430 -0
  158. package/dist/grounded-handoff.d.ts +4 -0
  159. package/dist/grounded-handoff.d.ts.map +1 -0
  160. package/dist/grounded-handoff.js +445 -0
  161. package/dist/grounded-orchestrator.d.ts +43 -0
  162. package/dist/grounded-orchestrator.d.ts.map +1 -0
  163. package/dist/grounded-orchestrator.js +1445 -0
  164. package/dist/grounded-prompt.d.ts +2 -0
  165. package/dist/grounded-prompt.d.ts.map +1 -0
  166. package/dist/grounded-prompt.js +17 -0
  167. package/dist/grounded-qa-hybrid.d.ts +36 -0
  168. package/dist/grounded-qa-hybrid.d.ts.map +1 -0
  169. package/dist/grounded-qa-hybrid.js +762 -0
  170. package/dist/grounded-qa-multi-source.d.ts +38 -0
  171. package/dist/grounded-qa-multi-source.d.ts.map +1 -0
  172. package/dist/grounded-qa-multi-source.js +461 -0
  173. package/dist/grounded-qa.d.ts +45 -0
  174. package/dist/grounded-qa.d.ts.map +1 -0
  175. package/dist/grounded-qa.js +877 -0
  176. package/dist/grounded-rerank.d.ts +26 -0
  177. package/dist/grounded-rerank.d.ts.map +1 -0
  178. package/dist/grounded-rerank.js +72 -0
  179. package/dist/grounded-turn-registry.d.ts +23 -0
  180. package/dist/grounded-turn-registry.d.ts.map +1 -0
  181. package/dist/grounded-turn-registry.js +102 -0
  182. package/dist/headers.d.ts +3 -0
  183. package/dist/headers.d.ts.map +1 -0
  184. package/dist/headers.js +22 -0
  185. package/dist/host-check.d.ts +3 -0
  186. package/dist/host-check.d.ts.map +1 -0
  187. package/dist/host-check.js +58 -0
  188. package/dist/index.d.ts +26 -0
  189. package/dist/index.d.ts.map +1 -0
  190. package/dist/index.js +33 -0
  191. package/dist/load-csp.d.ts +3 -0
  192. package/dist/load-csp.d.ts.map +1 -0
  193. package/dist/load-csp.js +100 -0
  194. package/dist/local-knowledge-grounded-qa.d.ts +42 -0
  195. package/dist/local-knowledge-grounded-qa.d.ts.map +1 -0
  196. package/dist/local-knowledge-grounded-qa.js +678 -0
  197. package/dist/local-knowledge-handlers.d.ts +24 -0
  198. package/dist/local-knowledge-handlers.d.ts.map +1 -0
  199. package/dist/local-knowledge-handlers.js +1285 -0
  200. package/dist/local-knowledge-indexing-registry.d.ts +13 -0
  201. package/dist/local-knowledge-indexing-registry.d.ts.map +1 -0
  202. package/dist/local-knowledge-indexing-registry.js +53 -0
  203. package/dist/localKnowledgeKeyProvider.d.ts +11 -0
  204. package/dist/localKnowledgeKeyProvider.d.ts.map +1 -0
  205. package/dist/localKnowledgeKeyProvider.js +48 -0
  206. package/dist/memory-audit-event-builders.d.ts +21 -0
  207. package/dist/memory-audit-event-builders.d.ts.map +1 -0
  208. package/dist/memory-audit-event-builders.js +187 -0
  209. package/dist/memory-audit-handler.d.ts +23 -0
  210. package/dist/memory-audit-handler.d.ts.map +1 -0
  211. package/dist/memory-audit-handler.js +191 -0
  212. package/dist/memory-capture-policy.d.ts +10 -0
  213. package/dist/memory-capture-policy.d.ts.map +1 -0
  214. package/dist/memory-capture-policy.js +44 -0
  215. package/dist/memory-consolidation-handlers.d.ts +6 -0
  216. package/dist/memory-consolidation-handlers.d.ts.map +1 -0
  217. package/dist/memory-consolidation-handlers.js +491 -0
  218. package/dist/memory-consolidation-registry.d.ts +47 -0
  219. package/dist/memory-consolidation-registry.d.ts.map +1 -0
  220. package/dist/memory-consolidation-registry.js +106 -0
  221. package/dist/memory-conv-handlers.d.ts +8 -0
  222. package/dist/memory-conv-handlers.d.ts.map +1 -0
  223. package/dist/memory-conv-handlers.js +369 -0
  224. package/dist/memory-conversation-context.d.ts +13 -0
  225. package/dist/memory-conversation-context.d.ts.map +1 -0
  226. package/dist/memory-conversation-context.js +22 -0
  227. package/dist/memory-diagnostics.d.ts +29 -0
  228. package/dist/memory-diagnostics.d.ts.map +1 -0
  229. package/dist/memory-diagnostics.js +122 -0
  230. package/dist/memory-embedding.d.ts +21 -0
  231. package/dist/memory-embedding.d.ts.map +1 -0
  232. package/dist/memory-embedding.js +264 -0
  233. package/dist/memory-handlers.d.ts +19 -0
  234. package/dist/memory-handlers.d.ts.map +1 -0
  235. package/dist/memory-handlers.js +1204 -0
  236. package/dist/memory-maintenance-handlers.d.ts +35 -0
  237. package/dist/memory-maintenance-handlers.d.ts.map +1 -0
  238. package/dist/memory-maintenance-handlers.js +219 -0
  239. package/dist/memory-record-builders.d.ts +4 -0
  240. package/dist/memory-record-builders.d.ts.map +1 -0
  241. package/dist/memory-record-builders.js +19 -0
  242. package/dist/memory-retention.d.ts +31 -0
  243. package/dist/memory-retention.d.ts.map +1 -0
  244. package/dist/memory-retention.js +151 -0
  245. package/dist/memory-retrieval-signals.d.ts +12 -0
  246. package/dist/memory-retrieval-signals.d.ts.map +1 -0
  247. package/dist/memory-retrieval-signals.js +100 -0
  248. package/dist/memory-salience.d.ts +12 -0
  249. package/dist/memory-salience.d.ts.map +1 -0
  250. package/dist/memory-salience.js +154 -0
  251. package/dist/memory-scope-sanitizer.d.ts +6 -0
  252. package/dist/memory-scope-sanitizer.d.ts.map +1 -0
  253. package/dist/memory-scope-sanitizer.js +106 -0
  254. package/dist/memory-target-resolver.d.ts +4 -0
  255. package/dist/memory-target-resolver.d.ts.map +1 -0
  256. package/dist/memory-target-resolver.js +73 -0
  257. package/dist/memory-workflow-port.d.ts +14 -0
  258. package/dist/memory-workflow-port.d.ts.map +1 -0
  259. package/dist/memory-workflow-port.js +186 -0
  260. package/dist/private-json.d.ts +3 -0
  261. package/dist/private-json.d.ts.map +1 -0
  262. package/dist/private-json.js +62 -0
  263. package/dist/promptEnhancer/index.d.ts +3 -0
  264. package/dist/promptEnhancer/index.d.ts.map +1 -0
  265. package/dist/promptEnhancer/index.js +5 -0
  266. package/dist/promptEnhancer/orchestrate.d.ts +2 -0
  267. package/dist/promptEnhancer/orchestrate.d.ts.map +1 -0
  268. package/dist/promptEnhancer/orchestrate.js +5 -0
  269. package/dist/promptEnhancer/routes.d.ts +9 -0
  270. package/dist/promptEnhancer/routes.d.ts.map +1 -0
  271. package/dist/promptEnhancer/routes.js +205 -0
  272. package/dist/qualityIntelligence/capsuleAdapter.d.ts +27 -0
  273. package/dist/qualityIntelligence/capsuleAdapter.d.ts.map +1 -0
  274. package/dist/qualityIntelligence/capsuleAdapter.js +57 -0
  275. package/dist/qualityIntelligence/connectorAuthorization.d.ts +22 -0
  276. package/dist/qualityIntelligence/connectorAuthorization.d.ts.map +1 -0
  277. package/dist/qualityIntelligence/connectorAuthorization.js +35 -0
  278. package/dist/qualityIntelligence/connectorErrors.d.ts +16 -0
  279. package/dist/qualityIntelligence/connectorErrors.d.ts.map +1 -0
  280. package/dist/qualityIntelligence/connectorErrors.js +56 -0
  281. package/dist/qualityIntelligence/connectorRoutes.d.ts +7 -0
  282. package/dist/qualityIntelligence/connectorRoutes.d.ts.map +1 -0
  283. package/dist/qualityIntelligence/connectorRoutes.js +167 -0
  284. package/dist/qualityIntelligence/editRoutes.d.ts +5 -0
  285. package/dist/qualityIntelligence/editRoutes.d.ts.map +1 -0
  286. package/dist/qualityIntelligence/editRoutes.js +293 -0
  287. package/dist/qualityIntelligence/exportAssembly.d.ts +22 -0
  288. package/dist/qualityIntelligence/exportAssembly.d.ts.map +1 -0
  289. package/dist/qualityIntelligence/exportAssembly.js +352 -0
  290. package/dist/qualityIntelligence/exportRoutes.d.ts +5 -0
  291. package/dist/qualityIntelligence/exportRoutes.d.ts.map +1 -0
  292. package/dist/qualityIntelligence/exportRoutes.js +320 -0
  293. package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts +8 -0
  294. package/dist/qualityIntelligence/figma/figmaConcurrency.d.ts.map +1 -0
  295. package/dist/qualityIntelligence/figma/figmaConcurrency.js +34 -0
  296. package/dist/qualityIntelligence/figma/figmaConnector.d.ts +65 -0
  297. package/dist/qualityIntelligence/figma/figmaConnector.d.ts.map +1 -0
  298. package/dist/qualityIntelligence/figma/figmaConnector.js +184 -0
  299. package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts +52 -0
  300. package/dist/qualityIntelligence/figma/figmaConnectorAudit.d.ts.map +1 -0
  301. package/dist/qualityIntelligence/figma/figmaConnectorAudit.js +63 -0
  302. package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts +31 -0
  303. package/dist/qualityIntelligence/figma/figmaConnectorErrors.d.ts.map +1 -0
  304. package/dist/qualityIntelligence/figma/figmaConnectorErrors.js +220 -0
  305. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts +44 -0
  306. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.d.ts.map +1 -0
  307. package/dist/qualityIntelligence/figma/figmaConnectorMetrics.js +49 -0
  308. package/dist/qualityIntelligence/figma/figmaConsent.d.ts +39 -0
  309. package/dist/qualityIntelligence/figma/figmaConsent.d.ts.map +1 -0
  310. package/dist/qualityIntelligence/figma/figmaConsent.js +62 -0
  311. package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts +28 -0
  312. package/dist/qualityIntelligence/figma/figmaHttpPort.d.ts.map +1 -0
  313. package/dist/qualityIntelligence/figma/figmaHttpPort.js +70 -0
  314. package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts +49 -0
  315. package/dist/qualityIntelligence/figma/figmaObservedActions.d.ts.map +1 -0
  316. package/dist/qualityIntelligence/figma/figmaObservedActions.js +89 -0
  317. package/dist/qualityIntelligence/figma/figmaReadiness.d.ts +32 -0
  318. package/dist/qualityIntelligence/figma/figmaReadiness.d.ts.map +1 -0
  319. package/dist/qualityIntelligence/figma/figmaReadiness.js +67 -0
  320. package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts +29 -0
  321. package/dist/qualityIntelligence/figma/figmaRenderPort.d.ts.map +1 -0
  322. package/dist/qualityIntelligence/figma/figmaRenderPort.js +93 -0
  323. package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts +28 -0
  324. package/dist/qualityIntelligence/figma/figmaResnapshot.d.ts.map +1 -0
  325. package/dist/qualityIntelligence/figma/figmaResnapshot.js +38 -0
  326. package/dist/qualityIntelligence/figma/figmaRetry.d.ts +31 -0
  327. package/dist/qualityIntelligence/figma/figmaRetry.d.ts.map +1 -0
  328. package/dist/qualityIntelligence/figma/figmaRetry.js +62 -0
  329. package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts +9 -0
  330. package/dist/qualityIntelligence/figma/figmaScopeRef.d.ts.map +1 -0
  331. package/dist/qualityIntelligence/figma/figmaScopeRef.js +18 -0
  332. package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts +86 -0
  333. package/dist/qualityIntelligence/figma/figmaScopedPagination.d.ts.map +1 -0
  334. package/dist/qualityIntelligence/figma/figmaScopedPagination.js +308 -0
  335. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts +31 -0
  336. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.d.ts.map +1 -0
  337. package/dist/qualityIntelligence/figma/figmaSnapshotBuilder.js +314 -0
  338. package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts +18 -0
  339. package/dist/qualityIntelligence/figma/figmaSnapshotHash.d.ts.map +1 -0
  340. package/dist/qualityIntelligence/figma/figmaSnapshotHash.js +63 -0
  341. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts +65 -0
  342. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.d.ts.map +1 -0
  343. package/dist/qualityIntelligence/figma/figmaSnapshotTypes.js +13 -0
  344. package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts +9 -0
  345. package/dist/qualityIntelligence/figma/figmaTokenSource.d.ts.map +1 -0
  346. package/dist/qualityIntelligence/figma/figmaTokenSource.js +61 -0
  347. package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts +19 -0
  348. package/dist/qualityIntelligence/figma/figmaTokenStore.d.ts.map +1 -0
  349. package/dist/qualityIntelligence/figma/figmaTokenStore.js +156 -0
  350. package/dist/qualityIntelligence/figma/figmaUrl.d.ts +6 -0
  351. package/dist/qualityIntelligence/figma/figmaUrl.d.ts.map +1 -0
  352. package/dist/qualityIntelligence/figma/figmaUrl.js +36 -0
  353. package/dist/qualityIntelligence/figma/index.d.ts +20 -0
  354. package/dist/qualityIntelligence/figma/index.d.ts.map +1 -0
  355. package/dist/qualityIntelligence/figma/index.js +26 -0
  356. package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts +28 -0
  357. package/dist/qualityIntelligence/figmaCodegenRoutes.d.ts.map +1 -0
  358. package/dist/qualityIntelligence/figmaCodegenRoutes.js +165 -0
  359. package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts +55 -0
  360. package/dist/qualityIntelligence/figmaSnapshotAdapter.d.ts.map +1 -0
  361. package/dist/qualityIntelligence/figmaSnapshotAdapter.js +219 -0
  362. package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts +64 -0
  363. package/dist/qualityIntelligence/figmaSnapshotOrchestration.d.ts.map +1 -0
  364. package/dist/qualityIntelligence/figmaSnapshotOrchestration.js +203 -0
  365. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +112 -0
  366. package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -0
  367. package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1063 -0
  368. package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts +19 -0
  369. package/dist/qualityIntelligence/figmaSnapshotScreenIds.d.ts.map +1 -0
  370. package/dist/qualityIntelligence/figmaSnapshotScreenIds.js +75 -0
  371. package/dist/qualityIntelligence/generationPort.d.ts +15 -0
  372. package/dist/qualityIntelligence/generationPort.d.ts.map +1 -0
  373. package/dist/qualityIntelligence/generationPort.js +185 -0
  374. package/dist/qualityIntelligence/handoffErrors.d.ts +9 -0
  375. package/dist/qualityIntelligence/handoffErrors.d.ts.map +1 -0
  376. package/dist/qualityIntelligence/handoffErrors.js +21 -0
  377. package/dist/qualityIntelligence/handoffRoutes.d.ts +15 -0
  378. package/dist/qualityIntelligence/handoffRoutes.d.ts.map +1 -0
  379. package/dist/qualityIntelligence/handoffRoutes.js +341 -0
  380. package/dist/qualityIntelligence/index.d.ts +17 -0
  381. package/dist/qualityIntelligence/index.d.ts.map +1 -0
  382. package/dist/qualityIntelligence/index.js +36 -0
  383. package/dist/qualityIntelligence/judgePort.d.ts +30 -0
  384. package/dist/qualityIntelligence/judgePort.d.ts.map +1 -0
  385. package/dist/qualityIntelligence/judgePort.js +326 -0
  386. package/dist/qualityIntelligence/modelSelection.d.ts +58 -0
  387. package/dist/qualityIntelligence/modelSelection.d.ts.map +1 -0
  388. package/dist/qualityIntelligence/modelSelection.js +148 -0
  389. package/dist/qualityIntelligence/reCheckRoutes.d.ts +6 -0
  390. package/dist/qualityIntelligence/reCheckRoutes.d.ts.map +1 -0
  391. package/dist/qualityIntelligence/reCheckRoutes.js +1157 -0
  392. package/dist/qualityIntelligence/retentionEnforcement.d.ts +13 -0
  393. package/dist/qualityIntelligence/retentionEnforcement.d.ts.map +1 -0
  394. package/dist/qualityIntelligence/retentionEnforcement.js +47 -0
  395. package/dist/qualityIntelligence/retentionRoutes.d.ts +8 -0
  396. package/dist/qualityIntelligence/retentionRoutes.d.ts.map +1 -0
  397. package/dist/qualityIntelligence/retentionRoutes.js +74 -0
  398. package/dist/qualityIntelligence/reviewRoutes.d.ts +5 -0
  399. package/dist/qualityIntelligence/reviewRoutes.d.ts.map +1 -0
  400. package/dist/qualityIntelligence/reviewRoutes.js +145 -0
  401. package/dist/qualityIntelligence/reviewStore.d.ts +75 -0
  402. package/dist/qualityIntelligence/reviewStore.d.ts.map +1 -0
  403. package/dist/qualityIntelligence/reviewStore.js +170 -0
  404. package/dist/qualityIntelligence/runExecution.d.ts +36 -0
  405. package/dist/qualityIntelligence/runExecution.d.ts.map +1 -0
  406. package/dist/qualityIntelligence/runExecution.js +180 -0
  407. package/dist/qualityIntelligence/runIngestion.d.ts +70 -0
  408. package/dist/qualityIntelligence/runIngestion.d.ts.map +1 -0
  409. package/dist/qualityIntelligence/runIngestion.js +1235 -0
  410. package/dist/qualityIntelligence/runRegistry.d.ts +31 -0
  411. package/dist/qualityIntelligence/runRegistry.d.ts.map +1 -0
  412. package/dist/qualityIntelligence/runRegistry.js +66 -0
  413. package/dist/qualityIntelligence/runRoutes.d.ts +16 -0
  414. package/dist/qualityIntelligence/runRoutes.d.ts.map +1 -0
  415. package/dist/qualityIntelligence/runRoutes.js +357 -0
  416. package/dist/qualityIntelligence/traceabilityRoutes.d.ts +5 -0
  417. package/dist/qualityIntelligence/traceabilityRoutes.d.ts.map +1 -0
  418. package/dist/qualityIntelligence/traceabilityRoutes.js +173 -0
  419. package/dist/qualityIntelligence/uiRoutes.d.ts +7 -0
  420. package/dist/qualityIntelligence/uiRoutes.d.ts.map +1 -0
  421. package/dist/qualityIntelligence/uiRoutes.js +336 -0
  422. package/dist/read-handlers.d.ts +9 -0
  423. package/dist/read-handlers.d.ts.map +1 -0
  424. package/dist/read-handlers.js +265 -0
  425. package/dist/relationship-handlers.d.ts +191 -0
  426. package/dist/relationship-handlers.d.ts.map +1 -0
  427. package/dist/relationship-handlers.js +0 -0
  428. package/dist/routes.d.ts +37 -0
  429. package/dist/routes.d.ts.map +1 -0
  430. package/dist/routes.js +507 -0
  431. package/dist/run-engine.d.ts +25 -0
  432. package/dist/run-engine.d.ts.map +1 -0
  433. package/dist/run-engine.js +385 -0
  434. package/dist/run-handlers.d.ts +9 -0
  435. package/dist/run-handlers.d.ts.map +1 -0
  436. package/dist/run-handlers.js +465 -0
  437. package/dist/run-request.d.ts +17 -0
  438. package/dist/run-request.d.ts.map +1 -0
  439. package/dist/run-request.js +219 -0
  440. package/dist/runs.d.ts +47 -0
  441. package/dist/runs.d.ts.map +1 -0
  442. package/dist/runs.js +100 -0
  443. package/dist/server.d.ts +13 -0
  444. package/dist/server.d.ts.map +1 -0
  445. package/dist/server.js +152 -0
  446. package/dist/sink.d.ts +28 -0
  447. package/dist/sink.d.ts.map +1 -0
  448. package/dist/sink.js +80 -0
  449. package/dist/sse-write.d.ts +9 -0
  450. package/dist/sse-write.d.ts.map +1 -0
  451. package/dist/sse-write.js +26 -0
  452. package/dist/sse.d.ts +8 -0
  453. package/dist/sse.d.ts.map +1 -0
  454. package/dist/sse.js +27 -0
  455. package/dist/static.d.ts +5 -0
  456. package/dist/static.d.ts.map +1 -0
  457. package/dist/static.js +76 -0
  458. package/dist/store/chats.d.ts +17 -0
  459. package/dist/store/chats.d.ts.map +1 -0
  460. package/dist/store/chats.js +624 -0
  461. package/dist/store/db.d.ts +11 -0
  462. package/dist/store/db.d.ts.map +1 -0
  463. package/dist/store/db.js +203 -0
  464. package/dist/store/errors.d.ts +13 -0
  465. package/dist/store/errors.d.ts.map +1 -0
  466. package/dist/store/errors.js +30 -0
  467. package/dist/store/index.d.ts +7 -0
  468. package/dist/store/index.d.ts.map +1 -0
  469. package/dist/store/index.js +6 -0
  470. package/dist/store/messages.d.ts +8 -0
  471. package/dist/store/messages.d.ts.map +1 -0
  472. package/dist/store/messages.js +149 -0
  473. package/dist/store/paths.d.ts +5 -0
  474. package/dist/store/paths.d.ts.map +1 -0
  475. package/dist/store/paths.js +84 -0
  476. package/dist/store/projects.d.ts +8 -0
  477. package/dist/store/projects.d.ts.map +1 -0
  478. package/dist/store/projects.js +59 -0
  479. package/dist/store/relationship-audit.d.ts +42 -0
  480. package/dist/store/relationship-audit.d.ts.map +1 -0
  481. package/dist/store/relationship-audit.js +155 -0
  482. package/dist/store/relationships.d.ts +191 -0
  483. package/dist/store/relationships.d.ts.map +1 -0
  484. package/dist/store/relationships.js +724 -0
  485. package/dist/store/schema.d.ts +4 -0
  486. package/dist/store/schema.d.ts.map +1 -0
  487. package/dist/store/schema.js +220 -0
  488. package/dist/store/types.d.ts +29 -0
  489. package/dist/store/types.d.ts.map +1 -0
  490. package/dist/store/types.js +8 -0
  491. package/dist/store/validation.d.ts +7 -0
  492. package/dist/store/validation.d.ts.map +1 -0
  493. package/dist/store/validation.js +117 -0
  494. package/dist/store-handlers.d.ts +17 -0
  495. package/dist/store-handlers.d.ts.map +1 -0
  496. package/dist/store-handlers.js +872 -0
  497. package/dist/terminal-errors.d.ts +22 -0
  498. package/dist/terminal-errors.d.ts.map +1 -0
  499. package/dist/terminal-errors.js +45 -0
  500. package/dist/terminal-evidence.d.ts +21 -0
  501. package/dist/terminal-evidence.d.ts.map +1 -0
  502. package/dist/terminal-evidence.js +65 -0
  503. package/dist/terminal-routes.d.ts +10 -0
  504. package/dist/terminal-routes.d.ts.map +1 -0
  505. package/dist/terminal-routes.js +219 -0
  506. package/dist/terminal.d.ts +68 -0
  507. package/dist/terminal.d.ts.map +1 -0
  508. package/dist/terminal.js +855 -0
  509. package/package.json +52 -0
@@ -0,0 +1,1157 @@
1
+ // Quality Intelligence drift re-check + targeted regeneration routes (Epic #735, Issue #743).
2
+ //
3
+ // NOTE on "current sources": The original run's START request sources are NOT persisted in the
4
+ // manifest. Therefore re-check and regenerate-stale both require the sources to be re-supplied
5
+ // by the caller. Both routes are POST to carry the sources in the body; the GET verb is avoided
6
+ // because sources can be arbitrarily large.
7
+ //
8
+ // POST /api/quality-intelligence/runs/:id/re-check
9
+ // Body: { sources: QualityIntelligenceInlineSource[] }
10
+ // Returns: QualityIntelligenceUiStalenessReport
11
+ //
12
+ // POST /api/quality-intelligence/runs/:id/regenerate-stale
13
+ // Body: { sources: QualityIntelligenceInlineSource[] }
14
+ // Returns: { runId: string; regeneratedCount: number; preservedCount: number }
15
+ //
16
+ // Both routes go through the central CSRF guard in server.ts (all POSTs do).
17
+ import { randomUUID } from "node:crypto";
18
+ import { isAbsolute } from "node:path";
19
+ import { QualityIntelligence } from "@oscharko-dev/keiko-contracts";
20
+ import { ALL_POLICY_PROFILES, buildAtomCoverageStatuses, buildCoverageMap, compareStaleness, deduplicateCandidates, regressionDefault, validateCandidates, } from "@oscharko-dev/keiko-quality-intelligence";
21
+ import { assertValidRunId, sha256Hex } from "@oscharko-dev/keiko-security";
22
+ import { createInMemoryQualityIntelligenceLocalStore, loadQualityIntelligenceCandidates, loadQualityIntelligenceRun, recordQualityIntelligenceCandidates, recordQualityIntelligenceRun, } from "@oscharko-dev/keiko-evidence";
23
+ import { QUALITY_INTELLIGENCE_DEFAULT_WORKFLOW_LIMITS, excerptsByAtomId, runQualityIntelligenceModelRoutedTestDesign, } from "@oscharko-dev/keiko-workflows";
24
+ import { currentRedactionSecrets } from "../deps.js";
25
+ import { makeCapsuleResolver } from "./capsuleAdapter.js";
26
+ import { makeFigmaSnapshotLoader, makeFigmaVisionHintProvider } from "./figmaSnapshotAdapter.js";
27
+ import { createQiGenerationPort, QiGenerationError } from "./generationPort.js";
28
+ import { createQiJudgePort } from "./judgePort.js";
29
+ import { resolveQiTestDesignSelection } from "./modelSelection.js";
30
+ import { ingestInlineSourcesAsync, QiIngestionError } from "./runIngestion.js";
31
+ import { parseFigmaSnapshotScreenIds } from "./figmaSnapshotScreenIds.js";
32
+ const MAX_BODY_BYTES = 2 * 1024 * 1024;
33
+ const REQUIREMENTS_ENVELOPE_PREFIX = "qi-src-req-";
34
+ const errorResult = (status, code, message) => ({
35
+ status,
36
+ body: { error: { code, message } },
37
+ });
38
+ // Validate the (already-non-empty) :id path param and map an invalid id to a 400 (mirrors the
39
+ // evidence read handlers) instead of letting it reach the store and surface as a generic 500 in the
40
+ // outer catch. assertValidRunId rejects separators / `..` / NUL, so a traversal-shaped id never
41
+ // reaches a filesystem path. Returns the error result to short-circuit, or null when the id is valid.
42
+ function invalidRunIdFormat(id) {
43
+ try {
44
+ assertValidRunId(id);
45
+ return null;
46
+ }
47
+ catch {
48
+ return errorResult(400, "QI_BAD_REQUEST", "Run id is invalid.");
49
+ }
50
+ }
51
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
52
+ const readBody = (req) => new Promise((resolve, reject) => {
53
+ const chunks = [];
54
+ let total = 0;
55
+ let capped = false;
56
+ req.on("data", (chunk) => {
57
+ total += chunk.length;
58
+ if (total > MAX_BODY_BYTES) {
59
+ if (!capped) {
60
+ capped = true;
61
+ chunks.length = 0;
62
+ reject(new Error("body too large"));
63
+ req.resume();
64
+ }
65
+ return;
66
+ }
67
+ chunks.push(chunk);
68
+ });
69
+ req.on("end", () => {
70
+ if (!capped)
71
+ resolve(Buffer.concat(chunks).toString("utf8"));
72
+ });
73
+ req.on("error", reject);
74
+ });
75
+ function cancellationResult(signal) {
76
+ return signal.aborted
77
+ ? errorResult(499, "QI_REQUEST_CANCELLED", "Quality Intelligence request was cancelled.")
78
+ : null;
79
+ }
80
+ function requestAbortSignal(ctx) {
81
+ const controller = new AbortController();
82
+ const abort = () => {
83
+ if (!controller.signal.aborted) {
84
+ controller.abort("quality intelligence request cancelled");
85
+ }
86
+ };
87
+ const abortOnIncompleteRequestClose = () => {
88
+ if (!ctx.req.complete)
89
+ abort();
90
+ };
91
+ const abortOnResponseClose = () => {
92
+ if (!ctx.res.writableEnded)
93
+ abort();
94
+ };
95
+ if (ctx.req.destroyed) {
96
+ abort();
97
+ return { signal: controller.signal, dispose: () => undefined };
98
+ }
99
+ ctx.req.once("aborted", abort);
100
+ ctx.req.once("close", abortOnIncompleteRequestClose);
101
+ if (typeof ctx.res.once === "function") {
102
+ ctx.res.once("close", abortOnResponseClose);
103
+ }
104
+ return {
105
+ signal: controller.signal,
106
+ dispose: () => {
107
+ ctx.req.off("aborted", abort);
108
+ ctx.req.off("close", abortOnIncompleteRequestClose);
109
+ if (typeof ctx.res.off === "function") {
110
+ ctx.res.off("close", abortOnResponseClose);
111
+ }
112
+ },
113
+ };
114
+ }
115
+ function validateCapsuleSource(label, raw) {
116
+ if (typeof raw.capsuleId !== "string" || raw.capsuleId.trim().length === 0)
117
+ return undefined;
118
+ return { kind: "capsule", label, capsuleId: raw.capsuleId };
119
+ }
120
+ function validateCapsuleSetSource(label, raw) {
121
+ if (typeof raw.capsuleSetId !== "string" || raw.capsuleSetId.trim().length === 0) {
122
+ return undefined;
123
+ }
124
+ return { kind: "capsule-set", label, capsuleSetId: raw.capsuleSetId };
125
+ }
126
+ function validateFigmaSnapshotSource(label, raw) {
127
+ if (typeof raw.snapshotRunId !== "string" || raw.snapshotRunId.trim().length === 0) {
128
+ return undefined;
129
+ }
130
+ // Shared with the start-run route so re-check accepts/rejects EXACTLY the same screenIds inputs:
131
+ // absent → whole snapshot; present → non-empty, bounded, canonicalised scope; empty array rejected.
132
+ const parsed = parseFigmaSnapshotScreenIds(raw.screenIds);
133
+ if (!parsed.ok) {
134
+ return undefined;
135
+ }
136
+ return {
137
+ kind: "figma-snapshot",
138
+ label,
139
+ snapshotRunId: raw.snapshotRunId,
140
+ ...(parsed.screenIds !== undefined ? { screenIds: parsed.screenIds } : {}),
141
+ };
142
+ }
143
+ function validateImageSource(label, raw) {
144
+ if (raw.sourceKind !== "figma-snapshot-screen")
145
+ return undefined;
146
+ if (typeof raw.snapshotRunId !== "string" || raw.snapshotRunId.trim().length === 0) {
147
+ return undefined;
148
+ }
149
+ if (typeof raw.screenId !== "string" || raw.screenId.trim().length === 0)
150
+ return undefined;
151
+ return {
152
+ kind: "image",
153
+ label,
154
+ sourceKind: "figma-snapshot-screen",
155
+ snapshotRunId: raw.snapshotRunId,
156
+ screenId: raw.screenId,
157
+ };
158
+ }
159
+ function validateConnectorSource(label, raw) {
160
+ if (raw.kind === "capsule") {
161
+ return validateCapsuleSource(label, raw);
162
+ }
163
+ if (raw.kind === "capsule-set") {
164
+ return validateCapsuleSetSource(label, raw);
165
+ }
166
+ if (raw.kind === "figma-snapshot") {
167
+ return validateFigmaSnapshotSource(label, raw);
168
+ }
169
+ if (raw.kind === "image") {
170
+ return validateImageSource(label, raw);
171
+ }
172
+ return undefined;
173
+ }
174
+ function validateSource(raw) {
175
+ if (!isObject(raw) || typeof raw.label !== "string")
176
+ return undefined;
177
+ const label = raw.label;
178
+ if (raw.kind === "requirements" && typeof raw.text === "string") {
179
+ return { kind: "requirements", label, text: raw.text };
180
+ }
181
+ if (raw.kind === "workspace" && typeof raw.path === "string") {
182
+ return { kind: "workspace", label, path: raw.path };
183
+ }
184
+ if (raw.kind === "file" && typeof raw.path === "string") {
185
+ return { kind: "file", label, path: raw.path };
186
+ }
187
+ return validateConnectorSource(label, raw);
188
+ }
189
+ async function parseSources(req) {
190
+ let raw;
191
+ try {
192
+ raw = await readBody(req);
193
+ }
194
+ catch {
195
+ return {
196
+ ok: false,
197
+ result: errorResult(413, "QI_BODY_TOO_LARGE", "Request body is too large."),
198
+ };
199
+ }
200
+ let parsed;
201
+ try {
202
+ parsed = JSON.parse(raw);
203
+ }
204
+ catch {
205
+ return {
206
+ ok: false,
207
+ result: errorResult(400, "QI_BAD_REQUEST", "Request body is not valid JSON."),
208
+ };
209
+ }
210
+ if (!isObject(parsed) || !Array.isArray(parsed.sources) || parsed.sources.length === 0) {
211
+ return {
212
+ ok: false,
213
+ result: errorResult(400, "QI_BAD_REQUEST", "At least one source is required."),
214
+ };
215
+ }
216
+ const sources = [];
217
+ for (const raw_ of parsed.sources) {
218
+ const source = validateSource(raw_);
219
+ if (source === undefined) {
220
+ return {
221
+ ok: false,
222
+ result: errorResult(400, "QI_BAD_SOURCE", "A source entry is malformed."),
223
+ };
224
+ }
225
+ if (source.kind === "file" && !isAbsolute(source.path)) {
226
+ return {
227
+ ok: false,
228
+ result: errorResult(400, "QI_BAD_SOURCE", "File source paths must be absolute local paths."),
229
+ };
230
+ }
231
+ sources.push(source);
232
+ }
233
+ return { ok: true, sources };
234
+ }
235
+ function buildJudgePortIfAvailable(deps, modelId) {
236
+ try {
237
+ return createQiJudgePort(deps, modelId);
238
+ }
239
+ catch {
240
+ return undefined;
241
+ }
242
+ }
243
+ function resolveProfile(profileId) {
244
+ if (profileId === undefined || profileId.trim().length === 0)
245
+ return regressionDefault;
246
+ return ALL_POLICY_PROFILES.find((profile) => profile.id === profileId) ?? regressionDefault;
247
+ }
248
+ function mapCurrentAtomFingerprints(ingestedAtoms) {
249
+ return ingestedAtoms.map((entry) => ({
250
+ atomId: String(entry.atom.id),
251
+ envelopeId: String(entry.atom.sourceEnvelopeId),
252
+ canonicalHashSha256Hex: entry.atom.canonicalHashSha256Hex,
253
+ ...(entry.replacementGroupId !== undefined
254
+ ? { replacementGroupId: entry.replacementGroupId }
255
+ : {}),
256
+ ...(entry.replacementOrdinal !== undefined
257
+ ? { replacementOrdinal: entry.replacementOrdinal }
258
+ : {}),
259
+ }));
260
+ }
261
+ function mapCurrentSourceFingerprints(ingestion) {
262
+ return ingestion.envelopes.map((envelope) => ({
263
+ envelopeId: String(envelope.id),
264
+ integrityHashSha256Hex: envelope.provenance.integrityHashSha256Hex,
265
+ }));
266
+ }
267
+ function toEvidenceRefs(ingestedAtoms) {
268
+ return Object.freeze(ingestedAtoms.map((entry) => Object.freeze({
269
+ envelopeId: String(entry.atom.sourceEnvelopeId),
270
+ atomId: String(entry.atom.id),
271
+ lifecycleStatus: entry.atom.lifecycleStatus,
272
+ })));
273
+ }
274
+ function rowToCandidate(row, runId) {
275
+ return {
276
+ id: QualityIntelligence.asQualityIntelligenceTestCaseId(row.id),
277
+ runId: QualityIntelligence.asQualityIntelligenceRunId(runId),
278
+ derivedFromAtomIds: row.derivedFromAtomIds.map((atomId) => QualityIntelligence.asQualityIntelligenceEvidenceAtomId(atomId)),
279
+ title: row.title,
280
+ preconditions: row.preconditions,
281
+ steps: row.steps,
282
+ expectedResults: row.expectedResults,
283
+ priority: row.priority,
284
+ riskClass: row.riskClass,
285
+ tags: row.tags,
286
+ status: row.status,
287
+ };
288
+ }
289
+ function toCandidateFindingRow(finding) {
290
+ return {
291
+ id: String(finding.id),
292
+ kind: finding.kind,
293
+ severity: finding.severity,
294
+ summaryRedacted: finding.summary,
295
+ ...(finding.candidateId !== undefined ? { candidateId: String(finding.candidateId) } : {}),
296
+ };
297
+ }
298
+ function buildCoverageGapFindingRow(runId, atomStatus, ordinal, excerpt) {
299
+ const payload = ["v1-cov-gap", String(runId), String(atomStatus.atomId), String(ordinal)].join("");
300
+ // Mirror the initial-run severity model (modelRoutedTestDesign): a zero-coverage requirement is
301
+ // the headline audit gap (high); a weakly-covered one is a softer "strengthen this" signal (low).
302
+ const severity = atomStatus.status === "uncovered" ? "high" : "low";
303
+ // Mirror the initial-run summary shape (#790): name the requirement via its redacted excerpt,
304
+ // not just the opaque atom id, so the regenerated run's gap findings stay auditor-readable.
305
+ const atomLabel = excerpt === undefined
306
+ ? `Atom ${String(atomStatus.atomId)}`
307
+ : `Atom ${String(atomStatus.atomId)} ("${excerpt}")`;
308
+ const summaryRedacted = atomStatus.status === "uncovered"
309
+ ? `${atomLabel} hat keinen zugeordneten Test (uncovered).`
310
+ : `${atomLabel} ist nur schwach abgedeckt (kein dedizierter Test referenziert dieses Atom).`;
311
+ return Object.freeze({
312
+ id: `qi-finding-${sha256Hex(payload).slice(0, 32)}`,
313
+ kind: "coverage-gap",
314
+ severity,
315
+ summaryRedacted,
316
+ });
317
+ }
318
+ function toCoverageMatrix(statuses, excerptByAtomId) {
319
+ return Object.freeze(statuses.map((status) => {
320
+ const excerpt = excerptByAtomId.get(String(status.atomId));
321
+ return Object.freeze({
322
+ atomId: String(status.atomId),
323
+ status: status.status,
324
+ confidence: status.confidence,
325
+ coveringCandidateIds: Object.freeze(status.coveringCandidateIds.map(String)),
326
+ ...(excerpt !== undefined ? { requirementExcerptRedacted: excerpt } : {}),
327
+ });
328
+ }));
329
+ }
330
+ function filteredJudgeFindings(findings, candidateIds) {
331
+ return findings.filter((finding) => finding.kind === "test-quality" &&
332
+ finding.candidateId !== undefined &&
333
+ candidateIds.has(finding.candidateId));
334
+ }
335
+ function selectedQualityScore(args) {
336
+ const { oldManifest, regeneratedManifest, preservedCount, regeneratedCount } = args;
337
+ if (preservedCount > 0 && regeneratedCount > 0)
338
+ return null;
339
+ if (regeneratedCount > 0)
340
+ return regeneratedManifest?.qualityScore ?? null;
341
+ if (preservedCount > 0)
342
+ return oldManifest.qualityScore ?? null;
343
+ return null;
344
+ }
345
+ function loadManifestForDrift(id, evidenceDir) {
346
+ const manifest = loadQualityIntelligenceRun(id, { evidenceDir });
347
+ return manifest === undefined
348
+ ? {
349
+ ok: false,
350
+ result: errorResult(404, "QI_NOT_FOUND", "Quality Intelligence run not found."),
351
+ }
352
+ : { ok: true, manifest };
353
+ }
354
+ async function ingestSourcesForDrift(sources, ingestRunId, deps) {
355
+ try {
356
+ return {
357
+ ok: true,
358
+ ingestion: await ingestInlineSourcesAsync({
359
+ request: { sources },
360
+ runId: ingestRunId,
361
+ registeredAt: new Date().toISOString(),
362
+ allowEmpty: true,
363
+ capsuleResolver: makeCapsuleResolver(deps),
364
+ // Drift must see the board's LATEST snapshot, not the pinned immutable record — a pinned
365
+ // write-once runId can never drift under its own identity (#735). Generate keeps pinning.
366
+ figmaSnapshotLoader: makeFigmaSnapshotLoader(deps, { resolveLatestByScope: true }),
367
+ figmaVision: makeFigmaVisionHintProvider(deps),
368
+ }),
369
+ };
370
+ }
371
+ catch (error) {
372
+ const code = error instanceof QiIngestionError ? error.code : "QI_INGESTION_FAILED";
373
+ const message = error instanceof QiIngestionError ? error.message : "Source ingestion failed.";
374
+ return { ok: false, result: errorResult(400, code, message) };
375
+ }
376
+ }
377
+ function buildDriftStaleness(manifest, oldCandidates, ingestion) {
378
+ const oldAtomFingerprints = manifest.atomFingerprints;
379
+ const oldFingerprints = oldAtomFingerprints === undefined ? Object.freeze([]) : (manifest.sourceFingerprints ?? []);
380
+ return compareStaleness({
381
+ // Newer runs persist atom fingerprints, which are required for exact per-test drift. Older
382
+ // source-only manifests cannot safely distinguish an unchanged workspace path set from edited
383
+ // file content, so fail closed instead of reporting "fresh" from envelope fingerprints alone.
384
+ oldFingerprints,
385
+ evidenceRefs: manifest.evidenceRefs.map((ref) => ({
386
+ envelopeId: ref.envelopeId,
387
+ atomId: ref.atomId,
388
+ })),
389
+ candidates: oldCandidates.map((candidate) => ({
390
+ id: candidate.id,
391
+ derivedFromAtomIds: candidate.derivedFromAtomIds,
392
+ })),
393
+ currentFingerprints: mapCurrentSourceFingerprints(ingestion),
394
+ currentAtomFingerprints: mapCurrentAtomFingerprints(ingestion.ingestedAtoms),
395
+ ...(oldAtomFingerprints !== undefined ? { oldAtomFingerprints } : {}),
396
+ });
397
+ }
398
+ function buildDriftContext(sources, manifest, ingestion, oldArtifact) {
399
+ const oldCandidates = oldArtifact?.candidates ?? [];
400
+ return {
401
+ sources,
402
+ manifest,
403
+ ingestion,
404
+ staleness: buildDriftStaleness(manifest, oldCandidates, ingestion),
405
+ oldCandidates,
406
+ oldEditedRevisions: oldArtifact?.editedRevisions ?? [],
407
+ };
408
+ }
409
+ async function computeDrift(req, evidenceDir, id, ingestRunId, deps) {
410
+ const parsed = await parseSources(req);
411
+ if (!parsed.ok)
412
+ return { ok: false, result: parsed.result };
413
+ const loaded = loadManifestForDrift(id, evidenceDir);
414
+ if (!loaded.ok)
415
+ return { ok: false, result: loaded.result };
416
+ const ingested = await ingestSourcesForDrift(parsed.sources, ingestRunId, deps);
417
+ if (!ingested.ok)
418
+ return { ok: false, result: ingested.result };
419
+ const oldArtifact = loadQualityIntelligenceCandidates(id, { evidenceDir });
420
+ if (oldArtifact === undefined && loaded.manifest.totals.candidates > 0) {
421
+ return {
422
+ ok: false,
423
+ result: errorResult(500, "QI_CANDIDATES_MISSING", "The candidate artifact for this Quality Intelligence run is missing."),
424
+ };
425
+ }
426
+ return {
427
+ ok: true,
428
+ value: buildDriftContext(parsed.sources, loaded.manifest, ingested.ingestion, oldArtifact),
429
+ };
430
+ }
431
+ const ALIGN_INSERT_DELETE_COST = 3;
432
+ const ALIGN_SUBSTITUTE_COST = 4;
433
+ const ALIGN_CROSS_OLD_ATOM_COST = 10;
434
+ function collectStaleIds(staleness) {
435
+ return new Set([
436
+ ...staleness.changedStale.map((reason) => reason.candidateId),
437
+ ...staleness.orphanedStale.map((reason) => reason.candidateId),
438
+ ]);
439
+ }
440
+ function buildPreservedState(drift, staleIds) {
441
+ const preservedCandidates = drift.oldCandidates.filter((candidate) => !staleIds.has(candidate.id));
442
+ const preservedIds = new Set(preservedCandidates.map((candidate) => candidate.id));
443
+ return {
444
+ preservedCandidates,
445
+ preservedEditedRevisions: drift.oldEditedRevisions.filter((revision) => preservedIds.has(revision.candidateId)),
446
+ };
447
+ }
448
+ function looksLikeLegacyRequirementsFallback(drift, staleIds) {
449
+ if (staleIds.size === 0 || drift.manifest.atomFingerprints !== undefined)
450
+ return false;
451
+ if (!(drift.sources.length > 0 && drift.sources.every((source) => source.kind === "requirements"))) {
452
+ return false;
453
+ }
454
+ const evidenceRefMap = new Map(drift.manifest.evidenceRefs.map((ref) => [ref.atomId, ref.envelopeId]));
455
+ return drift.oldCandidates.some((candidate) => staleIds.has(candidate.id) &&
456
+ candidate.derivedFromAtomIds.some((atomId) => evidenceRefMap.get(atomId)?.startsWith("qi-src-")));
457
+ }
458
+ function buildCurrentAtomIndexes(ingestion) {
459
+ const byId = new Map(ingestion.ingestedAtoms.map((entry) => [String(entry.atom.id), entry]));
460
+ const byEnvelope = new Map();
461
+ const replacementEntries = [];
462
+ for (const entry of ingestion.ingestedAtoms) {
463
+ const envelopeId = String(entry.atom.sourceEnvelopeId);
464
+ const current = byEnvelope.get(envelopeId);
465
+ if (current === undefined) {
466
+ byEnvelope.set(envelopeId, [entry]);
467
+ }
468
+ else {
469
+ current.push(entry);
470
+ }
471
+ if (entry.replacementGroupId !== undefined && entry.replacementOrdinal !== undefined) {
472
+ replacementEntries.push({
473
+ atomId: String(entry.atom.id),
474
+ canonicalHashSha256Hex: entry.atom.canonicalHashSha256Hex,
475
+ replacementGroupId: entry.replacementGroupId,
476
+ replacementOrdinal: entry.replacementOrdinal,
477
+ });
478
+ }
479
+ }
480
+ return {
481
+ byId,
482
+ byEnvelope,
483
+ replacementEntries,
484
+ envelopeIds: new Set(ingestion.envelopes.map((envelope) => String(envelope.id))),
485
+ };
486
+ }
487
+ function buildOldAtomIndexes(atomFingerprints) {
488
+ const byId = new Map(atomFingerprints.map((fp) => [
489
+ fp.atomId,
490
+ {
491
+ envelopeId: fp.envelopeId,
492
+ canonicalHashSha256Hex: fp.canonicalHashSha256Hex,
493
+ ...(fp.replacementGroupId !== undefined
494
+ ? { replacementGroupId: fp.replacementGroupId }
495
+ : {}),
496
+ ...(fp.replacementOrdinal !== undefined
497
+ ? { replacementOrdinal: fp.replacementOrdinal }
498
+ : {}),
499
+ },
500
+ ]));
501
+ const idsByEnvelope = new Map();
502
+ const idsInEnvelope = new Map();
503
+ const replacementEntries = [];
504
+ for (const fp of atomFingerprints) {
505
+ const ids = idsByEnvelope.get(fp.envelopeId);
506
+ if (ids === undefined) {
507
+ idsByEnvelope.set(fp.envelopeId, new Set([fp.atomId]));
508
+ }
509
+ else {
510
+ ids.add(fp.atomId);
511
+ }
512
+ const orderedIds = idsInEnvelope.get(fp.envelopeId);
513
+ if (orderedIds === undefined) {
514
+ idsInEnvelope.set(fp.envelopeId, [fp.atomId]);
515
+ }
516
+ else {
517
+ orderedIds.push(fp.atomId);
518
+ }
519
+ if (fp.replacementGroupId !== undefined && fp.replacementOrdinal !== undefined) {
520
+ replacementEntries.push({
521
+ atomId: fp.atomId,
522
+ canonicalHashSha256Hex: fp.canonicalHashSha256Hex,
523
+ replacementGroupId: fp.replacementGroupId,
524
+ replacementOrdinal: fp.replacementOrdinal,
525
+ });
526
+ }
527
+ }
528
+ return { byId, idsByEnvelope, idsInEnvelope, replacementEntries };
529
+ }
530
+ function replacementEntriesByGroup(entries) {
531
+ const groups = new Map();
532
+ for (const entry of entries) {
533
+ const group = groups.get(entry.replacementGroupId);
534
+ if (group === undefined) {
535
+ groups.set(entry.replacementGroupId, [entry]);
536
+ }
537
+ else {
538
+ group.push(entry);
539
+ }
540
+ }
541
+ for (const group of groups.values()) {
542
+ group.sort((a, b) => a.replacementOrdinal - b.replacementOrdinal);
543
+ }
544
+ return groups;
545
+ }
546
+ function alignmentPairCost(oldEntry, currentEntry, oldAtomIds) {
547
+ if (oldEntry.atomId === currentEntry.atomId)
548
+ return 0;
549
+ if (currentEntry.canonicalHashSha256Hex === oldEntry.canonicalHashSha256Hex)
550
+ return 0;
551
+ return oldAtomIds.has(currentEntry.atomId) ? ALIGN_CROSS_OLD_ATOM_COST : ALIGN_SUBSTITUTE_COST;
552
+ }
553
+ function replacementIndexEntryAt(entries, index) {
554
+ const entry = entries[index];
555
+ if (entry === undefined)
556
+ throw new Error("Replacement alignment index out of bounds.");
557
+ return entry;
558
+ }
559
+ function matrixValue(matrix, row, col) {
560
+ const value = matrix[row]?.[col];
561
+ if (value === undefined)
562
+ throw new Error("Replacement alignment matrix index out of bounds.");
563
+ return value;
564
+ }
565
+ function setMatrixValue(matrix, row, col, value) {
566
+ const rowValues = matrix[row];
567
+ if (rowValues === undefined) {
568
+ throw new Error("Replacement alignment matrix index out of bounds.");
569
+ }
570
+ rowValues[col] = value;
571
+ }
572
+ function buildAlignmentCostMatrix(oldEntries, currentEntries, oldAtomIds) {
573
+ const matrix = Array.from({ length: oldEntries.length + 1 }, () => Array.from({ length: currentEntries.length + 1 }, () => 0));
574
+ for (let oldIndex = 1; oldIndex <= oldEntries.length; oldIndex += 1) {
575
+ setMatrixValue(matrix, oldIndex, 0, oldIndex * ALIGN_INSERT_DELETE_COST);
576
+ }
577
+ for (let currentIndex = 1; currentIndex <= currentEntries.length; currentIndex += 1) {
578
+ setMatrixValue(matrix, 0, currentIndex, currentIndex * ALIGN_INSERT_DELETE_COST);
579
+ }
580
+ for (let oldIndex = 1; oldIndex <= oldEntries.length; oldIndex += 1) {
581
+ for (let currentIndex = 1; currentIndex <= currentEntries.length; currentIndex += 1) {
582
+ const pairCost = alignmentPairCost(replacementIndexEntryAt(oldEntries, oldIndex - 1), replacementIndexEntryAt(currentEntries, currentIndex - 1), oldAtomIds);
583
+ const pair = matrixValue(matrix, oldIndex - 1, currentIndex - 1) + pairCost;
584
+ const deletion = matrixValue(matrix, oldIndex - 1, currentIndex) + ALIGN_INSERT_DELETE_COST;
585
+ const insertion = matrixValue(matrix, oldIndex, currentIndex - 1) + ALIGN_INSERT_DELETE_COST;
586
+ setMatrixValue(matrix, oldIndex, currentIndex, Math.min(pair, deletion, insertion));
587
+ }
588
+ }
589
+ return matrix;
590
+ }
591
+ function alignReplacementEntries(oldEntries, currentEntries) {
592
+ const mapping = new Map();
593
+ const oldAtomIds = new Set(oldEntries.map((entry) => entry.atomId));
594
+ const costs = buildAlignmentCostMatrix(oldEntries, currentEntries, oldAtomIds);
595
+ let oldIndex = oldEntries.length;
596
+ let currentIndex = currentEntries.length;
597
+ while (oldIndex > 0 && currentIndex > 0) {
598
+ const oldEntry = replacementIndexEntryAt(oldEntries, oldIndex - 1);
599
+ const currentEntry = replacementIndexEntryAt(currentEntries, currentIndex - 1);
600
+ const pairCost = alignmentPairCost(oldEntry, currentEntry, oldAtomIds);
601
+ const currentCost = matrixValue(costs, oldIndex, currentIndex);
602
+ const pair = matrixValue(costs, oldIndex - 1, currentIndex - 1) + pairCost;
603
+ const deletion = matrixValue(costs, oldIndex - 1, currentIndex) + ALIGN_INSERT_DELETE_COST;
604
+ const insertion = matrixValue(costs, oldIndex, currentIndex - 1) + ALIGN_INSERT_DELETE_COST;
605
+ if (pairCost === 0 && currentCost === pair) {
606
+ mapping.set(oldEntry.atomId, currentEntry.atomId);
607
+ oldIndex -= 1;
608
+ currentIndex -= 1;
609
+ }
610
+ else if (currentCost === insertion) {
611
+ currentIndex -= 1;
612
+ }
613
+ else if (currentCost === deletion) {
614
+ oldIndex -= 1;
615
+ }
616
+ else {
617
+ mapping.set(oldEntry.atomId, currentEntry.atomId);
618
+ oldIndex -= 1;
619
+ currentIndex -= 1;
620
+ }
621
+ }
622
+ return mapping;
623
+ }
624
+ function buildReplacementAtomMap(oldEntries, currentEntries) {
625
+ const oldGroups = replacementEntriesByGroup(oldEntries);
626
+ const currentGroups = replacementEntriesByGroup(currentEntries);
627
+ const mapping = new Map();
628
+ for (const [groupId, oldGroup] of oldGroups) {
629
+ const currentGroup = currentGroups.get(groupId);
630
+ if (currentGroup === undefined)
631
+ continue;
632
+ for (const [oldAtomId, currentAtomId] of alignReplacementEntries(oldGroup, currentGroup)) {
633
+ mapping.set(oldAtomId, currentAtomId);
634
+ }
635
+ }
636
+ return mapping;
637
+ }
638
+ function addPositionalReplacementRequirementAtom(atomId, envelopeId, current, old, atomIdsToRegenerate) {
639
+ const oldIds = old.idsByEnvelope.get(envelopeId) ?? new Set();
640
+ const oldIdsInEnvelope = old.idsInEnvelope.get(envelopeId) ?? [];
641
+ const currentAtomsInEnvelope = current.byEnvelope.get(envelopeId) ?? [];
642
+ const oldIndex = oldIdsInEnvelope.indexOf(atomId);
643
+ const replacement = oldIndex >= 0 ? currentAtomsInEnvelope[oldIndex] : undefined;
644
+ const replacementId = replacement === undefined ? undefined : String(replacement.atom.id);
645
+ if (replacementId !== undefined && !oldIds.has(replacementId)) {
646
+ atomIdsToRegenerate.add(replacementId);
647
+ }
648
+ }
649
+ function addChangedCurrentAtom(oldAtom, currentEntry, atomIdsToRegenerate) {
650
+ if (currentEntry === undefined)
651
+ return false;
652
+ if (currentEntry.atom.canonicalHashSha256Hex === oldAtom.canonicalHashSha256Hex)
653
+ return false;
654
+ atomIdsToRegenerate.add(String(currentEntry.atom.id));
655
+ return true;
656
+ }
657
+ function addMappedReplacementAtom(atomId, current, old, replacementAtomIdsByOldAtomId, atomIdsToRegenerate) {
658
+ const replacementAtomId = replacementAtomIdsByOldAtomId.get(atomId);
659
+ if (replacementAtomId === undefined || replacementAtomId === atomId)
660
+ return false;
661
+ if (old.byId.has(replacementAtomId) || !current.byId.has(replacementAtomId))
662
+ return false;
663
+ atomIdsToRegenerate.add(replacementAtomId);
664
+ return true;
665
+ }
666
+ function addRegenerationAtomsForCandidate(candidate, current, old, replacementAtomIdsByOldAtomId, atomIdsToRegenerate) {
667
+ for (const atomId of candidate.derivedFromAtomIds) {
668
+ const oldAtom = old.byId.get(atomId);
669
+ const currentAtom = current.byId.get(atomId);
670
+ if (oldAtom === undefined || !current.envelopeIds.has(oldAtom.envelopeId))
671
+ continue;
672
+ if (addChangedCurrentAtom(oldAtom, currentAtom, atomIdsToRegenerate))
673
+ continue;
674
+ if (addMappedReplacementAtom(atomId, current, old, replacementAtomIdsByOldAtomId, atomIdsToRegenerate)) {
675
+ continue;
676
+ }
677
+ if (!oldAtom.envelopeId.startsWith(REQUIREMENTS_ENVELOPE_PREFIX))
678
+ continue;
679
+ addPositionalReplacementRequirementAtom(atomId, oldAtom.envelopeId, current, old, atomIdsToRegenerate);
680
+ }
681
+ }
682
+ function collectAtomsToRegenerate(drift, staleIds) {
683
+ const current = buildCurrentAtomIndexes(drift.ingestion);
684
+ const old = buildOldAtomIndexes(drift.manifest.atomFingerprints ?? []);
685
+ const replacementAtomIdsByOldAtomId = buildReplacementAtomMap(old.replacementEntries, current.replacementEntries);
686
+ const atomIdsToRegenerate = new Set();
687
+ for (const candidate of drift.oldCandidates) {
688
+ if (!staleIds.has(candidate.id))
689
+ continue;
690
+ addRegenerationAtomsForCandidate(candidate, current, old, replacementAtomIdsByOldAtomId, atomIdsToRegenerate);
691
+ }
692
+ return drift.ingestion.ingestedAtoms.filter((entry) => atomIdsToRegenerate.has(String(entry.atom.id)));
693
+ }
694
+ function narrowRegeneration(drift) {
695
+ const staleIds = collectStaleIds(drift.staleness);
696
+ const preserved = buildPreservedState(drift, staleIds);
697
+ const legacyRequirementsFallback = looksLikeLegacyRequirementsFallback(drift, staleIds);
698
+ if (legacyRequirementsFallback || staleIds.size === 0) {
699
+ return {
700
+ staleIds,
701
+ atomsToRegenerate: Object.freeze([]),
702
+ preservedCandidates: preserved.preservedCandidates,
703
+ preservedEditedRevisions: preserved.preservedEditedRevisions,
704
+ legacyRequirementsFallback,
705
+ };
706
+ }
707
+ return {
708
+ staleIds,
709
+ atomsToRegenerate: collectAtomsToRegenerate(drift, staleIds),
710
+ preservedCandidates: preserved.preservedCandidates,
711
+ preservedEditedRevisions: preserved.preservedEditedRevisions,
712
+ legacyRequirementsFallback: false,
713
+ };
714
+ }
715
+ function regenWorkflowDeps(deps, target, evidenceStore, capture, signal) {
716
+ return {
717
+ sink: { emit: () => undefined },
718
+ signal,
719
+ evidenceStore,
720
+ candidatesSink: {
721
+ record: (cands, generatedAt) => {
722
+ capture(cands, generatedAt);
723
+ },
724
+ },
725
+ generate: createQiGenerationPort(deps, target),
726
+ // The regenerate-stale judge deliberately shares the auto-selected generation model id rather than
727
+ // resolving an independent qi:judge-logic model the way the initial run does (runExecution.ts).
728
+ // This is safe because the regen target comes from resolveQiTestDesignSelection(deps) with NO
729
+ // explicit model request: auto-selection prefers structured-output models, so whenever a judge is
730
+ // possible (a structured-output model is configured) the generation model already satisfies
731
+ // qi:judge-logic, and when only chat-only models exist both generation and the judge degrade
732
+ // (judge skipped via buildJudgePortIfAvailable's typed-unavailable catch). The initial-run
733
+ // asymmetry — an explicitly requested chat-only generation model paired with a separate
734
+ // structured-output judge — cannot arise here because the regen path never carries an explicit
735
+ // generation-model request.
736
+ ...(target.kind === "model" ? { judge: buildJudgePortIfAvailable(deps, target.modelId) } : {}),
737
+ };
738
+ }
739
+ function buildScopedRegenPlan(newRunId, requestedAt) {
740
+ return {
741
+ id: QualityIntelligence.asQualityIntelligenceRunId(newRunId),
742
+ requestedAt,
743
+ plannerKind: "model-routed",
744
+ stages: [],
745
+ };
746
+ }
747
+ async function executeScopedWorkflow(args) {
748
+ const { deps, target, evidenceStore, capture, plan, ingestion, atomsToRegenerate, profile, signal, } = args;
749
+ try {
750
+ const summary = await runQualityIntelligenceModelRoutedTestDesign({
751
+ plan,
752
+ envelopes: ingestion.envelopes,
753
+ ingestedAtoms: atomsToRegenerate,
754
+ provenanceRefs: ingestion.provenanceRefs,
755
+ profile,
756
+ }, regenWorkflowDeps(deps, target, evidenceStore, capture, signal));
757
+ return summary.status === "succeeded"
758
+ ? null
759
+ : errorResult(500, "QI_REGEN_FAILED", "Scoped regeneration did not succeed.");
760
+ }
761
+ catch (error) {
762
+ const code = error instanceof QiGenerationError ? error.code : "QI_REGEN_FAILED";
763
+ const message = error instanceof QiGenerationError ? error.message : "Scoped regeneration failed.";
764
+ return errorResult(500, code, message);
765
+ }
766
+ }
767
+ function finalizeScopedWorkflow(evidenceStore, newRunId, generatedCandidates, generatedAt) {
768
+ const manifest = evidenceStore.load(newRunId);
769
+ if (manifest === undefined || generatedAt === undefined) {
770
+ return {
771
+ ok: false,
772
+ result: errorResult(500, "QI_REGEN_FAILED", "Scoped regeneration did not persist in memory."),
773
+ };
774
+ }
775
+ return {
776
+ ok: true,
777
+ value: { manifest, candidates: generatedCandidates, generatedAt },
778
+ };
779
+ }
780
+ async function runScopedEphemeral(args) {
781
+ const { deps, target, newRunId, requestedAt, ingestion, atomsToRegenerate, profile, signal } = args;
782
+ const evidenceStore = createInMemoryQualityIntelligenceLocalStore();
783
+ let generatedCandidates = [];
784
+ let generatedAt;
785
+ const failure = await executeScopedWorkflow({
786
+ deps,
787
+ target,
788
+ evidenceStore,
789
+ capture: (cands, ts) => {
790
+ generatedCandidates = [...cands];
791
+ generatedAt = ts;
792
+ },
793
+ plan: buildScopedRegenPlan(newRunId, requestedAt),
794
+ ingestion,
795
+ atomsToRegenerate,
796
+ profile,
797
+ signal,
798
+ });
799
+ if (failure !== null)
800
+ return { ok: false, result: failure };
801
+ return finalizeScopedWorkflow(evidenceStore, newRunId, generatedCandidates, generatedAt);
802
+ }
803
+ function buildMergedCandidates(newRunId, preservedCandidates, regeneratedCandidates) {
804
+ return deduplicateCandidates([
805
+ ...preservedCandidates.map((candidate) => rowToCandidate(candidate, newRunId)),
806
+ ...regeneratedCandidates,
807
+ ]);
808
+ }
809
+ function assertMergedCandidateBudget(preservedCandidates, regeneratedCandidates) {
810
+ const limit = QUALITY_INTELLIGENCE_DEFAULT_WORKFLOW_LIMITS.maxCandidatesPerRun;
811
+ if (preservedCandidates.length + regeneratedCandidates.length <= limit)
812
+ return null;
813
+ return errorResult(409, "QI_REGEN_CANDIDATE_CAP_EXCEEDED", "Regenerating the stale tests would exceed the per-run candidate limit. Reduce the stale scope or start a fresh QI run against the current source.");
814
+ }
815
+ function buildCoverageArtifacts(runId, ingestion, mergedCandidates) {
816
+ const atoms = ingestion.ingestedAtoms.map((entry) => entry.atom);
817
+ const coverageMap = buildCoverageMap({ runId, atoms, candidates: mergedCandidates });
818
+ const atomStatuses = buildAtomCoverageStatuses(atoms, coverageMap);
819
+ const excerptByAtomId = excerptsByAtomId(ingestion.ingestedAtoms);
820
+ return {
821
+ coverageMatrix: toCoverageMatrix(atomStatuses, excerptByAtomId),
822
+ coverageGapRows: atomStatuses
823
+ .map((status, index) => status.status === "covered"
824
+ ? null
825
+ : buildCoverageGapFindingRow(runId, status, index, excerptByAtomId.get(String(status.atomId))))
826
+ .filter((row) => row !== null),
827
+ };
828
+ }
829
+ // Order by severity (critical -> low) BEFORE truncating, then cap to the per-run limit — mirroring
830
+ // the initial run path (modelRoutedTestDesign). Sorting first guarantees that if the merge hits the
831
+ // cap, the most severe findings (including high-severity uncovered-requirement coverage gaps) survive
832
+ // rather than being dropped by array position. Array.prototype.sort is stable, so same-severity
833
+ // insertion order — coverage-gap rows first — is preserved, matching the initial path exactly.
834
+ function sortAndCapMergedFindings(rows, cap) {
835
+ return rows
836
+ .slice()
837
+ .sort((a, b) => QualityIntelligence.QUALITY_INTELLIGENCE_SEVERITY_RANK[a.severity] -
838
+ QualityIntelligence.QUALITY_INTELLIGENCE_SEVERITY_RANK[b.severity])
839
+ .slice(0, cap);
840
+ }
841
+ function buildMergedFindings(args) {
842
+ const preservedIds = new Set(args.preservedCandidates.map((candidate) => candidate.id));
843
+ const regeneratedIds = new Set(args.regeneratedCandidates.map((candidate) => String(candidate.id)));
844
+ const preservedJudgeRows = filteredJudgeFindings(args.oldManifest.findings, preservedIds);
845
+ const regeneratedJudgeRows = args.regeneratedManifest === undefined
846
+ ? []
847
+ : filteredJudgeFindings(args.regeneratedManifest.findings, regeneratedIds);
848
+ // Source the cap from the default workflow limits: the re-check regeneration sub-run
849
+ // (regenWorkflowDeps) passes no custom `limits`, so it runs under these defaults — keeping the
850
+ // merge cap consistent with the sub-run's own maxFindingsPerRun.
851
+ const merged = sortAndCapMergedFindings([
852
+ ...args.coverageGapRows,
853
+ ...validateCandidates(args.runId, args.mergedCandidates).map(toCandidateFindingRow),
854
+ ...preservedJudgeRows,
855
+ ...regeneratedJudgeRows,
856
+ ], QUALITY_INTELLIGENCE_DEFAULT_WORKFLOW_LIMITS.maxFindingsPerRun);
857
+ return Object.freeze(merged);
858
+ }
859
+ function buildMergedRunRecord(args) {
860
+ const { newRunId, requestedAt, profile, oldManifest, ingestion, preservedCandidates } = args;
861
+ return {
862
+ runId: newRunId,
863
+ planAt: requestedAt,
864
+ completedAt: args.completedAt,
865
+ status: "succeeded",
866
+ policyProfileIds: [profile.id],
867
+ retentionPolicyId: oldManifest.retentionPolicyId,
868
+ modelGatewayCallCount: args.regeneratedManifest?.modelGatewayCallCount ?? 0,
869
+ totals: {
870
+ candidates: args.preservedCandidates.length + args.regeneratedCandidates.length,
871
+ findings: args.findings.length,
872
+ exports: 0,
873
+ },
874
+ findings: args.findings,
875
+ exports: Object.freeze([]),
876
+ evidenceRefs: toEvidenceRefs(ingestion.ingestedAtoms),
877
+ provenanceRefs: ingestion.provenanceRefs,
878
+ coverageMatrix: args.coverageMatrix,
879
+ qualityScore: selectedQualityScore({
880
+ oldManifest,
881
+ regeneratedManifest: args.regeneratedManifest,
882
+ preservedCount: preservedCandidates.length,
883
+ regeneratedCount: args.regeneratedCandidates.length,
884
+ }),
885
+ sourceFingerprints: mapCurrentSourceFingerprints(ingestion),
886
+ atomFingerprints: mapCurrentAtomFingerprints(ingestion.ingestedAtoms),
887
+ ...optionalModelFields(args.regeneratedManifest),
888
+ };
889
+ }
890
+ // Carry forward the regenerated manifest's optional model provenance (modelId / modelParameters /
891
+ // seedUsed) only when present, so the merged record omits — rather than nulls — an absent field.
892
+ function optionalModelFields(regeneratedManifest) {
893
+ if (regeneratedManifest === undefined)
894
+ return {};
895
+ return {
896
+ ...(regeneratedManifest.modelId !== undefined ? { modelId: regeneratedManifest.modelId } : {}),
897
+ ...(regeneratedManifest.modelParameters !== undefined
898
+ ? { modelParameters: regeneratedManifest.modelParameters }
899
+ : {}),
900
+ ...(regeneratedManifest.seedUsed !== undefined
901
+ ? { seedUsed: regeneratedManifest.seedUsed }
902
+ : {}),
903
+ };
904
+ }
905
+ function recordMergedManifest(evidenceDir, args, additionalSecrets) {
906
+ // Persist-time secret scrubbing at parity with the initial-run path (runExecution.ts
907
+ // buildWorkflowDeps): the merged manifest carries judge rationales forwarded from the regenerated
908
+ // run, so the live additionalSecrets list must reach the manifest writer here too — otherwise a
909
+ // configured provider secret echoed in a rationale that the security-package builtin patterns do
910
+ // not match would survive into the on-disk merged manifest (Issue #747 defence-in-depth).
911
+ recordQualityIntelligenceRun(buildMergedRunRecord(args), {
912
+ evidenceDir,
913
+ redaction: { additionalSecrets },
914
+ });
915
+ }
916
+ function recordMergedCandidatesArtifact(args) {
917
+ recordQualityIntelligenceCandidates({
918
+ runId: args.newRunId,
919
+ generatedAt: args.completedAt,
920
+ candidates: args.mergedCandidates,
921
+ editedRevisions: args.preservedEditedRevisions,
922
+ evidenceDir: args.evidenceDir,
923
+ redact: args.deps.redactor,
924
+ });
925
+ }
926
+ function persistMergedRun(args) {
927
+ const mergedCandidates = buildMergedCandidates(args.newRunId, args.preservedCandidates, args.regeneratedCandidates);
928
+ const runId = QualityIntelligence.asQualityIntelligenceRunId(args.newRunId);
929
+ const coverage = buildCoverageArtifacts(runId, args.ingestion, mergedCandidates);
930
+ const findings = buildMergedFindings({
931
+ runId,
932
+ mergedCandidates,
933
+ coverageGapRows: coverage.coverageGapRows,
934
+ oldManifest: args.oldManifest,
935
+ preservedCandidates: args.preservedCandidates,
936
+ regeneratedCandidates: args.regeneratedCandidates,
937
+ regeneratedManifest: args.regeneratedManifest,
938
+ });
939
+ recordMergedCandidatesArtifact({
940
+ deps: args.deps,
941
+ evidenceDir: args.evidenceDir,
942
+ newRunId: args.newRunId,
943
+ completedAt: args.completedAt,
944
+ mergedCandidates,
945
+ preservedEditedRevisions: args.preservedEditedRevisions,
946
+ });
947
+ recordMergedManifest(args.evidenceDir, {
948
+ newRunId: args.newRunId,
949
+ requestedAt: args.requestedAt,
950
+ profile: args.profile,
951
+ oldManifest: args.oldManifest,
952
+ ingestion: args.ingestion,
953
+ preservedCandidates: args.preservedCandidates,
954
+ regeneratedCandidates: args.regeneratedCandidates,
955
+ regeneratedManifest: args.regeneratedManifest,
956
+ completedAt: args.completedAt,
957
+ findings,
958
+ coverageMatrix: coverage.coverageMatrix,
959
+ }, currentRedactionSecrets(args.deps));
960
+ }
961
+ function immediateRegenerationResult(narrowed) {
962
+ if (narrowed.legacyRequirementsFallback) {
963
+ return errorResult(409, "QI_REGEN_LEGACY_REQUIREMENTS_UNSUPPORTED", "This run predates atom-level requirements drift metadata. Start a new QI run against the current requirements sources instead.");
964
+ }
965
+ // Data-loss guard: targeted regeneration must never turn a non-empty run into an empty one. If
966
+ // every candidate is stale yet nothing maps to a regeneratable atom (preserved == 0 AND nothing to
967
+ // regenerate), persisting the merge would silently drop the entire run. This is the catastrophic
968
+ // shape an atom-id scheme drift would take; fail closed with an actionable error instead (Epic
969
+ // #735 drift correctness). The legitimate "some tests orphaned, some preserved" case keeps
970
+ // preserved > 0 and is unaffected.
971
+ if (narrowed.preservedCandidates.length === 0 && narrowed.atomsToRegenerate.length === 0) {
972
+ return errorResult(409, "QI_REGEN_WOULD_EMPTY", "Regenerating the stale tests would remove every test in this run because the current source no longer maps to any of them. Start a fresh QI run against the current source instead.");
973
+ }
974
+ return null;
975
+ }
976
+ function resolveScopedRegenerationTarget(deps) {
977
+ const selection = resolveQiTestDesignSelection(deps);
978
+ return selection.kind === "model"
979
+ ? { kind: "model", modelId: selection.modelId }
980
+ : { kind: "baseline" };
981
+ }
982
+ async function regenerateCandidateSlice(args) {
983
+ if (args.narrowed.atomsToRegenerate.length === 0) {
984
+ return {
985
+ ok: true,
986
+ value: { manifest: undefined, candidates: [], completedAt: new Date().toISOString() },
987
+ };
988
+ }
989
+ const outcome = await runScopedEphemeral({
990
+ deps: args.deps,
991
+ target: resolveScopedRegenerationTarget(args.deps),
992
+ newRunId: args.newRunId,
993
+ requestedAt: args.requestedAt,
994
+ ingestion: args.drift.ingestion,
995
+ atomsToRegenerate: args.narrowed.atomsToRegenerate,
996
+ profile: args.profile,
997
+ signal: args.signal,
998
+ });
999
+ return outcome.ok
1000
+ ? {
1001
+ ok: true,
1002
+ value: {
1003
+ manifest: outcome.value.manifest,
1004
+ candidates: outcome.value.candidates,
1005
+ completedAt: outcome.value.generatedAt,
1006
+ },
1007
+ }
1008
+ : outcome;
1009
+ }
1010
+ function persistRegenerationResult(args) {
1011
+ persistMergedRun({
1012
+ deps: args.deps,
1013
+ evidenceDir: args.evidenceDir,
1014
+ newRunId: args.newRunId,
1015
+ requestedAt: args.requestedAt,
1016
+ profile: args.profile,
1017
+ oldManifest: args.drift.manifest,
1018
+ ingestion: args.drift.ingestion,
1019
+ preservedCandidates: args.narrowed.preservedCandidates,
1020
+ preservedEditedRevisions: args.narrowed.preservedEditedRevisions,
1021
+ regeneratedCandidates: args.regenerated.candidates,
1022
+ regeneratedManifest: args.regenerated.manifest,
1023
+ completedAt: args.regenerated.completedAt,
1024
+ });
1025
+ }
1026
+ function regenerationSuccessResult(args) {
1027
+ return {
1028
+ status: 200,
1029
+ body: {
1030
+ runId: args.newRunId,
1031
+ regeneratedCount: args.regeneratedCount,
1032
+ preservedCount: args.preservedCount,
1033
+ },
1034
+ };
1035
+ }
1036
+ async function regenerateFromDrift(args) {
1037
+ const { deps, evidenceDir, newRunId, requestedAt, drift, signal } = args;
1038
+ const narrowed = narrowRegeneration(drift);
1039
+ const immediate = immediateRegenerationResult(narrowed);
1040
+ if (immediate !== null)
1041
+ return immediate;
1042
+ const cancelledBeforeRegeneration = cancellationResult(signal);
1043
+ if (cancelledBeforeRegeneration !== null)
1044
+ return cancelledBeforeRegeneration;
1045
+ const profile = resolveProfile(drift.manifest.policyProfileIds[0]);
1046
+ const regenerated = await regenerateCandidateSlice({
1047
+ deps,
1048
+ newRunId,
1049
+ requestedAt,
1050
+ drift,
1051
+ narrowed,
1052
+ profile,
1053
+ signal,
1054
+ });
1055
+ if (!regenerated.ok)
1056
+ return regenerated.result;
1057
+ const cancelledBeforePersist = cancellationResult(signal);
1058
+ if (cancelledBeforePersist !== null)
1059
+ return cancelledBeforePersist;
1060
+ const budgetError = assertMergedCandidateBudget(narrowed.preservedCandidates, regenerated.value.candidates);
1061
+ if (budgetError !== null)
1062
+ return budgetError;
1063
+ persistRegenerationResult({
1064
+ deps,
1065
+ evidenceDir,
1066
+ newRunId,
1067
+ requestedAt,
1068
+ drift,
1069
+ narrowed,
1070
+ profile,
1071
+ regenerated: regenerated.value,
1072
+ });
1073
+ return regenerationSuccessResult({
1074
+ newRunId,
1075
+ regeneratedCount: regenerated.value.candidates.length,
1076
+ preservedCount: narrowed.preservedCandidates.length,
1077
+ });
1078
+ }
1079
+ export async function handleQiReCheck(ctx, deps) {
1080
+ const { id } = ctx.params;
1081
+ if (id === undefined || id.trim().length === 0) {
1082
+ return errorResult(400, "QI_BAD_REQUEST", "Run id is required.");
1083
+ }
1084
+ const invalidId = invalidRunIdFormat(id);
1085
+ if (invalidId !== null)
1086
+ return invalidId;
1087
+ const evidenceDir = deps.evidenceDir;
1088
+ if (evidenceDir === undefined) {
1089
+ return errorResult(500, "QI_NO_EVIDENCE_DIR", "The evidence directory is not configured.");
1090
+ }
1091
+ try {
1092
+ const drift = await computeDrift(ctx.req, evidenceDir, id, `qi-recheck-${id}`, deps);
1093
+ if (!drift.ok)
1094
+ return drift.result;
1095
+ const { staleness } = drift.value;
1096
+ return {
1097
+ status: 200,
1098
+ body: {
1099
+ runId: id,
1100
+ staleCount: staleness.changedStale.length + staleness.orphanedStale.length,
1101
+ fresh: staleness.fresh,
1102
+ changedStale: staleness.changedStale,
1103
+ orphanedStale: staleness.orphanedStale,
1104
+ },
1105
+ };
1106
+ }
1107
+ catch {
1108
+ return errorResult(500, "QI_RECHECK_FAILED", "Failed to inspect the current sources for drift.");
1109
+ }
1110
+ }
1111
+ export async function handleQiRegenerateStale(ctx, deps) {
1112
+ const { id } = ctx.params;
1113
+ if (id === undefined || id.trim().length === 0) {
1114
+ return errorResult(400, "QI_BAD_REQUEST", "Run id is required.");
1115
+ }
1116
+ const invalidId = invalidRunIdFormat(id);
1117
+ if (invalidId !== null)
1118
+ return invalidId;
1119
+ const evidenceDir = deps.evidenceDir;
1120
+ if (evidenceDir === undefined) {
1121
+ return errorResult(500, "QI_NO_EVIDENCE_DIR", "The evidence directory is not configured.");
1122
+ }
1123
+ const newRunId = `qi-run-${randomUUID()}`;
1124
+ const requestedAt = new Date().toISOString();
1125
+ const abortScope = requestAbortSignal(ctx);
1126
+ try {
1127
+ const drift = await computeDrift(ctx.req, evidenceDir, id, newRunId, deps);
1128
+ if (!drift.ok)
1129
+ return drift.result;
1130
+ return await regenerateFromDrift({
1131
+ deps,
1132
+ evidenceDir,
1133
+ newRunId,
1134
+ requestedAt,
1135
+ drift: drift.value,
1136
+ signal: abortScope.signal,
1137
+ });
1138
+ }
1139
+ catch {
1140
+ return errorResult(500, "QI_REGEN_FAILED", "Failed to regenerate stale candidates.");
1141
+ }
1142
+ finally {
1143
+ abortScope.dispose();
1144
+ }
1145
+ }
1146
+ export const QI_RECHECK_ROUTE_GROUP = [
1147
+ {
1148
+ method: "POST",
1149
+ pattern: "/api/quality-intelligence/runs/:id/re-check",
1150
+ handler: handleQiReCheck,
1151
+ },
1152
+ {
1153
+ method: "POST",
1154
+ pattern: "/api/quality-intelligence/runs/:id/regenerate-stale",
1155
+ handler: handleQiRegenerateStale,
1156
+ },
1157
+ ];