@allocator-one/rcl 4.4.19

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 (663) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1415 -0
  3. package/dist/ci.d.ts +16 -0
  4. package/dist/ci.js +42 -0
  5. package/dist/ci.js.map +1 -0
  6. package/dist/config/data-dir.d.ts +6 -0
  7. package/dist/config/data-dir.js +12 -0
  8. package/dist/config/data-dir.js.map +1 -0
  9. package/dist/config/defaults.d.ts +82 -0
  10. package/dist/config/defaults.js +89 -0
  11. package/dist/config/defaults.js.map +1 -0
  12. package/dist/config/harness.d.ts +40 -0
  13. package/dist/config/harness.js +151 -0
  14. package/dist/config/harness.js.map +1 -0
  15. package/dist/config/loader.d.ts +13 -0
  16. package/dist/config/loader.js +131 -0
  17. package/dist/config/loader.js.map +1 -0
  18. package/dist/config/provider-concurrency.d.ts +4 -0
  19. package/dist/config/provider-concurrency.js +6 -0
  20. package/dist/config/provider-concurrency.js.map +1 -0
  21. package/dist/config/providers.d.ts +3 -0
  22. package/dist/config/providers.js +9 -0
  23. package/dist/config/providers.js.map +1 -0
  24. package/dist/config/schema.d.ts +179 -0
  25. package/dist/config/schema.js +115 -0
  26. package/dist/config/schema.js.map +1 -0
  27. package/dist/consensus/claim-contract.d.ts +9 -0
  28. package/dist/consensus/claim-contract.js +307 -0
  29. package/dist/consensus/claim-contract.js.map +1 -0
  30. package/dist/consensus/claim-identity.d.ts +5 -0
  31. package/dist/consensus/claim-identity.js +39 -0
  32. package/dist/consensus/claim-identity.js.map +1 -0
  33. package/dist/consensus/deduper.d.ts +43 -0
  34. package/dist/consensus/deduper.js +1052 -0
  35. package/dist/consensus/deduper.js.map +1 -0
  36. package/dist/consensus/finding-identity.d.ts +20 -0
  37. package/dist/consensus/finding-identity.js +49 -0
  38. package/dist/consensus/finding-identity.js.map +1 -0
  39. package/dist/consensus/gating.d.ts +215 -0
  40. package/dist/consensus/gating.js +699 -0
  41. package/dist/consensus/gating.js.map +1 -0
  42. package/dist/consensus/parser.d.ts +22 -0
  43. package/dist/consensus/parser.js +255 -0
  44. package/dist/consensus/parser.js.map +1 -0
  45. package/dist/consensus/semantic-deduper.d.ts +10 -0
  46. package/dist/consensus/semantic-deduper.js +70 -0
  47. package/dist/consensus/semantic-deduper.js.map +1 -0
  48. package/dist/consensus/types.d.ts +196 -0
  49. package/dist/consensus/types.js +2 -0
  50. package/dist/consensus/types.js.map +1 -0
  51. package/dist/consensus/verification-contract.d.ts +8 -0
  52. package/dist/consensus/verification-contract.js +95 -0
  53. package/dist/consensus/verification-contract.js.map +1 -0
  54. package/dist/consensus/voter.d.ts +29 -0
  55. package/dist/consensus/voter.js +394 -0
  56. package/dist/consensus/voter.js.map +1 -0
  57. package/dist/converge/attempt-budget.d.ts +88 -0
  58. package/dist/converge/attempt-budget.js +717 -0
  59. package/dist/converge/attempt-budget.js.map +1 -0
  60. package/dist/converge/bound-fix-recovery-source.d.ts +24 -0
  61. package/dist/converge/bound-fix-recovery-source.js +25 -0
  62. package/dist/converge/bound-fix-recovery-source.js.map +1 -0
  63. package/dist/converge/bound-fix-recovery.d.ts +15 -0
  64. package/dist/converge/bound-fix-recovery.js +65 -0
  65. package/dist/converge/bound-fix-recovery.js.map +1 -0
  66. package/dist/converge/correction-anchors.d.ts +1 -0
  67. package/dist/converge/correction-anchors.js +2 -0
  68. package/dist/converge/correction-anchors.js.map +1 -0
  69. package/dist/converge/cycle-remote.d.ts +6 -0
  70. package/dist/converge/cycle-remote.js +41 -0
  71. package/dist/converge/cycle-remote.js.map +1 -0
  72. package/dist/converge/delivery-reconciliation.d.ts +17 -0
  73. package/dist/converge/delivery-reconciliation.js +53 -0
  74. package/dist/converge/delivery-reconciliation.js.map +1 -0
  75. package/dist/converge/finding-identity.d.ts +6 -0
  76. package/dist/converge/finding-identity.js +7 -0
  77. package/dist/converge/finding-identity.js.map +1 -0
  78. package/dist/converge/fresh-review.d.ts +37 -0
  79. package/dist/converge/fresh-review.js +351 -0
  80. package/dist/converge/fresh-review.js.map +1 -0
  81. package/dist/converge/launch-guard.d.ts +55 -0
  82. package/dist/converge/launch-guard.js +363 -0
  83. package/dist/converge/launch-guard.js.map +1 -0
  84. package/dist/converge/launch-preflight.d.ts +5 -0
  85. package/dist/converge/launch-preflight.js +41 -0
  86. package/dist/converge/launch-preflight.js.map +1 -0
  87. package/dist/converge/launch-record.d.ts +136 -0
  88. package/dist/converge/launch-record.js +86 -0
  89. package/dist/converge/launch-record.js.map +1 -0
  90. package/dist/converge/legacy-launch-health.d.ts +311 -0
  91. package/dist/converge/legacy-launch-health.js +159 -0
  92. package/dist/converge/legacy-launch-health.js.map +1 -0
  93. package/dist/converge/legacy-roster.d.ts +29 -0
  94. package/dist/converge/legacy-roster.js +268 -0
  95. package/dist/converge/legacy-roster.js.map +1 -0
  96. package/dist/converge/native-lock.d.ts +18 -0
  97. package/dist/converge/native-lock.js +122 -0
  98. package/dist/converge/native-lock.js.map +1 -0
  99. package/dist/converge/ordinary-pending-export.d.ts +67 -0
  100. package/dist/converge/ordinary-pending-export.js +175 -0
  101. package/dist/converge/ordinary-pending-export.js.map +1 -0
  102. package/dist/converge/ordinary-pending-package.d.ts +28 -0
  103. package/dist/converge/ordinary-pending-package.js +36 -0
  104. package/dist/converge/ordinary-pending-package.js.map +1 -0
  105. package/dist/converge/pending-legacy-resume.d.ts +119 -0
  106. package/dist/converge/pending-legacy-resume.js +720 -0
  107. package/dist/converge/pending-legacy-resume.js.map +1 -0
  108. package/dist/converge/pending-recovery-source.d.ts +131 -0
  109. package/dist/converge/pending-recovery-source.js +71 -0
  110. package/dist/converge/pending-recovery-source.js.map +1 -0
  111. package/dist/converge/recovery-launch.d.ts +66 -0
  112. package/dist/converge/recovery-launch.js +518 -0
  113. package/dist/converge/recovery-launch.js.map +1 -0
  114. package/dist/converge/retained-report.d.ts +29 -0
  115. package/dist/converge/retained-report.js +218 -0
  116. package/dist/converge/retained-report.js.map +1 -0
  117. package/dist/converge/retry-source.d.ts +25 -0
  118. package/dist/converge/retry-source.js +13 -0
  119. package/dist/converge/retry-source.js.map +1 -0
  120. package/dist/converge/review-cycle.d.ts +49 -0
  121. package/dist/converge/review-cycle.js +26 -0
  122. package/dist/converge/review-cycle.js.map +1 -0
  123. package/dist/converge/round-gap-schema.d.ts +66 -0
  124. package/dist/converge/round-gap-schema.js +51 -0
  125. package/dist/converge/round-gap-schema.js.map +1 -0
  126. package/dist/converge/round-gap.d.ts +38 -0
  127. package/dist/converge/round-gap.js +334 -0
  128. package/dist/converge/round-gap.js.map +1 -0
  129. package/dist/converge/run-state.d.ts +224 -0
  130. package/dist/converge/run-state.js +564 -0
  131. package/dist/converge/run-state.js.map +1 -0
  132. package/dist/converge/stale-report-schema.d.ts +42 -0
  133. package/dist/converge/stale-report-schema.js +79 -0
  134. package/dist/converge/stale-report-schema.js.map +1 -0
  135. package/dist/converge/stale-report-storage.d.ts +47 -0
  136. package/dist/converge/stale-report-storage.js +172 -0
  137. package/dist/converge/stale-report-storage.js.map +1 -0
  138. package/dist/converge/stale-report.d.ts +14 -0
  139. package/dist/converge/stale-report.js +213 -0
  140. package/dist/converge/stale-report.js.map +1 -0
  141. package/dist/converge/target-ownership.d.ts +29 -0
  142. package/dist/converge/target-ownership.js +95 -0
  143. package/dist/converge/target-ownership.js.map +1 -0
  144. package/dist/converge/terminal-rejection-schema.d.ts +42 -0
  145. package/dist/converge/terminal-rejection-schema.js +39 -0
  146. package/dist/converge/terminal-rejection-schema.js.map +1 -0
  147. package/dist/converge/terminal-rejection.d.ts +13 -0
  148. package/dist/converge/terminal-rejection.js +275 -0
  149. package/dist/converge/terminal-rejection.js.map +1 -0
  150. package/dist/coordination/registry-lock.d.ts +82 -0
  151. package/dist/coordination/registry-lock.js +498 -0
  152. package/dist/coordination/registry-lock.js.map +1 -0
  153. package/dist/discuss.d.ts +53 -0
  154. package/dist/discuss.js +112 -0
  155. package/dist/discuss.js.map +1 -0
  156. package/dist/dispatch/adapter.d.ts +32 -0
  157. package/dist/dispatch/adapter.js +2 -0
  158. package/dist/dispatch/adapter.js.map +1 -0
  159. package/dist/dispatch/anthropic.d.ts +10 -0
  160. package/dist/dispatch/anthropic.js +235 -0
  161. package/dist/dispatch/anthropic.js.map +1 -0
  162. package/dist/dispatch/async-lane.d.ts +127 -0
  163. package/dist/dispatch/async-lane.js +414 -0
  164. package/dist/dispatch/async-lane.js.map +1 -0
  165. package/dist/dispatch/capture-council.d.ts +63 -0
  166. package/dist/dispatch/capture-council.js +137 -0
  167. package/dist/dispatch/capture-council.js.map +1 -0
  168. package/dist/dispatch/captured-async.d.ts +67 -0
  169. package/dist/dispatch/captured-async.js +40 -0
  170. package/dist/dispatch/captured-async.js.map +1 -0
  171. package/dist/dispatch/captured-inputs.d.ts +50 -0
  172. package/dist/dispatch/captured-inputs.js +190 -0
  173. package/dist/dispatch/captured-inputs.js.map +1 -0
  174. package/dist/dispatch/checkpoint-async-context.d.ts +27 -0
  175. package/dist/dispatch/checkpoint-async-context.js +51 -0
  176. package/dist/dispatch/checkpoint-async-context.js.map +1 -0
  177. package/dist/dispatch/checkpoint-async-execution.d.ts +22 -0
  178. package/dist/dispatch/checkpoint-async-execution.js +130 -0
  179. package/dist/dispatch/checkpoint-async-execution.js.map +1 -0
  180. package/dist/dispatch/checkpoint-async-store.d.ts +134 -0
  181. package/dist/dispatch/checkpoint-async-store.js +534 -0
  182. package/dist/dispatch/checkpoint-async-store.js.map +1 -0
  183. package/dist/dispatch/checkpoint-async-unknown.d.ts +19 -0
  184. package/dist/dispatch/checkpoint-async-unknown.js +46 -0
  185. package/dist/dispatch/checkpoint-async-unknown.js.map +1 -0
  186. package/dist/dispatch/checkpoint-async.d.ts +252 -0
  187. package/dist/dispatch/checkpoint-async.js +270 -0
  188. package/dist/dispatch/checkpoint-async.js.map +1 -0
  189. package/dist/dispatch/checkpoint-gating-execution.d.ts +26 -0
  190. package/dist/dispatch/checkpoint-gating-execution.js +84 -0
  191. package/dist/dispatch/checkpoint-gating-execution.js.map +1 -0
  192. package/dist/dispatch/checkpoint-phase-limits.d.ts +5 -0
  193. package/dist/dispatch/checkpoint-phase-limits.js +11 -0
  194. package/dist/dispatch/checkpoint-phase-limits.js.map +1 -0
  195. package/dist/dispatch/checkpoint-verification-context.d.ts +15 -0
  196. package/dist/dispatch/checkpoint-verification-context.js +48 -0
  197. package/dist/dispatch/checkpoint-verification-context.js.map +1 -0
  198. package/dist/dispatch/checkpoint-verification.d.ts +213 -0
  199. package/dist/dispatch/checkpoint-verification.js +381 -0
  200. package/dist/dispatch/checkpoint-verification.js.map +1 -0
  201. package/dist/dispatch/checkpoint.d.ts +329 -0
  202. package/dist/dispatch/checkpoint.js +1418 -0
  203. package/dist/dispatch/checkpoint.js.map +1 -0
  204. package/dist/dispatch/google.d.ts +10 -0
  205. package/dist/dispatch/google.js +158 -0
  206. package/dist/dispatch/google.js.map +1 -0
  207. package/dist/dispatch/late-audit.d.ts +23 -0
  208. package/dist/dispatch/late-audit.js +133 -0
  209. package/dist/dispatch/late-audit.js.map +1 -0
  210. package/dist/dispatch/merge.d.ts +29 -0
  211. package/dist/dispatch/merge.js +112 -0
  212. package/dist/dispatch/merge.js.map +1 -0
  213. package/dist/dispatch/openai-compat.d.ts +30 -0
  214. package/dist/dispatch/openai-compat.js +166 -0
  215. package/dist/dispatch/openai-compat.js.map +1 -0
  216. package/dist/dispatch/openai.d.ts +10 -0
  217. package/dist/dispatch/openai.js +159 -0
  218. package/dist/dispatch/openai.js.map +1 -0
  219. package/dist/dispatch/original-execution.d.ts +30 -0
  220. package/dist/dispatch/original-execution.js +91 -0
  221. package/dist/dispatch/original-execution.js.map +1 -0
  222. package/dist/dispatch/original-launch.d.ts +37 -0
  223. package/dist/dispatch/original-launch.js +117 -0
  224. package/dist/dispatch/original-launch.js.map +1 -0
  225. package/dist/dispatch/quorum.d.ts +15 -0
  226. package/dist/dispatch/quorum.js +33 -0
  227. package/dist/dispatch/quorum.js.map +1 -0
  228. package/dist/dispatch/recovery-operation.d.ts +44 -0
  229. package/dist/dispatch/recovery-operation.js +146 -0
  230. package/dist/dispatch/recovery-operation.js.map +1 -0
  231. package/dist/dispatch/recovery-policy.d.ts +54 -0
  232. package/dist/dispatch/recovery-policy.js +157 -0
  233. package/dist/dispatch/recovery-policy.js.map +1 -0
  234. package/dist/dispatch/recovery.d.ts +78 -0
  235. package/dist/dispatch/recovery.js +299 -0
  236. package/dist/dispatch/recovery.js.map +1 -0
  237. package/dist/dispatch/retained-async.d.ts +13 -0
  238. package/dist/dispatch/retained-async.js +101 -0
  239. package/dist/dispatch/retained-async.js.map +1 -0
  240. package/dist/dispatch/reviewer-identity.d.ts +10 -0
  241. package/dist/dispatch/reviewer-identity.js +19 -0
  242. package/dist/dispatch/reviewer-identity.js.map +1 -0
  243. package/dist/dispatch/runner.d.ts +76 -0
  244. package/dist/dispatch/runner.js +415 -0
  245. package/dist/dispatch/runner.js.map +1 -0
  246. package/dist/dispatch/utils.d.ts +159 -0
  247. package/dist/dispatch/utils.js +381 -0
  248. package/dist/dispatch/utils.js.map +1 -0
  249. package/dist/dispatch/verification-execution.d.ts +48 -0
  250. package/dist/dispatch/verification-execution.js +252 -0
  251. package/dist/dispatch/verification-execution.js.map +1 -0
  252. package/dist/dispatch/verification-late-audit.d.ts +17 -0
  253. package/dist/dispatch/verification-late-audit.js +117 -0
  254. package/dist/dispatch/verification-late-audit.js.map +1 -0
  255. package/dist/evidence/claim-recovery/authenticated-carrier-projection.d.ts +30 -0
  256. package/dist/evidence/claim-recovery/authenticated-carrier-projection.js +100 -0
  257. package/dist/evidence/claim-recovery/authenticated-carrier-projection.js.map +1 -0
  258. package/dist/evidence/claim-recovery/authenticated-receipts.d.ts +46 -0
  259. package/dist/evidence/claim-recovery/authenticated-receipts.js +120 -0
  260. package/dist/evidence/claim-recovery/authenticated-receipts.js.map +1 -0
  261. package/dist/evidence/claim-recovery/carrier-inventory.d.ts +49 -0
  262. package/dist/evidence/claim-recovery/carrier-inventory.js +340 -0
  263. package/dist/evidence/claim-recovery/carrier-inventory.js.map +1 -0
  264. package/dist/evidence/claim-recovery/claim-index.d.ts +12 -0
  265. package/dist/evidence/claim-recovery/claim-index.js +56 -0
  266. package/dist/evidence/claim-recovery/claim-index.js.map +1 -0
  267. package/dist/evidence/claim-recovery/confirmation.d.ts +53 -0
  268. package/dist/evidence/claim-recovery/confirmation.js +151 -0
  269. package/dist/evidence/claim-recovery/confirmation.js.map +1 -0
  270. package/dist/evidence/claim-recovery/context.d.ts +15 -0
  271. package/dist/evidence/claim-recovery/context.js +40 -0
  272. package/dist/evidence/claim-recovery/context.js.map +1 -0
  273. package/dist/evidence/claim-recovery/proof-storage.d.ts +6 -0
  274. package/dist/evidence/claim-recovery/proof-storage.js +97 -0
  275. package/dist/evidence/claim-recovery/proof-storage.js.map +1 -0
  276. package/dist/evidence/claim-recovery/validation/anchors.d.ts +20 -0
  277. package/dist/evidence/claim-recovery/validation/anchors.js +22 -0
  278. package/dist/evidence/claim-recovery/validation/anchors.js.map +1 -0
  279. package/dist/evidence/claim-recovery/validation/carrier-projection.d.ts +9 -0
  280. package/dist/evidence/claim-recovery/validation/carrier-projection.js +364 -0
  281. package/dist/evidence/claim-recovery/validation/carrier-projection.js.map +1 -0
  282. package/dist/evidence/claim-recovery/validation/carrier-types.d.ts +83 -0
  283. package/dist/evidence/claim-recovery/validation/carrier-types.js +2 -0
  284. package/dist/evidence/claim-recovery/validation/carrier-types.js.map +1 -0
  285. package/dist/evidence/claim-recovery/validation/claim-split.d.ts +57 -0
  286. package/dist/evidence/claim-recovery/validation/claim-split.js +209 -0
  287. package/dist/evidence/claim-recovery/validation/claim-split.js.map +1 -0
  288. package/dist/evidence/claim-recovery/validation/claims.d.ts +20 -0
  289. package/dist/evidence/claim-recovery/validation/claims.js +108 -0
  290. package/dist/evidence/claim-recovery/validation/claims.js.map +1 -0
  291. package/dist/evidence/claim-recovery/validation/current-projection.d.ts +36 -0
  292. package/dist/evidence/claim-recovery/validation/current-projection.js +424 -0
  293. package/dist/evidence/claim-recovery/validation/current-projection.js.map +1 -0
  294. package/dist/evidence/claim-recovery/validation/lexical.d.ts +18 -0
  295. package/dist/evidence/claim-recovery/validation/lexical.js +91 -0
  296. package/dist/evidence/claim-recovery/validation/lexical.js.map +1 -0
  297. package/dist/evidence/claim-recovery/validation/materials.d.ts +19 -0
  298. package/dist/evidence/claim-recovery/validation/materials.js +160 -0
  299. package/dist/evidence/claim-recovery/validation/materials.js.map +1 -0
  300. package/dist/evidence/claim-recovery/validation/native-material.d.ts +27 -0
  301. package/dist/evidence/claim-recovery/validation/native-material.js +35 -0
  302. package/dist/evidence/claim-recovery/validation/native-material.js.map +1 -0
  303. package/dist/evidence/claim-recovery/validation/native-occurrences.d.ts +79 -0
  304. package/dist/evidence/claim-recovery/validation/native-occurrences.js +187 -0
  305. package/dist/evidence/claim-recovery/validation/native-occurrences.js.map +1 -0
  306. package/dist/evidence/claim-recovery/validation/native-state.d.ts +45 -0
  307. package/dist/evidence/claim-recovery/validation/native-state.js +385 -0
  308. package/dist/evidence/claim-recovery/validation/native-state.js.map +1 -0
  309. package/dist/evidence/claim-recovery/validation/obligations.d.ts +15 -0
  310. package/dist/evidence/claim-recovery/validation/obligations.js +65 -0
  311. package/dist/evidence/claim-recovery/validation/obligations.js.map +1 -0
  312. package/dist/evidence/claim-recovery/validation/occurrence-source.d.ts +45 -0
  313. package/dist/evidence/claim-recovery/validation/occurrence-source.js +208 -0
  314. package/dist/evidence/claim-recovery/validation/occurrence-source.js.map +1 -0
  315. package/dist/evidence/claim-recovery/validation/occurrence-types.d.ts +101 -0
  316. package/dist/evidence/claim-recovery/validation/occurrence-types.js +2 -0
  317. package/dist/evidence/claim-recovery/validation/occurrence-types.js.map +1 -0
  318. package/dist/evidence/claim-recovery/validation/occurrence.d.ts +30 -0
  319. package/dist/evidence/claim-recovery/validation/occurrence.js +227 -0
  320. package/dist/evidence/claim-recovery/validation/occurrence.js.map +1 -0
  321. package/dist/evidence/claim-recovery/validation/primitives.d.ts +11 -0
  322. package/dist/evidence/claim-recovery/validation/primitives.js +41 -0
  323. package/dist/evidence/claim-recovery/validation/primitives.js.map +1 -0
  324. package/dist/evidence/claim-recovery/validation/receipts.d.ts +37 -0
  325. package/dist/evidence/claim-recovery/validation/receipts.js +83 -0
  326. package/dist/evidence/claim-recovery/validation/receipts.js.map +1 -0
  327. package/dist/evidence/claim-recovery/validation/recovery-json.d.ts +8 -0
  328. package/dist/evidence/claim-recovery/validation/recovery-json.js +42 -0
  329. package/dist/evidence/claim-recovery/validation/recovery-json.js.map +1 -0
  330. package/dist/evidence/claim-recovery/validation/semantic-cache.d.ts +3 -0
  331. package/dist/evidence/claim-recovery/validation/semantic-cache.js +22 -0
  332. package/dist/evidence/claim-recovery/validation/semantic-cache.js.map +1 -0
  333. package/dist/evidence/claim-recovery/validation/semantic-validation.d.ts +9 -0
  334. package/dist/evidence/claim-recovery/validation/semantic-validation.js +347 -0
  335. package/dist/evidence/claim-recovery/validation/semantic-validation.js.map +1 -0
  336. package/dist/evidence/claim-recovery/validation/sighting.d.ts +2 -0
  337. package/dist/evidence/claim-recovery/validation/sighting.js +15 -0
  338. package/dist/evidence/claim-recovery/validation/sighting.js.map +1 -0
  339. package/dist/evidence/claim-recovery/validation/sources.d.ts +15 -0
  340. package/dist/evidence/claim-recovery/validation/sources.js +2 -0
  341. package/dist/evidence/claim-recovery/validation/sources.js.map +1 -0
  342. package/dist/evidence/claim-recovery/validation/types.d.ts +126 -0
  343. package/dist/evidence/claim-recovery/validation/types.js +2 -0
  344. package/dist/evidence/claim-recovery/validation/types.js.map +1 -0
  345. package/dist/evidence/claim-split.d.ts +1 -0
  346. package/dist/evidence/claim-split.js +2 -0
  347. package/dist/evidence/claim-split.js.map +1 -0
  348. package/dist/evidence/event-receipts.d.ts +16 -0
  349. package/dist/evidence/event-receipts.js +41 -0
  350. package/dist/evidence/event-receipts.js.map +1 -0
  351. package/dist/evidence/finding-recovery.d.ts +23 -0
  352. package/dist/evidence/finding-recovery.js +74 -0
  353. package/dist/evidence/finding-recovery.js.map +1 -0
  354. package/dist/evidence/finding-retriage.d.ts +14 -0
  355. package/dist/evidence/finding-retriage.js +57 -0
  356. package/dist/evidence/finding-retriage.js.map +1 -0
  357. package/dist/evidence/format.d.ts +18 -0
  358. package/dist/evidence/format.js +152 -0
  359. package/dist/evidence/format.js.map +1 -0
  360. package/dist/evidence/original-run/decode.d.ts +30 -0
  361. package/dist/evidence/original-run/decode.js +208 -0
  362. package/dist/evidence/original-run/decode.js.map +1 -0
  363. package/dist/evidence/original-run/journal.d.ts +31 -0
  364. package/dist/evidence/original-run/journal.js +157 -0
  365. package/dist/evidence/original-run/journal.js.map +1 -0
  366. package/dist/evidence/original-run/lock-path.d.ts +17 -0
  367. package/dist/evidence/original-run/lock-path.js +116 -0
  368. package/dist/evidence/original-run/lock-path.js.map +1 -0
  369. package/dist/evidence/original-run/lock-scope.d.ts +21 -0
  370. package/dist/evidence/original-run/lock-scope.js +61 -0
  371. package/dist/evidence/original-run/lock-scope.js.map +1 -0
  372. package/dist/evidence/original-run/lock.d.ts +33 -0
  373. package/dist/evidence/original-run/lock.js +259 -0
  374. package/dist/evidence/original-run/lock.js.map +1 -0
  375. package/dist/evidence/original-run/remote.d.ts +23 -0
  376. package/dist/evidence/original-run/remote.js +110 -0
  377. package/dist/evidence/original-run/remote.js.map +1 -0
  378. package/dist/evidence/original-run/source.d.ts +61 -0
  379. package/dist/evidence/original-run/source.js +267 -0
  380. package/dist/evidence/original-run/source.js.map +1 -0
  381. package/dist/evidence/reads.d.ts +11 -0
  382. package/dist/evidence/reads.js +16 -0
  383. package/dist/evidence/reads.js.map +1 -0
  384. package/dist/evidence/recover-finding.d.ts +17 -0
  385. package/dist/evidence/recover-finding.js +65 -0
  386. package/dist/evidence/recover-finding.js.map +1 -0
  387. package/dist/evidence/recover-run.d.ts +26 -0
  388. package/dist/evidence/recover-run.js +180 -0
  389. package/dist/evidence/recover-run.js.map +1 -0
  390. package/dist/evidence/retained-time-budget.d.ts +9 -0
  391. package/dist/evidence/retained-time-budget.js +14 -0
  392. package/dist/evidence/retained-time-budget.js.map +1 -0
  393. package/dist/evidence/retriage-finding.d.ts +12 -0
  394. package/dist/evidence/retriage-finding.js +67 -0
  395. package/dist/evidence/retriage-finding.js.map +1 -0
  396. package/dist/evidence/reviewer-lineage.d.ts +35 -0
  397. package/dist/evidence/reviewer-lineage.js +293 -0
  398. package/dist/evidence/reviewer-lineage.js.map +1 -0
  399. package/dist/evidence/reviewer-original.d.ts +67 -0
  400. package/dist/evidence/reviewer-original.js +148 -0
  401. package/dist/evidence/reviewer-original.js.map +1 -0
  402. package/dist/evidence/reviewer-recovery.d.ts +74 -0
  403. package/dist/evidence/reviewer-recovery.js +179 -0
  404. package/dist/evidence/reviewer-recovery.js.map +1 -0
  405. package/dist/evidence/reviewer-status.d.ts +145 -0
  406. package/dist/evidence/reviewer-status.js +302 -0
  407. package/dist/evidence/reviewer-status.js.map +1 -0
  408. package/dist/evidence/show.d.ts +10 -0
  409. package/dist/evidence/show.js +41 -0
  410. package/dist/evidence/show.js.map +1 -0
  411. package/dist/evidence/status.d.ts +38 -0
  412. package/dist/evidence/status.js +73 -0
  413. package/dist/evidence/status.js.map +1 -0
  414. package/dist/evidence/target.d.ts +17 -0
  415. package/dist/evidence/target.js +74 -0
  416. package/dist/evidence/target.js.map +1 -0
  417. package/dist/evidence/types.d.ts +176 -0
  418. package/dist/evidence/types.js +109 -0
  419. package/dist/evidence/types.js.map +1 -0
  420. package/dist/index.d.ts +2 -0
  421. package/dist/index.js +2688 -0
  422. package/dist/index.js.map +1 -0
  423. package/dist/models/seed.d.ts +44 -0
  424. package/dist/models/seed.js +175 -0
  425. package/dist/models/seed.js.map +1 -0
  426. package/dist/models/server-stats.d.ts +80 -0
  427. package/dist/models/server-stats.js +109 -0
  428. package/dist/models/server-stats.js.map +1 -0
  429. package/dist/models/stats-store.d.ts +84 -0
  430. package/dist/models/stats-store.js +572 -0
  431. package/dist/models/stats-store.js.map +1 -0
  432. package/dist/output/artifacts.d.ts +16 -0
  433. package/dist/output/artifacts.js +29 -0
  434. package/dist/output/artifacts.js.map +1 -0
  435. package/dist/output/assembly-refusal.d.ts +8 -0
  436. package/dist/output/assembly-refusal.js +14 -0
  437. package/dist/output/assembly-refusal.js.map +1 -0
  438. package/dist/output/github.d.ts +12 -0
  439. package/dist/output/github.js +214 -0
  440. package/dist/output/github.js.map +1 -0
  441. package/dist/output/json.d.ts +3 -0
  442. package/dist/output/json.js +8 -0
  443. package/dist/output/json.js.map +1 -0
  444. package/dist/output/markdown.d.ts +3 -0
  445. package/dist/output/markdown.js +281 -0
  446. package/dist/output/markdown.js.map +1 -0
  447. package/dist/output/progress.d.ts +55 -0
  448. package/dist/output/progress.js +180 -0
  449. package/dist/output/progress.js.map +1 -0
  450. package/dist/output/sanitize.d.ts +18 -0
  451. package/dist/output/sanitize.js +46 -0
  452. package/dist/output/sanitize.js.map +1 -0
  453. package/dist/output/terminal.d.ts +2 -0
  454. package/dist/output/terminal.js +172 -0
  455. package/dist/output/terminal.js.map +1 -0
  456. package/dist/prepare/chunker.d.ts +18 -0
  457. package/dist/prepare/chunker.js +325 -0
  458. package/dist/prepare/chunker.js.map +1 -0
  459. package/dist/prepare/language.d.ts +1 -0
  460. package/dist/prepare/language.js +99 -0
  461. package/dist/prepare/language.js.map +1 -0
  462. package/dist/prepare/prompt-builder.d.ts +44 -0
  463. package/dist/prepare/prompt-builder.js +102 -0
  464. package/dist/prepare/prompt-builder.js.map +1 -0
  465. package/dist/prepare/unified-diff.d.ts +40 -0
  466. package/dist/prepare/unified-diff.js +135 -0
  467. package/dist/prepare/unified-diff.js.map +1 -0
  468. package/dist/prompts/base.d.ts +3 -0
  469. package/dist/prompts/base.js +53 -0
  470. package/dist/prompts/base.js.map +1 -0
  471. package/dist/prompts/hardening.d.ts +19 -0
  472. package/dist/prompts/hardening.js +67 -0
  473. package/dist/prompts/hardening.js.map +1 -0
  474. package/dist/prompts/languages/elixir.d.ts +1 -0
  475. package/dist/prompts/languages/elixir.js +13 -0
  476. package/dist/prompts/languages/elixir.js.map +1 -0
  477. package/dist/prompts/languages/generic.d.ts +1 -0
  478. package/dist/prompts/languages/generic.js +11 -0
  479. package/dist/prompts/languages/generic.js.map +1 -0
  480. package/dist/prompts/languages/python.d.ts +1 -0
  481. package/dist/prompts/languages/python.js +14 -0
  482. package/dist/prompts/languages/python.js.map +1 -0
  483. package/dist/prompts/languages/typescript.d.ts +1 -0
  484. package/dist/prompts/languages/typescript.js +13 -0
  485. package/dist/prompts/languages/typescript.js.map +1 -0
  486. package/dist/prompts/plan.d.ts +15 -0
  487. package/dist/prompts/plan.js +55 -0
  488. package/dist/prompts/plan.js.map +1 -0
  489. package/dist/report/aggregation-inputs.d.ts +52 -0
  490. package/dist/report/aggregation-inputs.js +114 -0
  491. package/dist/report/aggregation-inputs.js.map +1 -0
  492. package/dist/report/assembly.d.ts +43 -0
  493. package/dist/report/assembly.js +133 -0
  494. package/dist/report/assembly.js.map +1 -0
  495. package/dist/report/blocking-health.d.ts +64 -0
  496. package/dist/report/blocking-health.js +156 -0
  497. package/dist/report/blocking-health.js.map +1 -0
  498. package/dist/report/checkpoint-assembly.d.ts +10 -0
  499. package/dist/report/checkpoint-assembly.js +17 -0
  500. package/dist/report/checkpoint-assembly.js.map +1 -0
  501. package/dist/report/checkpoint-consensus.d.ts +59 -0
  502. package/dist/report/checkpoint-consensus.js +197 -0
  503. package/dist/report/checkpoint-consensus.js.map +1 -0
  504. package/dist/report/checkpoint-gating.d.ts +36 -0
  505. package/dist/report/checkpoint-gating.js +110 -0
  506. package/dist/report/checkpoint-gating.js.map +1 -0
  507. package/dist/report/checkpoint-projection.d.ts +76 -0
  508. package/dist/report/checkpoint-projection.js +122 -0
  509. package/dist/report/checkpoint-projection.js.map +1 -0
  510. package/dist/report/consensus-assembly.d.ts +37 -0
  511. package/dist/report/consensus-assembly.js +33 -0
  512. package/dist/report/consensus-assembly.js.map +1 -0
  513. package/dist/report/recovery-source.d.ts +7 -0
  514. package/dist/report/recovery-source.js +7 -0
  515. package/dist/report/recovery-source.js.map +1 -0
  516. package/dist/report/reviewer-artifact.d.ts +118 -0
  517. package/dist/report/reviewer-artifact.js +381 -0
  518. package/dist/report/reviewer-artifact.js.map +1 -0
  519. package/dist/report/reviewer-evidence-schema.d.ts +42 -0
  520. package/dist/report/reviewer-evidence-schema.js +28 -0
  521. package/dist/report/reviewer-evidence-schema.js.map +1 -0
  522. package/dist/report/reviewer-evidence.d.ts +54 -0
  523. package/dist/report/reviewer-evidence.js +229 -0
  524. package/dist/report/reviewer-evidence.js.map +1 -0
  525. package/dist/report/reviewer-health.d.ts +62 -0
  526. package/dist/report/reviewer-health.js +105 -0
  527. package/dist/report/reviewer-health.js.map +1 -0
  528. package/dist/report/run-header.d.ts +219 -0
  529. package/dist/report/run-header.js +307 -0
  530. package/dist/report/run-header.js.map +1 -0
  531. package/dist/report/supplemental-async.d.ts +19 -0
  532. package/dist/report/supplemental-async.js +85 -0
  533. package/dist/report/supplemental-async.js.map +1 -0
  534. package/dist/report/uuid.d.ts +16 -0
  535. package/dist/report/uuid.js +41 -0
  536. package/dist/report/uuid.js.map +1 -0
  537. package/dist/resolver/git.d.ts +16 -0
  538. package/dist/resolver/git.js +77 -0
  539. package/dist/resolver/git.js.map +1 -0
  540. package/dist/resolver/github-client.d.ts +82 -0
  541. package/dist/resolver/github-client.js +27 -0
  542. package/dist/resolver/github-client.js.map +1 -0
  543. package/dist/resolver/github.d.ts +29 -0
  544. package/dist/resolver/github.js +166 -0
  545. package/dist/resolver/github.js.map +1 -0
  546. package/dist/resolver/local.d.ts +3 -0
  547. package/dist/resolver/local.js +75 -0
  548. package/dist/resolver/local.js.map +1 -0
  549. package/dist/resolver/plan.d.ts +8 -0
  550. package/dist/resolver/plan.js +44 -0
  551. package/dist/resolver/plan.js.map +1 -0
  552. package/dist/resolver/target.d.ts +35 -0
  553. package/dist/resolver/target.js +112 -0
  554. package/dist/resolver/target.js.map +1 -0
  555. package/dist/resolver/types.d.ts +43 -0
  556. package/dist/resolver/types.js +2 -0
  557. package/dist/resolver/types.js.map +1 -0
  558. package/dist/roles/builtin.d.ts +4 -0
  559. package/dist/roles/builtin.js +282 -0
  560. package/dist/roles/builtin.js.map +1 -0
  561. package/dist/roles/dispatcher.d.ts +26 -0
  562. package/dist/roles/dispatcher.js +96 -0
  563. package/dist/roles/dispatcher.js.map +1 -0
  564. package/dist/roles/legacy-4.1.10.d.ts +2 -0
  565. package/dist/roles/legacy-4.1.10.js +320 -0
  566. package/dist/roles/legacy-4.1.10.js.map +1 -0
  567. package/dist/roles/legacy-4.4.9.d.ts +4 -0
  568. package/dist/roles/legacy-4.4.9.js +282 -0
  569. package/dist/roles/legacy-4.4.9.js.map +1 -0
  570. package/dist/roles/loader.d.ts +10 -0
  571. package/dist/roles/loader.js +104 -0
  572. package/dist/roles/loader.js.map +1 -0
  573. package/dist/roles/types.d.ts +13 -0
  574. package/dist/roles/types.js +2 -0
  575. package/dist/roles/types.js.map +1 -0
  576. package/dist/telemetry/abort-signal.d.ts +13 -0
  577. package/dist/telemetry/abort-signal.js +40 -0
  578. package/dist/telemetry/abort-signal.js.map +1 -0
  579. package/dist/telemetry/attest.d.ts +98 -0
  580. package/dist/telemetry/attest.js +331 -0
  581. package/dist/telemetry/attest.js.map +1 -0
  582. package/dist/telemetry/attested-retry.d.ts +79 -0
  583. package/dist/telemetry/attested-retry.js +195 -0
  584. package/dist/telemetry/attested-retry.js.map +1 -0
  585. package/dist/telemetry/attested-reviewer-delivery.d.ts +58 -0
  586. package/dist/telemetry/attested-reviewer-delivery.js +238 -0
  587. package/dist/telemetry/attested-reviewer-delivery.js.map +1 -0
  588. package/dist/telemetry/backfill.d.ts +95 -0
  589. package/dist/telemetry/backfill.js +460 -0
  590. package/dist/telemetry/backfill.js.map +1 -0
  591. package/dist/telemetry/credentials.d.ts +44 -0
  592. package/dist/telemetry/credentials.js +87 -0
  593. package/dist/telemetry/credentials.js.map +1 -0
  594. package/dist/telemetry/deliver.d.ts +152 -0
  595. package/dist/telemetry/deliver.js +662 -0
  596. package/dist/telemetry/deliver.js.map +1 -0
  597. package/dist/telemetry/envelope-validation.d.ts +13 -0
  598. package/dist/telemetry/envelope-validation.js +196 -0
  599. package/dist/telemetry/envelope-validation.js.map +1 -0
  600. package/dist/telemetry/envelope.d.ts +159 -0
  601. package/dist/telemetry/envelope.js +288 -0
  602. package/dist/telemetry/envelope.js.map +1 -0
  603. package/dist/telemetry/events.d.ts +68 -0
  604. package/dist/telemetry/events.js +79 -0
  605. package/dist/telemetry/events.js.map +1 -0
  606. package/dist/telemetry/notice.d.ts +15 -0
  607. package/dist/telemetry/notice.js +67 -0
  608. package/dist/telemetry/notice.js.map +1 -0
  609. package/dist/telemetry/outbox.d.ts +166 -0
  610. package/dist/telemetry/outbox.js +741 -0
  611. package/dist/telemetry/outbox.js.map +1 -0
  612. package/dist/telemetry/quarantine.d.ts +105 -0
  613. package/dist/telemetry/quarantine.js +224 -0
  614. package/dist/telemetry/quarantine.js.map +1 -0
  615. package/dist/telemetry/read-sink.d.ts +31 -0
  616. package/dist/telemetry/read-sink.js +25 -0
  617. package/dist/telemetry/read-sink.js.map +1 -0
  618. package/dist/telemetry/recovery/command.d.ts +16 -0
  619. package/dist/telemetry/recovery/command.js +70 -0
  620. package/dist/telemetry/recovery/command.js.map +1 -0
  621. package/dist/telemetry/recovery/discovery.d.ts +8 -0
  622. package/dist/telemetry/recovery/discovery.js +277 -0
  623. package/dist/telemetry/recovery/discovery.js.map +1 -0
  624. package/dist/telemetry/recovery/files.d.ts +20 -0
  625. package/dist/telemetry/recovery/files.js +123 -0
  626. package/dist/telemetry/recovery/files.js.map +1 -0
  627. package/dist/telemetry/recovery/plan.d.ts +10 -0
  628. package/dist/telemetry/recovery/plan.js +325 -0
  629. package/dist/telemetry/recovery/plan.js.map +1 -0
  630. package/dist/telemetry/recovery/source.d.ts +328 -0
  631. package/dist/telemetry/recovery/source.js +253 -0
  632. package/dist/telemetry/recovery/source.js.map +1 -0
  633. package/dist/telemetry/recovery/types.d.ts +100 -0
  634. package/dist/telemetry/recovery/types.js +2 -0
  635. package/dist/telemetry/recovery/types.js.map +1 -0
  636. package/dist/telemetry/recovery-request-budget.d.ts +39 -0
  637. package/dist/telemetry/recovery-request-budget.js +102 -0
  638. package/dist/telemetry/recovery-request-budget.js.map +1 -0
  639. package/dist/telemetry/report-consistency.d.ts +11 -0
  640. package/dist/telemetry/report-consistency.js +111 -0
  641. package/dist/telemetry/report-consistency.js.map +1 -0
  642. package/dist/telemetry/reviewer-delivery.d.ts +39 -0
  643. package/dist/telemetry/reviewer-delivery.js +279 -0
  644. package/dist/telemetry/reviewer-delivery.js.map +1 -0
  645. package/dist/telemetry/reviewer-preflight.d.ts +13 -0
  646. package/dist/telemetry/reviewer-preflight.js +56 -0
  647. package/dist/telemetry/reviewer-preflight.js.map +1 -0
  648. package/dist/telemetry/scrub.d.ts +53 -0
  649. package/dist/telemetry/scrub.js +205 -0
  650. package/dist/telemetry/scrub.js.map +1 -0
  651. package/dist/telemetry/sink.d.ts +212 -0
  652. package/dist/telemetry/sink.js +813 -0
  653. package/dist/telemetry/sink.js.map +1 -0
  654. package/dist/telemetry/terminal-reviewer-delivery.d.ts +18 -0
  655. package/dist/telemetry/terminal-reviewer-delivery.js +27 -0
  656. package/dist/telemetry/terminal-reviewer-delivery.js.map +1 -0
  657. package/dist/telemetry/transient-input.d.ts +2 -0
  658. package/dist/telemetry/transient-input.js +37 -0
  659. package/dist/telemetry/transient-input.js.map +1 -0
  660. package/dist/telemetry/verification.d.ts +9 -0
  661. package/dist/telemetry/verification.js +12 -0
  662. package/dist/telemetry/verification.js.map +1 -0
  663. package/package.json +73 -0
@@ -0,0 +1,813 @@
1
+ import { reviewCycleReceiptSchema } from '../converge/review-cycle.js';
2
+ import { createHash } from 'node:crypto';
3
+ import { abortSignalWithTimeout } from './abort-signal.js';
4
+ import { claimDescriptorSchema } from '../consensus/claim-identity.js';
5
+ import { decodeOriginalReport } from '../evidence/original-run/decode.js';
6
+ import { normalizeUrl } from './credentials.js';
7
+ import { scrubText } from './scrub.js';
8
+ import { parseAttestedExpiry } from './attested-retry.js';
9
+ import { MAX_ARTIFACT_BYTES } from './envelope-validation.js';
10
+ /**
11
+ * The HTTP side of evidence (epic IO-12475, sections 8.4 and 9): POST the
12
+ * envelope, PUT each declared artifact, POST converge events, and GET what
13
+ * Harness holds (a pull request's gate status, one run). Every request
14
+ * normally runs under a 10 s timeout (artifacts have bounded headroom), carries the client handshake the server's
15
+ * version floor reads, and sends the token only to the host that minted it
16
+ * (the credential is a `{url, token}` pair resolved elsewhere).
17
+ *
18
+ * Outcomes are classified for the caller: what the server accepted, what
19
+ * it refused for good (never retried), what is switched off for the org,
20
+ * and what could not be reached (spooled and retried later).
21
+ */
22
+ export const REQUEST_TIMEOUT_MS = 10_000;
23
+ /** Explicit envelope retries may wait longer for an idempotent receipt. */
24
+ export const MAX_ENVELOPE_TIMEOUT_MS = 120_000;
25
+ export function validateEnvelopeTimeoutMs(timeoutMs) {
26
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > MAX_ENVELOPE_TIMEOUT_MS) {
27
+ throw new Error(`Envelope timeout must be an integer between 1 and ${MAX_ENVELOPE_TIMEOUT_MS} ms.`);
28
+ }
29
+ return timeoutMs;
30
+ }
31
+ /** Bounded transfer headroom for supported 25 MB artifacts, not a throughput guarantee. */
32
+ export const ARTIFACT_TRANSFER_TIMEOUT_MS = 120_000;
33
+ /** A receipt is a few hundred bytes; anything past this is not a Harness answer. */
34
+ export const MAX_RESPONSE_BYTES = 64 * 1024;
35
+ /** A read carries a run's findings and calls (up to the bounded evidence envelope) or a gate status; anything past this is not one. */
36
+ export const MAX_READ_RESPONSE_BYTES = 8 * 1024 * 1024;
37
+ const SIGHTING_BINDING_FIELDS = ['version', 'finding_ref', 'report_json_sha256', 'claim_descriptor', 'match_rationale'];
38
+ /** Inspect retained bindings without normalizing or replacing their original values. */
39
+ export function validSightingBinding(entry) {
40
+ const key = (value) => typeof value === 'string' && value.length > 0 && value.length <= 64;
41
+ return entry.version === 1 && key(entry.identity_key) && key(entry.matched_identity) &&
42
+ ['new', 'repeat', 'suppressed', 'regating'].includes(entry.status) &&
43
+ typeof entry.finding_ref === 'string' && Buffer.byteLength(entry.finding_ref, 'utf8') > 0 &&
44
+ Buffer.byteLength(entry.finding_ref, 'utf8') <= 32 &&
45
+ typeof entry.report_json_sha256 === 'string' && /^[a-f0-9]{64}(?![\s\S])/.test(entry.report_json_sha256) &&
46
+ claimDescriptorSchema.safeParse(entry.claim_descriptor).success &&
47
+ ['new_claim', 'exact_descriptor', 'supported_paraphrase', 'ambiguous', 'explicit_split'].includes(entry.match_rationale) &&
48
+ (!Object.hasOwn(entry, 'pending_round') || entry.pending_round === null ||
49
+ (typeof entry.pending_round === 'number' && Number.isSafeInteger(entry.pending_round) && entry.pending_round >= 1));
50
+ }
51
+ function remainingRequestOptions(options, deadline) {
52
+ if (deadline === undefined)
53
+ return options;
54
+ const timeoutMs = Math.floor(deadline - performance.now());
55
+ return timeoutMs < 1 ? null : { ...options, timeoutMs };
56
+ }
57
+ export class HarnessSink {
58
+ credential;
59
+ rclVersion;
60
+ fetchImpl;
61
+ timeoutMs;
62
+ envelopeTimeoutMs;
63
+ artifactTimeoutMs;
64
+ artifactExpiry;
65
+ requestBudget;
66
+ constructor(options) {
67
+ if (options.envelopeTimeoutMs !== undefined)
68
+ validateEnvelopeTimeoutMs(options.envelopeTimeoutMs);
69
+ // The token travels to the host that minted it, over TLS (loopback
70
+ // excepted) — re-checked here so no caller can pair it with another URL.
71
+ // A trailing slash is the same origin and is normalized away.
72
+ const url = normalizeUrl(options.credential.url);
73
+ if (url === null) {
74
+ throw new Error(`Harness credential URL is not a deliverable base URL: ${hostOnly(options.credential.url)}`);
75
+ }
76
+ this.credential = { ...options.credential, url };
77
+ this.rclVersion = options.rclVersion;
78
+ this.fetchImpl = options.fetchImpl ?? fetch;
79
+ this.timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
80
+ if (options.envelopeTimeoutMs !== undefined) {
81
+ this.envelopeTimeoutMs = Math.min(options.envelopeTimeoutMs, options.timeoutMs ?? Infinity);
82
+ if (this.credentialSource === 'attest' && options.attestedExpiresAt === undefined) {
83
+ this.envelopeTimeoutMs = Math.min(this.envelopeTimeoutMs, REQUEST_TIMEOUT_MS);
84
+ }
85
+ }
86
+ this.artifactTimeoutMs = Math.min(ARTIFACT_TRANSFER_TIMEOUT_MS, options.timeoutMs ?? ARTIFACT_TRANSFER_TIMEOUT_MS);
87
+ if (this.credentialSource === 'attest') {
88
+ if (options.attestedExpiresAt === undefined) {
89
+ this.artifactTimeoutMs = Math.min(this.artifactTimeoutMs, REQUEST_TIMEOUT_MS);
90
+ }
91
+ else {
92
+ // Use the same accepted timestamp formats and monotonic lifetime rule
93
+ // as envelope recovery. A backwards wall clock cannot extend validity.
94
+ const epoch = parseAttestedExpiry(options.attestedExpiresAt) ?? 0;
95
+ this.artifactExpiry = { epoch, remainingMs: epoch - Date.now(), startedAt: performance.now() };
96
+ }
97
+ }
98
+ this.requestBudget = options.requestBudget;
99
+ }
100
+ get baseUrl() {
101
+ return this.credential.url;
102
+ }
103
+ /** Recovery never substitutes an ordinary credential for an attested one. */
104
+ get credentialSource() { return this.credential.token.startsWith('rbc_') ? 'attest' : this.credential.source; }
105
+ /** Actor-authorized cycle creation; never fall back from a run-bound credential. */
106
+ async startReviewCycle(repo, prNumber, request) {
107
+ if (this.credentialSource === 'attest')
108
+ throw new Error('fresh_review_requires_actor_credential');
109
+ if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo) || repo.split('/').some(segment => segment === '.' || segment === '..') || !Number.isSafeInteger(prNumber) || prNumber < 1)
110
+ throw new Error('fresh_review_requires_pr');
111
+ const path = `/api/v1/reviews/prs/${repo.split('/').map(encodeURIComponent).join('/')}/${prNumber}/cycles`;
112
+ const result = await this.request('POST', path, JSON.stringify(request), 'application/json');
113
+ return this.classify(result, body => {
114
+ const receipt = reviewCycleReceiptSchema.safeParse(body?.data);
115
+ return receipt.success ? receipt.data : null;
116
+ });
117
+ }
118
+ /** Exact bounded raw artifact read; never decode/re-encode original evidence. */
119
+ async getArtifact(runId, kind, limit, options = {}) {
120
+ if (!Number.isSafeInteger(limit) || limit < 0 || limit > 25_000_000)
121
+ throw new Error('invalid_artifact_read_limit');
122
+ if (kind !== 'report_json' && kind !== 'report_md')
123
+ return { kind: 'rejected', httpStatus: 0, error: 'unknown_artifact_kind', message: 'Unsupported artifact selection' };
124
+ if (this.artifactBudget(options) <= 0)
125
+ return { kind: 'unavailable', reason: 'artifact_transfer_expired' };
126
+ let dispose;
127
+ try {
128
+ const fetched = await this.fetchRequest(`${this.baseUrl}/api/v1/reviews/runs/${encodeURIComponent(runId)}/artifacts/${kind}`, {
129
+ method: 'GET', headers: this.headers('application/octet-stream'), redirect: 'manual',
130
+ }, () => this.artifactBudget(options), undefined, options.signal, this.artifactLifetime(options));
131
+ const { response, rateLimit } = fetched;
132
+ dispose = fetched.dispose;
133
+ if (response.status !== 200) {
134
+ const text = await readBounded(response, MAX_RESPONSE_BYTES);
135
+ let body = null;
136
+ try {
137
+ body = JSON.parse(text ?? 'null');
138
+ }
139
+ catch { /* Untrusted failure text is not a receipt. */ }
140
+ return this.classify({ status: response.type === 'opaqueredirect' ? 302 : response.status, body, ...(rateLimit ? { rateLimit } : {}) }, () => null);
141
+ }
142
+ const bytes = await readBoundedBytes(response, limit);
143
+ const digest = response.headers.get('x-artifact-sha256');
144
+ if (bytes === null || digest === null || !/^[0-9a-f]{64}(?![\s\S])/.test(digest) || createHash('sha256').update(bytes).digest('hex') !== digest) {
145
+ return { kind: 'rejected', httpStatus: 200, error: 'malformed_artifact_response', message: 'Raw artifact bytes do not match their receipt' };
146
+ }
147
+ return { kind: 'ok', httpStatus: 200, value: { bytes, sha256: digest } };
148
+ }
149
+ catch {
150
+ return { kind: 'unavailable', reason: 'artifact_read_failed' };
151
+ }
152
+ finally {
153
+ dispose?.();
154
+ }
155
+ }
156
+ /** Current authenticated capability only; never a paid-call or source-authority grant. */
157
+ async checkReviewerRecovery(options = {}) {
158
+ const budget = Math.min(this.timeoutMs, this.artifactBudget(options));
159
+ if (!Number.isFinite(budget) || budget <= 0)
160
+ return { kind: 'unavailable', reason: 'reviewer_artifact_expired' };
161
+ const response = await this.request('GET', '/api/v1/reviews/model-stats', undefined, 'application/json', { ...options, maxResponseBytes: MAX_RESPONSE_BYTES }, budget);
162
+ if ('failure' in response)
163
+ return { kind: 'unavailable', reason: 'reviewer_capability_unavailable' };
164
+ if (response.status !== 200)
165
+ return privateArtifactFailure(response.status);
166
+ const body = response.body;
167
+ if (!Array.isArray(body?.data?.models) || body?.meta?.reviewer_recovery_protocol !== 2 ||
168
+ body.meta.reviewer_checkpoint_plan_version !== 2 || body.meta.reviewer_capture_version !== 2 ||
169
+ body.meta.reviewer_provider_concurrency_version !== 1 || body.meta.reviewer_artifact_schema !== 1 || body.meta.reviewer_artifact_max_bytes !== MAX_ARTIFACT_BYTES) {
170
+ return { kind: 'rejected', httpStatus: 200, error: 'unsupported_reviewer_recovery', message: 'Private reviewer recovery is not supported by this credential endpoint' };
171
+ }
172
+ return { kind: 'ok', httpStatus: 200, value: { protocol: 2, schema: 1, planVersion: 2, captureVersion: 2, providerConcurrencyVersion: 1, maxBytes: MAX_ARTIFACT_BYTES } };
173
+ }
174
+ /** Exact private upload. The server independently authorizes the owner and validates the declared pair. */
175
+ async putReviewerArtifact(runId, bytes, expected, options = {}) {
176
+ if (!privateReference(runId, expected) || typeof bytes !== 'string' || Buffer.byteLength(bytes) !== expected.bytes ||
177
+ createHash('sha256').update(bytes, 'utf8').digest('hex') !== expected.sha256)
178
+ return privateArtifactFailure(0);
179
+ const startedAt = performance.now(), totalBudget = this.artifactBudget(options);
180
+ const capability = await this.checkReviewerRecovery(options);
181
+ if (capability.kind !== 'ok')
182
+ return capability;
183
+ const budget = Math.min(this.artifactBudget(options), totalBudget - (performance.now() - startedAt));
184
+ if (!Number.isFinite(budget) || budget <= 0)
185
+ return { kind: 'unavailable', reason: 'reviewer_artifact_expired' };
186
+ const response = await this.request('PUT', `/api/v1/reviews/runs/${encodeURIComponent(runId)}/reviewer-artifact`, bytes, 'application/octet-stream', { ...options, maxResponseBytes: MAX_RESPONSE_BYTES }, budget);
187
+ if ('failure' in response)
188
+ return { kind: 'unavailable', reason: 'reviewer_artifact_transfer_failed' };
189
+ if (response.status !== 200 && response.status !== 201)
190
+ return privateArtifactFailure(response.status);
191
+ const body = response.body;
192
+ const status = response.status === 201 ? 'created' : 'existing';
193
+ if (!privateReceipt(body?.data, runId, expected) || body?.meta?.status !== status)
194
+ return privateArtifactFailure(0);
195
+ return { kind: 'ok', httpStatus: response.status, value: { runId, sha256: expected.sha256, bytes: expected.bytes, status } };
196
+ }
197
+ /** Private owner/grant read. Only an exact, owner-checked pending response permits a subsequent PUT. */
198
+ async getReviewerArtifact(runId, expected, options = {}) {
199
+ if (!privateReference(runId, expected))
200
+ return privateArtifactFailure(0);
201
+ const startedAt = performance.now(), totalBudget = this.artifactBudget(options);
202
+ const capability = await this.checkReviewerRecovery(options);
203
+ if (capability.kind !== 'ok')
204
+ return capability;
205
+ const budget = Math.min(this.artifactBudget(options), totalBudget - (performance.now() - startedAt));
206
+ if (!Number.isFinite(budget) || budget <= 0)
207
+ return { kind: 'unavailable', reason: 'reviewer_artifact_expired' };
208
+ const signal = abortSignalWithTimeout(options.signal, budget);
209
+ try {
210
+ signal.signal.throwIfAborted();
211
+ const response = await this.fetchImpl(`${this.baseUrl}/api/v1/reviews/runs/${encodeURIComponent(runId)}/reviewer-artifact`, {
212
+ method: 'GET', headers: { ...this.headers('application/octet-stream'), accept: 'application/octet-stream' }, redirect: 'manual', signal: signal.signal,
213
+ });
214
+ if (response.status !== 200) {
215
+ const raw = await readBounded(response, MAX_RESPONSE_BYTES);
216
+ let body = null;
217
+ try {
218
+ body = JSON.parse(raw ?? 'null');
219
+ }
220
+ catch { /* Never surface private response text. */ }
221
+ if (response.status === 404 && body?.error === 'reviewer_artifact_pending' && privateReceipt(body.data, runId, expected)) {
222
+ const data = body.data;
223
+ return { kind: 'pending', runId, sha256: data.sha256, bytes: data.bytes };
224
+ }
225
+ return privateArtifactFailure(response.type === 'opaqueredirect' ? 302 : response.status);
226
+ }
227
+ const bytes = await readBoundedBytes(response, expected.bytes ?? MAX_ARTIFACT_BYTES);
228
+ const headers = response.headers;
229
+ if (bytes === null || (expected.bytes !== undefined && bytes.length !== expected.bytes) ||
230
+ headers.get('x-artifact-sha256') !== expected.sha256 || createHash('sha256').update(bytes).digest('hex') !== expected.sha256 ||
231
+ headers.get('content-type')?.split(';')[0]?.trim().toLowerCase() !== 'application/octet-stream' ||
232
+ !['private', 'no-store'].every(directive => headers.get('cache-control')?.split(',').map(value => value.trim().toLowerCase()).includes(directive)) ||
233
+ headers.get('cache-control')?.split(',').map(value => value.trim().toLowerCase()).includes('public') ||
234
+ !/^attachment(?:;|$)/i.test(headers.get('content-disposition') ?? '') || headers.get('x-content-type-options')?.toLowerCase() !== 'nosniff') {
235
+ return privateArtifactFailure(0);
236
+ }
237
+ return { kind: 'ok', httpStatus: 200, value: { bytes, sha256: expected.sha256 } };
238
+ }
239
+ catch {
240
+ return { kind: 'unavailable', reason: 'reviewer_artifact_read_failed' };
241
+ }
242
+ finally {
243
+ signal.dispose();
244
+ }
245
+ }
246
+ /** The reservation is scheduling only; callers must still prove source and packet freshness. */
247
+ async reserveRecoveryWrite() {
248
+ return this.requestBudget?.reserveWrite();
249
+ }
250
+ releaseRecoveryWrite(permit) {
251
+ if (permit)
252
+ this.requestBudget?.releaseWrite(permit);
253
+ }
254
+ artifactLifetime(options) {
255
+ const expiry = this.artifactExpiry;
256
+ if (!expiry)
257
+ return options.timeoutMs;
258
+ return Math.min(options.timeoutMs ?? Infinity, expiry.epoch - Date.now(), expiry.remainingMs - (performance.now() - expiry.startedAt));
259
+ }
260
+ artifactBudget(options) {
261
+ return Math.min(this.artifactTimeoutMs, this.artifactLifetime(options) ?? this.artifactTimeoutMs);
262
+ }
263
+ async fetchRequest(url, init, timeoutMs, permit, parentSignal, operationTimeoutMs) {
264
+ const deadline = operationTimeoutMs === undefined ? undefined : performance.now() + operationTimeoutMs;
265
+ const operation = operationTimeoutMs === undefined ? undefined : abortSignalWithTimeout(parentSignal, operationTimeoutMs);
266
+ const signal = operation?.signal ?? parentSignal;
267
+ try {
268
+ for (;;) {
269
+ signal?.throwIfAborted();
270
+ if (!permit && this.requestBudget)
271
+ await this.requestBudget.acquire(signal);
272
+ signal?.throwIfAborted();
273
+ // Admission and 429 pacing cannot renew an explicit caller deadline or
274
+ // credential lifetime. The ordinary network timer starts after admission.
275
+ const remaining = Math.min(typeof timeoutMs === 'function' ? timeoutMs() : timeoutMs, deadline === undefined ? Infinity : deadline - performance.now());
276
+ if (remaining <= 0)
277
+ throw new DOMException('The operation was aborted due to timeout', 'TimeoutError');
278
+ const lease = abortSignalWithTimeout(signal, remaining);
279
+ try {
280
+ lease.signal.throwIfAborted();
281
+ if (permit) {
282
+ if (init.method !== 'POST' || !this.requestBudget)
283
+ throw new Error('recovery_write_permit_invalid');
284
+ this.requestBudget.consumeWrite(permit);
285
+ }
286
+ const response = await this.fetchImpl(url, { ...init, signal: lease.signal });
287
+ // Keep both leases until the caller has consumed the response body too.
288
+ const dispose = () => { lease.dispose(); operation?.dispose(); };
289
+ if (response.status !== 429 || !this.requestBudget)
290
+ return { response, dispose };
291
+ const rateLimit = this.requestBudget.rateLimited(response.headers.get('retry-after'), response.headers.get('date'));
292
+ // Reads may restart after an explicit rejection. Writes remain receipt-driven;
293
+ // neither a 429 nor a lost acknowledgment authorizes a blind POST retry.
294
+ if (init.method !== 'GET' || !rateLimit.retryable)
295
+ return { response, rateLimit, dispose };
296
+ await response.body?.cancel().catch(() => undefined);
297
+ lease.dispose();
298
+ }
299
+ catch (error) {
300
+ lease.dispose();
301
+ throw error;
302
+ }
303
+ }
304
+ }
305
+ catch (error) {
306
+ operation?.dispose();
307
+ throw error;
308
+ }
309
+ }
310
+ headers(contentType) {
311
+ return {
312
+ authorization: `Bearer ${this.credential.token}`,
313
+ 'content-type': contentType,
314
+ accept: 'application/json',
315
+ 'x-harness-client': 'rcl',
316
+ 'x-harness-client-version': this.rclVersion,
317
+ 'user-agent': `rcl/${this.rclVersion} node/${process.versions.node}`,
318
+ };
319
+ }
320
+ async request(method, path, body, contentType, options = {}, timeoutCeilingMs = this.timeoutMs) {
321
+ let dispose;
322
+ try {
323
+ const fetched = await this.fetchRequest(`${this.credential.url}${path}`, {
324
+ method,
325
+ headers: this.headers(contentType),
326
+ ...(body !== undefined ? { body } : {}),
327
+ redirect: 'manual',
328
+ }, timeoutCeilingMs, options.recoveryWritePermit, options.signal, options.timeoutMs);
329
+ const { response, rateLimit } = fetched;
330
+ dispose = fetched.dispose;
331
+ // Node returns a manual redirect as the 3xx itself; a WHATWG client
332
+ // returns an opaque redirect with status 0. Both read as "redirected".
333
+ if (response.type === 'opaqueredirect')
334
+ return { status: 302, body: null };
335
+ const text = await readBounded(response, options.maxResponseBytes ?? MAX_RESPONSE_BYTES);
336
+ if (text === null) {
337
+ return { status: response.status, body: { error: 'malformed_response', message: 'response larger than the receipt limit' }, ...(rateLimit ? { rateLimit } : {}) };
338
+ }
339
+ let parsed = null;
340
+ if (text !== '') {
341
+ try {
342
+ if (options.requireCompleteRead) {
343
+ const decoded = decodeOriginalReport(text, { exactNumbers: true });
344
+ if (decoded.transformations.length)
345
+ throw new Error('transformed_receipt');
346
+ parsed = decoded.value;
347
+ }
348
+ else
349
+ parsed = JSON.parse(text);
350
+ }
351
+ catch {
352
+ parsed = options.requireCompleteRead
353
+ ? { error: 'malformed_response', message: 'invalid or ambiguous complete read response' }
354
+ : { message: text.slice(0, 200) };
355
+ }
356
+ }
357
+ return { status: response.status, body: parsed, ...(rateLimit ? { rateLimit } : {}) };
358
+ }
359
+ catch (err) {
360
+ return { failure: transportFailure(err) };
361
+ }
362
+ finally {
363
+ dispose?.();
364
+ }
365
+ }
366
+ classify(result, onOk) {
367
+ if ('failure' in result)
368
+ return { kind: 'unavailable', reason: result.failure };
369
+ const { status, body } = result;
370
+ const error = body?.error ?? '';
371
+ const message = body?.message ?? '';
372
+ if (status >= 200 && status < 300) {
373
+ if (error === 'malformed_response')
374
+ return { kind: 'rejected', httpStatus: status, error, message };
375
+ const value = onOk(body, status);
376
+ if (value === null)
377
+ return { kind: 'rejected', httpStatus: status, error: 'malformed_response', message: 'unexpected response body' };
378
+ return { kind: 'ok', value, httpStatus: status };
379
+ }
380
+ if (status === 403 && (error === 'reviews_disabled' || error === 'artifacts_disabled')) {
381
+ return { kind: 'disabled', reason: error, message };
382
+ }
383
+ if (status === 409)
384
+ return { kind: 'conflict', message };
385
+ if (status === 429 && result.rateLimit)
386
+ return {
387
+ kind: 'unavailable', reason: 'HTTP 429', httpStatus: 429, rateLimitReason: result.rateLimit.reason,
388
+ ...(result.rateLimit.retryAfterMs !== undefined ? { retryAfterMs: result.rateLimit.retryAfterMs } : {}),
389
+ };
390
+ if (status >= 500 || status === 429 || status === 408) {
391
+ return { kind: 'unavailable', reason: `HTTP ${status}${message ? ` ${message}` : ''}` };
392
+ }
393
+ // A rejected credential is not a rejected run: after `harness login` (or
394
+ // a fresh CI token) the same entry can still land, so it stays spooled.
395
+ if (status === 401) {
396
+ return { kind: 'unavailable', reason: `HTTP 401 credential rejected${message ? ` (${message})` : ''} - log in again` };
397
+ }
398
+ if (status >= 300 && status < 400) {
399
+ return { kind: 'rejected', httpStatus: status, error: 'redirected', message: 'the server redirected the request — check HARNESS_API_URL / the login host (redirects are not followed with a token)' };
400
+ }
401
+ return { kind: 'rejected', httpStatus: status, error: error || `http_${status}`, message };
402
+ }
403
+ /** Capability is read at the credential's own host; no speculative write. */
404
+ async requireEvidenceProtocol(options, boundClassification = false) {
405
+ // Old servers silently discard unknown provenance. The attested credential
406
+ // may read model-stats, but may not list runs or use an ordinary login.
407
+ const attested = this.credential.source === 'attest';
408
+ // Capability reads use normal read admission; the reserved slot belongs
409
+ // exclusively to the subsequent POST and must survive a failed preflight.
410
+ const { recoveryWritePermit: _permit, ...readOptions } = options;
411
+ const capability = await this.getJson(attested ? '/api/v1/reviews/model-stats' : '/api/v1/reviews/runs?page_size=1', (data, meta) => {
412
+ if (attested ? !data || typeof data !== 'object' || !Array.isArray(data.models) : !Array.isArray(data))
413
+ return null;
414
+ const version = meta?.evidence_protocol_version;
415
+ const boundVersion = meta?.bound_classification_protocol;
416
+ return { supported: typeof version === 'number' && Number.isInteger(version) && version >= 2 &&
417
+ (!boundClassification || boundVersion === 1) };
418
+ }, { ...readOptions, requireCompleteRead: true });
419
+ if (capability.kind !== 'ok')
420
+ return capability;
421
+ if (!capability.value.supported)
422
+ return {
423
+ kind: 'rejected', httpStatus: 0,
424
+ error: boundClassification ? 'unsupported_bound_classification_protocol' : 'unsupported_evidence_protocol',
425
+ message: boundClassification
426
+ ? 'The server has not confirmed evidence protocol 2 and bound classification protocol 1; events were not sent'
427
+ : 'The server has not confirmed evidence protocol version 2; versioned evidence was not sent',
428
+ };
429
+ return capability;
430
+ }
431
+ /** Retain immutable delivery primitives and exact bytes for one POST and any replay. */
432
+ preparePostRun(envelope, serializedEnvelope) {
433
+ const canonicalEnvelope = JSON.stringify(envelope);
434
+ if (serializedEnvelope !== undefined && serializedEnvelope !== canonicalEnvelope)
435
+ return {
436
+ kind: 'rejected', httpStatus: 0, error: 'serialized_envelope_mismatch',
437
+ message: 'The supplied serialized envelope does not match the validated envelope',
438
+ };
439
+ const gating = envelope.run.gating;
440
+ const hasBoundClassification = gating !== null && typeof gating === 'object' && 'bound_classification_protocol' in gating;
441
+ const binding = {
442
+ runId: envelope.run.id,
443
+ hasEvidenceProvenance: envelope.findings.some(finding => finding.location_provenance !== undefined || finding.claim_descriptor !== undefined),
444
+ boundClassification: hasBoundClassification ? (gating.bound_classification_protocol === 1 ? 'valid' : 'invalid') : 'absent',
445
+ artifactsDeclared: structuredClone(envelope.artifacts_declared),
446
+ };
447
+ return { kind: 'ready', serializedEnvelope: canonicalEnvelope,
448
+ post: (options = {}) => this.postPreparedRun(binding, canonicalEnvelope, options),
449
+ receipt: (options = {}) => this.getPreparedReceipt(binding, canonicalEnvelope, options) };
450
+ }
451
+ /** `POST /api/v1/reviews/runs` — idempotent on the run id. */
452
+ async postRun(envelope, options = {}, serializedEnvelope) {
453
+ const prepared = this.preparePostRun(envelope, serializedEnvelope);
454
+ return prepared.kind === 'ready' ? prepared.post(options) : prepared;
455
+ }
456
+ async postPreparedRun(binding, serializedEnvelope, options) {
457
+ if (this.envelopeTimeoutMs !== undefined) {
458
+ // Like artifact transfers, longer POSTs remain within the original
459
+ // attested lifetime, including preflight and transport admission.
460
+ options = { ...options, timeoutMs: this.artifactLifetime(options) };
461
+ }
462
+ const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
463
+ const boundClassification = binding.boundClassification === 'valid';
464
+ if (binding.boundClassification === 'invalid')
465
+ return {
466
+ kind: 'rejected', httpStatus: 0, error: 'invalid_bound_classification',
467
+ message: 'The run must declare bound classification protocol 1; the envelope was not sent',
468
+ };
469
+ if (boundClassification || binding.hasEvidenceProvenance) {
470
+ const remaining = remainingRequestOptions(options, deadline);
471
+ if (remaining === null)
472
+ return { kind: 'unavailable', reason: 'delivery_deadline_exceeded' };
473
+ const capability = await this.requireEvidenceProtocol(remaining, boundClassification);
474
+ if (capability.kind !== 'ok')
475
+ return capability;
476
+ }
477
+ const remaining = remainingRequestOptions(options, deadline);
478
+ if (remaining === null)
479
+ return { kind: 'unavailable', reason: 'delivery_deadline_exceeded' };
480
+ const envelopeTimeoutMs = this.envelopeTimeoutMs;
481
+ const result = await this.request('POST', '/api/v1/reviews/runs', serializedEnvelope, 'application/json', remaining, envelopeTimeoutMs === undefined ? this.timeoutMs : () => Math.min(envelopeTimeoutMs, this.artifactLifetime(remaining) ?? envelopeTimeoutMs));
482
+ return this.classify(result, (body, status) => {
483
+ const data = body?.data;
484
+ // A receipt names the run that was posted and says which artifacts the
485
+ // server expects; anything else is not a receipt.
486
+ if (!data || data['id'] !== binding.runId || typeof data['url'] !== 'string')
487
+ return null;
488
+ if (!Array.isArray(data['artifacts_expected']) || !data['artifacts_expected'].every((k) => typeof k === 'string'))
489
+ return null;
490
+ const meta = body.meta;
491
+ return {
492
+ id: data['id'],
493
+ url: data['url'],
494
+ ...(typeof data['received_at'] === 'string' ? { received_at: data['received_at'] } : {}),
495
+ ...(typeof data['repo_verified'] === 'boolean' ? { repo_verified: data['repo_verified'] } : {}),
496
+ ...(typeof data['head_verified'] === 'string' ? { head_verified: data['head_verified'] } : {}),
497
+ artifacts_expected: data['artifacts_expected'],
498
+ status: meta?.status === 'existing' || status === 200 ? 'existing' : 'created',
499
+ };
500
+ });
501
+ }
502
+ /**
503
+ * Read the restricted run-bound receipt after an uncertain attested POST.
504
+ * Only a 404 authorizes a replay; unavailable, rejected or malformed answers
505
+ * stop recovery. The server independently restricts this route to the
506
+ * credential's own live run and signed workflow subject.
507
+ */
508
+ async getAttestedRunReceipt(envelope, serializedEnvelope, options = {}) {
509
+ const prepared = this.preparePostRun(envelope, serializedEnvelope);
510
+ return prepared.kind === 'ready' ? prepared.receipt(options) : { kind: 'rejected' };
511
+ }
512
+ async getPreparedReceipt(binding, serializedEnvelope, options = {}) {
513
+ if (this.credentialSource !== 'attest')
514
+ return { kind: 'rejected' };
515
+ if (!binding.artifactsDeclared.some(({ kind }) => kind === 'report_json'))
516
+ return { kind: 'rejected' };
517
+ const result = await this.request('GET', `/api/v1/reviews/runs/${encodeURIComponent(binding.runId)}`, undefined, 'application/json', { ...options, maxResponseBytes: MAX_READ_RESPONSE_BYTES, requireCompleteRead: true });
518
+ if ('failure' in result)
519
+ return { kind: 'unavailable' };
520
+ if (result.status === 404)
521
+ return { kind: 'absent' };
522
+ if (result.status >= 500 || result.status === 429 || result.status === 408)
523
+ return { kind: 'unavailable' };
524
+ if (result.status !== 200 || !completeReadBody(result.body))
525
+ return { kind: 'rejected' };
526
+ const response = result.body;
527
+ const data = response?.data;
528
+ const envelopeSha256 = createHash('sha256').update(serializedEnvelope, 'utf8').digest('hex');
529
+ const receiptUrl = typeof data?.['url'] === 'string' ? data['url'] : undefined;
530
+ const credentialLocation = new URL(this.credential.url);
531
+ const expectedOrigin = credentialLocation.origin;
532
+ const basePath = credentialLocation.pathname.replace(/\/$/, '');
533
+ const expectedPath = `${basePath}/api/v1/reviews/runs/${encodeURIComponent(binding.runId)}`;
534
+ const receiptLocation = receiptUrl === undefined ? undefined : (() => { try {
535
+ return new URL(receiptUrl);
536
+ }
537
+ catch {
538
+ return undefined;
539
+ } })();
540
+ if (!data || data['id'] !== binding.runId || receiptLocation?.origin !== expectedOrigin || receiptLocation.pathname !== expectedPath || receiptLocation.search !== '' || receiptLocation.hash !== '' ||
541
+ typeof data['envelope_sha256'] !== 'string' || !/^[a-f0-9]{64}$/.test(data['envelope_sha256']) || data['envelope_sha256'] !== envelopeSha256 ||
542
+ response?.meta?.['status'] !== 'existing' || !sameDeclarations(data['artifacts_declared'], binding.artifactsDeclared))
543
+ return { kind: 'rejected' };
544
+ return {
545
+ kind: 'recorded',
546
+ value: {
547
+ id: binding.runId,
548
+ url: receiptUrl,
549
+ ...(typeof data['received_at'] === 'string' ? { received_at: data['received_at'] } : {}),
550
+ ...(typeof data['repo_verified'] === 'boolean' ? { repo_verified: data['repo_verified'] } : {}),
551
+ ...(typeof data['head_verified'] === 'string' ? { head_verified: data['head_verified'] } : {}),
552
+ artifacts_expected: binding.artifactsDeclared.map(({ kind }) => kind),
553
+ status: 'existing',
554
+ },
555
+ };
556
+ }
557
+ /** `PUT /api/v1/reviews/runs/:id/artifacts/:kind` — the raw bytes, never JSON. */
558
+ async putArtifact(runId, kind, bytes, options = {}) {
559
+ if (kind !== 'report_json' && kind !== 'report_md') {
560
+ return { kind: 'rejected', httpStatus: 0, error: 'unknown_artifact_kind', message: String(kind) };
561
+ }
562
+ const timeoutMs = this.artifactBudget(options);
563
+ if (timeoutMs <= 0)
564
+ return { kind: 'unavailable', reason: 'artifact_transfer_expired' };
565
+ const result = await this.request('PUT', `/api/v1/reviews/runs/${encodeURIComponent(runId)}/artifacts/${encodeURIComponent(kind)}`, bytes, 'application/octet-stream', { ...options, timeoutMs: this.artifactLifetime(options) }, () => this.artifactBudget(options));
566
+ // The receipt must name the artifact that was sent and carry the digest
567
+ // of exactly those bytes; anything else is not a receipt for this upload.
568
+ const digest = createHash('sha256').update(bytes, 'utf8').digest('hex');
569
+ return this.classify(result, (body, status) => {
570
+ const data = body?.data;
571
+ if (!data || data['kind'] !== kind || typeof data['sha256'] !== 'string' || data['sha256'].toLowerCase() !== digest)
572
+ return null;
573
+ const meta = body.meta;
574
+ return {
575
+ kind,
576
+ sha256: data['sha256'],
577
+ ...(typeof data['url'] === 'string' ? { url: data['url'] } : {}),
578
+ status: meta?.status === 'existing' || status === 200 ? 'existing' : 'created',
579
+ };
580
+ });
581
+ }
582
+ /** `POST /api/v1/reviews/converge/events` — idempotent on each event id. */
583
+ async postEvents(events, options = {}) {
584
+ if (!Array.isArray(events) || !events.every(event => event !== null && typeof event === 'object' && !Array.isArray(event))) {
585
+ return { kind: 'rejected', httpStatus: 0, error: 'invalid_event_payload',
586
+ message: 'events must be objects; events were not sent' };
587
+ }
588
+ const preparedEvents = structuredClone(events);
589
+ const deadline = options.timeoutMs === undefined ? undefined : performance.now() + options.timeoutMs;
590
+ // Retained JSON can violate the producer's type. Refuse the batch before
591
+ // inspecting provenance so the outbox preserves it and continues other entries.
592
+ if (preparedEvents.some(event => event.kind === 'round_processed' &&
593
+ (event.payload === null || typeof event.payload !== 'object' || Array.isArray(event.payload)))) {
594
+ return { kind: 'rejected', httpStatus: 0, error: 'invalid_event_payload',
595
+ message: 'round_processed payload must be an object; events were not sent' };
596
+ }
597
+ let boundClassification = false;
598
+ let versionedClassification = false;
599
+ for (const event of preparedEvents) {
600
+ if (event.kind !== 'round_processed')
601
+ continue;
602
+ const payload = event.payload;
603
+ const markedClassification = 'classification_version' in payload || 'legacy_pending_identities' in payload;
604
+ if (markedClassification) {
605
+ const pending = payload.legacy_pending_identities;
606
+ if (payload.classification_version !== 1 || typeof payload.report_json_sha256 !== 'string' ||
607
+ !/^[a-f0-9]{64}$/.test(payload.report_json_sha256) ||
608
+ !Number.isSafeInteger(event.round) || event.round < 1 ||
609
+ !Array.isArray(payload.identities) || !payload.identities.every((identity) => {
610
+ if (identity === null || typeof identity !== 'object' || Array.isArray(identity) ||
611
+ !Object.hasOwn(identity, 'pending_round'))
612
+ return false;
613
+ const value = identity.pending_round;
614
+ return value === null || (typeof value === 'number' && Number.isSafeInteger(value) && value >= 1 && value <= event.round);
615
+ }) ||
616
+ ('legacy_pending_identities' in payload && (!Array.isArray(pending) || pending.length === 0 || pending.length > 2000 ||
617
+ !pending.every((identity, index) => typeof identity === 'string' && /^[a-f0-9]{16}$/.test(identity) &&
618
+ (index === 0 || pending[index - 1] < identity))))) {
619
+ return { kind: 'rejected', httpStatus: 0, error: 'invalid_bound_classification',
620
+ message: 'Bound classification requires version 1, a lowercase report digest, pending snapshots and valid optional legacy pending identities; events were not sent' };
621
+ }
622
+ boundClassification = true;
623
+ }
624
+ const markedSightingRefs = new Set();
625
+ if (Array.isArray(payload.identities)) {
626
+ for (const entry of payload.identities) {
627
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry) ||
628
+ !SIGHTING_BINDING_FIELDS.some(field => Object.hasOwn(entry, field)))
629
+ continue;
630
+ if (!validSightingBinding(entry))
631
+ return {
632
+ kind: 'rejected', httpStatus: 0, error: 'invalid_sighting_binding',
633
+ message: 'Per-sighting bindings require a complete supported version 1 identity; events were not sent',
634
+ };
635
+ if (markedClassification && entry.report_json_sha256 !== payload.report_json_sha256)
636
+ return {
637
+ kind: 'rejected', httpStatus: 0, error: 'invalid_sighting_binding',
638
+ message: 'Marked per-sighting bindings must use the classification report digest; events were not sent',
639
+ };
640
+ if (markedClassification && markedSightingRefs.has(entry.finding_ref))
641
+ return {
642
+ kind: 'rejected', httpStatus: 0, error: 'invalid_sighting_binding',
643
+ message: 'Marked per-sighting bindings must use unique finding references; events were not sent',
644
+ };
645
+ if (markedClassification)
646
+ markedSightingRefs.add(entry.finding_ref);
647
+ versionedClassification = true;
648
+ }
649
+ }
650
+ }
651
+ if (boundClassification || versionedClassification) {
652
+ const remaining = remainingRequestOptions(options, deadline);
653
+ if (remaining === null)
654
+ return { kind: 'unavailable', reason: 'delivery_deadline_exceeded' };
655
+ const capability = await this.requireEvidenceProtocol(remaining, boundClassification);
656
+ if (capability.kind !== 'ok')
657
+ return capability;
658
+ }
659
+ const body = JSON.stringify({ events: preparedEvents });
660
+ const remaining = remainingRequestOptions(options, deadline);
661
+ if (remaining === null)
662
+ return { kind: 'unavailable', reason: 'delivery_deadline_exceeded' };
663
+ const result = await this.request('POST', '/api/v1/reviews/converge/events', body, 'application/json', remaining);
664
+ return this.classify(result, (body) => {
665
+ const data = body?.data;
666
+ const inserted = data?.['inserted'];
667
+ const duplicates = data?.['duplicates'];
668
+ const count = (n) => typeof n === 'number' && Number.isInteger(n) && n >= 0;
669
+ // Every event sent must be accounted for, inserted or already known.
670
+ if (!count(inserted) || !count(duplicates) || inserted + duplicates !== preparedEvents.length)
671
+ return null;
672
+ return { inserted, duplicates };
673
+ });
674
+ }
675
+ /**
676
+ * `GET <path>` — a read under the same timeout, handshake and host binding
677
+ * as deliveries. `validate` says what the caller asked for: it returns the
678
+ * typed value when `data` is about that thing and `null` otherwise, which
679
+ * is reported as `malformed_response` rather than trusted. Reads allow a
680
+ * larger body than a receipt (`MAX_READ_RESPONSE_BYTES`) unless told otherwise.
681
+ */
682
+ async getJson(path, validate, options = {}) {
683
+ const result = await this.request('GET', path, undefined, 'application/json', { maxResponseBytes: MAX_READ_RESPONSE_BYTES, ...options });
684
+ return this.classify(result, (body, status) => {
685
+ if (options.requireCompleteRead && (status !== 200 || !completeReadBody(body, options.requireCompleteRead !== 'data')))
686
+ return null;
687
+ const response = body;
688
+ return validate(response?.data, response?.meta);
689
+ });
690
+ }
691
+ }
692
+ function completeReadBody(body, requireMeta = true) {
693
+ return body !== null && typeof body === 'object' && !Array.isArray(body) &&
694
+ Object.hasOwn(body, 'data') && (!requireMeta || Object.hasOwn(body, 'meta')) &&
695
+ Object.keys(body).every(key => key === 'data' || key === 'meta');
696
+ }
697
+ /**
698
+ * The body as text, or `null` once it exceeds `limit` bytes — the stream is
699
+ * cancelled there, so a runaway response never fills memory.
700
+ */
701
+ /** The body up to `limit` bytes, or null once it exceeds them — the stream is cancelled, never buffered whole. */
702
+ export async function readBounded(response, limit) {
703
+ if (!response.body) {
704
+ const text = await response.text();
705
+ return Buffer.byteLength(text, 'utf8') > limit ? null : text;
706
+ }
707
+ const bytes = await readBoundedBytes(response, limit);
708
+ return bytes === null ? null : new TextDecoder().decode(bytes);
709
+ }
710
+ async function readBoundedBytes(response, limit) {
711
+ if (!response.body) {
712
+ const bytes = Buffer.from(await response.arrayBuffer());
713
+ return bytes.length > limit ? null : bytes;
714
+ }
715
+ const reader = response.body.getReader();
716
+ const chunks = [];
717
+ let total = 0;
718
+ for (;;) {
719
+ const { done, value } = await reader.read();
720
+ if (done)
721
+ break;
722
+ total += value.byteLength;
723
+ if (total > limit) {
724
+ await reader.cancel().catch(() => undefined);
725
+ return null;
726
+ }
727
+ chunks.push(value);
728
+ }
729
+ return Buffer.concat(chunks.map((c) => Buffer.from(c)));
730
+ }
731
+ function hostOnly(raw) {
732
+ try {
733
+ return new URL(raw).host || '(no host)';
734
+ }
735
+ catch {
736
+ return '(unparseable URL)';
737
+ }
738
+ }
739
+ /** A transport failure can carry Node's nested `Error.cause`; retain only safe, bounded diagnostics. */
740
+ function transportFailure(err) {
741
+ const describe = (value) => {
742
+ try {
743
+ return value instanceof Error ? `${value.name}: ${value.message}` : String(value);
744
+ }
745
+ catch {
746
+ return '[unstringifiable diagnostic]';
747
+ }
748
+ };
749
+ const primary = describe(err);
750
+ const cause = err instanceof Error ? err.cause : undefined;
751
+ return scrubText(cause === undefined ? primary : `${primary}; cause: ${describe(cause)}`, 300);
752
+ }
753
+ function sameDeclarations(raw, expected) {
754
+ if (!Array.isArray(raw) || raw.length !== expected.length)
755
+ return false;
756
+ const byKind = new Map();
757
+ for (const declaration of raw) {
758
+ if (!declaration || typeof declaration !== 'object')
759
+ return false;
760
+ const value = declaration;
761
+ if (typeof value['kind'] !== 'string' || typeof value['sha256'] !== 'string' ||
762
+ typeof value['bytes'] !== 'number' || !Number.isSafeInteger(value['bytes']) || value['bytes'] < 0 || byKind.has(value['kind']))
763
+ return false;
764
+ byKind.set(value['kind'], { kind: value['kind'], sha256: value['sha256'], bytes: value['bytes'] });
765
+ }
766
+ return expected.every(declaration => {
767
+ const actual = byKind.get(declaration.kind);
768
+ return actual?.sha256 === declaration.sha256 && actual.bytes === declaration.bytes;
769
+ });
770
+ }
771
+ /** Server or network text made safe for a terminal: scrubbed, control characters removed, bounded. */
772
+ function printable(text) {
773
+ return scrubText(text.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' '), 300);
774
+ }
775
+ /** One phrase for a non-ok outcome, safe to print (no token, no body dump, no control characters). */
776
+ export function describeOutcome(outcome) {
777
+ switch (outcome.kind) {
778
+ case 'ok':
779
+ return `HTTP ${outcome.httpStatus}`;
780
+ case 'disabled':
781
+ return outcome.reason === 'reviews_disabled'
782
+ ? 'the organization has not enabled review evidence'
783
+ : 'the organization caps review evidence at findings';
784
+ case 'conflict':
785
+ return `conflict: ${outcome.message ? printable(outcome.message) : 'run id already recorded with a different report'}`;
786
+ case 'rejected':
787
+ return `refused (HTTP ${outcome.httpStatus} ${printable(outcome.error)}${outcome.message ? `: ${printable(outcome.message)}` : ''})`;
788
+ case 'unavailable':
789
+ return `unreachable (${printable(outcome.reason)})`;
790
+ }
791
+ }
792
+ function privateReference(runId, expected) {
793
+ return typeof runId === 'string' && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}(?![\s\S])/i.test(runId) &&
794
+ expected !== null && typeof expected === 'object' && typeof expected.sha256 === 'string' && /^[a-f0-9]{64}(?![\s\S])/.test(expected.sha256) &&
795
+ (expected.bytes === undefined || Number.isSafeInteger(expected.bytes) && expected.bytes >= 0 && expected.bytes <= MAX_ARTIFACT_BYTES);
796
+ }
797
+ function privateReceipt(value, runId, expected) {
798
+ if (!value || typeof value !== 'object' || Array.isArray(value))
799
+ return false;
800
+ const data = value;
801
+ return Object.keys(data).sort().join(',') === 'bytes,run_id,sha256' && data.run_id === runId && data.sha256 === expected.sha256 &&
802
+ Number.isSafeInteger(data.bytes) && data.bytes >= 0 && data.bytes <= MAX_ARTIFACT_BYTES &&
803
+ (expected.bytes === undefined || data.bytes === expected.bytes);
804
+ }
805
+ /** Never echo private route bodies, URLs, prompts or adapter error causes. */
806
+ function privateArtifactFailure(status) {
807
+ if (status === 401 || status === 408 || status === 429 || status >= 500)
808
+ return { kind: 'unavailable', reason: `reviewer_artifact_http_${status}` };
809
+ if (status === 409)
810
+ return { kind: 'conflict', message: 'Private reviewer artifact conflicts with retained evidence' };
811
+ return { kind: 'rejected', httpStatus: status, error: status === 0 ? 'invalid_reviewer_artifact' : `reviewer_artifact_http_${status}`, message: 'Private reviewer artifact request refused' };
812
+ }
813
+ //# sourceMappingURL=sink.js.map