clinkx 0.2.3 → 1.0.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 (574) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/LICENSE +10 -0
  3. package/README.md +113 -18
  4. package/clinkx-workflows/dist/allowed-roots.d.ts +1 -0
  5. package/clinkx-workflows/dist/allowed-roots.js +5 -0
  6. package/clinkx-workflows/dist/allowed-roots.js.map +1 -0
  7. package/clinkx-workflows/dist/artifacts.d.ts +20 -1
  8. package/clinkx-workflows/dist/artifacts.js +38 -9
  9. package/clinkx-workflows/dist/artifacts.js.map +1 -1
  10. package/clinkx-workflows/dist/backend.d.ts +4 -0
  11. package/clinkx-workflows/dist/citation-check.d.ts +40 -0
  12. package/clinkx-workflows/dist/citation-check.js +348 -0
  13. package/clinkx-workflows/dist/citation-check.js.map +1 -0
  14. package/clinkx-workflows/dist/clink-client.d.ts +2 -1
  15. package/clinkx-workflows/dist/clink-client.js +17 -4
  16. package/clinkx-workflows/dist/clink-client.js.map +1 -1
  17. package/clinkx-workflows/dist/config.d.ts +145 -16
  18. package/clinkx-workflows/dist/config.js +384 -26
  19. package/clinkx-workflows/dist/config.js.map +1 -1
  20. package/clinkx-workflows/dist/contained-file.d.ts +172 -0
  21. package/clinkx-workflows/dist/contained-file.js +483 -0
  22. package/clinkx-workflows/dist/contained-file.js.map +1 -0
  23. package/clinkx-workflows/dist/context-builder-lifecycle-contract.d.ts +416 -0
  24. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js +328 -0
  25. package/clinkx-workflows/dist/context-builder-lifecycle-contract.js.map +1 -0
  26. package/clinkx-workflows/dist/context-builder-lifecycle-router.d.ts +24 -0
  27. package/clinkx-workflows/dist/context-builder-lifecycle-router.js +39 -0
  28. package/clinkx-workflows/dist/context-builder-lifecycle-router.js.map +1 -0
  29. package/clinkx-workflows/dist/context-cache-key.d.ts +53 -0
  30. package/clinkx-workflows/dist/context-cache-key.js +91 -0
  31. package/clinkx-workflows/dist/context-cache-key.js.map +1 -0
  32. package/clinkx-workflows/dist/context-mode-resolver.d.ts +27 -0
  33. package/clinkx-workflows/dist/context-mode-resolver.js +126 -0
  34. package/clinkx-workflows/dist/context-mode-resolver.js.map +1 -0
  35. package/clinkx-workflows/dist/context-preflight.d.ts +184 -0
  36. package/clinkx-workflows/dist/context-preflight.js +91 -0
  37. package/clinkx-workflows/dist/context-preflight.js.map +1 -0
  38. package/clinkx-workflows/dist/council-expander.d.ts +1 -1
  39. package/clinkx-workflows/dist/council-expander.js +2 -1
  40. package/clinkx-workflows/dist/council-expander.js.map +1 -1
  41. package/clinkx-workflows/dist/dashboard/activity-contract.d.ts +165 -0
  42. package/clinkx-workflows/dist/dashboard/activity-contract.js +105 -0
  43. package/clinkx-workflows/dist/dashboard/activity-contract.js.map +1 -0
  44. package/clinkx-workflows/dist/dashboard/activity-lease.d.ts +25 -0
  45. package/clinkx-workflows/dist/dashboard/activity-lease.js +34 -0
  46. package/clinkx-workflows/dist/dashboard/activity-lease.js.map +1 -0
  47. package/clinkx-workflows/dist/dashboard/activity-reader.d.ts +86 -0
  48. package/clinkx-workflows/dist/dashboard/activity-reader.js +480 -0
  49. package/clinkx-workflows/dist/dashboard/activity-reader.js.map +1 -0
  50. package/clinkx-workflows/dist/dashboard/api-routes.d.ts +36 -0
  51. package/clinkx-workflows/dist/dashboard/api-routes.js +1047 -0
  52. package/clinkx-workflows/dist/dashboard/api-routes.js.map +1 -0
  53. package/clinkx-workflows/dist/dashboard/artifact-index.d.ts +37 -0
  54. package/clinkx-workflows/dist/dashboard/artifact-index.js +145 -0
  55. package/clinkx-workflows/dist/dashboard/artifact-index.js.map +1 -0
  56. package/clinkx-workflows/dist/dashboard/auth/challenge-store.d.ts +31 -0
  57. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js +65 -0
  58. package/clinkx-workflows/dist/dashboard/auth/challenge-store.js.map +1 -0
  59. package/clinkx-workflows/dist/dashboard/auth/controller.d.ts +26 -0
  60. package/clinkx-workflows/dist/dashboard/auth/controller.js +596 -0
  61. package/clinkx-workflows/dist/dashboard/auth/controller.js.map +1 -0
  62. package/clinkx-workflows/dist/dashboard/auth/credential-store.d.ts +60 -0
  63. package/clinkx-workflows/dist/dashboard/auth/credential-store.js +776 -0
  64. package/clinkx-workflows/dist/dashboard/auth/credential-store.js.map +1 -0
  65. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.d.ts +9 -0
  66. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js +44 -0
  67. package/clinkx-workflows/dist/dashboard/auth/enrollment-gate.js.map +1 -0
  68. package/clinkx-workflows/dist/dashboard/auth/session-store.d.ts +17 -0
  69. package/clinkx-workflows/dist/dashboard/auth/session-store.js +77 -0
  70. package/clinkx-workflows/dist/dashboard/auth/session-store.js.map +1 -0
  71. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.d.ts +25 -0
  72. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js +71 -0
  73. package/clinkx-workflows/dist/dashboard/auth/webauthn-service.js.map +1 -0
  74. package/clinkx-workflows/dist/dashboard/context-run-liveness.d.ts +37 -0
  75. package/clinkx-workflows/dist/dashboard/context-run-liveness.js +49 -0
  76. package/clinkx-workflows/dist/dashboard/context-run-liveness.js.map +1 -0
  77. package/clinkx-workflows/dist/dashboard/context-run-reader.d.ts +222 -0
  78. package/clinkx-workflows/dist/dashboard/context-run-reader.js +1178 -0
  79. package/clinkx-workflows/dist/dashboard/context-run-reader.js.map +1 -0
  80. package/clinkx-workflows/dist/dashboard/contracts.d.ts +8936 -0
  81. package/clinkx-workflows/dist/dashboard/contracts.js +1157 -0
  82. package/clinkx-workflows/dist/dashboard/contracts.js.map +1 -0
  83. package/clinkx-workflows/dist/dashboard/generation.d.ts +14 -0
  84. package/clinkx-workflows/dist/dashboard/generation.js +41 -0
  85. package/clinkx-workflows/dist/dashboard/generation.js.map +1 -0
  86. package/clinkx-workflows/dist/dashboard/index.d.ts +28 -0
  87. package/clinkx-workflows/dist/dashboard/index.js +16 -0
  88. package/clinkx-workflows/dist/dashboard/index.js.map +1 -0
  89. package/clinkx-workflows/dist/dashboard/lazy-content.d.ts +101 -0
  90. package/clinkx-workflows/dist/dashboard/lazy-content.js +748 -0
  91. package/clinkx-workflows/dist/dashboard/lazy-content.js.map +1 -0
  92. package/clinkx-workflows/dist/dashboard/progress-report-contract.d.ts +303 -0
  93. package/clinkx-workflows/dist/dashboard/progress-report-contract.js +221 -0
  94. package/clinkx-workflows/dist/dashboard/progress-report-contract.js.map +1 -0
  95. package/clinkx-workflows/dist/dashboard/progress-report-reader.d.ts +50 -0
  96. package/clinkx-workflows/dist/dashboard/progress-report-reader.js +581 -0
  97. package/clinkx-workflows/dist/dashboard/progress-report-reader.js.map +1 -0
  98. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.d.ts +89 -0
  99. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js +71 -0
  100. package/clinkx-workflows/dist/dashboard/progress-report-status-overlay.js.map +1 -0
  101. package/clinkx-workflows/dist/dashboard/progress-report-status-store.d.ts +28 -0
  102. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js +110 -0
  103. package/clinkx-workflows/dist/dashboard/progress-report-status-store.js.map +1 -0
  104. package/clinkx-workflows/dist/dashboard/read-model.d.ts +86 -0
  105. package/clinkx-workflows/dist/dashboard/read-model.js +440 -0
  106. package/clinkx-workflows/dist/dashboard/read-model.js.map +1 -0
  107. package/clinkx-workflows/dist/dashboard/request-admission.d.ts +31 -0
  108. package/clinkx-workflows/dist/dashboard/request-admission.js +126 -0
  109. package/clinkx-workflows/dist/dashboard/request-admission.js.map +1 -0
  110. package/clinkx-workflows/dist/dashboard/scanner.d.ts +75 -0
  111. package/clinkx-workflows/dist/dashboard/scanner.js +1110 -0
  112. package/clinkx-workflows/dist/dashboard/scanner.js.map +1 -0
  113. package/clinkx-workflows/dist/dashboard/server-factory.d.ts +50 -0
  114. package/clinkx-workflows/dist/dashboard/server-factory.js +431 -0
  115. package/clinkx-workflows/dist/dashboard/server-factory.js.map +1 -0
  116. package/clinkx-workflows/dist/dashboard/session-log-contract.d.ts +2611 -0
  117. package/clinkx-workflows/dist/dashboard/session-log-contract.js +480 -0
  118. package/clinkx-workflows/dist/dashboard/session-log-contract.js.map +1 -0
  119. package/clinkx-workflows/dist/dashboard/session-log-identity.d.ts +21 -0
  120. package/clinkx-workflows/dist/dashboard/session-log-identity.js +101 -0
  121. package/clinkx-workflows/dist/dashboard/session-log-identity.js.map +1 -0
  122. package/clinkx-workflows/dist/dashboard/session-log-journal.d.ts +43 -0
  123. package/clinkx-workflows/dist/dashboard/session-log-journal.js +522 -0
  124. package/clinkx-workflows/dist/dashboard/session-log-journal.js.map +1 -0
  125. package/clinkx-workflows/dist/dashboard/session-log-reader.d.ts +61 -0
  126. package/clinkx-workflows/dist/dashboard/session-log-reader.js +378 -0
  127. package/clinkx-workflows/dist/dashboard/session-log-reader.js.map +1 -0
  128. package/clinkx-workflows/dist/dashboard/session-log-redaction.d.ts +6 -0
  129. package/clinkx-workflows/dist/dashboard/session-log-redaction.js +367 -0
  130. package/clinkx-workflows/dist/dashboard/session-log-redaction.js.map +1 -0
  131. package/clinkx-workflows/dist/dashboard/session-log-retention.d.ts +23 -0
  132. package/clinkx-workflows/dist/dashboard/session-log-retention.js +212 -0
  133. package/clinkx-workflows/dist/dashboard/session-log-retention.js.map +1 -0
  134. package/clinkx-workflows/dist/dashboard/static-responses.d.ts +10 -0
  135. package/clinkx-workflows/dist/dashboard/static-responses.js +94 -0
  136. package/clinkx-workflows/dist/dashboard/static-responses.js.map +1 -0
  137. package/clinkx-workflows/dist/dashboard/worker-lock-observer.d.ts +28 -0
  138. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js +241 -0
  139. package/clinkx-workflows/dist/dashboard/worker-lock-observer.js.map +1 -0
  140. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  141. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  142. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  143. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  144. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  145. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  146. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  147. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  148. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  149. package/clinkx-workflows/dist/dashboard-ui/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  150. package/clinkx-workflows/dist/dashboard-ui/assets/index-DOkMXTfB.js +25 -0
  151. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
  152. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  153. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  154. package/clinkx-workflows/dist/dashboard-ui/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
  155. package/clinkx-workflows/dist/dashboard-ui/assets/style-ChJsn0KD.css +1 -0
  156. package/clinkx-workflows/dist/dashboard-ui/contracts/council-plan-review-input-v1.json +158 -0
  157. package/clinkx-workflows/dist/dashboard-ui/index.html +15 -0
  158. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Mono-OFL.txt +93 -0
  159. package/clinkx-workflows/dist/dashboard-ui/licenses/IBM-Plex-Sans-OFL.txt +93 -0
  160. package/clinkx-workflows/dist/dashboard-ui/licenses/JetBrains-Mono-OFL.txt +93 -0
  161. package/clinkx-workflows/dist/dashboard-ui/licenses/NOTICE.md +32 -0
  162. package/clinkx-workflows/dist/dashboard-ui/theme-init.js +8 -0
  163. package/clinkx-workflows/dist/definition-normalizer.d.ts +19 -0
  164. package/clinkx-workflows/dist/definition-normalizer.js.map +1 -1
  165. package/clinkx-workflows/dist/delegate-validate.d.ts +78 -0
  166. package/clinkx-workflows/dist/delegate-validate.js +97 -0
  167. package/clinkx-workflows/dist/delegate-validate.js.map +1 -0
  168. package/clinkx-workflows/dist/engine.d.ts +141 -0
  169. package/clinkx-workflows/dist/engine.js +882 -28
  170. package/clinkx-workflows/dist/engine.js.map +1 -1
  171. package/clinkx-workflows/dist/errors.d.ts +2 -0
  172. package/clinkx-workflows/dist/errors.js.map +1 -1
  173. package/clinkx-workflows/dist/fingerprint.d.ts +1 -1
  174. package/clinkx-workflows/dist/fingerprint.js +1 -1
  175. package/clinkx-workflows/dist/index.d.ts +19 -0
  176. package/clinkx-workflows/dist/index.js +15 -0
  177. package/clinkx-workflows/dist/index.js.map +1 -1
  178. package/clinkx-workflows/dist/loader.d.ts +9 -3
  179. package/clinkx-workflows/dist/loader.js +92 -21
  180. package/clinkx-workflows/dist/loader.js.map +1 -1
  181. package/clinkx-workflows/dist/plan-review/admission.d.ts +160 -0
  182. package/clinkx-workflows/dist/plan-review/admission.js +290 -0
  183. package/clinkx-workflows/dist/plan-review/admission.js.map +1 -0
  184. package/clinkx-workflows/dist/plan-review/context-policy.d.ts +55 -0
  185. package/clinkx-workflows/dist/plan-review/context-policy.js +146 -0
  186. package/clinkx-workflows/dist/plan-review/context-policy.js.map +1 -0
  187. package/clinkx-workflows/dist/plan-review/contract.d.ts +763 -0
  188. package/clinkx-workflows/dist/plan-review/contract.js +289 -0
  189. package/clinkx-workflows/dist/plan-review/contract.js.map +1 -0
  190. package/clinkx-workflows/dist/plan-review/digest.d.ts +68 -0
  191. package/clinkx-workflows/dist/plan-review/digest.js +241 -0
  192. package/clinkx-workflows/dist/plan-review/digest.js.map +1 -0
  193. package/clinkx-workflows/dist/plan-review/discovery.d.ts +45 -0
  194. package/clinkx-workflows/dist/plan-review/discovery.js +36 -0
  195. package/clinkx-workflows/dist/plan-review/discovery.js.map +1 -0
  196. package/clinkx-workflows/dist/plan-review/execution-contract.d.ts +298 -0
  197. package/clinkx-workflows/dist/plan-review/execution-contract.js +194 -0
  198. package/clinkx-workflows/dist/plan-review/execution-contract.js.map +1 -0
  199. package/clinkx-workflows/dist/plan-review/expander.d.ts +48 -0
  200. package/clinkx-workflows/dist/plan-review/expander.js +243 -0
  201. package/clinkx-workflows/dist/plan-review/expander.js.map +1 -0
  202. package/clinkx-workflows/dist/plan-review/projector.d.ts +126 -0
  203. package/clinkx-workflows/dist/plan-review/projector.js +437 -0
  204. package/clinkx-workflows/dist/plan-review/projector.js.map +1 -0
  205. package/clinkx-workflows/dist/plan-review/publication.d.ts +95 -0
  206. package/clinkx-workflows/dist/plan-review/publication.js +182 -0
  207. package/clinkx-workflows/dist/plan-review/publication.js.map +1 -0
  208. package/clinkx-workflows/dist/plan-review/ranking-reducer.d.ts +34 -0
  209. package/clinkx-workflows/dist/plan-review/ranking-reducer.js +139 -0
  210. package/clinkx-workflows/dist/plan-review/ranking-reducer.js.map +1 -0
  211. package/clinkx-workflows/dist/plan-review/resume.d.ts +38 -0
  212. package/clinkx-workflows/dist/plan-review/resume.js +122 -0
  213. package/clinkx-workflows/dist/plan-review/resume.js.map +1 -0
  214. package/clinkx-workflows/dist/plan-review/rubric.d.ts +7 -0
  215. package/clinkx-workflows/dist/plan-review/rubric.js +24 -0
  216. package/clinkx-workflows/dist/plan-review/rubric.js.map +1 -0
  217. package/clinkx-workflows/dist/plan-review/run-state.d.ts +398 -0
  218. package/clinkx-workflows/dist/plan-review/run-state.js +183 -0
  219. package/clinkx-workflows/dist/plan-review/run-state.js.map +1 -0
  220. package/clinkx-workflows/dist/plan-review/runtime-policy.d.ts +74 -0
  221. package/clinkx-workflows/dist/plan-review/runtime-policy.js +186 -0
  222. package/clinkx-workflows/dist/plan-review/runtime-policy.js.map +1 -0
  223. package/clinkx-workflows/dist/plan-review/snapshot.d.ts +81 -0
  224. package/clinkx-workflows/dist/plan-review/snapshot.js +170 -0
  225. package/clinkx-workflows/dist/plan-review/snapshot.js.map +1 -0
  226. package/clinkx-workflows/dist/plan-review/stage-error.d.ts +19 -0
  227. package/clinkx-workflows/dist/plan-review/stage-error.js +20 -0
  228. package/clinkx-workflows/dist/plan-review/stage-error.js.map +1 -0
  229. package/clinkx-workflows/dist/post-processors/index.d.ts +20 -0
  230. package/clinkx-workflows/dist/post-processors/index.js +21 -0
  231. package/clinkx-workflows/dist/post-processors/index.js.map +1 -0
  232. package/clinkx-workflows/dist/prompt-budget.d.ts +7 -0
  233. package/clinkx-workflows/dist/prompt-budget.js +7 -1
  234. package/clinkx-workflows/dist/prompt-budget.js.map +1 -1
  235. package/clinkx-workflows/dist/reducers/index.js +2 -0
  236. package/clinkx-workflows/dist/reducers/index.js.map +1 -1
  237. package/clinkx-workflows/dist/run-lock-heartbeat.d.ts +2 -0
  238. package/clinkx-workflows/dist/run-lock-heartbeat.js +3 -0
  239. package/clinkx-workflows/dist/run-lock-heartbeat.js.map +1 -0
  240. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.d.ts +34 -0
  241. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js +74 -0
  242. package/clinkx-workflows/dist/run-summary/cards/council-plan-review.js.map +1 -0
  243. package/clinkx-workflows/dist/run-summary/cards/index.js +5 -0
  244. package/clinkx-workflows/dist/run-summary/cards/index.js.map +1 -1
  245. package/clinkx-workflows/dist/run-summary.d.ts +13 -0
  246. package/clinkx-workflows/dist/run-summary.js +29 -1
  247. package/clinkx-workflows/dist/run-summary.js.map +1 -1
  248. package/clinkx-workflows/dist/schema.d.ts +76 -21
  249. package/clinkx-workflows/dist/schema.js +10 -1
  250. package/clinkx-workflows/dist/schema.js.map +1 -1
  251. package/clinkx-workflows/dist/seed-terms.d.ts +30 -0
  252. package/clinkx-workflows/dist/seed-terms.js +176 -0
  253. package/clinkx-workflows/dist/seed-terms.js.map +1 -0
  254. package/clinkx-workflows/dist/server.d.ts +5 -2
  255. package/clinkx-workflows/dist/server.js +82 -52
  256. package/clinkx-workflows/dist/server.js.map +1 -1
  257. package/clinkx-workflows/dist/shutdown.d.ts +18 -20
  258. package/clinkx-workflows/dist/shutdown.js +43 -61
  259. package/clinkx-workflows/dist/shutdown.js.map +1 -1
  260. package/clinkx-workflows/dist/state-schema.d.ts +38 -0
  261. package/clinkx-workflows/dist/state.d.ts +170 -3
  262. package/clinkx-workflows/dist/state.js +1275 -28
  263. package/clinkx-workflows/dist/state.js.map +1 -1
  264. package/clinkx-workflows/dist/transport.d.ts +15 -0
  265. package/clinkx-workflows/dist/transport.js +19 -10
  266. package/clinkx-workflows/dist/transport.js.map +1 -1
  267. package/clinkx-workflows/dist/types.d.ts +14 -2
  268. package/clinkx-workflows/dist/validators/council.d.ts +318 -85
  269. package/clinkx-workflows/dist/validators/council.js +52 -13
  270. package/clinkx-workflows/dist/validators/council.js.map +1 -1
  271. package/clinkx-workflows/dist/validators/index.d.ts +1 -0
  272. package/clinkx-workflows/dist/validators/index.js +3 -0
  273. package/clinkx-workflows/dist/validators/index.js.map +1 -1
  274. package/clinkx-workflows/dist/validators/plan-review.d.ts +398 -0
  275. package/clinkx-workflows/dist/validators/plan-review.js +316 -0
  276. package/clinkx-workflows/dist/validators/plan-review.js.map +1 -0
  277. package/clinkx-workflows/dist/workflow-receipt.js +188 -4
  278. package/clinkx-workflows/dist/workflow-receipt.js.map +1 -1
  279. package/clinkx-workflows/dist/workflow-tools.d.ts +48 -3
  280. package/clinkx-workflows/dist/workflow-tools.js +1402 -59
  281. package/clinkx-workflows/dist/workflow-tools.js.map +1 -1
  282. package/clinkx-workflows/dist/workspace-signature.d.ts +16 -0
  283. package/clinkx-workflows/dist/workspace-signature.js +388 -0
  284. package/clinkx-workflows/dist/workspace-signature.js.map +1 -0
  285. package/clinkx-workflows/templates/council/answer-specialist.txt +2 -1
  286. package/clinkx-workflows/templates/council/code-review-chairman.txt +13 -0
  287. package/clinkx-workflows/templates/council/code-review-cross-reviewer.txt +4 -1
  288. package/clinkx-workflows/templates/council/code-review-reviewer.txt +19 -3
  289. package/clinkx-workflows/templates/council/debug-debugger.txt +2 -1
  290. package/clinkx-workflows/templates/council/discover-analyst.txt +2 -1
  291. package/clinkx-workflows/templates/council/member.txt +2 -1
  292. package/clinkx-workflows/templates/council/plan-review-chairman.txt +68 -0
  293. package/clinkx-workflows/templates/council/plan-review-cross-reviewer.txt +51 -0
  294. package/clinkx-workflows/templates/council/plan-review-reviewer.txt +60 -0
  295. package/clinkx-workflows/templates/plan-review/chairman.txt +48 -0
  296. package/clinkx-workflows/templates/plan-review/cross-review.txt +52 -0
  297. package/clinkx-workflows/templates/plan-review/ranking.txt +16 -0
  298. package/clinkx-workflows/templates/plan-review/reviewer.txt +57 -0
  299. package/clinkx-workflows/workflows/council-answer.yaml +97 -8
  300. package/clinkx-workflows/workflows/council-code-review.yaml +103 -8
  301. package/clinkx-workflows/workflows/council-debug.yaml +97 -8
  302. package/clinkx-workflows/workflows/council-default.yaml +97 -8
  303. package/clinkx-workflows/workflows/council-discover.yaml +97 -8
  304. package/clinkx-workflows/workflows/council-plan-review.yaml +250 -0
  305. package/clinkx-workflows/workflows/plan-review-receipts.yaml +230 -0
  306. package/conf/adapters/agy.json +49 -0
  307. package/conf/adapters/claude.json +5 -0
  308. package/conf/adapters/codex-app-server.json +44 -0
  309. package/conf/adapters/codex.json +7 -2
  310. package/conf/adapters/glm.json +5 -0
  311. package/conf/adapters/grok.json +60 -0
  312. package/conf/adapters/hapi/agy.json +52 -0
  313. package/conf/adapters/hapi/claude.json +5 -0
  314. package/conf/adapters/hapi/codex.json +7 -2
  315. package/conf/adapters/hapi/glm.json +5 -0
  316. package/conf/prompts/json-codereviewer.txt +1 -0
  317. package/conf/prompts/json.txt +1 -0
  318. package/dist/activity-journal.d.ts +42 -0
  319. package/dist/activity-journal.js +381 -0
  320. package/dist/activity-journal.js.map +1 -0
  321. package/dist/allowed-roots.d.ts +53 -0
  322. package/dist/allowed-roots.js +216 -0
  323. package/dist/allowed-roots.js.map +1 -0
  324. package/dist/codex/activity-session-log.d.ts +14 -0
  325. package/dist/codex/activity-session-log.js +206 -0
  326. package/dist/codex/activity-session-log.js.map +1 -0
  327. package/dist/codex/agent-tools.d.ts +23 -0
  328. package/dist/codex/agent-tools.js +1270 -0
  329. package/dist/codex/agent-tools.js.map +1 -0
  330. package/dist/codex/app-server-client.d.ts +76 -0
  331. package/dist/codex/app-server-client.js +582 -0
  332. package/dist/codex/app-server-client.js.map +1 -0
  333. package/dist/codex/app-server-registry.d.ts +389 -0
  334. package/dist/codex/app-server-registry.js +1407 -0
  335. package/dist/codex/app-server-registry.js.map +1 -0
  336. package/dist/codex/client-types.d.ts +109 -0
  337. package/dist/codex/client-types.js +2 -0
  338. package/dist/codex/client-types.js.map +1 -0
  339. package/dist/codex/protocol.d.ts +5375 -0
  340. package/dist/codex/protocol.js +614 -0
  341. package/dist/codex/protocol.js.map +1 -0
  342. package/dist/codex/session.d.ts +263 -0
  343. package/dist/codex/session.js +466 -0
  344. package/dist/codex/session.js.map +1 -0
  345. package/dist/config.d.ts +185 -19
  346. package/dist/config.js +82 -16
  347. package/dist/config.js.map +1 -1
  348. package/dist/context-builder/anchors.d.ts +12 -0
  349. package/dist/context-builder/anchors.js +22 -0
  350. package/dist/context-builder/anchors.js.map +1 -0
  351. package/dist/context-builder/bundle.d.ts +109 -0
  352. package/dist/context-builder/bundle.js +1498 -0
  353. package/dist/context-builder/bundle.js.map +1 -0
  354. package/dist/context-builder/codemap.d.ts +22 -0
  355. package/dist/context-builder/codemap.js +675 -0
  356. package/dist/context-builder/codemap.js.map +1 -0
  357. package/dist/context-builder/config.d.ts +61 -0
  358. package/dist/context-builder/config.js +216 -0
  359. package/dist/context-builder/config.js.map +1 -0
  360. package/dist/context-builder/context-run-catalog.d.ts +17 -0
  361. package/dist/context-builder/context-run-catalog.js +99 -0
  362. package/dist/context-builder/context-run-catalog.js.map +1 -0
  363. package/dist/context-builder/delegate-packet.d.ts +219 -0
  364. package/dist/context-builder/delegate-packet.js +1190 -0
  365. package/dist/context-builder/delegate-packet.js.map +1 -0
  366. package/dist/context-builder/delegate-reconstruction.d.ts +13 -0
  367. package/dist/context-builder/delegate-reconstruction.js +220 -0
  368. package/dist/context-builder/delegate-reconstruction.js.map +1 -0
  369. package/dist/context-builder/delegate-validation.d.ts +27 -0
  370. package/dist/context-builder/delegate-validation.js +479 -0
  371. package/dist/context-builder/delegate-validation.js.map +1 -0
  372. package/dist/context-builder/delegate.d.ts +26 -0
  373. package/dist/context-builder/delegate.js +134 -0
  374. package/dist/context-builder/delegate.js.map +1 -0
  375. package/dist/context-builder/diagnostics.d.ts +36 -0
  376. package/dist/context-builder/diagnostics.js +107 -0
  377. package/dist/context-builder/diagnostics.js.map +1 -0
  378. package/dist/context-builder/discovery.d.ts +146 -0
  379. package/dist/context-builder/discovery.js +2129 -0
  380. package/dist/context-builder/discovery.js.map +1 -0
  381. package/dist/context-builder/globs.d.ts +14 -0
  382. package/dist/context-builder/globs.js +175 -0
  383. package/dist/context-builder/globs.js.map +1 -0
  384. package/dist/context-builder/handler.d.ts +41 -0
  385. package/dist/context-builder/handler.js +2590 -0
  386. package/dist/context-builder/handler.js.map +1 -0
  387. package/dist/context-builder/lifecycle-contract.d.ts +424 -0
  388. package/dist/context-builder/lifecycle-contract.js +523 -0
  389. package/dist/context-builder/lifecycle-contract.js.map +1 -0
  390. package/dist/context-builder/lifecycle-journal.d.ts +62 -0
  391. package/dist/context-builder/lifecycle-journal.js +88 -0
  392. package/dist/context-builder/lifecycle-journal.js.map +1 -0
  393. package/dist/context-builder/lifecycle-observer.d.ts +102 -0
  394. package/dist/context-builder/lifecycle-observer.js +258 -0
  395. package/dist/context-builder/lifecycle-observer.js.map +1 -0
  396. package/dist/context-builder/lifecycle-reconcile.d.ts +32 -0
  397. package/dist/context-builder/lifecycle-reconcile.js +191 -0
  398. package/dist/context-builder/lifecycle-reconcile.js.map +1 -0
  399. package/dist/context-builder/lifecycle-recovery.d.ts +2 -0
  400. package/dist/context-builder/lifecycle-recovery.js +14 -0
  401. package/dist/context-builder/lifecycle-recovery.js.map +1 -0
  402. package/dist/context-builder/path-signals.d.ts +7 -0
  403. package/dist/context-builder/path-signals.js +70 -0
  404. package/dist/context-builder/path-signals.js.map +1 -0
  405. package/dist/context-builder/paths.d.ts +30 -0
  406. package/dist/context-builder/paths.js +153 -0
  407. package/dist/context-builder/paths.js.map +1 -0
  408. package/dist/context-builder/progress.d.ts +17 -0
  409. package/dist/context-builder/progress.js +93 -0
  410. package/dist/context-builder/progress.js.map +1 -0
  411. package/dist/context-builder/receipt.d.ts +91 -0
  412. package/dist/context-builder/receipt.js +180 -0
  413. package/dist/context-builder/receipt.js.map +1 -0
  414. package/dist/context-builder/refine-handler.d.ts +14 -0
  415. package/dist/context-builder/refine-handler.js +790 -0
  416. package/dist/context-builder/refine-handler.js.map +1 -0
  417. package/dist/context-builder/review-coverage.d.ts +84 -0
  418. package/dist/context-builder/review-coverage.js +437 -0
  419. package/dist/context-builder/review-coverage.js.map +1 -0
  420. package/dist/context-builder/run-id.d.ts +7 -0
  421. package/dist/context-builder/run-id.js +31 -0
  422. package/dist/context-builder/run-id.js.map +1 -0
  423. package/dist/context-builder/schema.d.ts +669 -0
  424. package/dist/context-builder/schema.js +399 -0
  425. package/dist/context-builder/schema.js.map +1 -0
  426. package/dist/context-builder/secrets.d.ts +41 -0
  427. package/dist/context-builder/secrets.js +1651 -0
  428. package/dist/context-builder/secrets.js.map +1 -0
  429. package/dist/context-builder/selection.d.ts +56 -0
  430. package/dist/context-builder/selection.js +1111 -0
  431. package/dist/context-builder/selection.js.map +1 -0
  432. package/dist/context-builder/session-log-identity.d.ts +1 -0
  433. package/dist/context-builder/session-log-identity.js +66 -0
  434. package/dist/context-builder/session-log-identity.js.map +1 -0
  435. package/dist/context-builder/slice-range-math.d.ts +16 -0
  436. package/dist/context-builder/slice-range-math.js +137 -0
  437. package/dist/context-builder/slice-range-math.js.map +1 -0
  438. package/dist/context-builder/status.d.ts +66 -0
  439. package/dist/context-builder/status.js +160 -0
  440. package/dist/context-builder/status.js.map +1 -0
  441. package/dist/context-builder/storage.d.ts +118 -0
  442. package/dist/context-builder/storage.js +1003 -0
  443. package/dist/context-builder/storage.js.map +1 -0
  444. package/dist/context-builder/token-estimate.d.ts +19 -0
  445. package/dist/context-builder/token-estimate.js +90 -0
  446. package/dist/context-builder/token-estimate.js.map +1 -0
  447. package/dist/context-builder/versions.d.ts +29 -0
  448. package/dist/context-builder/versions.js +30 -0
  449. package/dist/context-builder/versions.js.map +1 -0
  450. package/dist/context-builder/workflow-runner.d.ts +69 -0
  451. package/dist/context-builder/workflow-runner.js +1051 -0
  452. package/dist/context-builder/workflow-runner.js.map +1 -0
  453. package/dist/context-builder/write-watchdog.d.ts +14 -0
  454. package/dist/context-builder/write-watchdog.js +38 -0
  455. package/dist/context-builder/write-watchdog.js.map +1 -0
  456. package/dist/dashboard-adapter-catalog.d.ts +9 -0
  457. package/dist/dashboard-adapter-catalog.js +46 -0
  458. package/dist/dashboard-adapter-catalog.js.map +1 -0
  459. package/dist/dashboard-cli.d.ts +79 -0
  460. package/dist/dashboard-cli.js +585 -0
  461. package/dist/dashboard-cli.js.map +1 -0
  462. package/dist/dashboard-companion/client.d.ts +36 -0
  463. package/dist/dashboard-companion/client.js +210 -0
  464. package/dist/dashboard-companion/client.js.map +1 -0
  465. package/dist/dashboard-companion/config.d.ts +8 -0
  466. package/dist/dashboard-companion/config.js +52 -0
  467. package/dist/dashboard-companion/config.js.map +1 -0
  468. package/dist/dashboard-companion/http-proxy.d.ts +10 -0
  469. package/dist/dashboard-companion/http-proxy.js +234 -0
  470. package/dist/dashboard-companion/http-proxy.js.map +1 -0
  471. package/dist/dashboard-companion/protocol.d.ts +80 -0
  472. package/dist/dashboard-companion/protocol.js +455 -0
  473. package/dist/dashboard-companion/protocol.js.map +1 -0
  474. package/dist/dashboard-companion/standalone-assets.d.ts +12 -0
  475. package/dist/dashboard-companion/standalone-assets.js +72 -0
  476. package/dist/dashboard-companion/standalone-assets.js.map +1 -0
  477. package/dist/dashboard-companion/standalone-entry.d.ts +1 -0
  478. package/dist/dashboard-companion/standalone-entry.js +17 -0
  479. package/dist/dashboard-companion/standalone-entry.js.map +1 -0
  480. package/dist/dashboard-gateway/config.d.ts +9 -0
  481. package/dist/dashboard-gateway/config.js +44 -0
  482. package/dist/dashboard-gateway/config.js.map +1 -0
  483. package/dist/dashboard-gateway/index.d.ts +11 -0
  484. package/dist/dashboard-gateway/index.js +755 -0
  485. package/dist/dashboard-gateway/index.js.map +1 -0
  486. package/dist/dashboard-gateway/main.d.ts +1 -0
  487. package/dist/dashboard-gateway/main.js +25 -0
  488. package/dist/dashboard-gateway/main.js.map +1 -0
  489. package/dist/dashboard-pairing-token.d.ts +1 -0
  490. package/dist/dashboard-pairing-token.js +5 -0
  491. package/dist/dashboard-pairing-token.js.map +1 -0
  492. package/dist/elaris/client.d.ts +73 -0
  493. package/dist/elaris/client.js +825 -0
  494. package/dist/elaris/client.js.map +1 -0
  495. package/dist/elaris/config.d.ts +18 -0
  496. package/dist/elaris/config.js +75 -0
  497. package/dist/elaris/config.js.map +1 -0
  498. package/dist/elaris/errors.d.ts +41 -0
  499. package/dist/elaris/errors.js +205 -0
  500. package/dist/elaris/errors.js.map +1 -0
  501. package/dist/elaris/github-repo.d.ts +18 -0
  502. package/dist/elaris/github-repo.js +89 -0
  503. package/dist/elaris/github-repo.js.map +1 -0
  504. package/dist/elaris/index.d.ts +10 -0
  505. package/dist/elaris/index.js +11 -0
  506. package/dist/elaris/index.js.map +1 -0
  507. package/dist/elaris/redact.d.ts +89 -0
  508. package/dist/elaris/redact.js +423 -0
  509. package/dist/elaris/redact.js.map +1 -0
  510. package/dist/elaris/run-id.d.ts +5 -0
  511. package/dist/elaris/run-id.js +20 -0
  512. package/dist/elaris/run-id.js.map +1 -0
  513. package/dist/elaris/status.d.ts +45 -0
  514. package/dist/elaris/status.js +122 -0
  515. package/dist/elaris/status.js.map +1 -0
  516. package/dist/elaris/tool-schemas.d.ts +314 -0
  517. package/dist/elaris/tool-schemas.js +208 -0
  518. package/dist/elaris/tool-schemas.js.map +1 -0
  519. package/dist/elaris/tools.d.ts +73 -0
  520. package/dist/elaris/tools.js +1065 -0
  521. package/dist/elaris/tools.js.map +1 -0
  522. package/dist/elaris/types.d.ts +1541 -0
  523. package/dist/elaris/types.js +252 -0
  524. package/dist/elaris/types.js.map +1 -0
  525. package/dist/errors.d.ts +16 -0
  526. package/dist/errors.js +19 -0
  527. package/dist/errors.js.map +1 -1
  528. package/dist/handler.d.ts +4 -0
  529. package/dist/handler.js +10 -1
  530. package/dist/handler.js.map +1 -1
  531. package/dist/index.d.ts +2 -1
  532. package/dist/index.js +13 -3
  533. package/dist/index.js.map +1 -1
  534. package/dist/local-clink-backend.d.ts +3 -0
  535. package/dist/local-clink-backend.js +4 -0
  536. package/dist/local-clink-backend.js.map +1 -1
  537. package/dist/package-info.d.ts +2 -0
  538. package/dist/package-info.js +18 -0
  539. package/dist/package-info.js.map +1 -0
  540. package/dist/parsers/codex-jsonl.js +48 -3
  541. package/dist/parsers/codex-jsonl.js.map +1 -1
  542. package/dist/parsers/extract.d.ts +10 -1
  543. package/dist/parsers/extract.js +11 -3
  544. package/dist/parsers/extract.js.map +1 -1
  545. package/dist/paths.d.ts +8 -0
  546. package/dist/paths.js +31 -43
  547. package/dist/paths.js.map +1 -1
  548. package/dist/pipeline.d.ts +51 -3
  549. package/dist/pipeline.js +264 -47
  550. package/dist/pipeline.js.map +1 -1
  551. package/dist/run-dir.d.ts +24 -0
  552. package/dist/run-dir.js +201 -2
  553. package/dist/run-dir.js.map +1 -1
  554. package/dist/runner.d.ts +13 -0
  555. package/dist/runner.js +184 -15
  556. package/dist/runner.js.map +1 -1
  557. package/dist/safety.d.ts +4 -4
  558. package/dist/safety.js +4 -4
  559. package/dist/schema.d.ts +7 -7
  560. package/dist/server.d.ts +2 -0
  561. package/dist/server.js +396 -24
  562. package/dist/server.js.map +1 -1
  563. package/dist/session-log-stream.d.ts +18 -0
  564. package/dist/session-log-stream.js +319 -0
  565. package/dist/session-log-stream.js.map +1 -0
  566. package/package.json +47 -5
  567. package/conf/adapters/gemini.json +0 -44
  568. package/conf/adapters/hapi/gemini.json +0 -47
  569. package/dist/artifacts.d.ts +0 -9
  570. package/dist/artifacts.js +0 -24
  571. package/dist/artifacts.js.map +0 -1
  572. package/dist/parsers/gemini-json.d.ts +0 -2
  573. package/dist/parsers/gemini-json.js +0 -72
  574. package/dist/parsers/gemini-json.js.map +0 -1
@@ -0,0 +1,2590 @@
1
+ import { appendArtifactOnlyRootsToEnv, isArtifactOnlyAllowedRootEntry, isSamePathOrDescendant, parseAllowedRootsEnv, stripArtifactOnlyMarker, } from "../allowed-roots.js";
2
+ import { CancellationError, ExecutionError, InvalidParamsError, } from "../errors.js";
3
+ import { realpathSync } from "node:fs";
4
+ import { realpath as fsRealpath } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { AdapterRegistry as RuntimeAdapterRegistry } from "../registry.js";
7
+ import { logger } from "../logger.js";
8
+ import { assertContextBuilderAnchorSufficiency } from "./anchors.js";
9
+ import { writeContextBuilderFinalArtifacts, } from "./bundle.js";
10
+ import { CONTEXT_BUILDER_INTERNAL_CAP_DEFAULTS, getSharedContextBuilderSemaphore, resolveContextBuilderRunsDir, } from "./config.js";
11
+ import { buildContextBuilderCuratorPacket, buildContextBuilderFinalizerPacket, CONTEXT_BUILDER_DELEGATE_PACKET_CAP_PROFILE_DEFAULTS, contextBuilderCandidateId, } from "./delegate-packet.js";
12
+ import { reconstructContextBuilderDelegateSelection } from "./delegate-reconstruction.js";
13
+ import { runContextBuilderDelegateCall, } from "./delegate.js";
14
+ import { validateContextBuilderCuratorOutput, validateContextBuilderDelegateCoverageSelection, validateContextBuilderFinalizerOutput, } from "./delegate-validation.js";
15
+ import { discoverContextBuilderCandidates, mergeLineRanges, } from "./discovery.js";
16
+ import { evaluateSearchCapMateriality } from "./status.js";
17
+ import { boundContextBuilderPathList, isRequestedEvidenceOmission, sanitizeDelegateFailureText, sanitizeDiagnosticText, } from "./diagnostics.js";
18
+ import { compileContextBuilderGlobs, CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS, validateContextBuilderGlobs, } from "./globs.js";
19
+ import { resolveContextBuilderFocusPaths, resolveContextBuilderRoots, } from "./paths.js";
20
+ import { createContextBuilderProgressReporter, } from "./progress.js";
21
+ import { ContextBuilderLifecycleController, ContextBuilderLifecycleObserverError, } from "./lifecycle-observer.js";
22
+ import { createContextBuilderLifecycleJournal } from "./lifecycle-journal.js";
23
+ import { recordContextRunCatalogMarker } from "./context-run-catalog.js";
24
+ import { appendLifecycleCompletionWarning } from "./lifecycle-recovery.js";
25
+ import { withContextBuilderWriteWatchdog } from "./write-watchdog.js";
26
+ import { createContextBuilderReceiptTextResponse, } from "./receipt.js";
27
+ import { generateContextBuilderRunId } from "./run-id.js";
28
+ import { CONTEXT_BUILDER_INTENT_PROFILE_DEFAULTS, parseContextBuilderInput, } from "./schema.js";
29
+ import { evaluateContextCoverage, resolveContextCoveragePolicy, } from "./review-coverage.js";
30
+ import { selectContextBuilderFinalContext, } from "./selection.js";
31
+ import { cleanupContextBuilderIncompleteRun, createContextBuilderRun, } from "./storage.js";
32
+ import { admitJournalContextGeneration } from "./session-log-identity.js";
33
+ import { estimateContextBuilderTokensFromUtf8Bytes } from "./token-estimate.js";
34
+ let defaultAdapterRegistry;
35
+ const artifactOnlyAllowedRoots = new Set();
36
+ const CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION = "cb-delegate-v3-scored-obligations";
37
+ const MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS = 24;
38
+ export async function handleContextBuilder(args, options = {}) {
39
+ const input = parseContextBuilderInput(args);
40
+ const coveragePolicy = resolveContextCoveragePolicy(input.profile);
41
+ const semaphore = options.semaphore ?? getDefaultContextBuilderSemaphore();
42
+ const progress = createContextBuilderProgressReporter(options.progress);
43
+ const ownsLifecycle = options.lifecycleController == null;
44
+ semaphore.acquire();
45
+ let lifecycle;
46
+ let storage;
47
+ let committed = false;
48
+ try {
49
+ // Resolve and validate the runs dir up front so a broad/forbidden runs_dir
50
+ // is rejected with its canonical error BEFORE any lifecycle transition, and
51
+ // so the journal reuses this one already-validated path instead of
52
+ // re-resolving (a second resolution would wrap that rejection as an opaque
53
+ // observer failure).
54
+ const runsDir = await resolveContextBuilderRunsDir(options.runsDir);
55
+ // A standalone operation that owns its lifecycle (no injected observer)
56
+ // persists authoritatively through the .context-runs journal; the injected
57
+ // path — workflow preflight — supplies its own store instead.
58
+ const journal = ownsLifecycle && options.lifecycle == null
59
+ ? createContextBuilderLifecycleJournal({
60
+ runsDir,
61
+ resolveRunsDir: async () => runsDir,
62
+ })
63
+ : undefined;
64
+ lifecycle = options.lifecycleController ??
65
+ new ContextBuilderLifecycleController({
66
+ operationKind: "standalone",
67
+ execution: lifecycleExecutionConfig(input),
68
+ ...(options.lifecycle != null
69
+ ? {
70
+ observer: options.lifecycle.observer,
71
+ ...(options.lifecycle.origin == null
72
+ ? {}
73
+ : { origin: options.lifecycle.origin }),
74
+ }
75
+ : journal == null
76
+ ? {}
77
+ : { observer: journal.observe }),
78
+ ...(progress == null ? {} : { notificationObserver: progress.observe }),
79
+ ...(options.now == null ? {} : { now: options.now }),
80
+ });
81
+ if (ownsLifecycle) {
82
+ await lifecycle?.transition("resolving");
83
+ }
84
+ const createdAt = (options.now?.() ?? new Date()).toISOString();
85
+ const runId = input.run_id ?? generateContextBuilderRunId();
86
+ await lifecycle?.transition("validating", {
87
+ ...(ownsLifecycle ? { artifactRunId: runId } : {}),
88
+ });
89
+ const internalCaps = options.internalCaps ?? CONTEXT_BUILDER_INTERNAL_CAP_DEFAULTS;
90
+ const currentAllowedRoots = parseAllowedRootsEnv({
91
+ includeArtifactOnly: false,
92
+ });
93
+ const explicitlyConfiguredArtifactRoots = getExactlyConfiguredArtifactRoots();
94
+ const sourceAllowedRoots = currentAllowedRoots.filter((root) => !artifactOnlyAllowedRoots.has(root) ||
95
+ explicitlyConfiguredArtifactRoots.has(root));
96
+ validateContextBuilderGlobs({
97
+ include: input.include_globs,
98
+ exclude: input.exclude_globs,
99
+ });
100
+ await rejectArtifactOnlySourceRoots(input.roots, {
101
+ explicitlyConfiguredArtifactRoots,
102
+ });
103
+ const rootResolution = await resolveContextBuilderRoots(input.roots, {
104
+ allowedRoots: sourceAllowedRoots,
105
+ });
106
+ const focusPaths = await resolveContextBuilderFocusPaths(input.focus_paths, rootResolution.roots);
107
+ assertContextBuilderAnchorSufficiency({
108
+ focus_paths: focusPaths,
109
+ include_globs: input.include_globs,
110
+ search_terms: input.search_terms,
111
+ allowRootWideDiscovery: options.allowRootWideDiscovery ?? false,
112
+ });
113
+ assertNoContextBuilderFocusGlobConflicts({
114
+ focusPaths,
115
+ includeGlobs: input.include_globs,
116
+ excludeGlobs: input.exclude_globs,
117
+ });
118
+ appendArtifactOnlyRootToEnv(runsDir);
119
+ if (!sourceAllowedRoots.includes(runsDir)) {
120
+ artifactOnlyAllowedRoots.add(runsDir);
121
+ }
122
+ const excludedArtifactRoots = [...artifactOnlyAllowedRoots].filter((root) => !explicitlyConfiguredArtifactRoots.has(root));
123
+ const writingStorage = await createContextBuilderRun({
124
+ runsDir,
125
+ runId,
126
+ createdAt,
127
+ });
128
+ storage = writingStorage;
129
+ let sessionLogContext;
130
+ if (journal !== undefined) {
131
+ try {
132
+ sessionLogContext = {
133
+ contextRunId: lifecycle.contextRunId,
134
+ contextGeneration: await admitJournalContextGeneration(runsDir, lifecycle.contextRunId),
135
+ };
136
+ }
137
+ catch {
138
+ logger.debug({ contextRunId: lifecycle.contextRunId }, "context session-log identity admission failed open");
139
+ }
140
+ }
141
+ else if (options.sessionLogContext !== undefined) {
142
+ // Workflow preflight path: the runner minted the operation id (via the
143
+ // controller) and created the run-dir generation sidecar. Use that pair
144
+ // instead of the journal (which the injected-controller path does not
145
+ // create). Both flow into the context-family session-log owner emitted for
146
+ // the curator/finalizer delegate calls.
147
+ const injected = options.sessionLogContext;
148
+ sessionLogContext = injected.contextRunId === lifecycle.contextRunId
149
+ ? injected
150
+ : { contextRunId: lifecycle.contextRunId, contextGeneration: injected.contextGeneration };
151
+ }
152
+ await lifecycle?.transition("scanning");
153
+ const discovery = await discoverContextBuilderCandidates({
154
+ roots: rootResolution.roots,
155
+ task: input.task,
156
+ allowSecrets: input.allow_secrets,
157
+ focus_paths: focusPaths,
158
+ include_globs: input.include_globs,
159
+ exclude_globs: input.exclude_globs,
160
+ search_terms: input.search_terms,
161
+ coverage_policy: coveragePolicy,
162
+ budget: input.budget,
163
+ internal_caps: internalCaps,
164
+ excluded_real_roots: excludedArtifactRoots,
165
+ ...(options.signal == null ? {} : { signal: options.signal }),
166
+ });
167
+ await lifecycle?.transition("searching", {
168
+ counts: lifecycleCountsFromDiscovery(discovery),
169
+ });
170
+ await lifecycle?.transition("selecting");
171
+ const selection = selectContextBuilderFinalContext({
172
+ task: input.task,
173
+ tree: discovery.tree,
174
+ search: discovery.search,
175
+ slices: discovery.slices,
176
+ budget: input.budget,
177
+ coverage_policy: coveragePolicy,
178
+ evidence_classes_by_candidate: evidenceClassesByCandidate(discovery.content_candidates),
179
+ ...(discovery.review_obligations == null
180
+ ? {}
181
+ : { review_obligations: discovery.review_obligations }),
182
+ });
183
+ const effectiveDelegatePacketCaps = resolveEffectiveDelegatePacketCaps(input.profile, options.delegatePacketCaps);
184
+ const delegateResult = input.delegate.enabled
185
+ ? await resolveDelegateSelection({
186
+ input,
187
+ discovery,
188
+ deterministicSelection: selection,
189
+ focusPaths,
190
+ baselineOmissions: rootResolution.omissions,
191
+ sourceRoots: rootResolution.roots,
192
+ registry: options.registry ?? getDefaultAdapterRegistry(),
193
+ coveragePolicy,
194
+ ...(effectiveDelegatePacketCaps == null
195
+ ? {}
196
+ : { delegatePacketCaps: effectiveDelegatePacketCaps }),
197
+ ...(lifecycle == null ? {} : { lifecycle }),
198
+ ...(options.signal == null ? {} : { signal: options.signal }),
199
+ ...(sessionLogContext == null ? {} : { sessionLogContext }),
200
+ })
201
+ : undefined;
202
+ const effectiveDelegateResult = delegateResult;
203
+ const finalSelection = effectiveDelegateResult?.selection ?? selection;
204
+ const coverage = evaluateSelectionCoverage({
205
+ policy: coveragePolicy,
206
+ candidates: discovery.content_candidates,
207
+ selected: finalSelection.selected,
208
+ ...(discovery.review_obligations == null
209
+ ? {}
210
+ : { obligations: discovery.review_obligations }),
211
+ });
212
+ const rawWarnings = [...(effectiveDelegateResult?.warnings ?? [])];
213
+ const openQuestions = effectiveDelegateResult?.openQuestions ?? [];
214
+ const omissions = [
215
+ ...rootResolution.omissions,
216
+ ...discovery.omissions,
217
+ ...finalSelection.omissions,
218
+ ...(effectiveDelegateResult?.omissions ?? []),
219
+ ];
220
+ const metrics = buildContextBuilderMetrics({
221
+ discovery: discovery.metrics,
222
+ selection: finalSelection.metrics,
223
+ omissionCount: omissions.length,
224
+ });
225
+ const reconciledWarnings = reconcileContextBuilderWarnings({
226
+ warnings: rawWarnings,
227
+ selected: finalSelection.selected,
228
+ omissions,
229
+ metrics,
230
+ delegate: effectiveDelegateResult?.delegate,
231
+ });
232
+ // A material search cap must be explained in the receipt, not just
233
+ // classified: the warning carries the bounded coverage summary while the
234
+ // cap event (built from metrics + search_coverage in the status reducer)
235
+ // carries the partial classification.
236
+ const searchCapWarning = searchCapCoverageWarning(metrics.search_cap_hit, discovery.search_coverage);
237
+ const warnings = searchCapWarning == null
238
+ ? reconciledWarnings
239
+ : [...reconciledWarnings, searchCapWarning];
240
+ const delegateMetadata = effectiveDelegateResult?.delegate == null
241
+ ? undefined
242
+ : {
243
+ ...effectiveDelegateResult.delegate,
244
+ selected_count: finalSelection.selected.length,
245
+ warning_count: warnings.length,
246
+ };
247
+ const delegatePacketEstimatedTokens = effectiveDelegateResult?.observability.delegate_packet_size ?? 0;
248
+ const delegatePacketCompaction = effectiveDelegateResult?.observability.delegate_packet_compaction;
249
+ const observability = {
250
+ curator_ms: effectiveDelegateResult?.observability.curator_ms ?? null,
251
+ finalizer_ms: effectiveDelegateResult?.observability.finalizer_ms ?? null,
252
+ candidate_pool_size: discovery.content_candidates.length,
253
+ // Deprecated alias (see ContextBuilderObservabilityMetrics): historical
254
+ // name for the packet ESTIMATED-TOKEN metric. Kept byte-identical for
255
+ // compatibility; new readers should use delegate_packet_estimated_tokens.
256
+ delegate_packet_size: delegatePacketEstimatedTokens,
257
+ delegate_packet_estimated_tokens: delegatePacketEstimatedTokens,
258
+ ...(delegatePacketCompaction == null
259
+ ? {}
260
+ : { delegate_packet_compaction: delegatePacketCompaction }),
261
+ };
262
+ const renderInput = {
263
+ run_id: runId,
264
+ created_at: createdAt,
265
+ task: input.task,
266
+ allowSecrets: input.allow_secrets,
267
+ roots: rootResolution.roots.map((root) => ({
268
+ root_index: root.root_index,
269
+ input_path: root.input_path,
270
+ path: root.path,
271
+ })),
272
+ inputs: {
273
+ effective_profile: input.profile ?? null,
274
+ roots: input.roots,
275
+ focus_paths: input.focus_paths,
276
+ include_globs: input.include_globs,
277
+ exclude_globs: input.exclude_globs,
278
+ search_terms: input.search_terms,
279
+ resolved_budget: input.budget,
280
+ delegate: input.delegate,
281
+ ...(input.run_id == null ? {} : { run_id: input.run_id }),
282
+ },
283
+ budget: input.budget,
284
+ internal_caps: internalCaps,
285
+ tree: finalSelection.tree,
286
+ search: finalSelection.search,
287
+ slices: finalSelection.slices,
288
+ selected_context: finalSelection.selected,
289
+ omissions,
290
+ warnings,
291
+ metrics,
292
+ search_coverage: discovery.search_coverage,
293
+ open_questions: openQuestions,
294
+ ...(delegateMetadata == null ? {} : { delegate: delegateMetadata }),
295
+ observability,
296
+ coverage,
297
+ status_evidence: { coverage },
298
+ debug: input.debug,
299
+ };
300
+ await lifecycle?.transition("writing", {
301
+ ...delegateLifecyclePatch(effectiveDelegateResult, lifecycle),
302
+ counts: lifecycleCountsFromMetrics(metrics, discovery.search.length),
303
+ coverage: lifecycleCoverage(coverage),
304
+ ...(effectiveDelegateResult?.delegate.fallback_used === true
305
+ ? {
306
+ fallback: {
307
+ status: "used",
308
+ reason_code: "delegate_fallback",
309
+ },
310
+ reasonCodes: ["delegate_fallback"],
311
+ }
312
+ : {}),
313
+ });
314
+ // Capture into a const so the beforeManifestCommit closure keeps the
315
+ // narrowed non-undefined type (the outer binding is a reassignable let).
316
+ const committingLifecycle = lifecycle;
317
+ const receipt = await withContextBuilderWriteWatchdog((signal) => writeContextBuilderFinalArtifacts(writingStorage, renderInput, {
318
+ signal,
319
+ ...(writingStorage.ownerNonce == null ? {} : { ownerNonce: writingStorage.ownerNonce }),
320
+ ...(!ownsLifecycle || committingLifecycle == null
321
+ ? {}
322
+ : { beforeManifestCommit: () => committingLifecycle.transition("committing") }),
323
+ }), options.signal == null ? {} : { signal: options.signal });
324
+ committed = true;
325
+ if (ownsLifecycle) {
326
+ await recordContextRunCatalogMarker({
327
+ runsDir,
328
+ kind: "artifact",
329
+ id: receipt.run_id,
330
+ createdAt,
331
+ }).catch(() => {
332
+ logger.warn({ contextRunId: receipt.run_id }, "context run catalog marker write failed");
333
+ });
334
+ }
335
+ let responseReceipt = receipt;
336
+ if (ownsLifecycle) {
337
+ try {
338
+ await lifecycle?.complete(receipt.status, {
339
+ artifactRunId: receipt.run_id,
340
+ counts: lifecycleCountsFromMetrics(receipt.metrics, discovery.search.length),
341
+ coverage: lifecycleCoverage(coverage),
342
+ });
343
+ }
344
+ catch (error) {
345
+ if (!(error instanceof ContextBuilderLifecycleObserverError))
346
+ throw error;
347
+ logger.warn({ contextRunId: lifecycle?.contextRunId }, "context run lifecycle completion unavailable after retry");
348
+ responseReceipt = appendLifecycleCompletionWarning(receipt, internalCaps.max_warnings);
349
+ }
350
+ }
351
+ return createContextBuilderReceiptTextResponse(responseReceipt);
352
+ }
353
+ catch (err) {
354
+ if (storage != null && !committed && storage.ownerNonce != null) {
355
+ await cleanupContextBuilderIncompleteRun(storage, storage.ownerNonce).catch(() => { });
356
+ }
357
+ if (ownsLifecycle && lifecycle != null &&
358
+ !(err instanceof ContextBuilderLifecycleObserverError)) {
359
+ const cancelled = err instanceof CancellationError || options.signal?.aborted === true;
360
+ const delegateFailed = input.delegate.enabled && delegateExecutionActive(lifecycle);
361
+ await lifecycle.fail(cancelled ? "cancelled" : "failed", {
362
+ ...terminalExecutionPatch(lifecycle, cancelled),
363
+ reasonCodes: [
364
+ cancelled
365
+ ? "operation_cancelled"
366
+ : delegateFailed
367
+ ? "delegate_failed"
368
+ : "internal_failure",
369
+ ],
370
+ ...(!cancelled && delegateFailed
371
+ ? {
372
+ fallback: {
373
+ status: "failed",
374
+ reason_code: "delegate_failed",
375
+ },
376
+ }
377
+ : {}),
378
+ });
379
+ }
380
+ throw err;
381
+ }
382
+ finally {
383
+ semaphore.release();
384
+ }
385
+ }
386
+ function lifecycleExecutionConfig(input) {
387
+ if (!input.delegate.enabled) {
388
+ return {
389
+ curator: { configured: false, enabled: false },
390
+ finalizer: { configured: false, enabled: false },
391
+ };
392
+ }
393
+ return {
394
+ curator: { configured: true, enabled: true },
395
+ finalizer: {
396
+ configured: true,
397
+ enabled: contextBuilderFinalizerEnabled(input),
398
+ },
399
+ };
400
+ }
401
+ function lifecycleCountsFromDiscovery(discovery) {
402
+ return {
403
+ files_seen: discovery.metrics.files_seen,
404
+ files_considered: discovery.metrics.files_considered,
405
+ files_selected: 0,
406
+ files_omitted: discovery.omissions.length,
407
+ search_results: discovery.search.length,
408
+ bytes_read: discovery.metrics.bytes_read,
409
+ bytes_written: 0,
410
+ };
411
+ }
412
+ function lifecycleCountsFromMetrics(metrics, searchResults) {
413
+ return {
414
+ files_seen: metrics.files_seen,
415
+ files_considered: metrics.files_considered,
416
+ files_selected: metrics.files_selected,
417
+ files_omitted: metrics.files_omitted,
418
+ search_results: searchResults,
419
+ bytes_read: metrics.bytes_read,
420
+ bytes_written: metrics.bytes_written,
421
+ };
422
+ }
423
+ function lifecycleCoverage(coverage) {
424
+ const required = coverage.required_when_available.length;
425
+ const missing = coverage.missing_required.length;
426
+ return {
427
+ policy: coverage.policy_id,
428
+ status: coverage.status,
429
+ required,
430
+ selected: Math.max(0, required - missing),
431
+ missing,
432
+ };
433
+ }
434
+ function delegateLifecyclePatch(result, lifecycle) {
435
+ if (result == null)
436
+ return {};
437
+ if (result.delegate.fallback_used) {
438
+ if (lifecycle?.snapshot?.phase === "finalizing") {
439
+ return { curatorStatus: "succeeded", finalizerStatus: "failed" };
440
+ }
441
+ return {
442
+ curatorStatus: "failed",
443
+ ...(lifecycle?.snapshot?.finalizer.effective_enabled === true
444
+ ? { finalizerStatus: "skipped" }
445
+ : {}),
446
+ };
447
+ }
448
+ return {
449
+ curatorStatus: "succeeded",
450
+ ...(result.delegate.finalizer_score == null
451
+ ? {}
452
+ : {
453
+ finalizerStatus: "succeeded",
454
+ finalizerScore: result.delegate.finalizer_score,
455
+ }),
456
+ };
457
+ }
458
+ function terminalExecutionPatch(lifecycle, cancelled) {
459
+ const snapshot = lifecycle.snapshot;
460
+ if (snapshot == null)
461
+ return {};
462
+ const terminalStatus = cancelled ? "cancelled" : "failed";
463
+ return {
464
+ ...(snapshot.curator.execution_status === "running"
465
+ ? { curatorStatus: terminalStatus }
466
+ : snapshot.curator.execution_status === "pending"
467
+ ? { curatorStatus: "skipped" }
468
+ : {}),
469
+ ...(snapshot.finalizer.execution_status === "running"
470
+ ? { finalizerStatus: terminalStatus }
471
+ : snapshot.finalizer.execution_status === "pending"
472
+ ? { finalizerStatus: "skipped" }
473
+ : {}),
474
+ };
475
+ }
476
+ function delegateExecutionActive(lifecycle) {
477
+ const snapshot = lifecycle.snapshot;
478
+ return snapshot?.curator.execution_status === "running" ||
479
+ snapshot?.finalizer.execution_status === "running";
480
+ }
481
+ function evidenceClassesByCandidate(candidates) {
482
+ return new Map(candidates.map((candidate) => [
483
+ `${candidate.root_index}\u0000${candidate.path}`,
484
+ candidate.evidence_classes ?? [],
485
+ ]));
486
+ }
487
+ function evaluateSelectionCoverage(input) {
488
+ const classesByCandidate = evidenceClassesByCandidate(input.candidates);
489
+ const selectedClasses = input.selected.flatMap((file) => classesByCandidate.get(`${file.root_index}\u0000${file.path}`) ?? []);
490
+ return evaluateContextCoverage({
491
+ policy: input.policy,
492
+ available: input.candidates.flatMap((candidate) => candidate.evidence_classes ?? []),
493
+ selected: selectedClasses,
494
+ unclassifiable: input.policy.id == null
495
+ ? []
496
+ : ["public_entrypoint", "reverse_consumer"],
497
+ ...(input.obligations == null ? {} : { obligations: input.obligations }),
498
+ selectedRanges: input.selected,
499
+ });
500
+ }
501
+ function delegateCoverageSelectionFailure(selected, candidates, coveragePolicy) {
502
+ try {
503
+ validateContextBuilderDelegateCoverageSelection(selected.map((file) => ({
504
+ candidate_id: contextBuilderCandidateId(file.root_index, file.path),
505
+ ranges: file.ranges,
506
+ })), candidates, coveragePolicy);
507
+ return null;
508
+ }
509
+ catch (error) {
510
+ return error instanceof Error ? error.message : String(error);
511
+ }
512
+ }
513
+ async function rejectArtifactOnlySourceRoots(inputRoots, input) {
514
+ if (artifactOnlyAllowedRoots.size === 0) {
515
+ return;
516
+ }
517
+ const cwd = path.resolve(process.cwd());
518
+ for (const inputRoot of inputRoots) {
519
+ const lexicalRoot = path.resolve(cwd, inputRoot);
520
+ const resolvedRoot = await fsRealpath(lexicalRoot).catch(() => undefined);
521
+ if (resolvedRoot == null) {
522
+ continue;
523
+ }
524
+ for (const artifactRoot of artifactOnlyAllowedRoots) {
525
+ if (input.explicitlyConfiguredArtifactRoots.has(artifactRoot) ||
526
+ !isSamePathOrDescendant(resolvedRoot, artifactRoot)) {
527
+ continue;
528
+ }
529
+ throw new InvalidParamsError(`context_builder root is outside allowed roots: ${inputRoot}`);
530
+ }
531
+ }
532
+ }
533
+ /**
534
+ * Fail-fast admission check: an explicitly-focused path that the caller's own
535
+ * (or the default) globs would prune is a contradictory request. Discovery
536
+ * only surfaces these later, one gate at a time, as fatal runtime omissions,
537
+ * so aggregate EVERY conflict up front into one InvalidParamsError before any
538
+ * scanning or delegate subprocess work begins.
539
+ *
540
+ * The check mirrors discovery's glob semantics exactly: the matcher is
541
+ * compiled from include_globs plus CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS +
542
+ * exclude_globs (discoverContextBuilderCandidates does the same), focus FILES
543
+ * are matched on their own root-relative path (processFile's includes/excludes
544
+ * gates), and focus DIRECTORIES are flagged only when the directory itself is
545
+ * exclude-pruned via the same `<dir>` / `<dir>/__dir__` probes traverseDirectory
546
+ * uses. Ancestor-directory exclusion and directory-vs-include pruning are
547
+ * deliberately NOT modeled here (they depend on traversal state), so anything
548
+ * not flagged falls through to discovery's existing runtime handling.
549
+ */
550
+ function assertNoContextBuilderFocusGlobConflicts(input) {
551
+ const matcher = compileContextBuilderGlobs({
552
+ include: [...input.includeGlobs],
553
+ exclude: [
554
+ ...CONTEXT_BUILDER_DEFAULT_EXCLUDE_GLOBS,
555
+ ...input.excludeGlobs,
556
+ ],
557
+ });
558
+ const conflicts = [];
559
+ const seen = new Set();
560
+ for (const focus of input.focusPaths) {
561
+ const relative = focus.root_relative_path;
562
+ if (relative === "") {
563
+ continue;
564
+ }
565
+ const key = `${focus.root_index}\u0000${relative}\u0000${focus.kind}`;
566
+ if (seen.has(key)) {
567
+ continue;
568
+ }
569
+ seen.add(key);
570
+ if (focus.kind === "directory") {
571
+ if (matcher.excludes(relative) ||
572
+ matcher.excludes(`${relative}/__dir__`)) {
573
+ conflicts.push(`focus directory "${relative}" (root_index ${focus.root_index}) is pruned by exclude_globs`);
574
+ }
575
+ continue;
576
+ }
577
+ if (matcher.excludes(relative)) {
578
+ conflicts.push(`focus path "${relative}" (root_index ${focus.root_index}) is omitted by exclude_globs`);
579
+ continue;
580
+ }
581
+ if (!matcher.includes(relative)) {
582
+ conflicts.push(`focus path "${relative}" (root_index ${focus.root_index}) is outside include_globs`);
583
+ }
584
+ }
585
+ if (conflicts.length > 0) {
586
+ throw new InvalidParamsError(`context_builder focus_paths conflict with the effective include/exclude globs (${conflicts.length} conflict${conflicts.length === 1 ? "" : "s"}): ${conflicts.join("; ")}`);
587
+ }
588
+ }
589
+ function getExactlyConfiguredArtifactRoots() {
590
+ const rawEntries = rawAllowedRootEntries();
591
+ const configured = new Set();
592
+ const cwd = path.resolve(process.cwd());
593
+ for (const artifactRoot of artifactOnlyAllowedRoots) {
594
+ for (const rawEntry of rawEntries) {
595
+ if (isArtifactOnlyAllowedRootEntry(rawEntry) &&
596
+ realpathSyncOrUndefined(path.resolve(cwd, stripArtifactOnlyMarker(rawEntry))) === artifactRoot) {
597
+ continue;
598
+ }
599
+ const resolvedEntry = realpathSyncOrUndefined(path.resolve(cwd, rawEntry));
600
+ if (resolvedEntry === artifactRoot) {
601
+ configured.add(artifactRoot);
602
+ break;
603
+ }
604
+ }
605
+ }
606
+ return configured;
607
+ }
608
+ function appendArtifactOnlyRootToEnv(runsDir) {
609
+ appendArtifactOnlyRootsToEnv([runsDir], { requireExisting: false });
610
+ }
611
+ function rawAllowedRootEntries() {
612
+ const raw = process.env["CLINKX_ALLOWED_ROOTS"];
613
+ if (raw == null || raw.trim() === "") {
614
+ return [];
615
+ }
616
+ return raw
617
+ .split(",")
618
+ .map((root) => root.trim())
619
+ .filter((root) => root.length > 0);
620
+ }
621
+ function realpathSyncOrUndefined(input) {
622
+ try {
623
+ return realpathSync(input);
624
+ }
625
+ catch {
626
+ return undefined;
627
+ }
628
+ }
629
+ function getDefaultContextBuilderSemaphore() {
630
+ return getSharedContextBuilderSemaphore();
631
+ }
632
+ function getDefaultAdapterRegistry() {
633
+ if (defaultAdapterRegistry == null) {
634
+ defaultAdapterRegistry = RuntimeAdapterRegistry.load();
635
+ }
636
+ return defaultAdapterRegistry;
637
+ }
638
+ function resolveEffectiveDelegatePacketCaps(profile, explicitCaps) {
639
+ const profileCaps = profile == null
640
+ ? undefined
641
+ : CONTEXT_BUILDER_DELEGATE_PACKET_CAP_PROFILE_DEFAULTS[profile];
642
+ if (profileCaps == null && explicitCaps == null) {
643
+ return undefined;
644
+ }
645
+ return {
646
+ ...(profileCaps ?? {}),
647
+ ...(explicitCaps ?? {}),
648
+ };
649
+ }
650
+ function contextBuilderFinalizerEnabled(input) {
651
+ if (!input.delegate.enabled) {
652
+ return false;
653
+ }
654
+ const explicit = input.delegate.finalizer.enabled;
655
+ if (explicit != null) {
656
+ return explicit;
657
+ }
658
+ if (input.profile != null) {
659
+ return CONTEXT_BUILDER_INTENT_PROFILE_DEFAULTS[input.profile]
660
+ .finalizer_enabled;
661
+ }
662
+ return true;
663
+ }
664
+ function delegateFinalizerReceiptMetadata(finalizer) {
665
+ return {
666
+ cli_name: finalizer.cli_name,
667
+ role: finalizer.role,
668
+ min_score: finalizer.min_score,
669
+ };
670
+ }
671
+ async function resolveDelegateSelection(input) {
672
+ const delegate = input.input.delegate;
673
+ if (!delegate.enabled) {
674
+ throw new ExecutionError("context_builder delegate orchestration requires enabled delegate config");
675
+ }
676
+ const finalizerEnabled = contextBuilderFinalizerEnabled(input.input);
677
+ const delegatePacketInput = {
678
+ task: input.input.task,
679
+ allowSecrets: input.input.allow_secrets,
680
+ discovery: {
681
+ tree: input.discovery.tree,
682
+ content_candidates: input.discovery.content_candidates,
683
+ search: input.discovery.search,
684
+ slices: input.discovery.slices,
685
+ omissions: [...input.baselineOmissions, ...input.discovery.omissions],
686
+ metrics: input.discovery.metrics,
687
+ ...(input.discovery.review_obligations == null
688
+ ? {}
689
+ : { review_obligations: input.discovery.review_obligations }),
690
+ },
691
+ selection: input.deterministicSelection,
692
+ budget: input.input.budget,
693
+ focus_paths: input.focusPaths,
694
+ caller_search_terms: input.input.search_terms,
695
+ coveragePolicy: input.coveragePolicy,
696
+ ...(input.delegatePacketCaps == null
697
+ ? {}
698
+ : { caps: input.delegatePacketCaps }),
699
+ };
700
+ const candidateFallbackCount = input.deterministicSelection.selected.length;
701
+ let curatorMs = null;
702
+ let finalizerMs = null;
703
+ let delegatePacketSize = 0;
704
+ // Compaction counters from the last-built packet (finalizer overrides
705
+ // curator) and bounded T3 evidence warnings, surfaced additively.
706
+ let delegatePacketCompaction;
707
+ const packetEvidenceWarnings = [];
708
+ const recordPacketEvidenceWarning = (warning) => {
709
+ if (warning == null ||
710
+ packetEvidenceWarnings.some((existing) => existing.code === warning.code)) {
711
+ return;
712
+ }
713
+ packetEvidenceWarnings.push({
714
+ code: warning.code,
715
+ message: warning.message,
716
+ });
717
+ };
718
+ const currentDelegateObservability = () => ({
719
+ curator_ms: curatorMs,
720
+ finalizer_ms: finalizerMs,
721
+ delegate_packet_size: delegatePacketSize,
722
+ ...(delegatePacketCompaction == null
723
+ ? {}
724
+ : { delegate_packet_compaction: delegatePacketCompaction }),
725
+ });
726
+ const sessionLogOwner = (executor) => input.sessionLogContext == null
727
+ ? undefined
728
+ : {
729
+ family: "context",
730
+ contextRunId: input.sessionLogContext.contextRunId,
731
+ contextGeneration: input.sessionLogContext.contextGeneration,
732
+ executor,
733
+ executionId: `${executor}_${input.sessionLogContext.contextRunId.slice(-32)}`,
734
+ attempt: 1,
735
+ };
736
+ const requestedFocusOmissions = input.discovery.omissions.filter(isRequestedEvidenceOmission);
737
+ if (requestedFocusOmissions.length > 0) {
738
+ return handleDelegateRuntimeFailure({
739
+ delegate,
740
+ reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(requestedFocusOmissions)}`,
741
+ deterministicSelection: input.deterministicSelection,
742
+ omissionCount: input.baselineOmissions.length +
743
+ input.discovery.omissions.length +
744
+ input.deterministicSelection.omissions.length,
745
+ candidateFallbackCount,
746
+ observability: currentDelegateObservability(),
747
+ });
748
+ }
749
+ await input.lifecycle?.transition("curating", { curatorStatus: "running" });
750
+ const curatorPacket = buildContextBuilderCuratorPacket(delegatePacketInput);
751
+ delegatePacketSize = curatorPacket.metrics.packet_estimated_tokens;
752
+ delegatePacketCompaction = curatorPacket.metrics.compaction;
753
+ recordPacketEvidenceWarning(curatorPacket.evidence_warning);
754
+ const curatorPacketRequestedOmissions = curatorPacket.omissions.filter(isRequestedEvidenceOmission);
755
+ if (curatorPacketRequestedOmissions.length > 0) {
756
+ return handleDelegateRuntimeFailure({
757
+ delegate,
758
+ reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(curatorPacketRequestedOmissions)}`,
759
+ deterministicSelection: input.deterministicSelection,
760
+ omissions: curatorPacketRequestedOmissions,
761
+ omissionCount: input.baselineOmissions.length +
762
+ input.discovery.omissions.length +
763
+ input.deterministicSelection.omissions.length +
764
+ curatorPacketRequestedOmissions.length,
765
+ candidateFallbackCount,
766
+ observability: currentDelegateObservability(),
767
+ });
768
+ }
769
+ if (curatorPacket.candidates.length === 0) {
770
+ return handleDelegateRuntimeFailure({
771
+ delegate,
772
+ reason: `curator packet contained no selectable candidates; ${delegatePromptDiagnostic(curatorPacket)}`,
773
+ deterministicSelection: input.deterministicSelection,
774
+ omissionCount: input.baselineOmissions.length +
775
+ input.discovery.omissions.length +
776
+ input.deterministicSelection.omissions.length,
777
+ candidateFallbackCount,
778
+ observability: currentDelegateObservability(),
779
+ });
780
+ }
781
+ const curatorStartMs = Date.now();
782
+ const curatorSessionLogOwner = sessionLogOwner("curator");
783
+ const curatorCall = await runContextBuilderDelegateCall({
784
+ prompt: renderDelegatePrompt("curator", curatorPacket),
785
+ provider: delegate.curator,
786
+ registry: input.registry,
787
+ timeout_seconds: delegate.timeout_seconds,
788
+ max_response_chars: delegate.max_response_chars,
789
+ ...(input.signal == null ? {} : { signal: input.signal }),
790
+ ...(curatorSessionLogOwner == null
791
+ ? {}
792
+ : { sessionLogOwner: curatorSessionLogOwner }),
793
+ });
794
+ curatorMs = Math.max(0, Date.now() - curatorStartMs);
795
+ if (!curatorCall.ok) {
796
+ return handleDelegateRuntimeFailure({
797
+ delegate,
798
+ reason: failureReasonFromCall("curator", curatorCall),
799
+ failureKind: curatorCall.reason,
800
+ deterministicSelection: input.deterministicSelection,
801
+ omissionCount: input.baselineOmissions.length +
802
+ input.discovery.omissions.length +
803
+ input.deterministicSelection.omissions.length,
804
+ candidateFallbackCount,
805
+ observability: currentDelegateObservability(),
806
+ });
807
+ }
808
+ let curatorProposal;
809
+ let curatorSelection;
810
+ try {
811
+ const validatedCuratorProposal = validateContextBuilderCuratorOutput(curatorCall.json, {
812
+ candidates: curatorPacket.candidates,
813
+ budget: input.input.budget,
814
+ allowSecrets: input.input.allow_secrets,
815
+ coveragePolicy: curatorPacket.coverage_policy,
816
+ });
817
+ curatorProposal = augmentCuratorProposalWithDeterministicBaseline(validatedCuratorProposal, curatorPacket, input.input.budget.max_files);
818
+ curatorSelection = reconstructContextBuilderDelegateSelection({
819
+ selected: curatorProposal.selected,
820
+ candidates: curatorPacket.candidates,
821
+ slices: input.discovery.slices,
822
+ tree: input.discovery.tree,
823
+ search: input.discovery.search,
824
+ budget: input.input.budget,
825
+ });
826
+ if (curatorSelection.omissions.length > 0 &&
827
+ curatorProposal !== validatedCuratorProposal) {
828
+ const rawCuratorSelection = reconstructContextBuilderDelegateSelection({
829
+ selected: validatedCuratorProposal.selected,
830
+ candidates: curatorPacket.candidates,
831
+ slices: input.discovery.slices,
832
+ tree: input.discovery.tree,
833
+ search: input.discovery.search,
834
+ budget: input.input.budget,
835
+ });
836
+ if (rawCuratorSelection.selected.length > 0 &&
837
+ rawCuratorSelection.omissions.length === 0) {
838
+ curatorProposal = {
839
+ ...validatedCuratorProposal,
840
+ warnings: [
841
+ ...validatedCuratorProposal.warnings,
842
+ "server-retained deterministic baseline exceeded local reconstruction budget; using curator-selected evidence only",
843
+ ],
844
+ };
845
+ curatorSelection = rawCuratorSelection;
846
+ }
847
+ }
848
+ if (curatorSelection.selected.length > 0 &&
849
+ curatorSelection.omissions.length > 0) {
850
+ curatorProposal = pruneDelegateProposalToLocalSelection(curatorProposal, curatorPacket.candidates, curatorSelection, "curator selection exceeded local reconstruction budget; using budget-pruned selected evidence only");
851
+ }
852
+ }
853
+ catch (err) {
854
+ if (err instanceof CancellationError) {
855
+ throw err;
856
+ }
857
+ return handleDelegateRuntimeFailure({
858
+ delegate,
859
+ reason: failureReasonFromError("curator_validation", err, delegatePromptDiagnostic(curatorPacket)),
860
+ deterministicSelection: input.deterministicSelection,
861
+ omissionCount: input.baselineOmissions.length +
862
+ input.discovery.omissions.length +
863
+ input.deterministicSelection.omissions.length,
864
+ candidateFallbackCount,
865
+ observability: currentDelegateObservability(),
866
+ });
867
+ }
868
+ if (curatorSelection.selected.length === 0) {
869
+ return handleDelegateRuntimeFailure({
870
+ delegate,
871
+ reason: "curator produced empty local selection",
872
+ deterministicSelection: input.deterministicSelection,
873
+ omissionCount: input.baselineOmissions.length +
874
+ input.discovery.omissions.length +
875
+ input.deterministicSelection.omissions.length,
876
+ candidateFallbackCount,
877
+ observability: currentDelegateObservability(),
878
+ });
879
+ }
880
+ if (!finalizerEnabled) {
881
+ const repairedSelection = repairDelegateSelection({
882
+ curatorSelection,
883
+ contentCandidates: input.discovery.content_candidates,
884
+ focusPaths: input.focusPaths,
885
+ budget: input.input.budget,
886
+ expandSelectedFiles: true,
887
+ });
888
+ const warnings = curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message));
889
+ warnings.push(...packetEvidenceWarnings);
890
+ appendDelegateRepairWarnings(warnings, repairedSelection);
891
+ const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
892
+ const omissionCount = input.baselineOmissions.length +
893
+ input.discovery.omissions.length +
894
+ repairedSelection.selection.omissions.length;
895
+ const delegateEvidenceCoverageFailure = delegateCoverageSelectionFailure(restoreDroppedDegenerateReviewTargetsForValidation({
896
+ preRepairSelected: curatorSelection.selected,
897
+ postRepairSelected: repairedSelection.selection.selected,
898
+ candidates: curatorPacket.candidates,
899
+ coveragePolicy: curatorPacket.coverage_policy,
900
+ }), curatorPacket.candidates, curatorPacket.coverage_policy);
901
+ if (delegateEvidenceCoverageFailure != null) {
902
+ return handleDelegateRuntimeFailure({
903
+ delegate,
904
+ reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
905
+ deterministicSelection: input.deterministicSelection,
906
+ omissionCount,
907
+ candidateFallbackCount,
908
+ observability: currentDelegateObservability(),
909
+ });
910
+ }
911
+ // Re-verify required-focus presence against the actual returned selection.
912
+ // The curator-only branch does not fold required-focus ranges, so it checks
913
+ // whole-file presence here; range coverage is re-verified only in the
914
+ // finalizer branch, which folds requested focus evidence first.
915
+ const curatorOnlyCoverageFailure = postRepairRequiredFocusEvictionFailure({
916
+ focusPaths: input.focusPaths,
917
+ candidates: curatorPacket.candidates,
918
+ preRepairSelected: curatorSelection.selected,
919
+ postRepairSelected: repairedSelection.selection.selected,
920
+ });
921
+ if (curatorOnlyCoverageFailure != null) {
922
+ return handleDelegateRuntimeFailure({
923
+ delegate,
924
+ reason: `post-repair required-focus coverage failed: ${curatorOnlyCoverageFailure}`,
925
+ deterministicSelection: input.deterministicSelection,
926
+ omissionCount: input.baselineOmissions.length +
927
+ input.discovery.omissions.length +
928
+ input.deterministicSelection.omissions.length,
929
+ candidateFallbackCount,
930
+ observability: currentDelegateObservability(),
931
+ });
932
+ }
933
+ return {
934
+ selection: repairedSelection.selection,
935
+ warnings,
936
+ omissions: [],
937
+ openQuestions,
938
+ delegate: {
939
+ enabled: true,
940
+ status: "succeeded",
941
+ failure_policy: delegate.failure_policy,
942
+ fallback_used: false,
943
+ curator: { ...delegate.curator },
944
+ finalizer: delegateFinalizerReceiptMetadata(delegate.finalizer),
945
+ selected_count: repairedSelection.selection.selected.length,
946
+ warning_count: warnings.length,
947
+ omission_count: omissionCount,
948
+ },
949
+ observability: currentDelegateObservability(),
950
+ };
951
+ }
952
+ await input.lifecycle?.transition("finalizing", {
953
+ curatorStatus: "succeeded",
954
+ finalizerStatus: "running",
955
+ });
956
+ const finalizerPacket = buildContextBuilderFinalizerPacket({
957
+ ...delegatePacketInput,
958
+ selection: curatorSelection,
959
+ curator_proposal: curatorProposal,
960
+ });
961
+ delegatePacketSize = finalizerPacket.metrics.packet_estimated_tokens;
962
+ delegatePacketCompaction = finalizerPacket.metrics.compaction;
963
+ recordPacketEvidenceWarning(finalizerPacket.evidence_warning);
964
+ const finalizerPacketRequestedOmissions = finalizerPacket.omissions.filter(isRequestedEvidenceOmission);
965
+ if (finalizerPacketRequestedOmissions.length > 0) {
966
+ return handleDelegateRuntimeFailure({
967
+ delegate,
968
+ reason: `requested local evidence omitted: ${formatOmissionsForDelegateFailure(finalizerPacketRequestedOmissions)}`,
969
+ deterministicSelection: input.deterministicSelection,
970
+ omissions: finalizerPacketRequestedOmissions,
971
+ omissionCount: input.baselineOmissions.length +
972
+ input.discovery.omissions.length +
973
+ input.deterministicSelection.omissions.length +
974
+ finalizerPacketRequestedOmissions.length,
975
+ candidateFallbackCount,
976
+ observability: currentDelegateObservability(),
977
+ });
978
+ }
979
+ if ((finalizerPacket.curator_proposal?.selected.length ?? 0) === 0) {
980
+ return handleDelegateRuntimeFailure({
981
+ delegate,
982
+ reason: `finalizer packet lost curator proposal selection; ${delegatePromptDiagnostic(finalizerPacket)}`,
983
+ deterministicSelection: input.deterministicSelection,
984
+ omissionCount: input.baselineOmissions.length +
985
+ input.discovery.omissions.length +
986
+ input.deterministicSelection.omissions.length,
987
+ candidateFallbackCount,
988
+ observability: currentDelegateObservability(),
989
+ });
990
+ }
991
+ const finalizerStartMs = Date.now();
992
+ const finalizerSessionLogOwner = sessionLogOwner("finalizer");
993
+ const finalizerCall = await runContextBuilderDelegateCall({
994
+ prompt: renderDelegatePrompt("finalizer", finalizerPacket, {
995
+ minScore: delegate.finalizer.min_score,
996
+ }),
997
+ provider: delegate.finalizer,
998
+ registry: input.registry,
999
+ timeout_seconds: delegate.timeout_seconds,
1000
+ max_response_chars: delegate.max_response_chars,
1001
+ ...(input.signal == null ? {} : { signal: input.signal }),
1002
+ ...(finalizerSessionLogOwner == null
1003
+ ? {}
1004
+ : { sessionLogOwner: finalizerSessionLogOwner }),
1005
+ });
1006
+ finalizerMs = Math.max(0, Date.now() - finalizerStartMs);
1007
+ if (!finalizerCall.ok) {
1008
+ return handleDelegateRuntimeFailure({
1009
+ delegate,
1010
+ reason: failureReasonFromCall("finalizer", finalizerCall),
1011
+ failureKind: finalizerCall.reason,
1012
+ deterministicSelection: input.deterministicSelection,
1013
+ omissionCount: input.baselineOmissions.length +
1014
+ input.discovery.omissions.length +
1015
+ input.deterministicSelection.omissions.length,
1016
+ candidateFallbackCount,
1017
+ observability: currentDelegateObservability(),
1018
+ });
1019
+ }
1020
+ try {
1021
+ const finalizerProposal = validateContextBuilderFinalizerOutput(finalizerCall.json, {
1022
+ candidates: finalizerPacket.candidates,
1023
+ budget: input.input.budget,
1024
+ minScore: delegate.finalizer.min_score,
1025
+ allowSecrets: input.input.allow_secrets,
1026
+ coveragePolicy: finalizerPacket.coverage_policy,
1027
+ });
1028
+ if (finalizerProposal.omissions.length > 0) {
1029
+ return handleDelegateRuntimeFailure({
1030
+ delegate,
1031
+ reason: `finalizer reported omissions: ${finalizerProposal.omissions.join("; ")}`,
1032
+ deterministicSelection: input.deterministicSelection,
1033
+ omissionCount: input.baselineOmissions.length +
1034
+ input.discovery.omissions.length +
1035
+ input.deterministicSelection.omissions.length +
1036
+ finalizerProposal.omissions.length,
1037
+ candidateFallbackCount,
1038
+ observability: currentDelegateObservability(),
1039
+ });
1040
+ }
1041
+ const foldedFinalizerSelection = foldRequiredFocusEvidenceIntoSelection(finalizerProposal.selected, requiredFocusEvidenceSelections({
1042
+ focusPaths: input.focusPaths,
1043
+ candidates: finalizerPacket.candidates,
1044
+ }));
1045
+ const finalSelection = reconstructContextBuilderDelegateSelection({
1046
+ selected: foldedFinalizerSelection.selected,
1047
+ candidates: finalizerPacket.candidates,
1048
+ slices: input.discovery.slices,
1049
+ tree: input.discovery.tree,
1050
+ search: input.discovery.search,
1051
+ budget: input.input.budget,
1052
+ });
1053
+ if (finalSelection.selected.length === 0) {
1054
+ return handleDelegateRuntimeFailure({
1055
+ delegate,
1056
+ reason: "finalizer produced empty local selection",
1057
+ deterministicSelection: input.deterministicSelection,
1058
+ omissionCount: input.baselineOmissions.length +
1059
+ input.discovery.omissions.length +
1060
+ input.deterministicSelection.omissions.length,
1061
+ candidateFallbackCount,
1062
+ observability: currentDelegateObservability(),
1063
+ });
1064
+ }
1065
+ // A per-file reconstruction-budget overrun no longer fails the entire
1066
+ // delegate selection. Over-budget candidates (e.g. large generated/i18n
1067
+ // catalogs the finalizer over-selected) are dropped while the rest of the
1068
+ // curated bundle is retained. Completeness is re-checked against the
1069
+ // MATERIALIZED (post-budget) selection, so a dropped REQUIRED-FOCUS file
1070
+ // still fails closed below, while non-required oversized files are pruned
1071
+ // and surfaced as a warning further down rather than collapsing the bundle.
1072
+ // Required-focus coverage is additionally re-verified against the
1073
+ // post-repair selection below, so eviction/compaction during repair also
1074
+ // fails closed.
1075
+ const completenessFailure = delegateCompletenessFailure({
1076
+ focusPaths: input.focusPaths,
1077
+ candidates: finalizerPacket.candidates,
1078
+ finalizerSelected: materializeFinalizerSelection(finalSelection.selected, finalizerPacket.candidates),
1079
+ curatorWarnings: curatorProposal.warnings,
1080
+ finalizerWarnings: finalizerProposal.warnings,
1081
+ });
1082
+ if (completenessFailure != null) {
1083
+ return handleDelegateRuntimeFailure({
1084
+ delegate,
1085
+ reason: completenessFailure,
1086
+ deterministicSelection: input.deterministicSelection,
1087
+ omissionCount: input.baselineOmissions.length +
1088
+ input.discovery.omissions.length +
1089
+ input.deterministicSelection.omissions.length,
1090
+ candidateFallbackCount,
1091
+ observability: currentDelegateObservability(),
1092
+ });
1093
+ }
1094
+ const repairedSelection = repairDelegateSelection({
1095
+ curatorSelection: finalSelection,
1096
+ contentCandidates: input.discovery.content_candidates,
1097
+ focusPaths: input.focusPaths,
1098
+ budget: input.input.budget,
1099
+ expandSelectedFiles: false,
1100
+ });
1101
+ const delegateEvidenceCoverageFailure = delegateCoverageSelectionFailure(repairedSelection.selection.selected, finalizerPacket.candidates, finalizerPacket.coverage_policy);
1102
+ if (delegateEvidenceCoverageFailure != null) {
1103
+ return handleDelegateRuntimeFailure({
1104
+ delegate,
1105
+ reason: `post-repair evidence coverage failed: ${delegateEvidenceCoverageFailure}`,
1106
+ deterministicSelection: input.deterministicSelection,
1107
+ omissionCount: input.baselineOmissions.length +
1108
+ input.discovery.omissions.length +
1109
+ repairedSelection.selection.omissions.length,
1110
+ candidateFallbackCount,
1111
+ observability: currentDelegateObservability(),
1112
+ });
1113
+ }
1114
+ // Re-verify required-focus completeness against the actual returned
1115
+ // selection. This keeps the fallback contract tied to the final materialized
1116
+ // packet rather than the delegate's requested selection.
1117
+ const postRepairCoverageFailure = requiredFocusCoverageFailure({
1118
+ focusPaths: input.focusPaths,
1119
+ candidates: finalizerPacket.candidates,
1120
+ finalizerSelected: materializeFinalizerSelection(repairedSelection.selection.selected, finalizerPacket.candidates),
1121
+ });
1122
+ if (postRepairCoverageFailure != null) {
1123
+ return handleDelegateRuntimeFailure({
1124
+ delegate,
1125
+ reason: `post-repair required-focus coverage failed: ${postRepairCoverageFailure}`,
1126
+ deterministicSelection: input.deterministicSelection,
1127
+ omissionCount: input.baselineOmissions.length +
1128
+ input.discovery.omissions.length +
1129
+ input.deterministicSelection.omissions.length,
1130
+ candidateFallbackCount,
1131
+ observability: currentDelegateObservability(),
1132
+ });
1133
+ }
1134
+ const warnings = [
1135
+ ...curatorProposal.warnings.map((message) => delegateWarning("delegate_curator_warning", message)),
1136
+ ...finalizerProposal.warnings.map((message) => delegateWarning("delegate_finalizer_warning", message)),
1137
+ ];
1138
+ if (foldedFinalizerSelection.foldedPaths.length > 0) {
1139
+ warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer omitted requested local evidence; server folded it back into the selection: ${boundContextBuilderPathList(foldedFinalizerSelection.foldedPaths).join(", ")}`));
1140
+ }
1141
+ if (finalSelection.omissions.length > 0) {
1142
+ warnings.push(delegateWarning("delegate_finalizer_warning", `finalizer selection pruned to fit the local reconstruction budget; omitted: ${finalSelection.omissions
1143
+ .map(formatOmissionForDelegateFailure)
1144
+ .join("; ")}`));
1145
+ }
1146
+ warnings.push(...packetEvidenceWarnings);
1147
+ appendDelegateRepairWarnings(warnings, repairedSelection);
1148
+ const openQuestions = curatorProposal.open_questions.map((question) => sanitizeDiagnosticText(question));
1149
+ const omissionCount = input.baselineOmissions.length +
1150
+ input.discovery.omissions.length +
1151
+ repairedSelection.selection.omissions.length;
1152
+ return {
1153
+ selection: repairedSelection.selection,
1154
+ warnings,
1155
+ omissions: [],
1156
+ openQuestions,
1157
+ delegate: {
1158
+ enabled: true,
1159
+ status: "succeeded",
1160
+ failure_policy: delegate.failure_policy,
1161
+ fallback_used: false,
1162
+ curator: { ...delegate.curator },
1163
+ finalizer: delegateFinalizerReceiptMetadata(delegate.finalizer),
1164
+ finalizer_score: finalizerProposal.score,
1165
+ selected_count: repairedSelection.selection.selected.length,
1166
+ warning_count: warnings.length,
1167
+ omission_count: omissionCount,
1168
+ },
1169
+ observability: currentDelegateObservability(),
1170
+ };
1171
+ }
1172
+ catch (err) {
1173
+ if (err instanceof CancellationError) {
1174
+ throw err;
1175
+ }
1176
+ if (err instanceof ExecutionError) {
1177
+ throw err;
1178
+ }
1179
+ return handleDelegateRuntimeFailure({
1180
+ delegate,
1181
+ reason: failureReasonFromError("finalizer_validation", err, `${delegatePromptDiagnostic(finalizerPacket)}${finalizerScoreDiagnostic(finalizerCall.json, delegate.finalizer.min_score)}`),
1182
+ deterministicSelection: input.deterministicSelection,
1183
+ omissionCount: input.baselineOmissions.length +
1184
+ input.discovery.omissions.length +
1185
+ input.deterministicSelection.omissions.length,
1186
+ candidateFallbackCount,
1187
+ observability: currentDelegateObservability(),
1188
+ });
1189
+ }
1190
+ }
1191
+ function restoreDroppedDegenerateReviewTargetsForValidation(input) {
1192
+ const targetKeys = new Set((input.coveragePolicy.obligations?.targets ?? []).flatMap((target) => {
1193
+ const candidate = input.candidates.find((item) => item.id === target.candidate_id);
1194
+ return candidate == null
1195
+ ? []
1196
+ : [`${candidate.root_index}\u0000${candidate.path}`];
1197
+ }));
1198
+ if (targetKeys.size === 0) {
1199
+ return [...input.postRepairSelected];
1200
+ }
1201
+ const postRepairKeys = new Set(input.postRepairSelected.map((file) => selectedFileKey(file)));
1202
+ const droppedDegenerateTargets = input.preRepairSelected.filter((file) => {
1203
+ const key = selectedFileKey(file);
1204
+ return (targetKeys.has(key) &&
1205
+ !postRepairKeys.has(key) &&
1206
+ file.content.trim().length === 0);
1207
+ });
1208
+ return [...input.postRepairSelected, ...droppedDegenerateTargets];
1209
+ }
1210
+ function repairDelegateSelection(input) {
1211
+ const focusExpanded = input.expandSelectedFiles
1212
+ ? expandSelectedFocusFilesToFullCandidates(input.curatorSelection, input.contentCandidates, input.focusPaths, input.budget)
1213
+ : { selection: input.curatorSelection, expandedCount: 0 };
1214
+ const smallSlicesExpanded = input.expandSelectedFiles
1215
+ ? expandSelectedSmallFilesToFullCandidates(focusExpanded.selection, input.contentCandidates, input.focusPaths, input.budget)
1216
+ : focusExpanded;
1217
+ const emptyDropped = dropEmptySelectedFiles(smallSlicesExpanded.selection);
1218
+ return {
1219
+ selection: emptyDropped.selection,
1220
+ expandedCount: focusExpanded.expandedCount + smallSlicesExpanded.expandedCount,
1221
+ droppedDegenerateCount: emptyDropped.droppedCount,
1222
+ };
1223
+ }
1224
+ function appendDelegateRepairWarnings(warnings, repair) {
1225
+ if (repair.expandedCount > 0) {
1226
+ warnings.push(delegateWarning("delegate_curator_warning", `server-expanded ${repair.expandedCount} selected file(s) to full context`));
1227
+ }
1228
+ if (repair.droppedDegenerateCount > 0) {
1229
+ warnings.push(delegateWarning("delegate_curator_warning", `server-dropped ${repair.droppedDegenerateCount} degenerate selected slice(s)`));
1230
+ }
1231
+ }
1232
+ function expandSelectedFocusFilesToFullCandidates(selection, candidates, focusPaths, budget) {
1233
+ const focusKeys = new Set(focusPaths
1234
+ .filter((focus) => focus.kind === "file")
1235
+ .map((focus) => `${focus.root_index}\u0000${focus.root_relative_path}`));
1236
+ if (focusKeys.size === 0) {
1237
+ return { selection, expandedCount: 0 };
1238
+ }
1239
+ const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
1240
+ let totalBytes = selection.metrics.bytes_selected;
1241
+ let totalTokens = selection.metrics.estimated_tokens;
1242
+ let expandedCount = 0;
1243
+ const selected = selection.selected.map((file) => {
1244
+ const key = selectedFileKey(file);
1245
+ if (!focusKeys.has(key)) {
1246
+ return file;
1247
+ }
1248
+ if (file.ranges.length !== 1) {
1249
+ return file;
1250
+ }
1251
+ const candidate = candidatesByKey.get(key);
1252
+ if (candidate == null) {
1253
+ return file;
1254
+ }
1255
+ const fullFile = selectedFileFromCandidate(candidate);
1256
+ if (fullFile.bytes <= file.bytes || fileCoversCandidate(file, candidate)) {
1257
+ return file;
1258
+ }
1259
+ const totalsWithoutFile = {
1260
+ totalBytes: totalBytes - file.bytes,
1261
+ totalTokens: totalTokens - file.estimated_tokens,
1262
+ };
1263
+ if (fullFile.bytes > budget.max_bytes_per_file ||
1264
+ totalsWithoutFile.totalBytes + fullFile.bytes > budget.max_total_bytes ||
1265
+ totalsWithoutFile.totalTokens + fullFile.estimated_tokens >
1266
+ budget.target_estimated_tokens) {
1267
+ return file;
1268
+ }
1269
+ totalBytes = totalsWithoutFile.totalBytes + fullFile.bytes;
1270
+ totalTokens = totalsWithoutFile.totalTokens + fullFile.estimated_tokens;
1271
+ expandedCount += 1;
1272
+ return { ...fullFile, source: file.source };
1273
+ });
1274
+ if (expandedCount === 0) {
1275
+ return { selection, expandedCount };
1276
+ }
1277
+ return {
1278
+ expandedCount,
1279
+ selection: {
1280
+ ...selection,
1281
+ selected,
1282
+ metrics: {
1283
+ files_selected: selected.length,
1284
+ files_omitted: selection.metrics.files_omitted,
1285
+ bytes_selected: totalBytes,
1286
+ estimated_tokens: totalTokens,
1287
+ truncated: selection.metrics.truncated,
1288
+ },
1289
+ },
1290
+ };
1291
+ }
1292
+ const SMALL_SELECTED_SLICE_FULL_EXPANSION_MAX_BYTES = 64_000;
1293
+ function expandSelectedSmallFilesToFullCandidates(selection, candidates, focusPaths, budget) {
1294
+ const candidatesByKey = new Map(candidates.map((candidate) => [selectedFileKey(candidate), candidate]));
1295
+ const focusKeys = new Set(focusPaths
1296
+ .filter((focus) => focus.kind === "file")
1297
+ .map((focus) => `${focus.root_index}\u0000${focus.root_relative_path}`));
1298
+ let totalBytes = selection.metrics.bytes_selected;
1299
+ let totalTokens = selection.metrics.estimated_tokens;
1300
+ let expandedCount = 0;
1301
+ const selected = selection.selected.map((file) => {
1302
+ if (focusKeys.has(selectedFileKey(file)) || file.ranges.length !== 1) {
1303
+ return file;
1304
+ }
1305
+ const candidate = candidatesByKey.get(selectedFileKey(file));
1306
+ if (candidate == null || fileCoversCandidate(file, candidate)) {
1307
+ return file;
1308
+ }
1309
+ if (candidate.byte_length > SMALL_SELECTED_SLICE_FULL_EXPANSION_MAX_BYTES) {
1310
+ return file;
1311
+ }
1312
+ const fullFile = selectedFileFromCandidate(candidate);
1313
+ if (fullFile.bytes <= file.bytes) {
1314
+ return file;
1315
+ }
1316
+ const totalsWithoutFile = {
1317
+ totalBytes: totalBytes - file.bytes,
1318
+ totalTokens: totalTokens - file.estimated_tokens,
1319
+ };
1320
+ if (fullFile.bytes > budget.max_bytes_per_file ||
1321
+ totalsWithoutFile.totalBytes + fullFile.bytes > budget.max_total_bytes ||
1322
+ totalsWithoutFile.totalTokens + fullFile.estimated_tokens >
1323
+ budget.target_estimated_tokens) {
1324
+ return file;
1325
+ }
1326
+ totalBytes = totalsWithoutFile.totalBytes + fullFile.bytes;
1327
+ totalTokens = totalsWithoutFile.totalTokens + fullFile.estimated_tokens;
1328
+ expandedCount += 1;
1329
+ return { ...fullFile, source: file.source };
1330
+ });
1331
+ if (expandedCount === 0) {
1332
+ return { selection, expandedCount };
1333
+ }
1334
+ return {
1335
+ expandedCount,
1336
+ selection: {
1337
+ ...selection,
1338
+ selected,
1339
+ metrics: {
1340
+ files_selected: selected.length,
1341
+ files_omitted: selection.metrics.files_omitted,
1342
+ bytes_selected: totalBytes,
1343
+ estimated_tokens: totalTokens,
1344
+ truncated: selection.metrics.truncated,
1345
+ },
1346
+ },
1347
+ };
1348
+ }
1349
+ function dropEmptySelectedFiles(selection) {
1350
+ const selected = selection.selected.filter((file) => file.content.trim().length > 0);
1351
+ const droppedCount = selection.selected.length - selected.length;
1352
+ if (droppedCount === 0) {
1353
+ return { selection, droppedCount };
1354
+ }
1355
+ return {
1356
+ selection: selectionWithSelectedFiles(selection, selected),
1357
+ droppedCount,
1358
+ };
1359
+ }
1360
+ function selectionWithSelectedFiles(selection, selected) {
1361
+ const bytes = selected.reduce((total, file) => total + file.bytes, 0);
1362
+ const tokens = selected.reduce((total, file) => total + file.estimated_tokens, 0);
1363
+ return {
1364
+ ...selection,
1365
+ selected,
1366
+ metrics: {
1367
+ files_selected: selected.length,
1368
+ files_omitted: selection.metrics.files_omitted,
1369
+ bytes_selected: bytes,
1370
+ estimated_tokens: tokens,
1371
+ truncated: selection.metrics.truncated,
1372
+ },
1373
+ };
1374
+ }
1375
+ function selectedFileKey(file) {
1376
+ return `${file.root_index}\u0000${file.path}`;
1377
+ }
1378
+ function selectedFileFromCandidate(candidate) {
1379
+ const bytes = Buffer.byteLength(candidate.content, "utf8");
1380
+ return {
1381
+ root_index: candidate.root_index,
1382
+ path: candidate.path,
1383
+ ranges: [{ start_line: 1, end_line: Math.max(1, candidate.line_count) }],
1384
+ content: candidate.content,
1385
+ bytes,
1386
+ estimated_tokens: estimateContextBuilderTokensFromUtf8Bytes(bytes),
1387
+ source: "search",
1388
+ };
1389
+ }
1390
+ function fileCoversCandidate(file, candidate) {
1391
+ return file.ranges.some((range) => range.start_line <= 1 &&
1392
+ range.end_line >= Math.max(1, candidate.line_count));
1393
+ }
1394
+ function handleDelegateRuntimeFailure(input) {
1395
+ // Delegate-failure reasons aggregate many per-entry details (each already
1396
+ // individually sanitized/redacted where assembled); the wider marked-
1397
+ // truncation bound keeps the tail from being silently dropped.
1398
+ const failureReason = sanitizeDelegateFailureText(input.reason);
1399
+ if (input.delegate.failure_policy === "fail") {
1400
+ const error = new ExecutionError(`context_builder delegate failed: ${failureReason}`);
1401
+ if (input.failureKind === "timeout") {
1402
+ error.failureClass = "transient";
1403
+ }
1404
+ throw error;
1405
+ }
1406
+ const warnings = [
1407
+ delegateWarning("delegate_fallback", `Delegate curation failed; deterministic selection was used. ${failureReason}`),
1408
+ ];
1409
+ return {
1410
+ selection: input.deterministicSelection,
1411
+ warnings,
1412
+ omissions: [...(input.omissions ?? [])],
1413
+ openQuestions: [],
1414
+ delegate: {
1415
+ enabled: true,
1416
+ status: "partial",
1417
+ failure_policy: input.delegate.failure_policy,
1418
+ fallback_used: true,
1419
+ curator: { ...input.delegate.curator },
1420
+ finalizer: delegateFinalizerReceiptMetadata(input.delegate.finalizer),
1421
+ selected_count: input.candidateFallbackCount,
1422
+ warning_count: warnings.length,
1423
+ omission_count: input.omissionCount,
1424
+ failure_reason: failureReason,
1425
+ },
1426
+ observability: input.observability ?? {
1427
+ curator_ms: null,
1428
+ finalizer_ms: null,
1429
+ delegate_packet_size: 0,
1430
+ },
1431
+ };
1432
+ }
1433
+ function formatOmissionForDelegateFailure(omission) {
1434
+ const paths = omission.paths == null || omission.paths.length === 0
1435
+ ? "unknown focused path"
1436
+ : boundContextBuilderPathList(omission.paths).join(", ");
1437
+ return `${omission.reason} (${paths}): ${sanitizeDiagnosticText(omission.message)}`;
1438
+ }
1439
+ // Bounds for the aggregated delegate-failure omission list. Entries past the
1440
+ // cap are dropped with an explicit `(+N more omissions)` marker and the total
1441
+ // count always leads the message, so truncation is never silent.
1442
+ const DELEGATE_FAILURE_MAX_OMISSION_ENTRIES = 6;
1443
+ const DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH = 240;
1444
+ const DELEGATE_FAILURE_MAX_ENTRY_PATHS = 5;
1445
+ function formatOmissionsForDelegateFailure(omissions) {
1446
+ const total = omissions.length;
1447
+ const shown = omissions
1448
+ .slice(0, DELEGATE_FAILURE_MAX_OMISSION_ENTRIES)
1449
+ .map(formatOmissionEntryForDelegateFailure);
1450
+ const droppedSuffix = total > shown.length ? ` (+${total - shown.length} more omissions)` : "";
1451
+ return `${total} omission${total === 1 ? "" : "s"}: ${shown.join("; ")}${droppedSuffix}`;
1452
+ }
1453
+ function formatOmissionEntryForDelegateFailure(omission) {
1454
+ // Redaction happens per entry, BEFORE assembly into the aggregate message:
1455
+ // paths through boundContextBuilderPathList (secret-path redaction) and free
1456
+ // text through the marked bounded sanitizer, so no entry can smuggle secret
1457
+ // material into the joined reason and no entry ends in an unmarked "...".
1458
+ const allPaths = omission.paths ?? [];
1459
+ const shownPaths = boundContextBuilderPathList(allPaths, DELEGATE_FAILURE_MAX_ENTRY_PATHS);
1460
+ const pathText = shownPaths.length === 0
1461
+ ? "unknown focused path"
1462
+ : allPaths.length > shownPaths.length
1463
+ ? `${shownPaths.join(", ")} (+${allPaths.length - shownPaths.length} more paths)`
1464
+ : shownPaths.join(", ");
1465
+ return `${sanitizeDelegateFailureText(omission.reason, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH)} (${pathText}): ${sanitizeDelegateFailureText(omission.message, DELEGATE_FAILURE_MAX_ENTRY_TEXT_LENGTH)}`;
1466
+ }
1467
+ function rangeCoveredByRanges(target, ranges) {
1468
+ let nextLine = target.start_line;
1469
+ for (const range of [...ranges].sort((left, right) => left.start_line - right.start_line || left.end_line - right.end_line)) {
1470
+ if (range.end_line < nextLine) {
1471
+ continue;
1472
+ }
1473
+ if (range.start_line > nextLine) {
1474
+ return false;
1475
+ }
1476
+ nextLine = Math.max(nextLine, range.end_line + 1);
1477
+ if (nextLine > target.end_line) {
1478
+ return true;
1479
+ }
1480
+ }
1481
+ return false;
1482
+ }
1483
+ function pruneDelegateProposalToLocalSelection(proposal, candidates, selection, warning) {
1484
+ const candidatesByPath = new Map();
1485
+ for (const candidate of candidates) {
1486
+ candidatesByPath.set(`${candidate.root_index}:${candidate.path}`, candidate);
1487
+ }
1488
+ const selected = selection.selected.flatMap((file) => {
1489
+ const candidate = candidatesByPath.get(`${file.root_index}:${file.path}`);
1490
+ if (candidate == null) {
1491
+ return [];
1492
+ }
1493
+ const original = proposal.selected.find((item) => item.candidate_id === candidate.id);
1494
+ return [
1495
+ {
1496
+ candidate_id: candidate.id,
1497
+ ranges: file.ranges.map((range) => ({
1498
+ start_line: range.start_line,
1499
+ end_line: range.end_line,
1500
+ reason: "budget-pruned local selection range",
1501
+ })),
1502
+ reason: original?.reason ??
1503
+ "retained because it fit the local reconstruction budget",
1504
+ },
1505
+ ];
1506
+ });
1507
+ return {
1508
+ ...proposal,
1509
+ selected,
1510
+ warnings: proposal.warnings.includes(warning)
1511
+ ? proposal.warnings
1512
+ : [...proposal.warnings, warning],
1513
+ };
1514
+ }
1515
+ function delegateCompletenessFailure(input) {
1516
+ const materialWarnings = [
1517
+ ...input.curatorWarnings,
1518
+ ...input.finalizerWarnings,
1519
+ ].filter((warning) => isMaterialDelegateEvidenceWarning(warning) &&
1520
+ !warningReferencesSelectedDelegateCandidate({
1521
+ warning,
1522
+ candidates: input.candidates,
1523
+ selected: input.finalizerSelected,
1524
+ }));
1525
+ if (materialWarnings.length > 0) {
1526
+ return `delegate warnings reported material evidence gaps: ${materialWarnings.join("; ")}`;
1527
+ }
1528
+ return requiredFocusCoverageFailure(input);
1529
+ }
1530
+ function requiredFocusCoverageFailure(input) {
1531
+ const required = requiredFocusEvidenceSelections(input);
1532
+ if (required.length === 0) {
1533
+ return undefined;
1534
+ }
1535
+ const selectedByCandidate = new Map();
1536
+ for (const selection of input.finalizerSelected) {
1537
+ selectedByCandidate.set(selection.candidate_id, [
1538
+ ...(selectedByCandidate.get(selection.candidate_id) ?? []),
1539
+ ...selection.ranges,
1540
+ ]);
1541
+ }
1542
+ const missing = required.filter((selection) => {
1543
+ const selectedRanges = selectedByCandidate.get(selection.candidate_id) ?? [];
1544
+ return selection.ranges.some((range) => !rangeCoveredByRanges(range, selectedRanges));
1545
+ });
1546
+ if (missing.length === 0) {
1547
+ return undefined;
1548
+ }
1549
+ return `delegate finalizer omitted requested local evidence: ${missing
1550
+ .map((selection) => `${selection.path}#${selection.ranges
1551
+ .map((range) => `${range.start_line}-${range.end_line}`)
1552
+ .join(",")}`)
1553
+ .join("; ")}`;
1554
+ }
1555
+ // Curator-only post-repair guard. Unlike the finalizer branch, the curator-only
1556
+ // path does not fold required-focus ranges, so its baseline is the curator's
1557
+ // own pre-repair selection. Repair is allowed to:
1558
+ // - COMPACT a required-focus file's ranges to fit budget pressure (the file
1559
+ // stays in the bundle, just sliced — the documented focus-slicing behavior); and
1560
+ // - DROP a degenerate (whitespace-only) selected slice entirely (server-dropped).
1561
+ // The HIGH failure mode this guards is repair evicting a NON-degenerate
1562
+ // required-focus candidate ENTIRELY: a focus file with real content that the
1563
+ // curator selected and that survived reconstruction must not silently vanish
1564
+ // from the post-repair selection. Whole-file eviction of non-empty required
1565
+ // focus fails closed.
1566
+ function postRepairRequiredFocusEvictionFailure(input) {
1567
+ // Root-aware required-focus key set: a multi-root run can carry the same
1568
+ // relative path under two roots, only one of which is required focus. Keying
1569
+ // the required set by bare path would misclassify the non-required twin.
1570
+ const requiredKeys = new Set(input.candidates
1571
+ .filter((candidate) => candidate.focus_requested === true &&
1572
+ (candidate.required_focus_ranges?.length ?? 0) > 0 &&
1573
+ candidateMatchesFocusPath(candidate, input.focusPaths))
1574
+ .map((candidate) => `${candidate.root_index}:${candidate.path}`));
1575
+ if (requiredKeys.size === 0) {
1576
+ return undefined;
1577
+ }
1578
+ const selectedFileKey = (file) => `${file.root_index}:${file.path}`;
1579
+ // Pre-repair required-focus files that carried real (non-whitespace) content.
1580
+ // A whitespace-only focus slice is legitimately server-dropped by repair, so
1581
+ // exempt it — only its eviction is a no-op, never a hard failure.
1582
+ const preRepairRequired = new Map();
1583
+ for (const file of input.preRepairSelected) {
1584
+ if (requiredKeys.has(selectedFileKey(file)) &&
1585
+ file.ranges.length > 0 &&
1586
+ file.content.trim().length > 0) {
1587
+ preRepairRequired.set(selectedFileKey(file), file);
1588
+ }
1589
+ }
1590
+ const postRepairKeys = new Set(input.postRepairSelected
1591
+ .filter((file) => file.ranges.length > 0)
1592
+ .map(selectedFileKey));
1593
+ const evicted = [...preRepairRequired.values()]
1594
+ .filter((file) => !postRepairKeys.has(selectedFileKey(file)))
1595
+ .map((file) => `root ${file.root_index}: ${file.path}`);
1596
+ if (evicted.length === 0) {
1597
+ return undefined;
1598
+ }
1599
+ return `repair evicted required-focus evidence the curator selection carried: ${evicted.join("; ")}`;
1600
+ }
1601
+ export const postRepairRequiredFocusEvictionFailureForTest = postRepairRequiredFocusEvictionFailure;
1602
+ function warningReferencesSelectedDelegateCandidate(input) {
1603
+ const selectedCandidateIds = new Set(input.selected.map((selection) => selection.candidate_id));
1604
+ const selectedFiles = input.candidates
1605
+ .filter((candidate) => selectedCandidateIds.has(candidate.id))
1606
+ .map((candidate) => ({
1607
+ root_index: candidate.root_index,
1608
+ path: candidate.path,
1609
+ ranges: candidate.ranges.map((range) => ({
1610
+ start_line: range.start_line,
1611
+ end_line: range.end_line,
1612
+ })),
1613
+ content: "",
1614
+ bytes: candidate.size_bytes,
1615
+ estimated_tokens: candidate.estimated_tokens,
1616
+ source: candidate.discovery_reason === "focus_path" ? "focus" : "search",
1617
+ }));
1618
+ return isStaleSelectedContextOmissionWarning(input.warning.toLowerCase(), selectedFiles);
1619
+ }
1620
+ /**
1621
+ * Re-express a materialized (post-budget reconstruction) selection as
1622
+ * candidate-id-keyed proposal selections so completeness can be evaluated
1623
+ * against what actually survived the local reconstruction budget — not what
1624
+ * the finalizer merely requested. A candidate dropped by the budget guard is
1625
+ * absent here, so any required-focus range it carried reads as uncovered and
1626
+ * fails closed in delegateCompletenessFailure.
1627
+ */
1628
+ function materializeFinalizerSelection(selected, candidates) {
1629
+ const candidateIdByKey = new Map();
1630
+ for (const candidate of candidates) {
1631
+ candidateIdByKey.set(`${candidate.root_index}:${candidate.path}`, candidate.id);
1632
+ }
1633
+ const materialized = [];
1634
+ for (const file of selected) {
1635
+ const candidateId = candidateIdByKey.get(`${file.root_index}:${file.path}`);
1636
+ if (candidateId == null) {
1637
+ continue;
1638
+ }
1639
+ materialized.push({
1640
+ candidate_id: candidateId,
1641
+ reason: "materialized post-budget selection",
1642
+ ranges: file.ranges.map((range) => ({
1643
+ start_line: range.start_line,
1644
+ end_line: range.end_line,
1645
+ reason: "materialized post-budget selection range",
1646
+ })),
1647
+ });
1648
+ }
1649
+ return materialized;
1650
+ }
1651
+ function requiredFocusEvidenceSelections(input) {
1652
+ if (input.focusPaths.length === 0) {
1653
+ return [];
1654
+ }
1655
+ return input.candidates
1656
+ .filter((candidate) => candidate.focus_requested === true &&
1657
+ (candidate.required_focus_ranges?.length ?? 0) > 0 &&
1658
+ candidateMatchesFocusPath(candidate, input.focusPaths))
1659
+ .map((candidate) => ({
1660
+ candidate_id: candidate.id,
1661
+ path: candidate.path,
1662
+ ranges: candidate.required_focus_ranges.map((range) => ({
1663
+ start_line: range.start_line,
1664
+ end_line: range.end_line,
1665
+ })),
1666
+ }));
1667
+ }
1668
+ function foldRequiredFocusEvidenceIntoSelection(finalizerSelected, requiredFocus) {
1669
+ if (requiredFocus.length === 0) {
1670
+ return { selected: [...finalizerSelected], foldedPaths: [] };
1671
+ }
1672
+ const selectedByCandidate = new Map();
1673
+ for (const selection of finalizerSelected) {
1674
+ selectedByCandidate.set(selection.candidate_id, {
1675
+ candidate_id: selection.candidate_id,
1676
+ reason: selection.reason,
1677
+ ranges: selection.ranges.map((range) => ({ ...range })),
1678
+ });
1679
+ }
1680
+ const foldedPaths = [];
1681
+ for (const required of requiredFocus) {
1682
+ const existing = selectedByCandidate.get(required.candidate_id);
1683
+ const existingRanges = existing?.ranges ?? [];
1684
+ const hasUncoveredRange = required.ranges.some((range) => !rangeCoveredByRanges(range, existingRanges));
1685
+ if (!hasUncoveredRange) {
1686
+ continue;
1687
+ }
1688
+ foldedPaths.push(required.path);
1689
+ const ranges = mergeLineRanges([
1690
+ ...existingRanges.map((range) => ({
1691
+ start_line: range.start_line,
1692
+ end_line: range.end_line,
1693
+ })),
1694
+ ...required.ranges,
1695
+ ]).map((range) => ({
1696
+ start_line: range.start_line,
1697
+ end_line: range.end_line,
1698
+ reason: "server-folded requested local evidence omitted by finalizer",
1699
+ }));
1700
+ selectedByCandidate.set(required.candidate_id, {
1701
+ candidate_id: required.candidate_id,
1702
+ reason: existing?.reason ?? "requested local focus evidence preserved by server",
1703
+ ranges,
1704
+ });
1705
+ }
1706
+ return { selected: [...selectedByCandidate.values()], foldedPaths };
1707
+ }
1708
+ function candidateMatchesFocusPath(candidate, focusPaths) {
1709
+ return focusPaths.some((focus) => {
1710
+ if (focus.root_index !== candidate.root_index) {
1711
+ return false;
1712
+ }
1713
+ if (focus.kind === "file") {
1714
+ return candidate.path === focus.root_relative_path;
1715
+ }
1716
+ return isSameRootRelativePathOrDescendant(candidate.path, focus.root_relative_path);
1717
+ });
1718
+ }
1719
+ function isSameRootRelativePathOrDescendant(candidatePath, parentPath) {
1720
+ if (parentPath === "") {
1721
+ return true;
1722
+ }
1723
+ return candidatePath === parentPath || candidatePath.startsWith(`${parentPath}/`);
1724
+ }
1725
+ function isMaterialDelegateEvidenceWarning(message) {
1726
+ const text = message.toLowerCase();
1727
+ if (isNonBlockingDelegateWarning(text)) {
1728
+ return false;
1729
+ }
1730
+ return (/\b(required|requested|focus(?:ed)?)\b.{0,80}\b(missing|missed|incomplete|unavailable|insufficient|omitted|not selected|not selectable|unselectable|not present)\b/.test(text) ||
1731
+ /\b(missing|missed|incomplete|unavailable|insufficient|omitted|not selected|not selectable|unselectable|not present)\b.{0,80}\b(required|requested|focus(?:ed)?)\b/.test(text) ||
1732
+ /\bselected\b.{0,80}\b(not selectable|unselectable|not present)\b/.test(text) ||
1733
+ /\boutside (?:the )?selected ranges?\b/.test(text));
1734
+ }
1735
+ function isNonBlockingDelegateWarning(text) {
1736
+ return (/\bpacket evidence omitted full source excerpts\b/.test(text) ||
1737
+ /\bfull source excerpts? (?:were )?omitted\b/.test(text) ||
1738
+ /\bfull-file focus ranges?\b.{0,120}\b(?:excerpts?|full source)\b.{0,80}\b(?:omitted|truncated)\b/.test(text) ||
1739
+ /\bfull source excerpts?\b.{0,80}\bunavailable\b.{0,120}\bwhole-file focus ranges?\b/.test(text) ||
1740
+ isSecretSafetyOmissionWarning(text) ||
1741
+ /\bomitted from deterministic_selection\b.{0,120}\bbut selected\b/.test(text) ||
1742
+ isSpeculativeIrrelevantOmissionWarning(text) ||
1743
+ isFocusPreservingSkipWarning(text) ||
1744
+ isDeterministicExpansionWarning(text));
1745
+ }
1746
+ function isSecretSafetyOmissionWarning(text) {
1747
+ return (/\b(?:high-confidence secret|secret detection|secret safety|secret[- ]safe|redacted|redacted-safe|redaction)\b/.test(text) &&
1748
+ /\b(?:omitted|excluded|not selected|not requested|should not be requested|unavailable)\b/.test(text));
1749
+ }
1750
+ function isFocusPreservingSkipWarning(text) {
1751
+ return (/\b(?:unrelated|irrelevant|out-of-scope|low[- ]signal)\b.{0,120}\b(?:not selected|omitted|skipped)\b.{0,80}\bpreserve focus\b/.test(text) ||
1752
+ /\b(?:not selected|omitted|skipped)\b.{0,120}\b(?:unrelated|irrelevant|out-of-scope|low[- ]signal)\b.{0,80}\bpreserve focus\b/.test(text));
1753
+ }
1754
+ function isSpeculativeIrrelevantOmissionWarning(text) {
1755
+ if (!/\b(?:may|might|could)\b.{0,80}\b(?:omit|omits|omitted|missing)\b/.test(text)) {
1756
+ return false;
1757
+ }
1758
+ if (/\b(required|requested)\b/.test(text)) {
1759
+ return false;
1760
+ }
1761
+ return /\b(additional|broader|other|unrelated|irrelevant|repository|out-of-packet|outside (?:this|the) bounded packet)\b/.test(text);
1762
+ }
1763
+ function isDeterministicExpansionWarning(text) {
1764
+ return (/\bselection\b.{0,80}\b(?:intentionally\s+)?(?:exceeds|exceeded|expands beyond|expanded beyond|extends beyond|extended beyond)\b.{0,120}\bdeterministic\b.{0,160}\binsufficient\b/.test(text) &&
1765
+ !/\b(missing|missed|omitted|not selected|not selectable|unselectable|not present|unavailable)\b/.test(text));
1766
+ }
1767
+ function augmentCuratorProposalWithDeterministicBaseline(proposal, packet, maxSelections) {
1768
+ const deterministicSelected = packet.deterministic_selection?.selected ?? [];
1769
+ if (deterministicSelected.length === 0 ||
1770
+ proposal.selected.length >= maxSelections) {
1771
+ return proposal;
1772
+ }
1773
+ const selectedByCandidate = new Map();
1774
+ for (const selection of proposal.selected) {
1775
+ selectedByCandidate.set(selection.candidate_id, {
1776
+ candidate_id: selection.candidate_id,
1777
+ ranges: [...selection.ranges],
1778
+ reason: selection.reason,
1779
+ });
1780
+ }
1781
+ let added = 0;
1782
+ for (const selection of deterministicSelected) {
1783
+ const existing = selectedByCandidate.get(selection.candidate_id);
1784
+ if (existing == null) {
1785
+ if (selectedByCandidate.size >= maxSelections) {
1786
+ break;
1787
+ }
1788
+ selectedByCandidate.set(selection.candidate_id, {
1789
+ candidate_id: selection.candidate_id,
1790
+ ranges: selection.ranges.map((range) => ({
1791
+ start_line: range.start_line,
1792
+ end_line: range.end_line,
1793
+ reason: "deterministic baseline retained because curator underselected local evidence",
1794
+ })),
1795
+ reason: "server-retained deterministic baseline candidate for finalizer review",
1796
+ });
1797
+ added += 1;
1798
+ continue;
1799
+ }
1800
+ const deterministicRanges = selection.ranges.map((range) => ({
1801
+ start_line: range.start_line,
1802
+ end_line: range.end_line,
1803
+ reason: "deterministic baseline range retained because curator expanded local evidence",
1804
+ }));
1805
+ if (totalSelectedRangeLines(deterministicRanges) < totalSelectedRangeLines(existing.ranges)) {
1806
+ existing.ranges = deterministicRanges;
1807
+ added += 1;
1808
+ }
1809
+ }
1810
+ if (added === 0) {
1811
+ return proposal;
1812
+ }
1813
+ return {
1814
+ selected: [...selectedByCandidate.values()],
1815
+ warnings: [
1816
+ ...proposal.warnings,
1817
+ `curator selected fewer local candidates than the deterministic baseline; retained ${added} deterministic baseline candidate/range item(s) for finalizer review`,
1818
+ ],
1819
+ open_questions: proposal.open_questions,
1820
+ };
1821
+ }
1822
+ function totalSelectedRangeLines(ranges) {
1823
+ return ranges.reduce((total, range) => total + Math.max(0, range.end_line - range.start_line + 1), 0);
1824
+ }
1825
+ /**
1826
+ * Renders the server-enforced coverage obligations as explicit prompt rules.
1827
+ *
1828
+ * The obligations are already present in the serialized packet, but the packet
1829
+ * alone never told the delegate they were MANDATORY, so a schema-valid proposal
1830
+ * could miss one and be rejected by
1831
+ * {@link validateContextBuilderDelegateCoverageSelection}.
1832
+ *
1833
+ * Branch selection here tracks that validator's branch selection: it enters the
1834
+ * obligation branch on `obligations != null`, NOT on a non-empty target list.
1835
+ * Branching on `targets.length > 0` instead would render the satisfiable
1836
+ * evidence-class contract for a packet the validator rejects outright, i.e.
1837
+ * instruct the delegate to do something that provably cannot pass.
1838
+ */
1839
+ function packetCoverageObligationRules(packet) {
1840
+ if (packet == null || typeof packet !== "object") {
1841
+ return [];
1842
+ }
1843
+ const policy = packet["coverage_policy"];
1844
+ if (policy == null || typeof policy !== "object") {
1845
+ return [];
1846
+ }
1847
+ const policyRecord = policy;
1848
+ if (policyRecord["policy_id"] !== "review_deep_v1") {
1849
+ return [];
1850
+ }
1851
+ const requiredClasses = policyRecord["required_when_available"];
1852
+ if (!Array.isArray(requiredClasses) || requiredClasses.length === 0) {
1853
+ return [];
1854
+ }
1855
+ const obligations = policyRecord["obligations"];
1856
+ if (obligations != null && typeof obligations === "object") {
1857
+ const targets = obligations["targets"];
1858
+ if (!Array.isArray(targets) || targets.length === 0) {
1859
+ // The validator rejects a present-but-empty target list unconditionally,
1860
+ // so no instruction rendered here could be satisfied. Stay silent rather
1861
+ // than describe a contract the delegate cannot meet. (The server building
1862
+ // a packet it will always reject is a separate defect; do not paper over
1863
+ // it by rendering the evidence-class rules the validator will not apply.)
1864
+ return [];
1865
+ }
1866
+ return [
1867
+ "Coverage obligations (mandatory, server-enforced):",
1868
+ "- packet.coverage_policy.obligations.targets is required coverage, not a suggestion. Server validation rejects your output if any obligation below is unmet, even when every other rule is satisfied.",
1869
+ "- For EVERY target: selected MUST contain that target's candidate_id with at least one range that overlaps one of that target's ranges.",
1870
+ "- For EVERY target whose regression_links is non-empty: selected MUST also contain at least ONE of those links' candidate_id with an overlapping range. One linked candidate per target is sufficient; you do not need all of them.",
1871
+ "- Obligations hold even when the task reads narrower than the obligation set. Add the obligation candidates alongside your task-driven picks rather than dropping them.",
1872
+ ];
1873
+ }
1874
+ const classes = requiredClasses.filter((entry) => typeof entry === "string");
1875
+ if (classes.length === 0) {
1876
+ return [];
1877
+ }
1878
+ return [
1879
+ "Coverage obligations (mandatory, server-enforced):",
1880
+ `- packet.coverage_policy.required_when_available lists required evidence classes: ${classes.join(", ")}.`,
1881
+ "- For every listed class that ANY packet candidate carries in its evidence_classes, selected MUST include at least one candidate carrying that class. Server validation rejects your output otherwise.",
1882
+ ];
1883
+ }
1884
+ /**
1885
+ * Renders the finalizer scoring contract.
1886
+ *
1887
+ * `min_score` is a server-side gate
1888
+ * ({@link validateContextBuilderFinalizerOutput}); before this it was never
1889
+ * sent to the model, so the finalizer was asked to clear a bar it could not
1890
+ * see, on a scale that was never defined. The rubric below is deliberately
1891
+ * anchored so that an approving score lands at or above the threshold, and it
1892
+ * names the dominant real-world failure mode: penalizing the packet for
1893
+ * evidence the packet was never meant to contain.
1894
+ */
1895
+ function finalizerScoringRules(minScore) {
1896
+ if (minScore == null) {
1897
+ return [];
1898
+ }
1899
+ // A threshold of 0 cannot reject anything (the gate is `score < minScore`), so
1900
+ // the usual "below X, do not approve" band would render a range no score can
1901
+ // occupy and instruct the delegate to "honestly reach 0". State the degenerate
1902
+ // case plainly instead of rendering an impossible rule.
1903
+ const scoreBands = minScore <= 0
1904
+ ? [
1905
+ `- score does not gate this request (any score clears a minimum of ${minScore}); express insufficiency through approved:false and concrete omissions, never through a low score.`,
1906
+ ]
1907
+ : [
1908
+ "- score rates ONLY whether the `selected` you are returning is sufficient, correct local evidence for packet.task, judged against the evidence actually present in this packet:",
1909
+ // Bands are stated relative to the live threshold rather than as fixed
1910
+ // ranges. Fixed bands (9-10 / 6-8 / 0-5) left the shipped 8.5 default
1911
+ // in an undefined gap, and at a lower configured threshold they
1912
+ // labelled the passing score as deficient — telling the delegate to
1913
+ // approve while self-reporting a score whose own band calls the
1914
+ // selection inadequate.
1915
+ ` - ${minScore}-10: selected is sufficient for the task; no packet evidence the task requires was left out.`,
1916
+ ` - below ${minScore}: selected is wrong, or omits locally-available packet evidence the task needs. Do not approve.`,
1917
+ `- If you would approve, return a score at or above ${minScore}. If you cannot honestly reach ${minScore}, set approved:false and list concrete omissions rather than approving with a failing score.`,
1918
+ ];
1919
+ return [
1920
+ "Scoring (server-enforced):",
1921
+ `- score >= ${minScore} clears the score gate; below ${minScore} fails the whole request regardless of everything else. Clearing the score gate is necessary but NOT sufficient — approved, omissions, warnings, selections, coverage, and budget are each validated separately.`,
1922
+ ...scoreBands,
1923
+ "- Do NOT lower score for evidence that is absent from the packet, outside the bounded task, or that you merely suspect exists elsewhere in the repository. The packet is intentionally bounded and is the only source of truth for this call.",
1924
+ // The server scans warnings with a keyword-proximity matcher
1925
+ // (isMaterialEvidenceGapWarning) that has no negation handling, so an
1926
+ // affirmative all-clear is rejected exactly like a real gap report. Name the
1927
+ // exact fatal phrasings; a partial description reads as authoritative and is
1928
+ // worse than none.
1929
+ "- warnings carry speculative or out-of-scope notes ONLY, and the server scans them for evidence-gap phrasing. Do not mention required, requested, or focused evidence in warnings at all — not even to state that nothing is missing — and never phrase a warning as 'outside the selected range(s)' or as 'selected ... not present/not selectable/unselectable'. That scan is a keyword-proximity match with no negation handling, so an affirmative all-clear is rejected exactly like a real gap report.",
1930
+ "- If required evidence is genuinely missing, set approved:false with concrete omissions. Never route a real coverage gap into warnings.",
1931
+ ];
1932
+ }
1933
+ /**
1934
+ * Single source for every score exemplar the finalizer prompt shows.
1935
+ *
1936
+ * The shape example, the rubric bands, and the minimum-output example must all
1937
+ * derive from the live threshold: hardcoding them let the prompt display a
1938
+ * score that its own rubric did not cover, and (at `min_score` above the
1939
+ * hardcoded value — 9.25 is schema-legal) an example that would fail the very
1940
+ * gate it was illustrating.
1941
+ */
1942
+ function finalizerExampleScore(minScore) {
1943
+ // A non-gating threshold gets the neutral fallback rather than its own value:
1944
+ // "emit the lowest score that still clears the gate" is the right anti-inflation
1945
+ // rule wherever a gate exists, but at 0 it would render `{"score":0,"approved":true}`
1946
+ // and an unsure finalizer copying that reports a 0-scored approval into
1947
+ // `finalizer_score` observability. A higher exemplar always clears a lower gate.
1948
+ return minScore == null || minScore <= 0 ? 9 : minScore;
1949
+ }
1950
+ export function renderDelegatePrompt(stage, packet, options) {
1951
+ // Diagnostic-array exemplars are non-empty on purpose: empty arrays cannot
1952
+ // communicate element types, and a live finalizer failure returned
1953
+ // object-shaped `omissions`. The angle-bracket entries are placeholders that
1954
+ // show each element is one plain string.
1955
+ const outputShape = stage === "curator"
1956
+ ? '{"selected":[{"candidate_id":"c:0:path","ranges":[{"start_line":1,"end_line":1,"reason":"why this range"}],"reason":"why this file"}],"warnings":["<short plain-string caveat>"],"open_questions":["<short plain-string follow-up question>"]}'
1957
+ : `{"score":${finalizerExampleScore(options?.minScore)},"approved":true,"selected":[{"candidate_id":"c:0:path","ranges":[{"start_line":1,"end_line":1,"reason":"why this range"}],"reason":"why this file"}],"warnings":["<short plain-string caveat>"],"omissions":["<short plain string describing an omitted file or range; must be [] when approving>"]}`;
1958
+ const stageRules = stage === "curator"
1959
+ ? [
1960
+ "Curator-specific rules:",
1961
+ "- The packet is the complete local evidence. Do not run commands, read files, discover paths, or use filesystem state.",
1962
+ "- selected:[] is invalid when packet.candidates is non-empty. It will fail server validation.",
1963
+ "- If packet.candidates is non-empty, selected MUST contain at least one candidate.",
1964
+ "- Use packet.deterministic_selection.selected as the safe starting point; keep relevant deterministic candidates unless the task clearly excludes them.",
1965
+ "- Treat the minimum valid JSON example as a floor, not a complete packet. Add task-critical candidates from packet.candidates when deterministic_selection is sparse or omissions show important local evidence was budget-pruned.",
1966
+ "- Do not return selected:[] to express uncertainty. Select the closest relevant local candidate and put caveats in warnings or open_questions.",
1967
+ // The validator requires string elements (SafeTextSchema) in every
1968
+ // diagnostic array; empty exemplars alone let models invent object
1969
+ // entries, which fail schema validation.
1970
+ "- warnings and open_questions are arrays of plain strings. Each element is one short plain string; objects inside these arrays are invalid and fail server validation.",
1971
+ ]
1972
+ : [
1973
+ "Finalizer-specific rules:",
1974
+ "- The packet is the complete local evidence. Do not run commands, read files, discover paths, or use filesystem state.",
1975
+ "- selected:[] is invalid when approving. It will fail server validation.",
1976
+ "- If approving, selected MUST contain at least one candidate.",
1977
+ "- Start from packet.curator_proposal.selected and keep only candidates that are locally supported by packet.candidates.",
1978
+ "- Treat packet.deterministic_selection.selected as server-ranked required baseline evidence. If the curator underselected or warned about sparse evidence, add relevant deterministic candidates/ranges that are present in packet.candidates instead of lowering the score for evidence the packet already provides.",
1979
+ "- Do not reject solely because the curator warning says broader implementation may exist when deterministic_selection and packet.candidates contain concrete task-critical ranges for that concern.",
1980
+ "- If the selected packet evidence is insufficient for the bounded task, set approved:false and explain concrete omissions; the server will treat that as delegate failure according to failure_policy.",
1981
+ // "'may contain more detail' caveats" invited exactly the phrasing
1982
+ // isMaterialEvidenceGapWarning rejects outright
1983
+ // (/\boutside (?:the )?selected ranges?\b/), so obeying this rule got
1984
+ // the finalizer hard-failed. Steer the caveat at evidence absent from
1985
+ // the packet instead of at the selected ranges.
1986
+ "- If approving with score at or above the requested minimum, omissions MUST be []. Put speculative concerns about evidence absent from this packet in warnings instead.",
1987
+ // The observed live schema failure was object-shaped `omissions`;
1988
+ // state the element type explicitly instead of relying on exemplars.
1989
+ // (No open_questions here: the finalizer schema is strict and would
1990
+ // reject that key as unrecognized.)
1991
+ "- warnings and omissions are arrays of plain strings. Each element is one short plain string; objects inside these arrays are invalid and fail server validation.",
1992
+ ];
1993
+ const minimumOutput = buildMinimumDelegateOutput(stage, packet, options?.minScore);
1994
+ return [
1995
+ `You are the context_builder ${stage}.`,
1996
+ `Prompt version: ${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}.`,
1997
+ "Return exactly one strict JSON object and no prose.",
1998
+ "For this tool call, the JSON packet below is the only source of truth.",
1999
+ stage === "curator"
2000
+ ? "Never answer with selected:[]. If candidates exist, choose at least one candidate_id and one eligible range from packet.candidates or packet.deterministic_selection.selected."
2001
+ : "Never answer with selected:[]. If approving, choose at least one candidate_id and one eligible range from packet.curator_proposal.selected.",
2002
+ "Use only candidate_id and ranges from the packet. Do not add paths or source content.",
2003
+ "Every selected range must stay within one of that candidate's packet.ranges entries.",
2004
+ stage === "curator"
2005
+ ? "Use the excerpt, search_hits, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content."
2006
+ : "Use the excerpt, search_hits, curator_proposal, deterministic_selection, omissions, and task fields as evidence; the packet intentionally omits full source content.",
2007
+ ...stageRules,
2008
+ ...(stage === "finalizer" ? finalizerScoringRules(options?.minScore) : []),
2009
+ ...packetCoverageObligationRules(packet),
2010
+ ...(minimumOutput == null
2011
+ ? []
2012
+ : [
2013
+ "If unsure, return this minimum valid JSON object rather than an empty selection:",
2014
+ minimumOutput,
2015
+ ]),
2016
+ `Required JSON shape: ${outputShape}`,
2017
+ "Invalid output example: {\"selected\":[],\"warnings\":[],\"open_questions\":[]}",
2018
+ "",
2019
+ "Packet:",
2020
+ JSON.stringify(packet),
2021
+ "",
2022
+ "Return the JSON object now. If candidates exist, selected must be non-empty.",
2023
+ ].join("\n");
2024
+ }
2025
+ /**
2026
+ * True when `selections` would clear the packet's coverage obligations.
2027
+ *
2028
+ * Mirrors {@link validateContextBuilderDelegateCoverageSelection}: every target
2029
+ * needs an overlapping selected range, and a target with regression links needs
2030
+ * an overlapping range from at least ONE of them (the link check is disjunctive).
2031
+ */
2032
+ function selectionsSatisfyPacketObligations(selections, packet) {
2033
+ if (packet == null || typeof packet !== "object") {
2034
+ return true;
2035
+ }
2036
+ const policy = packet["coverage_policy"];
2037
+ if (policy == null || typeof policy !== "object") {
2038
+ return true;
2039
+ }
2040
+ const policyRecord = policy;
2041
+ if (policyRecord["policy_id"] !== "review_deep_v1") {
2042
+ return true;
2043
+ }
2044
+ const requiredClasses = policyRecord["required_when_available"];
2045
+ if (!Array.isArray(requiredClasses) || requiredClasses.length === 0) {
2046
+ return true;
2047
+ }
2048
+ const obligations = policyRecord["obligations"];
2049
+ if (obligations == null || typeof obligations !== "object") {
2050
+ // The validator does NOT concede this branch: it falls through to the
2051
+ // evidence-class check and still rejects a selection that omits an
2052
+ // available required class. Returning true here would advertise an example
2053
+ // the validator rejects. Unreachable through the current packet builder
2054
+ // (normalizeCoveragePolicy attaches obligations for every review_deep_v1
2055
+ // policy), but the two must not disagree — that divergence is the bug
2056
+ // generator this whole change exists to close.
2057
+ const candidates = packet["candidates"];
2058
+ if (!Array.isArray(candidates)) {
2059
+ return true;
2060
+ }
2061
+ const selectedIds = new Set(selections.map((selection) => selection.candidate_id));
2062
+ const evidenceClassesOf = (candidate) => {
2063
+ if (candidate == null || typeof candidate !== "object") {
2064
+ return [];
2065
+ }
2066
+ const classes = candidate["evidence_classes"];
2067
+ return Array.isArray(classes) ? classes : [];
2068
+ };
2069
+ return requiredClasses.every((evidenceClass) => {
2070
+ const available = candidates.some((candidate) => evidenceClassesOf(candidate).includes(evidenceClass));
2071
+ if (!available) {
2072
+ return true;
2073
+ }
2074
+ return candidates.some((candidate) => {
2075
+ const id = candidate != null && typeof candidate === "object"
2076
+ ? candidate["id"]
2077
+ : undefined;
2078
+ return (typeof id === "string" &&
2079
+ selectedIds.has(id) &&
2080
+ evidenceClassesOf(candidate).includes(evidenceClass));
2081
+ });
2082
+ });
2083
+ }
2084
+ const targets = obligations["targets"];
2085
+ if (!Array.isArray(targets)) {
2086
+ return true;
2087
+ }
2088
+ if (targets.length === 0) {
2089
+ // The validator rejects a present-but-empty target list unconditionally, so
2090
+ // no example can be valid.
2091
+ return false;
2092
+ }
2093
+ const selectedById = new Map(selections.map((selection) => [selection.candidate_id, selection.ranges]));
2094
+ const covers = (value) => {
2095
+ if (value == null || typeof value !== "object") {
2096
+ return false;
2097
+ }
2098
+ const record = value;
2099
+ const candidateId = record["candidate_id"];
2100
+ if (typeof candidateId !== "string") {
2101
+ return false;
2102
+ }
2103
+ const selectedRanges = selectedById.get(candidateId);
2104
+ if (selectedRanges == null || selectedRanges.length === 0) {
2105
+ return false;
2106
+ }
2107
+ const obligationRanges = record["ranges"];
2108
+ if (!Array.isArray(obligationRanges) || obligationRanges.length === 0) {
2109
+ return false;
2110
+ }
2111
+ return obligationRanges.some((range) => {
2112
+ if (range == null || typeof range !== "object") {
2113
+ return false;
2114
+ }
2115
+ const start = range["start_line"];
2116
+ const end = range["end_line"];
2117
+ if (typeof start !== "number" || typeof end !== "number") {
2118
+ return false;
2119
+ }
2120
+ return selectedRanges.some((selectedRange) => selectedRange.start_line <= end && start <= selectedRange.end_line);
2121
+ });
2122
+ };
2123
+ return targets.every((target) => {
2124
+ if (!covers(target)) {
2125
+ return false;
2126
+ }
2127
+ const links = target["regression_links"];
2128
+ if (!Array.isArray(links) || links.length === 0) {
2129
+ return true;
2130
+ }
2131
+ return links.some((link) => covers(link));
2132
+ });
2133
+ }
2134
+ /** The packet's `budget.max_files`, when it carries a usable one. */
2135
+ function packetBudgetMaxFiles(packet) {
2136
+ if (packet == null || typeof packet !== "object") {
2137
+ return null;
2138
+ }
2139
+ const budget = packet["budget"];
2140
+ if (budget == null || typeof budget !== "object") {
2141
+ return null;
2142
+ }
2143
+ const maxFiles = budget["max_files"];
2144
+ return typeof maxFiles === "number" && Number.isFinite(maxFiles) && maxFiles > 0
2145
+ ? maxFiles
2146
+ : null;
2147
+ }
2148
+ function buildMinimumDelegateOutput(stage, packet, minScore) {
2149
+ const allSelections = packetMinimumSelections(stage, packet);
2150
+ if (allSelections.length === 0) {
2151
+ return null;
2152
+ }
2153
+ // The example is advertised as "minimum valid" and a delegate may copy it
2154
+ // verbatim, so it must satisfy every gate the copy would face.
2155
+ //
2156
+ // packetMinimumSelections bounds by MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS,
2157
+ // which is unrelated to the caller's budget: validateBudget rejects a
2158
+ // selection longer than budget.max_files, so an unbounded example was invalid
2159
+ // whenever max_files was the smaller limit. Apply the budget first, then judge
2160
+ // coverage on exactly what is advertised.
2161
+ const maxFiles = packetBudgetMaxFiles(packet);
2162
+ const minimumSelections = maxFiles == null ? allSelections : allSelections.slice(0, maxFiles);
2163
+ if (minimumSelections.length === 0) {
2164
+ return null;
2165
+ }
2166
+ // Neither bound ranks obligations first, so a required target can fall outside
2167
+ // the cap — offering an example that fails coverage validation. Withhold the
2168
+ // example rather than advertise an invalid one.
2169
+ if (!selectionsSatisfyPacketObligations(minimumSelections, packet)) {
2170
+ return null;
2171
+ }
2172
+ const selected = minimumSelections.map((selection) => ({
2173
+ candidate_id: selection.candidate_id,
2174
+ ranges: selection.ranges.map((range) => ({
2175
+ start_line: range.start_line,
2176
+ end_line: range.end_line,
2177
+ reason: "locally supported range from the packet",
2178
+ })),
2179
+ reason: "minimum valid local selection for the requested context",
2180
+ }));
2181
+ const output = stage === "curator"
2182
+ ? {
2183
+ selected,
2184
+ warnings: [
2185
+ "minimum deterministic selection used; review packet evidence for more context",
2186
+ ],
2187
+ open_questions: [],
2188
+ }
2189
+ : {
2190
+ // This example is offered to an UNSURE finalizer, and it carries a
2191
+ // deliberately minimal selection. Stamping it `score: 10` would pair
2192
+ // the top of the scale with the floor of the evidence and teach
2193
+ // "when uncertain, emit 10" — inflating the very self-score the gate
2194
+ // relies on. Emit the lowest score that still clears the gate, so the
2195
+ // minimum selection reads as the minimum passing answer.
2196
+ score: finalizerExampleScore(minScore),
2197
+ approved: true,
2198
+ selected,
2199
+ warnings: [
2200
+ "minimum curator proposal selection used; review packet evidence for more context",
2201
+ ],
2202
+ omissions: [],
2203
+ };
2204
+ return JSON.stringify(output);
2205
+ }
2206
+ function packetMinimumSelections(stage, packet) {
2207
+ if (packet == null || typeof packet !== "object") {
2208
+ return [];
2209
+ }
2210
+ const record = packet;
2211
+ if (stage === "finalizer") {
2212
+ const proposal = record["curator_proposal"];
2213
+ const proposalSelected = proposal != null && typeof proposal === "object"
2214
+ ? proposal["selected"]
2215
+ : undefined;
2216
+ return packetSelectionsFromValues(proposalSelected);
2217
+ }
2218
+ const deterministic = record["deterministic_selection"];
2219
+ const deterministicSelected = deterministic != null && typeof deterministic === "object"
2220
+ ? deterministic["selected"]
2221
+ : undefined;
2222
+ const deterministicSelections = packetSelectionsFromValues(deterministicSelected);
2223
+ const candidateSelections = packetCandidateSelectionsFromValues(record["candidates"]);
2224
+ if (deterministicSelections.length > 0) {
2225
+ return mergePacketSelections([
2226
+ ...deterministicSelections,
2227
+ ...candidateSelections,
2228
+ ]).slice(0, MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS);
2229
+ }
2230
+ return candidateSelections;
2231
+ }
2232
+ function packetSelectionsFromValues(values) {
2233
+ if (!Array.isArray(values)) {
2234
+ return [];
2235
+ }
2236
+ return mergePacketSelections(values
2237
+ .slice(0, MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS)
2238
+ .map((value) => packetSelectionFromValue(value, "candidate_id"))
2239
+ .filter((selection) => selection != null));
2240
+ }
2241
+ function packetCandidateSelectionsFromValues(values) {
2242
+ if (!Array.isArray(values)) {
2243
+ return [];
2244
+ }
2245
+ return mergePacketSelections(values
2246
+ .slice(0, MAX_DELEGATE_MINIMUM_OUTPUT_SELECTIONS)
2247
+ .map((value) => packetSelectionFromValue(value, "id"))
2248
+ .filter((selection) => selection != null));
2249
+ }
2250
+ function mergePacketSelections(selections) {
2251
+ const merged = new Map();
2252
+ for (const selection of selections) {
2253
+ const ranges = merged.get(selection.candidate_id) ?? [];
2254
+ // Deduplicate by bounds. Sources overlap by construction — a candidate in
2255
+ // deterministic_selection is normally also present in packet.candidates —
2256
+ // so concatenating produced the SAME range twice, and validateSelections
2257
+ // rejects a duplicate range outright. That made the advertised "minimum
2258
+ // valid JSON" example invalid in the ordinary case: a delegate copying it
2259
+ // was hard-failed by the server that offered it.
2260
+ for (const range of selection.ranges) {
2261
+ if (!ranges.some((existing) => existing.start_line === range.start_line &&
2262
+ existing.end_line === range.end_line)) {
2263
+ ranges.push(range);
2264
+ }
2265
+ }
2266
+ merged.set(selection.candidate_id, ranges);
2267
+ }
2268
+ return [...merged].map(([candidate_id, ranges]) => ({
2269
+ candidate_id,
2270
+ ranges,
2271
+ }));
2272
+ }
2273
+ function packetSelectionFromValue(value, candidateIdKey) {
2274
+ if (value == null || typeof value !== "object") {
2275
+ return null;
2276
+ }
2277
+ const record = value;
2278
+ const candidateId = record[candidateIdKey];
2279
+ const ranges = record["ranges"];
2280
+ const validRanges = Array.isArray(ranges)
2281
+ ? ranges.map(packetRangeBounds).filter((range) => range != null)
2282
+ : [];
2283
+ return typeof candidateId === "string" && validRanges.length > 0
2284
+ ? { candidate_id: candidateId, ranges: validRanges }
2285
+ : null;
2286
+ }
2287
+ function packetRangeBounds(value) {
2288
+ if (value == null || typeof value !== "object") {
2289
+ return null;
2290
+ }
2291
+ const record = value;
2292
+ const startLine = record["start_line"];
2293
+ const endLine = record["end_line"];
2294
+ if (typeof startLine !== "number" || typeof endLine !== "number") {
2295
+ return null;
2296
+ }
2297
+ return Number.isInteger(startLine) &&
2298
+ Number.isInteger(endLine) &&
2299
+ startLine > 0 &&
2300
+ endLine >= startLine
2301
+ ? { start_line: startLine, end_line: endLine }
2302
+ : null;
2303
+ }
2304
+ function delegatePromptDiagnostic(packet) {
2305
+ if (packet == null || typeof packet !== "object") {
2306
+ return `prompt_version=${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}; packet=invalid`;
2307
+ }
2308
+ const record = packet;
2309
+ const candidateCount = Array.isArray(record["candidates"])
2310
+ ? record["candidates"].length
2311
+ : 0;
2312
+ const deterministic = record["deterministic_selection"];
2313
+ const deterministicSelected = deterministic != null && typeof deterministic === "object"
2314
+ ? deterministic["selected"]
2315
+ : undefined;
2316
+ const deterministicCount = Array.isArray(deterministicSelected)
2317
+ ? deterministicSelected.length
2318
+ : 0;
2319
+ // `curator_proposal` only exists on a finalizer packet (curator packets are
2320
+ // built before any proposal exists). Reporting `curator_selected=0` for a
2321
+ // curator packet described the packet's missing field, not the curator's
2322
+ // output, so every curator-stage diagnostic claimed an empty selection that
2323
+ // had in fact never been observed. Omit the field unless the packet carries a
2324
+ // proposal to count.
2325
+ const proposal = record["curator_proposal"];
2326
+ const proposalSelected = proposal != null && typeof proposal === "object"
2327
+ ? proposal["selected"]
2328
+ : undefined;
2329
+ // `min` must report what the prompt actually carried, not whether a minimum
2330
+ // selection could be computed: buildMinimumDelegateOutput also withholds the
2331
+ // example when it would fail budget or coverage, and a withheld floor example
2332
+ // is exactly the condition worth seeing next to a delegate failure. Derive it
2333
+ // from the same call the prompt makes, at the stage this packet belongs to.
2334
+ const stage = Array.isArray(proposalSelected)
2335
+ ? "finalizer"
2336
+ : "curator";
2337
+ return [
2338
+ `pv=${CONTEXT_BUILDER_DELEGATE_PROMPT_VERSION}`,
2339
+ `candidates=${candidateCount}`,
2340
+ `det=${deterministicCount}`,
2341
+ ...(Array.isArray(proposalSelected)
2342
+ ? [`curator_selected=${proposalSelected.length}`]
2343
+ : []),
2344
+ `min=${buildMinimumDelegateOutput(stage, packet) != null}`,
2345
+ ].join("; ");
2346
+ }
2347
+ function failureReasonFromCall(stage, call) {
2348
+ return `${stage} ${call.reason}: ${call.message}`;
2349
+ }
2350
+ /**
2351
+ * Safe-scalar score context for finalizer rejections. A not-approved rejection
2352
+ * previously carried no score at all; surfacing the reported score next to the
2353
+ * configured min_score makes rejection and under-score failures diagnosable
2354
+ * from the failure reason alone. Only a finite numeric `score` read from the
2355
+ * finalizer's raw JSON is ever included — never free-form provider text.
2356
+ */
2357
+ function finalizerScoreDiagnostic(json, minScore) {
2358
+ const rawScore = json != null && typeof json === "object"
2359
+ ? json["score"]
2360
+ : undefined;
2361
+ return typeof rawScore === "number" && Number.isFinite(rawScore)
2362
+ ? `; finalizer_score=${rawScore}; min_score=${minScore}`
2363
+ : `; min_score=${minScore}`;
2364
+ }
2365
+ function failureReasonFromError(stage, err, diagnostic) {
2366
+ const message = err instanceof Error ? err.message : "unknown delegate error";
2367
+ return diagnostic == null
2368
+ ? `${stage}: ${message}`
2369
+ : `${stage}: ${diagnostic}; ${message}`;
2370
+ }
2371
+ function delegateWarning(code, message) {
2372
+ return {
2373
+ code: sanitizeDiagnosticText(code),
2374
+ message: sanitizeDiagnosticText(message),
2375
+ };
2376
+ }
2377
+ // Bounded per-term detail in the search-cap coverage warning. Full per-term
2378
+ // rows (up to the public 20-term cap) persist in manifest/summary
2379
+ // `search_coverage`; the warning message stays a compact summary.
2380
+ const MAX_SEARCH_CAP_WARNING_TERM_DETAILS = 6;
2381
+ /**
2382
+ * Builds the receipt-visible explanation for a MATERIAL search cap. Returns
2383
+ * undefined when the cap was not hit or coverage proves it non-material.
2384
+ * The warning `code` is the stable materiality reason string
2385
+ * (`search_cap_unresolved_coverage` / `search_cap_unsearched_candidates`);
2386
+ * classification itself rides on the cap event in the status reducer, so this
2387
+ * warning is explanatory and never double-counts toward `partial`.
2388
+ * Terms are referenced by `search_term_index`, never by value, so the warning
2389
+ * cannot leak caller term text; the full (sanitized) rows live in
2390
+ * `search_coverage`. The first unsearched candidate's rank/path is not
2391
+ * reported: T1's coverage counters are aggregate-only by design and the
2392
+ * caller-phase scheduler is term-major round-robin, so the searched set is not
2393
+ * an ordering prefix and no per-candidate identity is cheaply available.
2394
+ */
2395
+ function searchCapCoverageWarning(searchCapHit, coverage) {
2396
+ if (!searchCapHit) {
2397
+ return undefined;
2398
+ }
2399
+ const materiality = evaluateSearchCapMateriality(coverage);
2400
+ if (!materiality.material) {
2401
+ return undefined;
2402
+ }
2403
+ const incompleteTerms = coverage.caller_terms.filter((term) => !term.scan_complete);
2404
+ const termDetails = incompleteTerms
2405
+ .slice(0, MAX_SEARCH_CAP_WARNING_TERM_DETAILS)
2406
+ .map((term) => `#${term.search_term_index}(hits=${term.hit_count})`)
2407
+ .join(", ");
2408
+ const hiddenIncomplete = incompleteTerms.length - Math.min(incompleteTerms.length, MAX_SEARCH_CAP_WARNING_TERM_DETAILS);
2409
+ const callerSummary = coverage.caller_term_count === 0
2410
+ ? "caller terms 0"
2411
+ : `caller terms ${coverage.caller_term_count} (${incompleteTerms.length} incomplete${termDetails.length === 0 ? "" : `: ${termDetails}`}${hiddenIncomplete > 0 ? `, +${hiddenIncomplete} more` : ""}; ${coverage.caller_record_count} records)`;
2412
+ return {
2413
+ code: materiality.reason,
2414
+ message: `search cap ended discovery before coverage was proven: searched ` +
2415
+ `${coverage.searched_candidate_count}/${coverage.eligible_candidate_count} eligible candidates ` +
2416
+ `(${coverage.unsearched_candidate_count} unsearched); ${callerSummary}; ` +
2417
+ `derived terms ${coverage.derived_term_count} (${coverage.derived_record_count} records); ` +
2418
+ `full per-term coverage is in manifest.json search_coverage`,
2419
+ };
2420
+ }
2421
+ function reconcileContextBuilderWarnings(input) {
2422
+ if (input.warnings.length === 0) {
2423
+ return [];
2424
+ }
2425
+ return input.warnings.filter((warning) => {
2426
+ const text = warning.message.toLowerCase();
2427
+ if (isStaleTruncationWarning(text, input)) {
2428
+ return false;
2429
+ }
2430
+ if (isStaleSelectedContextOmissionWarning(text, input.selected)) {
2431
+ return false;
2432
+ }
2433
+ if (isStaleSelectedContextRangeWarning(text, input.selected)) {
2434
+ return false;
2435
+ }
2436
+ return true;
2437
+ });
2438
+ }
2439
+ function isStaleTruncationWarning(text, input) {
2440
+ if (!isTruncationWarning(text)) {
2441
+ return false;
2442
+ }
2443
+ return !contextBuilderPacketIsDegraded(input);
2444
+ }
2445
+ function contextBuilderPacketIsDegraded(input) {
2446
+ return (input.metrics.truncated ||
2447
+ input.omissions.some(isMaterialWarningOmission) ||
2448
+ input.delegate?.fallback_used === true ||
2449
+ input.delegate?.status === "partial");
2450
+ }
2451
+ function isMaterialWarningOmission(omission) {
2452
+ if (omission.reason === "excluded" ||
2453
+ omission.reason === "exclude_glob" ||
2454
+ omission.reason === "caller_include_glob" ||
2455
+ omission.reason === "content_secret" ||
2456
+ omission.reason === "secret_path" ||
2457
+ omission.reason === "symlink_omitted" ||
2458
+ omission.reason === "symlink_file" ||
2459
+ omission.reason === "binary_file" ||
2460
+ omission.reason === "duplicate_root" ||
2461
+ omission.reason === "noisy_file" ||
2462
+ omission.reason === "default_exclude" ||
2463
+ omission.reason === "default_noisy_exclude") {
2464
+ return false;
2465
+ }
2466
+ return true;
2467
+ }
2468
+ function isTruncationWarning(text) {
2469
+ return /\b(?:truncat(?:ed|ion)|token[- ]?truncated|token budget|budget[- ]limited|budget limited)\b/.test(text);
2470
+ }
2471
+ function isStaleSelectedContextOmissionWarning(text, selected) {
2472
+ if (selected.length === 0 ||
2473
+ !isOmissionWarning(text) ||
2474
+ isPathOrSecretOmissionWarning(text)) {
2475
+ return false;
2476
+ }
2477
+ const references = selectedContextWarningReferences(selected);
2478
+ const groupedRefs = concreteWarningReferenceGroups(text);
2479
+ if (groupedRefs.length > 0) {
2480
+ if (groupedRefs.some((group) => group.every((ref) => references.tokens.has(ref)))) {
2481
+ return true;
2482
+ }
2483
+ }
2484
+ return warningReferencesSelectedContext(text, references);
2485
+ }
2486
+ function isStaleSelectedContextRangeWarning(text, selected) {
2487
+ if (selected.length === 0 ||
2488
+ !/\b(?:available as|available only|only available|only includes|only selected|partial|range|ranges|slice|slices|line|lines|clipped|narrow)\b/.test(text)) {
2489
+ return false;
2490
+ }
2491
+ return warningReferencesSelectedContext(text, selectedContextWarningReferences(selected));
2492
+ }
2493
+ function isOmissionWarning(text) {
2494
+ return /\b(?:missing|missed|omission|omissions|omit|omits|omitted|not selected|not be selected|not present|unavailable|insufficient)\b/.test(text);
2495
+ }
2496
+ function isPathOrSecretOmissionWarning(text) {
2497
+ return /\b(?:secret|sensitive|credential|private key|api key|redacted|allowed roots?|outside allowed|unsafe path|path traversal|symlink|permission denied|eacces|enoent|not readable)\b/.test(text);
2498
+ }
2499
+ function selectedContextWarningReferences(selected) {
2500
+ const compactTextRefs = new Set();
2501
+ const tokens = new Set();
2502
+ for (const file of selected) {
2503
+ const normalizedPath = normalizeWarningReference(file.path);
2504
+ if (normalizedPath.length > 0) {
2505
+ compactTextRefs.add(normalizedPath);
2506
+ }
2507
+ const basename = file.path.split("/").at(-1) ?? file.path;
2508
+ const basenameRef = normalizeWarningReference(basename);
2509
+ if (basenameRef.length > 0) {
2510
+ compactTextRefs.add(basenameRef);
2511
+ }
2512
+ const stem = basename.split(".")[0] ?? basename;
2513
+ const stemRef = normalizeWarningReference(stem);
2514
+ if (isConcreteWarningReference(stemRef)) {
2515
+ compactTextRefs.add(stemRef);
2516
+ tokens.add(stemRef);
2517
+ }
2518
+ }
2519
+ return { compactTextRefs, tokens };
2520
+ }
2521
+ function warningReferencesSelectedContext(text, references) {
2522
+ const compactText = normalizeWarningReference(text);
2523
+ for (const ref of references.compactTextRefs) {
2524
+ if (isConcreteWarningReference(ref) && compactText.includes(ref)) {
2525
+ return true;
2526
+ }
2527
+ }
2528
+ return false;
2529
+ }
2530
+ function concreteWarningReferenceGroups(text) {
2531
+ const groups = [];
2532
+ const groupPatterns = [
2533
+ /\b([a-z0-9_.-]+(?:\/[a-z0-9_.-]+)+)\s+(?:schemas?|tests?|files?)\b/g,
2534
+ /\b([a-z0-9_.-]+)\s+and\s+([a-z0-9_.-]+)\s+(?:schemas?|tests?|files?)\b/g,
2535
+ ];
2536
+ for (const pattern of groupPatterns) {
2537
+ for (const match of text.matchAll(pattern)) {
2538
+ const refs = match
2539
+ .slice(1)
2540
+ .flatMap((value) => value.split("/"))
2541
+ .map(normalizeWarningReference)
2542
+ .filter(isConcreteWarningReference);
2543
+ if (refs.length > 0) {
2544
+ groups.push(refs);
2545
+ }
2546
+ }
2547
+ }
2548
+ return groups;
2549
+ }
2550
+ function normalizeWarningReference(value) {
2551
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "");
2552
+ }
2553
+ function isConcreteWarningReference(value) {
2554
+ if (value.length < 4) {
2555
+ return false;
2556
+ }
2557
+ return !new Set([
2558
+ "backend",
2559
+ "frontend",
2560
+ "schema",
2561
+ "schemas",
2562
+ "service",
2563
+ "services",
2564
+ "source",
2565
+ "test",
2566
+ "tests",
2567
+ "types",
2568
+ ]).has(value);
2569
+ }
2570
+ function buildContextBuilderMetrics(input) {
2571
+ return {
2572
+ files_seen: input.discovery.files_seen,
2573
+ files_considered: input.discovery.files_considered,
2574
+ files_selected: input.selection.files_selected,
2575
+ files_omitted: input.omissionCount,
2576
+ scan_cap_hit: input.discovery.scan_cap_hit,
2577
+ search_cap_hit: input.discovery.search_cap_hit,
2578
+ discovery_byte_cap_hit: input.discovery.discovery_byte_cap_hit,
2579
+ discovery_candidate_cap_hit: input.discovery.discovery_candidate_cap_hit,
2580
+ candidate_cap_hit: input.discovery.candidate_cap_hit,
2581
+ bytes_read: input.discovery.bytes_read,
2582
+ bytes_written: 0,
2583
+ estimated_tokens: input.selection.estimated_tokens,
2584
+ truncated: input.selection.truncated ||
2585
+ input.discovery.scan_cap_hit ||
2586
+ input.discovery.discovery_byte_cap_hit ||
2587
+ input.discovery.discovery_candidate_cap_hit,
2588
+ };
2589
+ }
2590
+ //# sourceMappingURL=handler.js.map