open-multi-agent-kit 0.90.7 → 0.90.9

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 (331) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +17 -10
  3. package/dist/cli/args.d.ts.map +1 -1
  4. package/dist/cli/args.js +5 -1
  5. package/dist/cli/args.js.map +1 -1
  6. package/dist/commands/doctor-provider-cli.d.ts +33 -0
  7. package/dist/commands/doctor-provider-cli.d.ts.map +1 -0
  8. package/dist/commands/doctor-provider-cli.js +215 -0
  9. package/dist/commands/doctor-provider-cli.js.map +1 -0
  10. package/dist/commands/doctor-provider-transport.d.ts +35 -0
  11. package/dist/commands/doctor-provider-transport.d.ts.map +1 -0
  12. package/dist/commands/doctor-provider-transport.js +185 -0
  13. package/dist/commands/doctor-provider-transport.js.map +1 -0
  14. package/dist/commands/doctor-provider.d.ts +121 -7
  15. package/dist/commands/doctor-provider.d.ts.map +1 -1
  16. package/dist/commands/doctor-provider.js +1295 -102
  17. package/dist/commands/doctor-provider.js.map +1 -1
  18. package/dist/commands/session-doctor-cli.d.ts +43 -0
  19. package/dist/commands/session-doctor-cli.d.ts.map +1 -0
  20. package/dist/commands/session-doctor-cli.js +494 -0
  21. package/dist/commands/session-doctor-cli.js.map +1 -0
  22. package/dist/commands/verify-ci.d.ts +21 -0
  23. package/dist/commands/verify-ci.d.ts.map +1 -0
  24. package/dist/commands/verify-ci.js +66 -0
  25. package/dist/commands/verify-ci.js.map +1 -0
  26. package/dist/core/agent-session-services.d.ts +3 -0
  27. package/dist/core/agent-session-services.d.ts.map +1 -1
  28. package/dist/core/agent-session-services.js +3 -0
  29. package/dist/core/agent-session-services.js.map +1 -1
  30. package/dist/core/agent-session.d.ts +116 -2
  31. package/dist/core/agent-session.d.ts.map +1 -1
  32. package/dist/core/agent-session.js +1015 -71
  33. package/dist/core/agent-session.js.map +1 -1
  34. package/dist/core/agent-tool-settings.d.ts +35 -0
  35. package/dist/core/agent-tool-settings.d.ts.map +1 -0
  36. package/dist/core/agent-tool-settings.js +155 -0
  37. package/dist/core/agent-tool-settings.js.map +1 -0
  38. package/dist/core/atomic-session-file.d.ts +14 -0
  39. package/dist/core/atomic-session-file.d.ts.map +1 -0
  40. package/dist/core/atomic-session-file.js +104 -0
  41. package/dist/core/atomic-session-file.js.map +1 -0
  42. package/dist/core/compaction/compaction.d.ts +6 -0
  43. package/dist/core/compaction/compaction.d.ts.map +1 -1
  44. package/dist/core/compaction/compaction.js +12 -1
  45. package/dist/core/compaction/compaction.js.map +1 -1
  46. package/dist/core/compaction/hysteresis.d.ts +27 -0
  47. package/dist/core/compaction/hysteresis.d.ts.map +1 -0
  48. package/dist/core/compaction/hysteresis.js +84 -0
  49. package/dist/core/compaction/hysteresis.js.map +1 -0
  50. package/dist/core/compaction/index.d.ts +2 -0
  51. package/dist/core/compaction/index.d.ts.map +1 -1
  52. package/dist/core/compaction/index.js +2 -0
  53. package/dist/core/compaction/index.js.map +1 -1
  54. package/dist/core/compaction/transaction.d.ts +172 -0
  55. package/dist/core/compaction/transaction.d.ts.map +1 -0
  56. package/dist/core/compaction/transaction.js +824 -0
  57. package/dist/core/compaction/transaction.js.map +1 -0
  58. package/dist/core/context-budget-governor-v2.d.ts +2 -1
  59. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  60. package/dist/core/context-budget-governor-v2.js +2 -1
  61. package/dist/core/context-budget-governor-v2.js.map +1 -1
  62. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  63. package/dist/core/context-budget-headroom.js +5 -1
  64. package/dist/core/context-budget-headroom.js.map +1 -1
  65. package/dist/core/context-budget-reserved-tokens.d.ts +110 -0
  66. package/dist/core/context-budget-reserved-tokens.d.ts.map +1 -0
  67. package/dist/core/context-budget-reserved-tokens.js +188 -0
  68. package/dist/core/context-budget-reserved-tokens.js.map +1 -0
  69. package/dist/core/context-budget-v2-cache-invalidation.d.ts +133 -0
  70. package/dist/core/context-budget-v2-cache-invalidation.d.ts.map +1 -0
  71. package/dist/core/context-budget-v2-cache-invalidation.js +452 -0
  72. package/dist/core/context-budget-v2-cache-invalidation.js.map +1 -0
  73. package/dist/core/context-budget-v2-cache-keys.d.ts +3 -0
  74. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -1
  75. package/dist/core/context-budget-v2-cache-keys.js +6 -0
  76. package/dist/core/context-budget-v2-cache-keys.js.map +1 -1
  77. package/dist/core/context-budget-v2-cache-provider.d.ts +8 -2
  78. package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -1
  79. package/dist/core/context-budget-v2-cache-provider.js +28 -5
  80. package/dist/core/context-budget-v2-cache-provider.js.map +1 -1
  81. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -1
  82. package/dist/core/context-budget-v2-plan-cache-keys.js +1 -0
  83. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -1
  84. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  85. package/dist/core/context-budget-v2-planner.js +1 -0
  86. package/dist/core/context-budget-v2-planner.js.map +1 -1
  87. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  88. package/dist/core/context-budget-v2-selection.js +9 -0
  89. package/dist/core/context-budget-v2-selection.js.map +1 -1
  90. package/dist/core/context-budget-v2-types.d.ts +4 -0
  91. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  92. package/dist/core/context-budget-v2-types.js.map +1 -1
  93. package/dist/core/durable-file-directory.d.ts +4 -0
  94. package/dist/core/durable-file-directory.d.ts.map +1 -0
  95. package/dist/core/durable-file-directory.js +76 -0
  96. package/dist/core/durable-file-directory.js.map +1 -0
  97. package/dist/core/durable-file-identity.d.ts +26 -0
  98. package/dist/core/durable-file-identity.d.ts.map +1 -0
  99. package/dist/core/durable-file-identity.js +118 -0
  100. package/dist/core/durable-file-identity.js.map +1 -0
  101. package/dist/core/durable-file-io.d.ts +29 -0
  102. package/dist/core/durable-file-io.d.ts.map +1 -0
  103. package/dist/core/durable-file-io.js +179 -0
  104. package/dist/core/durable-file-io.js.map +1 -0
  105. package/dist/core/durable-file-lock-cleanup.d.ts +12 -0
  106. package/dist/core/durable-file-lock-cleanup.d.ts.map +1 -0
  107. package/dist/core/durable-file-lock-cleanup.js +39 -0
  108. package/dist/core/durable-file-lock-cleanup.js.map +1 -0
  109. package/dist/core/durable-file-lock-owner.d.ts +18 -0
  110. package/dist/core/durable-file-lock-owner.d.ts.map +1 -0
  111. package/dist/core/durable-file-lock-owner.js +78 -0
  112. package/dist/core/durable-file-lock-owner.js.map +1 -0
  113. package/dist/core/durable-file-lock-storage.d.ts +35 -0
  114. package/dist/core/durable-file-lock-storage.d.ts.map +1 -0
  115. package/dist/core/durable-file-lock-storage.js +263 -0
  116. package/dist/core/durable-file-lock-storage.js.map +1 -0
  117. package/dist/core/durable-file-mode.d.ts +8 -0
  118. package/dist/core/durable-file-mode.d.ts.map +1 -0
  119. package/dist/core/durable-file-mode.js +41 -0
  120. package/dist/core/durable-file-mode.js.map +1 -0
  121. package/dist/core/extensions/loader.d.ts +12 -0
  122. package/dist/core/extensions/loader.d.ts.map +1 -1
  123. package/dist/core/extensions/loader.js +50 -37
  124. package/dist/core/extensions/loader.js.map +1 -1
  125. package/dist/core/extensions/types.d.ts +2 -0
  126. package/dist/core/extensions/types.d.ts.map +1 -1
  127. package/dist/core/extensions/types.js.map +1 -1
  128. package/dist/core/index.d.ts +6 -2
  129. package/dist/core/index.d.ts.map +1 -1
  130. package/dist/core/index.js +5 -0
  131. package/dist/core/index.js.map +1 -1
  132. package/dist/core/loop-gates.d.ts.map +1 -1
  133. package/dist/core/loop-gates.js +25 -1
  134. package/dist/core/loop-gates.js.map +1 -1
  135. package/dist/core/model-resolver.d.ts.map +1 -1
  136. package/dist/core/model-resolver.js +1 -1
  137. package/dist/core/model-resolver.js.map +1 -1
  138. package/dist/core/pi-package-intake.d.ts +1 -0
  139. package/dist/core/pi-package-intake.d.ts.map +1 -1
  140. package/dist/core/pi-package-intake.js +7 -0
  141. package/dist/core/pi-package-intake.js.map +1 -1
  142. package/dist/core/redaction.d.ts +8 -0
  143. package/dist/core/redaction.d.ts.map +1 -0
  144. package/dist/core/redaction.js +31 -0
  145. package/dist/core/redaction.js.map +1 -0
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +36 -0
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/run-journal-store.d.ts +53 -0
  150. package/dist/core/run-journal-store.d.ts.map +1 -0
  151. package/dist/core/run-journal-store.js +278 -0
  152. package/dist/core/run-journal-store.js.map +1 -0
  153. package/dist/core/run-journal.d.ts +172 -0
  154. package/dist/core/run-journal.d.ts.map +1 -0
  155. package/dist/core/run-journal.js +1053 -0
  156. package/dist/core/run-journal.js.map +1 -0
  157. package/dist/core/sdk.d.ts +7 -0
  158. package/dist/core/sdk.d.ts.map +1 -1
  159. package/dist/core/sdk.js +94 -3
  160. package/dist/core/sdk.js.map +1 -1
  161. package/dist/core/session-doctor-plan.d.ts +128 -0
  162. package/dist/core/session-doctor-plan.d.ts.map +1 -0
  163. package/dist/core/session-doctor-plan.js +833 -0
  164. package/dist/core/session-doctor-plan.js.map +1 -0
  165. package/dist/core/session-file-compaction.d.ts +6 -0
  166. package/dist/core/session-file-compaction.d.ts.map +1 -0
  167. package/dist/core/session-file-compaction.js +169 -0
  168. package/dist/core/session-file-compaction.js.map +1 -0
  169. package/dist/core/session-file-head.d.ts +10 -0
  170. package/dist/core/session-file-head.d.ts.map +1 -0
  171. package/dist/core/session-file-head.js +78 -0
  172. package/dist/core/session-file-head.js.map +1 -0
  173. package/dist/core/session-file-persistence.d.ts +30 -0
  174. package/dist/core/session-file-persistence.d.ts.map +1 -0
  175. package/dist/core/session-file-persistence.js +106 -0
  176. package/dist/core/session-file-persistence.js.map +1 -0
  177. package/dist/core/session-integrity.d.ts +43 -0
  178. package/dist/core/session-integrity.d.ts.map +1 -0
  179. package/dist/core/session-integrity.js +420 -0
  180. package/dist/core/session-integrity.js.map +1 -0
  181. package/dist/core/session-manager.d.ts +24 -4
  182. package/dist/core/session-manager.d.ts.map +1 -1
  183. package/dist/core/session-manager.js +242 -125
  184. package/dist/core/session-manager.js.map +1 -1
  185. package/dist/core/session-owner-lease.d.ts +22 -0
  186. package/dist/core/session-owner-lease.d.ts.map +1 -0
  187. package/dist/core/session-owner-lease.js +176 -0
  188. package/dist/core/session-owner-lease.js.map +1 -0
  189. package/dist/core/session-path-policy.d.ts +132 -0
  190. package/dist/core/session-path-policy.d.ts.map +1 -0
  191. package/dist/core/session-path-policy.js +780 -0
  192. package/dist/core/session-path-policy.js.map +1 -0
  193. package/dist/core/session-repair-plan.d.ts +49 -0
  194. package/dist/core/session-repair-plan.d.ts.map +1 -0
  195. package/dist/core/session-repair-plan.js +202 -0
  196. package/dist/core/session-repair-plan.js.map +1 -0
  197. package/dist/core/session-termination.d.ts +100 -0
  198. package/dist/core/session-termination.d.ts.map +1 -0
  199. package/dist/core/session-termination.js +403 -0
  200. package/dist/core/session-termination.js.map +1 -0
  201. package/dist/core/settings-manager.d.ts +32 -0
  202. package/dist/core/settings-manager.d.ts.map +1 -1
  203. package/dist/core/settings-manager.js +52 -5
  204. package/dist/core/settings-manager.js.map +1 -1
  205. package/dist/core/system-prompt.d.ts.map +1 -1
  206. package/dist/core/system-prompt.js +1 -1
  207. package/dist/core/system-prompt.js.map +1 -1
  208. package/dist/core/verified-bash-adapter.d.ts +36 -0
  209. package/dist/core/verified-bash-adapter.d.ts.map +1 -0
  210. package/dist/core/verified-bash-adapter.js +90 -0
  211. package/dist/core/verified-bash-adapter.js.map +1 -0
  212. package/dist/guardrails/command-redaction.d.ts +32 -0
  213. package/dist/guardrails/command-redaction.d.ts.map +1 -0
  214. package/dist/guardrails/command-redaction.js +776 -0
  215. package/dist/guardrails/command-redaction.js.map +1 -0
  216. package/dist/guardrails/evidence-attestation.d.ts +31 -0
  217. package/dist/guardrails/evidence-attestation.d.ts.map +1 -0
  218. package/dist/guardrails/evidence-attestation.js +178 -0
  219. package/dist/guardrails/evidence-attestation.js.map +1 -0
  220. package/dist/guardrails/evidence-receipt-store.d.ts +33 -0
  221. package/dist/guardrails/evidence-receipt-store.d.ts.map +1 -0
  222. package/dist/guardrails/evidence-receipt-store.js +446 -0
  223. package/dist/guardrails/evidence-receipt-store.js.map +1 -0
  224. package/dist/guardrails/evidence-receipt.d.ts +58 -0
  225. package/dist/guardrails/evidence-receipt.d.ts.map +1 -0
  226. package/dist/guardrails/evidence-receipt.js +389 -0
  227. package/dist/guardrails/evidence-receipt.js.map +1 -0
  228. package/dist/guardrails/evidence-system.d.ts +40 -9
  229. package/dist/guardrails/evidence-system.d.ts.map +1 -1
  230. package/dist/guardrails/evidence-system.js +379 -83
  231. package/dist/guardrails/evidence-system.js.map +1 -1
  232. package/dist/guardrails/replay-ledger-io.d.ts +6 -0
  233. package/dist/guardrails/replay-ledger-io.d.ts.map +1 -0
  234. package/dist/guardrails/replay-ledger-io.js +42 -0
  235. package/dist/guardrails/replay-ledger-io.js.map +1 -0
  236. package/dist/guardrails/replay-ledger-lock-owner.d.ts +32 -0
  237. package/dist/guardrails/replay-ledger-lock-owner.d.ts.map +1 -0
  238. package/dist/guardrails/replay-ledger-lock-owner.js +138 -0
  239. package/dist/guardrails/replay-ledger-lock-owner.js.map +1 -0
  240. package/dist/guardrails/replay-ledger-lock.d.ts +24 -0
  241. package/dist/guardrails/replay-ledger-lock.d.ts.map +1 -0
  242. package/dist/guardrails/replay-ledger-lock.js +135 -0
  243. package/dist/guardrails/replay-ledger-lock.js.map +1 -0
  244. package/dist/guardrails/replay-ledger-mutation-gate.d.ts +24 -0
  245. package/dist/guardrails/replay-ledger-mutation-gate.d.ts.map +1 -0
  246. package/dist/guardrails/replay-ledger-mutation-gate.js +205 -0
  247. package/dist/guardrails/replay-ledger-mutation-gate.js.map +1 -0
  248. package/dist/guardrails/replay-ledger-quarantine.d.ts +2 -0
  249. package/dist/guardrails/replay-ledger-quarantine.d.ts.map +1 -0
  250. package/dist/guardrails/replay-ledger-quarantine.js +19 -0
  251. package/dist/guardrails/replay-ledger-quarantine.js.map +1 -0
  252. package/dist/guardrails/replay-ledger-store.d.ts +32 -0
  253. package/dist/guardrails/replay-ledger-store.d.ts.map +1 -0
  254. package/dist/guardrails/replay-ledger-store.js +211 -0
  255. package/dist/guardrails/replay-ledger-store.js.map +1 -0
  256. package/dist/guardrails/shell-command-name-glob.d.ts +3 -0
  257. package/dist/guardrails/shell-command-name-glob.d.ts.map +1 -0
  258. package/dist/guardrails/shell-command-name-glob.js +233 -0
  259. package/dist/guardrails/shell-command-name-glob.js.map +1 -0
  260. package/dist/guardrails/shell-command-option.d.ts +7 -0
  261. package/dist/guardrails/shell-command-option.d.ts.map +1 -0
  262. package/dist/guardrails/shell-command-option.js +24 -0
  263. package/dist/guardrails/shell-command-option.js.map +1 -0
  264. package/dist/guardrails/shell-command-tokenizer.d.ts +29 -0
  265. package/dist/guardrails/shell-command-tokenizer.d.ts.map +1 -0
  266. package/dist/guardrails/shell-command-tokenizer.js +145 -0
  267. package/dist/guardrails/shell-command-tokenizer.js.map +1 -0
  268. package/dist/guardrails/shell-command-wrapper.d.ts +18 -0
  269. package/dist/guardrails/shell-command-wrapper.d.ts.map +1 -0
  270. package/dist/guardrails/shell-command-wrapper.js +42 -0
  271. package/dist/guardrails/shell-command-wrapper.js.map +1 -0
  272. package/dist/guardrails/verified-executor.d.ts +65 -0
  273. package/dist/guardrails/verified-executor.d.ts.map +1 -0
  274. package/dist/guardrails/verified-executor.js +171 -0
  275. package/dist/guardrails/verified-executor.js.map +1 -0
  276. package/dist/guardrails/workspace-fingerprint.d.ts +23 -0
  277. package/dist/guardrails/workspace-fingerprint.d.ts.map +1 -0
  278. package/dist/guardrails/workspace-fingerprint.js +634 -0
  279. package/dist/guardrails/workspace-fingerprint.js.map +1 -0
  280. package/dist/index.d.ts +15 -3
  281. package/dist/index.d.ts.map +1 -1
  282. package/dist/index.js +13 -1
  283. package/dist/index.js.map +1 -1
  284. package/dist/main.d.ts.map +1 -1
  285. package/dist/main.js +12 -0
  286. package/dist/main.js.map +1 -1
  287. package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/control-panel-layout.js +1 -1
  289. package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
  290. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  291. package/dist/modes/interactive/components/footer.js +1 -1
  292. package/dist/modes/interactive/components/footer.js.map +1 -1
  293. package/dist/modes/interactive/interactive-mode.d.ts +3 -0
  294. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  295. package/dist/modes/interactive/interactive-mode.js +59 -9
  296. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  297. package/dist/modes/print-mode.d.ts.map +1 -1
  298. package/dist/modes/print-mode.js +43 -2
  299. package/dist/modes/print-mode.js.map +1 -1
  300. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  301. package/dist/modes/rpc/rpc-mode.js +23 -5
  302. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  303. package/dist/modes/rpc/rpc-types.d.ts +3 -0
  304. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  305. package/dist/modes/rpc/rpc-types.js.map +1 -1
  306. package/dist/types/evidence.d.ts +215 -1
  307. package/dist/types/evidence.d.ts.map +1 -1
  308. package/dist/types/evidence.js.map +1 -1
  309. package/dist/verify-ci.d.ts +3 -0
  310. package/dist/verify-ci.d.ts.map +1 -0
  311. package/dist/verify-ci.js +30 -0
  312. package/dist/verify-ci.js.map +1 -0
  313. package/docs/compaction.md +44 -4
  314. package/docs/extensions.md +2 -0
  315. package/docs/providers.md +6 -0
  316. package/docs/sdk.md +238 -1
  317. package/docs/settings.md +67 -4
  318. package/docs/usage.md +38 -0
  319. package/examples/extensions/correctness-wall/README.md +6 -6
  320. package/examples/extensions/correctness-wall/index.ts +37 -11
  321. package/examples/extensions/custom-provider-anthropic/package-lock.json +5 -5
  322. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  323. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  324. package/examples/extensions/gondolin/package-lock.json +2 -2
  325. package/examples/extensions/gondolin/package.json +1 -1
  326. package/examples/extensions/sandbox/package-lock.json +2 -2
  327. package/examples/extensions/sandbox/package.json +1 -1
  328. package/examples/extensions/with-deps/package-lock.json +4 -4
  329. package/examples/extensions/with-deps/package.json +1 -1
  330. package/npm-shrinkwrap.json +12 -12
  331. package/package.json +4 -4
@@ -0,0 +1,446 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { chmodSync, closeSync, constants as fsConstants, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, realpathSync, rmdirSync, unlinkSync, writeSync, } from "node:fs";
3
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import { isSafeEvidenceReceiptId, parseEvidenceReceipt, serializeEvidenceReceipt, validateEvidenceReceipt, } from "./evidence-receipt.js";
5
+ function errorCode(error) {
6
+ return typeof error === "object" && error !== null && "code" in error
7
+ ? String(error.code)
8
+ : undefined;
9
+ }
10
+ const UNSUPPORTED_DIRECTORY_FSYNC_CODES = new Set([
11
+ "EBADF",
12
+ "EINVAL",
13
+ "EISDIR",
14
+ "ENOSYS",
15
+ "ENOTSUP",
16
+ ]);
17
+ function classifiedDirectoryFsyncError(path, error) {
18
+ const code = errorCode(error);
19
+ if (code === undefined || !UNSUPPORTED_DIRECTORY_FSYNC_CODES.has(code))
20
+ return error;
21
+ return new Error(`receipt store strict durability unavailable: directory fsync unsupported for ${path} (${code})`, {
22
+ cause: error,
23
+ });
24
+ }
25
+ function fsyncDirectory(path) {
26
+ let fd;
27
+ let failure;
28
+ try {
29
+ fd = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0) | (fsConstants.O_NOFOLLOW ?? 0));
30
+ fsyncSync(fd);
31
+ }
32
+ catch (error) {
33
+ failure = classifiedDirectoryFsyncError(path, error);
34
+ }
35
+ finally {
36
+ if (fd !== undefined) {
37
+ try {
38
+ closeSync(fd);
39
+ }
40
+ catch (error) {
41
+ if (failure === undefined)
42
+ failure = error;
43
+ }
44
+ }
45
+ }
46
+ if (failure !== undefined)
47
+ throw failure;
48
+ }
49
+ function writeAll(fd, bytes) {
50
+ let offset = 0;
51
+ while (offset < bytes.byteLength) {
52
+ const written = writeSync(fd, bytes, offset, bytes.byteLength - offset);
53
+ if (written <= 0)
54
+ throw new Error("receipt store could not complete the file write");
55
+ offset += written;
56
+ }
57
+ }
58
+ function identityFrom(stat) {
59
+ return { dev: stat.dev, ino: stat.ino };
60
+ }
61
+ function hasFileIdentity(identity) {
62
+ return identity.dev !== 0 || identity.ino !== 0;
63
+ }
64
+ function matchesFileIdentity(expected, actual) {
65
+ const actualIdentity = identityFrom(actual);
66
+ const expectedHasIdentity = hasFileIdentity(expected);
67
+ const actualHasIdentity = hasFileIdentity(actualIdentity);
68
+ return ((!expectedHasIdentity && !actualHasIdentity) ||
69
+ (expectedHasIdentity &&
70
+ actualHasIdentity &&
71
+ expected.dev === actualIdentity.dev &&
72
+ expected.ino === actualIdentity.ino));
73
+ }
74
+ function missingDirectoryPaths(path) {
75
+ const missing = [];
76
+ let current = path;
77
+ while (true) {
78
+ try {
79
+ lstatSync(current);
80
+ return missing;
81
+ }
82
+ catch (error) {
83
+ if (errorCode(error) !== "ENOENT")
84
+ throw error;
85
+ missing.push(current);
86
+ const parent = dirname(current);
87
+ if (parent === current)
88
+ throw new Error("receipt store root has no existing ancestor");
89
+ current = parent;
90
+ }
91
+ }
92
+ }
93
+ function captureDirectoryIdentity(path, label) {
94
+ const stat = lstatSync(path);
95
+ const canonicalPath = realpathSync(path);
96
+ const canonicalStat = lstatSync(canonicalPath);
97
+ if (stat.isSymbolicLink() ||
98
+ !stat.isDirectory() ||
99
+ canonicalStat.isSymbolicLink() ||
100
+ !canonicalStat.isDirectory() ||
101
+ realpathSync(canonicalPath) !== canonicalPath ||
102
+ !matchesFileIdentity(identityFrom(stat), canonicalStat)) {
103
+ throw new Error(`${label} is not a trusted directory`);
104
+ }
105
+ return { path: canonicalPath, identity: identityFrom(canonicalStat) };
106
+ }
107
+ function assertDirectoryIdentity(directory, label) {
108
+ const stat = lstatSync(directory.path);
109
+ if (stat.isSymbolicLink() ||
110
+ !stat.isDirectory() ||
111
+ realpathSync(directory.path) !== directory.path ||
112
+ !matchesFileIdentity(directory.identity, stat)) {
113
+ throw new Error(`${label} changed during root durability setup`);
114
+ }
115
+ }
116
+ function cleanupCreatedDirectories(directories) {
117
+ let failure;
118
+ for (const directory of directories) {
119
+ let removed = false;
120
+ try {
121
+ assertDirectoryIdentity(directory, "new receipt store directory");
122
+ rmdirSync(directory.path);
123
+ removed = true;
124
+ }
125
+ catch (error) {
126
+ if (failure === undefined)
127
+ failure = error;
128
+ }
129
+ if (!removed)
130
+ continue;
131
+ try {
132
+ // Persist each successful removal in its still-existing parent before moving outward.
133
+ fsyncDirectory(dirname(directory.path));
134
+ }
135
+ catch (error) {
136
+ if (failure === undefined)
137
+ failure = error;
138
+ }
139
+ }
140
+ return failure;
141
+ }
142
+ function isStrictlyUnder(root, path) {
143
+ const relativePath = relative(root, path);
144
+ return (relativePath !== "" && relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath));
145
+ }
146
+ function atomicNoOverwritePublish(tempPath, finalPath) {
147
+ try {
148
+ linkSync(tempPath, finalPath);
149
+ }
150
+ catch (error) {
151
+ throw new Error(`receipt store atomic no-overwrite publish failed (${errorCode(error) ?? "unknown"}); destination was not replaced`, { cause: error });
152
+ }
153
+ }
154
+ /**
155
+ * Owner-only, no-overwrite receipt persistence rooted at one trusted directory.
156
+ * Phase A assumes same-UID path mutation is quiescent; identity checks detect observed
157
+ * replacement but are not filesystem isolation, and persistence alone must not activate EvidenceGate.
158
+ */
159
+ export class EvidenceReceiptStore {
160
+ root;
161
+ rootIdentity;
162
+ faultInjector;
163
+ constructor(root, options = {}) {
164
+ if (typeof root !== "string" || root.length === 0)
165
+ throw new Error("receipt store root must be non-empty");
166
+ const resolvedRoot = resolve(root);
167
+ const missingPaths = missingDirectoryPaths(resolvedRoot);
168
+ let canonicalRoot;
169
+ if (missingPaths.length === 0) {
170
+ const stat = lstatSync(resolvedRoot);
171
+ if (stat.isSymbolicLink())
172
+ throw new Error("receipt store root must not be a symlink");
173
+ if (!stat.isDirectory())
174
+ throw new Error("receipt store root must be a directory");
175
+ chmodSync(resolvedRoot, 0o700);
176
+ canonicalRoot = realpathSync(resolvedRoot);
177
+ }
178
+ else {
179
+ mkdirSync(resolvedRoot, { recursive: true, mode: 0o700 });
180
+ chmodSync(resolvedRoot, 0o700);
181
+ canonicalRoot = realpathSync(resolvedRoot);
182
+ const createdDirectories = missingPaths.map((path) => captureDirectoryIdentity(path, "new receipt store directory"));
183
+ const outermostCreated = createdDirectories.at(-1);
184
+ if (outermostCreated === undefined)
185
+ throw new Error("receipt store root creation was not observed");
186
+ try {
187
+ // Persist each new directory before its parent, walking from the root outward.
188
+ for (const directory of createdDirectories) {
189
+ assertDirectoryIdentity(directory, "new receipt store directory");
190
+ fsyncDirectory(directory.path);
191
+ assertDirectoryIdentity(directory, "new receipt store directory");
192
+ }
193
+ const parent = captureDirectoryIdentity(dirname(outermostCreated.path), "receipt store root parent");
194
+ options.faultInjector?.("before-root-parent-directory-fsync");
195
+ assertDirectoryIdentity(parent, "receipt store root parent");
196
+ fsyncDirectory(parent.path);
197
+ assertDirectoryIdentity(parent, "receipt store root parent");
198
+ }
199
+ catch (error) {
200
+ const failure = classifiedDirectoryFsyncError(dirname(outermostCreated.path), error);
201
+ const cleanupFailure = cleanupCreatedDirectories(createdDirectories);
202
+ if (cleanupFailure !== undefined) {
203
+ const message = failure instanceof Error ? failure.message : String(failure);
204
+ const cleanupMessage = cleanupFailure instanceof Error ? cleanupFailure.message : String(cleanupFailure);
205
+ throw new Error(`receipt store root durability failed and cleanup was incomplete: ${message}; cleanup failure: ${cleanupMessage}`, { cause: new AggregateError([failure, cleanupFailure], "receipt store root rollback failed") });
206
+ }
207
+ throw failure;
208
+ }
209
+ }
210
+ this.root = canonicalRoot;
211
+ this.rootIdentity = identityFrom(lstatSync(this.root));
212
+ this.faultInjector = options.faultInjector;
213
+ this.assertRoot();
214
+ }
215
+ getRoot() {
216
+ return this.root;
217
+ }
218
+ getReceiptPath(receiptId) {
219
+ this.assertReceiptId(receiptId);
220
+ return join(this.root, receiptId, "receipt.json");
221
+ }
222
+ assertReceiptId(receiptId) {
223
+ if (!isSafeEvidenceReceiptId(receiptId))
224
+ throw new Error("receiptId is not safe for storage");
225
+ }
226
+ assertRoot() {
227
+ const stat = lstatSync(this.root);
228
+ if (stat.isSymbolicLink() ||
229
+ !stat.isDirectory() ||
230
+ realpathSync(this.root) !== this.root ||
231
+ !matchesFileIdentity(this.rootIdentity, stat)) {
232
+ throw new Error("receipt store root is no longer the trusted directory");
233
+ }
234
+ }
235
+ captureReceiptDirectory(receiptDirectory) {
236
+ this.assertRoot();
237
+ const stat = lstatSync(receiptDirectory);
238
+ const livePath = realpathSync(receiptDirectory);
239
+ if (stat.isSymbolicLink() ||
240
+ !stat.isDirectory() ||
241
+ livePath !== receiptDirectory ||
242
+ !isStrictlyUnder(this.root, livePath)) {
243
+ throw new Error("receipt directory is not a trusted directory");
244
+ }
245
+ this.assertRoot();
246
+ return identityFrom(stat);
247
+ }
248
+ assertReceiptDirectory(receiptDirectory, expected) {
249
+ this.assertRoot();
250
+ const stat = lstatSync(receiptDirectory);
251
+ const livePath = realpathSync(receiptDirectory);
252
+ if (stat.isSymbolicLink() ||
253
+ !stat.isDirectory() ||
254
+ livePath !== receiptDirectory ||
255
+ !isStrictlyUnder(this.root, livePath) ||
256
+ !matchesFileIdentity(expected, stat)) {
257
+ throw new Error("receipt directory changed during persistence");
258
+ }
259
+ this.assertRoot();
260
+ }
261
+ assertReceiptFile(path, receiptDirectory, directoryIdentity, expectedFileIdentity, label) {
262
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
263
+ const stat = lstatSync(path);
264
+ const livePath = realpathSync(path);
265
+ if (stat.isSymbolicLink() ||
266
+ !stat.isFile() ||
267
+ livePath !== path ||
268
+ dirname(livePath) !== receiptDirectory ||
269
+ !isStrictlyUnder(this.root, livePath) ||
270
+ !matchesFileIdentity(expectedFileIdentity, stat)) {
271
+ throw new Error(`${label} path changed during persistence`);
272
+ }
273
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
274
+ return stat;
275
+ }
276
+ assertFinalAbsent(finalPath, receiptDirectory, directoryIdentity) {
277
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
278
+ try {
279
+ lstatSync(finalPath);
280
+ }
281
+ catch (error) {
282
+ if (errorCode(error) === "ENOENT") {
283
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
284
+ return;
285
+ }
286
+ throw error;
287
+ }
288
+ throw new Error("receipt destination already exists");
289
+ }
290
+ inject(stage) {
291
+ this.faultInjector?.(stage);
292
+ }
293
+ cleanupTemp(tempPath, receiptDirectory, directoryIdentity, tempIdentity) {
294
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
295
+ unlinkSync(tempPath);
296
+ this.inject("before-temp-cleanup-directory-fsync");
297
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
298
+ fsyncDirectory(receiptDirectory);
299
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
300
+ }
301
+ cleanupReceiptDirectory(receiptDirectory, directoryIdentity) {
302
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
303
+ rmdirSync(receiptDirectory);
304
+ this.inject("before-receipt-directory-cleanup-root-fsync");
305
+ this.assertRoot();
306
+ fsyncDirectory(this.root);
307
+ this.assertRoot();
308
+ }
309
+ /** Persist once using hard-link publication, which cannot replace an existing destination. */
310
+ write(receipt) {
311
+ const validated = validateEvidenceReceipt(receipt);
312
+ const receiptId = validated.core.receiptId;
313
+ this.assertReceiptId(receiptId);
314
+ this.assertRoot();
315
+ const receiptDirectory = join(this.root, receiptId);
316
+ const finalPath = join(receiptDirectory, "receipt.json");
317
+ const tempPath = join(receiptDirectory, `.receipt-${randomUUID()}.tmp`);
318
+ const bytes = Buffer.from(`${serializeEvidenceReceipt(validated)}\n`, "utf8");
319
+ let fd;
320
+ let directoryIdentity;
321
+ let tempIdentity;
322
+ let tempExists = false;
323
+ let published = false;
324
+ let failure;
325
+ const cleanupFailures = [];
326
+ try {
327
+ // mkdir without recursive atomically reserves this receipt ID.
328
+ mkdirSync(receiptDirectory, { mode: 0o700 });
329
+ directoryIdentity = this.captureReceiptDirectory(receiptDirectory);
330
+ this.inject("after-receipt-directory");
331
+ this.assertReceiptDirectory(receiptDirectory, directoryIdentity);
332
+ fd = openSync(tempPath, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0), 0o600);
333
+ tempExists = true;
334
+ tempIdentity = identityFrom(fstatSync(fd));
335
+ this.inject("after-temp-open");
336
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
337
+ writeAll(fd, bytes);
338
+ this.inject("after-temp-write");
339
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
340
+ fsyncSync(fd);
341
+ this.inject("after-temp-fsync");
342
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
343
+ closeSync(fd);
344
+ fd = undefined;
345
+ this.inject("after-temp-close");
346
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
347
+ this.assertFinalAbsent(finalPath, receiptDirectory, directoryIdentity);
348
+ this.inject("before-link");
349
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
350
+ atomicNoOverwritePublish(tempPath, finalPath);
351
+ published = true;
352
+ this.inject("after-link");
353
+ this.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, "receipt temp file");
354
+ this.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, "stored receipt");
355
+ unlinkSync(tempPath);
356
+ tempExists = false;
357
+ this.inject("after-temp-unlink");
358
+ this.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, "stored receipt");
359
+ fsyncDirectory(receiptDirectory);
360
+ this.inject("after-receipt-directory-fsync");
361
+ this.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, "stored receipt");
362
+ this.assertRoot();
363
+ fsyncDirectory(this.root);
364
+ this.inject("after-root-directory-fsync");
365
+ this.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, "stored receipt");
366
+ }
367
+ catch (error) {
368
+ failure = error;
369
+ }
370
+ finally {
371
+ if (fd !== undefined) {
372
+ try {
373
+ closeSync(fd);
374
+ }
375
+ catch (error) {
376
+ cleanupFailures.push(error);
377
+ }
378
+ }
379
+ }
380
+ if (failure !== undefined) {
381
+ if (tempExists && directoryIdentity !== undefined && tempIdentity !== undefined) {
382
+ try {
383
+ this.cleanupTemp(tempPath, receiptDirectory, directoryIdentity, tempIdentity);
384
+ }
385
+ catch (error) {
386
+ cleanupFailures.push(error);
387
+ }
388
+ }
389
+ if (!published && directoryIdentity !== undefined) {
390
+ try {
391
+ this.cleanupReceiptDirectory(receiptDirectory, directoryIdentity);
392
+ }
393
+ catch (error) {
394
+ cleanupFailures.push(error);
395
+ }
396
+ }
397
+ if (cleanupFailures.length > 0) {
398
+ const message = failure instanceof Error ? failure.message : String(failure);
399
+ const cleanupMessage = cleanupFailures
400
+ .map((cleanupFailure) => cleanupFailure instanceof Error ? cleanupFailure.message : String(cleanupFailure))
401
+ .join("; ");
402
+ throw new Error(`receipt store persistence failed and cleanup was incomplete: ${message}; cleanup failure: ${cleanupMessage}`, {
403
+ cause: new AggregateError([failure, ...cleanupFailures], "receipt store write rollback failed"),
404
+ });
405
+ }
406
+ throw failure;
407
+ }
408
+ return finalPath;
409
+ }
410
+ /** Read only bytes whose live path remains bound to the opened owner-controlled file. */
411
+ read(receiptId) {
412
+ this.assertReceiptId(receiptId);
413
+ this.assertRoot();
414
+ const receiptDirectory = join(this.root, receiptId);
415
+ const directoryIdentity = this.captureReceiptDirectory(receiptDirectory);
416
+ const path = this.getReceiptPath(receiptId);
417
+ const fd = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
418
+ try {
419
+ const before = fstatSync(fd);
420
+ if (!before.isFile())
421
+ throw new Error("stored receipt is not a regular file");
422
+ const fileIdentity = identityFrom(before);
423
+ this.assertReceiptFile(path, receiptDirectory, directoryIdentity, fileIdentity, "stored receipt");
424
+ const bytes = readFileSync(fd);
425
+ const after = fstatSync(fd);
426
+ this.assertReceiptFile(path, receiptDirectory, directoryIdentity, fileIdentity, "stored receipt");
427
+ if (!after.isFile() ||
428
+ !matchesFileIdentity(fileIdentity, after) ||
429
+ before.size !== after.size ||
430
+ before.mtimeMs !== after.mtimeMs ||
431
+ before.ctimeMs !== after.ctimeMs ||
432
+ after.size !== bytes.byteLength) {
433
+ throw new Error("stored receipt changed while it was read");
434
+ }
435
+ const receipt = parseEvidenceReceipt(bytes.toString("utf8"));
436
+ if (receipt.core.receiptId !== receiptId) {
437
+ throw new Error("stored receiptId does not match its storage key");
438
+ }
439
+ return receipt;
440
+ }
441
+ finally {
442
+ closeSync(fd);
443
+ }
444
+ }
445
+ }
446
+ //# sourceMappingURL=evidence-receipt-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence-receipt-store.js","sourceRoot":"","sources":["../../src/guardrails/evidence-receipt-store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { Stats } from \"node:fs\";\nimport {\n\tchmodSync,\n\tcloseSync,\n\tconstants as fsConstants,\n\tfstatSync,\n\tfsyncSync,\n\tlinkSync,\n\tlstatSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trealpathSync,\n\trmdirSync,\n\tunlinkSync,\n\twriteSync,\n} from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve, sep } from \"node:path\";\nimport type { EvidenceReceipt } from \"../types/evidence.ts\";\nimport {\n\tisSafeEvidenceReceiptId,\n\tparseEvidenceReceipt,\n\tserializeEvidenceReceipt,\n\tvalidateEvidenceReceipt,\n} from \"./evidence-receipt.ts\";\n\nexport type EvidenceReceiptStoreFaultStage =\n\t| \"before-root-parent-directory-fsync\"\n\t| \"after-receipt-directory\"\n\t| \"after-temp-open\"\n\t| \"after-temp-write\"\n\t| \"after-temp-fsync\"\n\t| \"after-temp-close\"\n\t| \"before-link\"\n\t| \"after-link\"\n\t| \"after-temp-unlink\"\n\t| \"before-temp-cleanup-directory-fsync\"\n\t| \"before-receipt-directory-cleanup-root-fsync\"\n\t| \"after-receipt-directory-fsync\"\n\t| \"after-root-directory-fsync\";\n\nexport interface EvidenceReceiptStoreOptions {\n\t/** Tests only: throw or mutate paths at a selected persistence stage. */\n\treadonly faultInjector?: (stage: EvidenceReceiptStoreFaultStage) => void;\n}\n\ntype FileStat = Stats;\n\ninterface FileIdentity {\n\treadonly dev: number;\n\treadonly ino: number;\n}\n\ninterface DirectoryIdentity {\n\treadonly path: string;\n\treadonly identity: FileIdentity;\n}\n\nfunction errorCode(error: unknown): string | undefined {\n\treturn typeof error === \"object\" && error !== null && \"code\" in error\n\t\t? String((error as { code?: unknown }).code)\n\t\t: undefined;\n}\n\nconst UNSUPPORTED_DIRECTORY_FSYNC_CODES: ReadonlySet<string> = new Set([\n\t\"EBADF\",\n\t\"EINVAL\",\n\t\"EISDIR\",\n\t\"ENOSYS\",\n\t\"ENOTSUP\",\n]);\n\nfunction classifiedDirectoryFsyncError(path: string, error: unknown): unknown {\n\tconst code = errorCode(error);\n\tif (code === undefined || !UNSUPPORTED_DIRECTORY_FSYNC_CODES.has(code)) return error;\n\treturn new Error(`receipt store strict durability unavailable: directory fsync unsupported for ${path} (${code})`, {\n\t\tcause: error,\n\t});\n}\n\nfunction fsyncDirectory(path: string): void {\n\tlet fd: number | undefined;\n\tlet failure: unknown;\n\ttry {\n\t\tfd = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0) | (fsConstants.O_NOFOLLOW ?? 0));\n\t\tfsyncSync(fd);\n\t} catch (error) {\n\t\tfailure = classifiedDirectoryFsyncError(path, error);\n\t} finally {\n\t\tif (fd !== undefined) {\n\t\t\ttry {\n\t\t\t\tcloseSync(fd);\n\t\t\t} catch (error) {\n\t\t\t\tif (failure === undefined) failure = error;\n\t\t\t}\n\t\t}\n\t}\n\tif (failure !== undefined) throw failure;\n}\n\nfunction writeAll(fd: number, bytes: Uint8Array): void {\n\tlet offset = 0;\n\twhile (offset < bytes.byteLength) {\n\t\tconst written = writeSync(fd, bytes, offset, bytes.byteLength - offset);\n\t\tif (written <= 0) throw new Error(\"receipt store could not complete the file write\");\n\t\toffset += written;\n\t}\n}\n\nfunction identityFrom(stat: FileStat): FileIdentity {\n\treturn { dev: stat.dev, ino: stat.ino };\n}\n\nfunction hasFileIdentity(identity: FileIdentity): boolean {\n\treturn identity.dev !== 0 || identity.ino !== 0;\n}\n\nfunction matchesFileIdentity(expected: FileIdentity, actual: FileStat): boolean {\n\tconst actualIdentity = identityFrom(actual);\n\tconst expectedHasIdentity = hasFileIdentity(expected);\n\tconst actualHasIdentity = hasFileIdentity(actualIdentity);\n\treturn (\n\t\t(!expectedHasIdentity && !actualHasIdentity) ||\n\t\t(expectedHasIdentity &&\n\t\t\tactualHasIdentity &&\n\t\t\texpected.dev === actualIdentity.dev &&\n\t\t\texpected.ino === actualIdentity.ino)\n\t);\n}\n\nfunction missingDirectoryPaths(path: string): string[] {\n\tconst missing: string[] = [];\n\tlet current = path;\n\twhile (true) {\n\t\ttry {\n\t\t\tlstatSync(current);\n\t\t\treturn missing;\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) !== \"ENOENT\") throw error;\n\t\t\tmissing.push(current);\n\t\t\tconst parent = dirname(current);\n\t\t\tif (parent === current) throw new Error(\"receipt store root has no existing ancestor\");\n\t\t\tcurrent = parent;\n\t\t}\n\t}\n}\n\nfunction captureDirectoryIdentity(path: string, label: string): DirectoryIdentity {\n\tconst stat = lstatSync(path);\n\tconst canonicalPath = realpathSync(path);\n\tconst canonicalStat = lstatSync(canonicalPath);\n\tif (\n\t\tstat.isSymbolicLink() ||\n\t\t!stat.isDirectory() ||\n\t\tcanonicalStat.isSymbolicLink() ||\n\t\t!canonicalStat.isDirectory() ||\n\t\trealpathSync(canonicalPath) !== canonicalPath ||\n\t\t!matchesFileIdentity(identityFrom(stat), canonicalStat)\n\t) {\n\t\tthrow new Error(`${label} is not a trusted directory`);\n\t}\n\treturn { path: canonicalPath, identity: identityFrom(canonicalStat) };\n}\n\nfunction assertDirectoryIdentity(directory: DirectoryIdentity, label: string): void {\n\tconst stat = lstatSync(directory.path);\n\tif (\n\t\tstat.isSymbolicLink() ||\n\t\t!stat.isDirectory() ||\n\t\trealpathSync(directory.path) !== directory.path ||\n\t\t!matchesFileIdentity(directory.identity, stat)\n\t) {\n\t\tthrow new Error(`${label} changed during root durability setup`);\n\t}\n}\n\nfunction cleanupCreatedDirectories(directories: readonly DirectoryIdentity[]): unknown | undefined {\n\tlet failure: unknown;\n\tfor (const directory of directories) {\n\t\tlet removed = false;\n\t\ttry {\n\t\t\tassertDirectoryIdentity(directory, \"new receipt store directory\");\n\t\t\trmdirSync(directory.path);\n\t\t\tremoved = true;\n\t\t} catch (error) {\n\t\t\tif (failure === undefined) failure = error;\n\t\t}\n\t\tif (!removed) continue;\n\t\ttry {\n\t\t\t// Persist each successful removal in its still-existing parent before moving outward.\n\t\t\tfsyncDirectory(dirname(directory.path));\n\t\t} catch (error) {\n\t\t\tif (failure === undefined) failure = error;\n\t\t}\n\t}\n\treturn failure;\n}\n\nfunction isStrictlyUnder(root: string, path: string): boolean {\n\tconst relativePath = relative(root, path);\n\treturn (\n\t\trelativePath !== \"\" && relativePath !== \"..\" && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath)\n\t);\n}\n\nfunction atomicNoOverwritePublish(tempPath: string, finalPath: string): void {\n\ttry {\n\t\tlinkSync(tempPath, finalPath);\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`receipt store atomic no-overwrite publish failed (${errorCode(error) ?? \"unknown\"}); destination was not replaced`,\n\t\t\t{ cause: error },\n\t\t);\n\t}\n}\n\n/**\n * Owner-only, no-overwrite receipt persistence rooted at one trusted directory.\n * Phase A assumes same-UID path mutation is quiescent; identity checks detect observed\n * replacement but are not filesystem isolation, and persistence alone must not activate EvidenceGate.\n */\nexport class EvidenceReceiptStore {\n\tprivate readonly root: string;\n\tprivate readonly rootIdentity: FileIdentity;\n\tprivate readonly faultInjector: ((stage: EvidenceReceiptStoreFaultStage) => void) | undefined;\n\n\tconstructor(root: string, options: EvidenceReceiptStoreOptions = {}) {\n\t\tif (typeof root !== \"string\" || root.length === 0) throw new Error(\"receipt store root must be non-empty\");\n\t\tconst resolvedRoot = resolve(root);\n\t\tconst missingPaths = missingDirectoryPaths(resolvedRoot);\n\t\tlet canonicalRoot: string;\n\t\tif (missingPaths.length === 0) {\n\t\t\tconst stat = lstatSync(resolvedRoot);\n\t\t\tif (stat.isSymbolicLink()) throw new Error(\"receipt store root must not be a symlink\");\n\t\t\tif (!stat.isDirectory()) throw new Error(\"receipt store root must be a directory\");\n\t\t\tchmodSync(resolvedRoot, 0o700);\n\t\t\tcanonicalRoot = realpathSync(resolvedRoot);\n\t\t} else {\n\t\t\tmkdirSync(resolvedRoot, { recursive: true, mode: 0o700 });\n\t\t\tchmodSync(resolvedRoot, 0o700);\n\t\t\tcanonicalRoot = realpathSync(resolvedRoot);\n\t\t\tconst createdDirectories = missingPaths.map((path) =>\n\t\t\t\tcaptureDirectoryIdentity(path, \"new receipt store directory\"),\n\t\t\t);\n\t\t\tconst outermostCreated = createdDirectories.at(-1);\n\t\t\tif (outermostCreated === undefined) throw new Error(\"receipt store root creation was not observed\");\n\t\t\ttry {\n\t\t\t\t// Persist each new directory before its parent, walking from the root outward.\n\t\t\t\tfor (const directory of createdDirectories) {\n\t\t\t\t\tassertDirectoryIdentity(directory, \"new receipt store directory\");\n\t\t\t\t\tfsyncDirectory(directory.path);\n\t\t\t\t\tassertDirectoryIdentity(directory, \"new receipt store directory\");\n\t\t\t\t}\n\t\t\t\tconst parent = captureDirectoryIdentity(dirname(outermostCreated.path), \"receipt store root parent\");\n\t\t\t\toptions.faultInjector?.(\"before-root-parent-directory-fsync\");\n\t\t\t\tassertDirectoryIdentity(parent, \"receipt store root parent\");\n\t\t\t\tfsyncDirectory(parent.path);\n\t\t\t\tassertDirectoryIdentity(parent, \"receipt store root parent\");\n\t\t\t} catch (error) {\n\t\t\t\tconst failure = classifiedDirectoryFsyncError(dirname(outermostCreated.path), error);\n\t\t\t\tconst cleanupFailure = cleanupCreatedDirectories(createdDirectories);\n\t\t\t\tif (cleanupFailure !== undefined) {\n\t\t\t\t\tconst message = failure instanceof Error ? failure.message : String(failure);\n\t\t\t\t\tconst cleanupMessage = cleanupFailure instanceof Error ? cleanupFailure.message : String(cleanupFailure);\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`receipt store root durability failed and cleanup was incomplete: ${message}; cleanup failure: ${cleanupMessage}`,\n\t\t\t\t\t\t{ cause: new AggregateError([failure, cleanupFailure], \"receipt store root rollback failed\") },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow failure;\n\t\t\t}\n\t\t}\n\t\tthis.root = canonicalRoot;\n\t\tthis.rootIdentity = identityFrom(lstatSync(this.root));\n\t\tthis.faultInjector = options.faultInjector;\n\t\tthis.assertRoot();\n\t}\n\n\tgetRoot(): string {\n\t\treturn this.root;\n\t}\n\n\tgetReceiptPath(receiptId: string): string {\n\t\tthis.assertReceiptId(receiptId);\n\t\treturn join(this.root, receiptId, \"receipt.json\");\n\t}\n\n\tprivate assertReceiptId(receiptId: unknown): asserts receiptId is string {\n\t\tif (!isSafeEvidenceReceiptId(receiptId)) throw new Error(\"receiptId is not safe for storage\");\n\t}\n\n\tprivate assertRoot(): void {\n\t\tconst stat = lstatSync(this.root);\n\t\tif (\n\t\t\tstat.isSymbolicLink() ||\n\t\t\t!stat.isDirectory() ||\n\t\t\trealpathSync(this.root) !== this.root ||\n\t\t\t!matchesFileIdentity(this.rootIdentity, stat)\n\t\t) {\n\t\t\tthrow new Error(\"receipt store root is no longer the trusted directory\");\n\t\t}\n\t}\n\n\tprivate captureReceiptDirectory(receiptDirectory: string): FileIdentity {\n\t\tthis.assertRoot();\n\t\tconst stat = lstatSync(receiptDirectory);\n\t\tconst livePath = realpathSync(receiptDirectory);\n\t\tif (\n\t\t\tstat.isSymbolicLink() ||\n\t\t\t!stat.isDirectory() ||\n\t\t\tlivePath !== receiptDirectory ||\n\t\t\t!isStrictlyUnder(this.root, livePath)\n\t\t) {\n\t\t\tthrow new Error(\"receipt directory is not a trusted directory\");\n\t\t}\n\t\tthis.assertRoot();\n\t\treturn identityFrom(stat);\n\t}\n\n\tprivate assertReceiptDirectory(receiptDirectory: string, expected: FileIdentity): void {\n\t\tthis.assertRoot();\n\t\tconst stat = lstatSync(receiptDirectory);\n\t\tconst livePath = realpathSync(receiptDirectory);\n\t\tif (\n\t\t\tstat.isSymbolicLink() ||\n\t\t\t!stat.isDirectory() ||\n\t\t\tlivePath !== receiptDirectory ||\n\t\t\t!isStrictlyUnder(this.root, livePath) ||\n\t\t\t!matchesFileIdentity(expected, stat)\n\t\t) {\n\t\t\tthrow new Error(\"receipt directory changed during persistence\");\n\t\t}\n\t\tthis.assertRoot();\n\t}\n\n\tprivate assertReceiptFile(\n\t\tpath: string,\n\t\treceiptDirectory: string,\n\t\tdirectoryIdentity: FileIdentity,\n\t\texpectedFileIdentity: FileIdentity,\n\t\tlabel: string,\n\t): FileStat {\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\tconst stat = lstatSync(path);\n\t\tconst livePath = realpathSync(path);\n\t\tif (\n\t\t\tstat.isSymbolicLink() ||\n\t\t\t!stat.isFile() ||\n\t\t\tlivePath !== path ||\n\t\t\tdirname(livePath) !== receiptDirectory ||\n\t\t\t!isStrictlyUnder(this.root, livePath) ||\n\t\t\t!matchesFileIdentity(expectedFileIdentity, stat)\n\t\t) {\n\t\t\tthrow new Error(`${label} path changed during persistence`);\n\t\t}\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\treturn stat;\n\t}\n\n\tprivate assertFinalAbsent(finalPath: string, receiptDirectory: string, directoryIdentity: FileIdentity): void {\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\ttry {\n\t\t\tlstatSync(finalPath);\n\t\t} catch (error) {\n\t\t\tif (errorCode(error) === \"ENOENT\") {\n\t\t\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t\tthrow new Error(\"receipt destination already exists\");\n\t}\n\n\tprivate inject(stage: EvidenceReceiptStoreFaultStage): void {\n\t\tthis.faultInjector?.(stage);\n\t}\n\n\tprivate cleanupTemp(\n\t\ttempPath: string,\n\t\treceiptDirectory: string,\n\t\tdirectoryIdentity: FileIdentity,\n\t\ttempIdentity: FileIdentity,\n\t): void {\n\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\t\tunlinkSync(tempPath);\n\t\tthis.inject(\"before-temp-cleanup-directory-fsync\");\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\tfsyncDirectory(receiptDirectory);\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t}\n\n\tprivate cleanupReceiptDirectory(receiptDirectory: string, directoryIdentity: FileIdentity): void {\n\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\trmdirSync(receiptDirectory);\n\t\tthis.inject(\"before-receipt-directory-cleanup-root-fsync\");\n\t\tthis.assertRoot();\n\t\tfsyncDirectory(this.root);\n\t\tthis.assertRoot();\n\t}\n\n\t/** Persist once using hard-link publication, which cannot replace an existing destination. */\n\twrite(receipt: EvidenceReceipt): string {\n\t\tconst validated = validateEvidenceReceipt(receipt);\n\t\tconst receiptId = validated.core.receiptId;\n\t\tthis.assertReceiptId(receiptId);\n\t\tthis.assertRoot();\n\t\tconst receiptDirectory = join(this.root, receiptId);\n\t\tconst finalPath = join(receiptDirectory, \"receipt.json\");\n\t\tconst tempPath = join(receiptDirectory, `.receipt-${randomUUID()}.tmp`);\n\t\tconst bytes = Buffer.from(`${serializeEvidenceReceipt(validated)}\\n`, \"utf8\");\n\t\tlet fd: number | undefined;\n\t\tlet directoryIdentity: FileIdentity | undefined;\n\t\tlet tempIdentity: FileIdentity | undefined;\n\t\tlet tempExists = false;\n\t\tlet published = false;\n\t\tlet failure: unknown;\n\t\tconst cleanupFailures: unknown[] = [];\n\n\t\ttry {\n\t\t\t// mkdir without recursive atomically reserves this receipt ID.\n\t\t\tmkdirSync(receiptDirectory, { mode: 0o700 });\n\t\t\tdirectoryIdentity = this.captureReceiptDirectory(receiptDirectory);\n\t\t\tthis.inject(\"after-receipt-directory\");\n\t\t\tthis.assertReceiptDirectory(receiptDirectory, directoryIdentity);\n\n\t\t\tfd = openSync(\n\t\t\t\ttempPath,\n\t\t\t\tfsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY | (fsConstants.O_NOFOLLOW ?? 0),\n\t\t\t\t0o600,\n\t\t\t);\n\t\t\ttempExists = true;\n\t\t\ttempIdentity = identityFrom(fstatSync(fd));\n\t\t\tthis.inject(\"after-temp-open\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\n\t\t\twriteAll(fd, bytes);\n\t\t\tthis.inject(\"after-temp-write\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\t\t\tfsyncSync(fd);\n\t\t\tthis.inject(\"after-temp-fsync\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\t\t\tcloseSync(fd);\n\t\t\tfd = undefined;\n\t\t\tthis.inject(\"after-temp-close\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\t\t\tthis.assertFinalAbsent(finalPath, receiptDirectory, directoryIdentity);\n\t\t\tthis.inject(\"before-link\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\n\t\t\tatomicNoOverwritePublish(tempPath, finalPath);\n\t\t\tpublished = true;\n\t\t\tthis.inject(\"after-link\");\n\t\t\tthis.assertReceiptFile(tempPath, receiptDirectory, directoryIdentity, tempIdentity, \"receipt temp file\");\n\t\t\tthis.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, \"stored receipt\");\n\t\t\tunlinkSync(tempPath);\n\t\t\ttempExists = false;\n\t\t\tthis.inject(\"after-temp-unlink\");\n\t\t\tthis.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, \"stored receipt\");\n\n\t\t\tfsyncDirectory(receiptDirectory);\n\t\t\tthis.inject(\"after-receipt-directory-fsync\");\n\t\t\tthis.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, \"stored receipt\");\n\t\t\tthis.assertRoot();\n\t\t\tfsyncDirectory(this.root);\n\t\t\tthis.inject(\"after-root-directory-fsync\");\n\t\t\tthis.assertReceiptFile(finalPath, receiptDirectory, directoryIdentity, tempIdentity, \"stored receipt\");\n\t\t} catch (error) {\n\t\t\tfailure = error;\n\t\t} finally {\n\t\t\tif (fd !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tcloseSync(fd);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcleanupFailures.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (failure !== undefined) {\n\t\t\tif (tempExists && directoryIdentity !== undefined && tempIdentity !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.cleanupTemp(tempPath, receiptDirectory, directoryIdentity, tempIdentity);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcleanupFailures.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!published && directoryIdentity !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\tthis.cleanupReceiptDirectory(receiptDirectory, directoryIdentity);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tcleanupFailures.push(error);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (cleanupFailures.length > 0) {\n\t\t\t\tconst message = failure instanceof Error ? failure.message : String(failure);\n\t\t\t\tconst cleanupMessage = cleanupFailures\n\t\t\t\t\t.map((cleanupFailure) =>\n\t\t\t\t\t\tcleanupFailure instanceof Error ? cleanupFailure.message : String(cleanupFailure),\n\t\t\t\t\t)\n\t\t\t\t\t.join(\"; \");\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`receipt store persistence failed and cleanup was incomplete: ${message}; cleanup failure: ${cleanupMessage}`,\n\t\t\t\t\t{\n\t\t\t\t\t\tcause: new AggregateError([failure, ...cleanupFailures], \"receipt store write rollback failed\"),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow failure;\n\t\t}\n\t\treturn finalPath;\n\t}\n\n\t/** Read only bytes whose live path remains bound to the opened owner-controlled file. */\n\tread(receiptId: string): EvidenceReceipt {\n\t\tthis.assertReceiptId(receiptId);\n\t\tthis.assertRoot();\n\t\tconst receiptDirectory = join(this.root, receiptId);\n\t\tconst directoryIdentity = this.captureReceiptDirectory(receiptDirectory);\n\t\tconst path = this.getReceiptPath(receiptId);\n\t\tconst fd = openSync(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));\n\t\ttry {\n\t\t\tconst before = fstatSync(fd);\n\t\t\tif (!before.isFile()) throw new Error(\"stored receipt is not a regular file\");\n\t\t\tconst fileIdentity = identityFrom(before);\n\t\t\tthis.assertReceiptFile(path, receiptDirectory, directoryIdentity, fileIdentity, \"stored receipt\");\n\t\t\tconst bytes = readFileSync(fd);\n\t\t\tconst after = fstatSync(fd);\n\t\t\tthis.assertReceiptFile(path, receiptDirectory, directoryIdentity, fileIdentity, \"stored receipt\");\n\t\t\tif (\n\t\t\t\t!after.isFile() ||\n\t\t\t\t!matchesFileIdentity(fileIdentity, after) ||\n\t\t\t\tbefore.size !== after.size ||\n\t\t\t\tbefore.mtimeMs !== after.mtimeMs ||\n\t\t\t\tbefore.ctimeMs !== after.ctimeMs ||\n\t\t\t\tafter.size !== bytes.byteLength\n\t\t\t) {\n\t\t\t\tthrow new Error(\"stored receipt changed while it was read\");\n\t\t\t}\n\t\t\tconst receipt = parseEvidenceReceipt(bytes.toString(\"utf8\"));\n\t\t\tif (receipt.core.receiptId !== receiptId) {\n\t\t\t\tthrow new Error(\"stored receiptId does not match its storage key\");\n\t\t\t}\n\t\t\treturn receipt;\n\t\t} finally {\n\t\t\tcloseSync(fd);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import type { CommandHmacBinding, CommandRedactionSummary, EvidenceCommandDescriptor, EvidenceExecutor, EvidenceReceipt, EvidenceReceiptCore, EvidenceReceiptDisposition, EvidenceReceiptLedgerBinding, EvidenceReceiptReplayPayload, Sha256Hex, TrustedEvidenceAttestation, WorkspaceFingerprint } from "../types/evidence.ts";
2
+ /** Combined stdout/stderr bytes accepted from an already-redacted caller. */
3
+ export declare const MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES: number;
4
+ export interface AlreadyRedactedOutputBytes {
5
+ readonly redactionPolicyId: string;
6
+ readonly stdout: Uint8Array;
7
+ readonly stderr: Uint8Array;
8
+ }
9
+ export interface EvidenceReceiptCoreInputFields {
10
+ readonly receiptId: string;
11
+ readonly goalId: string;
12
+ readonly laneId?: string;
13
+ readonly claim: string;
14
+ readonly command: EvidenceCommandDescriptor;
15
+ readonly cwd: string;
16
+ readonly timeoutMs: number | null;
17
+ readonly startedAt: string;
18
+ readonly finishedAt: string;
19
+ readonly durationMs: number;
20
+ readonly workspaceBefore: WorkspaceFingerprint;
21
+ readonly workspaceAfter: WorkspaceFingerprint;
22
+ readonly alreadyRedactedOutput: AlreadyRedactedOutputBytes;
23
+ readonly executor: EvidenceExecutor;
24
+ readonly toolCallId?: string;
25
+ readonly commandRedaction?: CommandRedactionSummary;
26
+ readonly commandBinding?: CommandHmacBinding;
27
+ }
28
+ export type CreateEvidenceReceiptInput = EvidenceReceiptCoreInputFields & EvidenceReceiptDisposition & {
29
+ readonly ledgerBinding?: EvidenceReceiptLedgerBinding;
30
+ readonly trustedAttestation?: TrustedEvidenceAttestation;
31
+ };
32
+ export interface EvidenceReceiptEnvelopeMetadata {
33
+ readonly ledgerBinding?: EvidenceReceiptLedgerBinding;
34
+ readonly trustedAttestation?: TrustedEvidenceAttestation;
35
+ }
36
+ export declare function isSha256Hex(value: unknown): value is Sha256Hex;
37
+ export declare function parseSha256Hex(value: unknown, label?: string): Sha256Hex;
38
+ /** Decode validated hex before comparing, avoiding string timing and malformed-buffer pitfalls. */
39
+ export declare function constantTimeSha256Equal(left: unknown, right: unknown): boolean;
40
+ export declare function isSafeEvidenceReceiptId(value: unknown): value is string;
41
+ /** Strict shape parser for immutable receipt core data. */
42
+ export declare function parseEvidenceReceiptCore(value: unknown): EvidenceReceiptCore;
43
+ export declare function serializeEvidenceReceiptCore(core: EvidenceReceiptCore): string;
44
+ /** Domain-separated SHA-256 of one exact, credential-free structured command descriptor. */
45
+ export declare function computeEvidenceCommandSha256(command: EvidenceCommandDescriptor): Sha256Hex;
46
+ /** Domain-separated SHA-256 of the immutable canonical core only. */
47
+ export declare function computeEvidenceReceiptCoreSha256(core: EvidenceReceiptCore): Sha256Hex;
48
+ /** Strict shape and digest validation for an already-decoded receipt value. */
49
+ export declare function validateEvidenceReceipt(value: unknown): EvidenceReceipt;
50
+ export declare function parseEvidenceReceipt(serialized: string): EvidenceReceipt;
51
+ export declare function serializeEvidenceReceipt(receipt: EvidenceReceipt): string;
52
+ export declare function verifyEvidenceReceiptCoreDigest(receipt: EvidenceReceipt): boolean;
53
+ /** Build a receipt from caller-supplied execution facts without executing a command or activating a gate. */
54
+ export declare function createEvidenceReceipt(input: CreateEvidenceReceiptInput): EvidenceReceipt;
55
+ /** Attach non-core metadata after a ledger commits {receiptId, coreSha256}. */
56
+ export declare function withEvidenceReceiptEnvelope(receipt: EvidenceReceipt, metadata: EvidenceReceiptEnvelopeMetadata): EvidenceReceipt;
57
+ export declare function evidenceReceiptReplayPayload(receipt: EvidenceReceipt): EvidenceReceiptReplayPayload;
58
+ //# sourceMappingURL=evidence-receipt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence-receipt.d.ts","sourceRoot":"","sources":["../../src/guardrails/evidence-receipt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,gBAAgB,EAEhB,eAAe,EACf,mBAAmB,EAEnB,0BAA0B,EAE1B,4BAA4B,EAC5B,4BAA4B,EAC5B,SAAS,EACT,0BAA0B,EAC1B,oBAAoB,EACpB,MAAM,sBAAsB,CAAC;AAgB9B,6EAA6E;AAC7E,eAAO,MAAM,iCAAiC,QAAY,CAAC;AAE3D,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,8BAA8B;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,eAAe,EAAE,oBAAoB,CAAC;IAC/C,QAAQ,CAAC,cAAc,EAAE,oBAAoB,CAAC;IAC9C,QAAQ,CAAC,qBAAqB,EAAE,0BAA0B,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,gBAAgB,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IACpD,QAAQ,CAAC,cAAc,CAAC,EAAE,kBAAkB,CAAC;CAC7C;AAED,MAAM,MAAM,0BAA0B,GAAG,8BAA8B,GACtE,0BAA0B,GAAG;IAC5B,QAAQ,CAAC,aAAa,CAAC,EAAE,4BAA4B,CAAC;IACtD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;CACzD,CAAC;AAEH,MAAM,WAAW,+BAA+B;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,4BAA4B,CAAC;IACtD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,0BAA0B,CAAC;CACzD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,SAAS,CAE9D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAW,GAAG,SAAS,CAK1E;AAED,mGAAmG;AACnG,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAG9E;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvE;AAwHD,2DAA2D;AAC3D,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAgF5E;AAgBD,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAE9E;AAED,4FAA4F;AAC5F,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAK1F;AAED,qEAAqE;AACrE,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,mBAAmB,GAAG,SAAS,CAKrF;AAmCD,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CASvE;AAED,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,CASxE;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAEzE;AAED,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAOjF;AA+BD,6GAA6G;AAC7G,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,eAAe,CA8BxF;AAED,+EAA+E;AAC/E,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,+BAA+B,GACvC,eAAe,CAUjB;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,GAAG,4BAA4B,CAMnG","sourcesContent":["import { createHash, timingSafeEqual } from \"node:crypto\";\nimport type {\n\tCommandHmacBinding,\n\tCommandRedactionSummary,\n\tEvidenceCommandDescriptor,\n\tEvidenceExecutor,\n\tEvidenceOutputCapture,\n\tEvidenceReceipt,\n\tEvidenceReceiptCore,\n\tEvidenceReceiptCoreFields,\n\tEvidenceReceiptDisposition,\n\tEvidenceReceiptEnvelope,\n\tEvidenceReceiptLedgerBinding,\n\tEvidenceReceiptReplayPayload,\n\tSha256Hex,\n\tTrustedEvidenceAttestation,\n\tWorkspaceFingerprint,\n} from \"../types/evidence.ts\";\nimport {\n\tassertCredentialFreeEvidenceCommand,\n\tparseCommandHmacBinding,\n\tparseCommandRedactionSummary,\n\tparseEvidenceCommandShape,\n} from \"./command-redaction.ts\";\nimport { parseWorkspaceFingerprint } from \"./workspace-fingerprint.ts\";\n\nconst SHA256_HEX = /^[0-9a-f]{64}$/;\nconst SAFE_RECEIPT_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;\nconst CORE_DIGEST_DOMAIN = \"omk:evidence:receipt-v3:core\\0\";\nconst COMMAND_DIGEST_DOMAIN = \"omk:evidence:receipt-v3:command\\0\";\nconst EXECUTORS: ReadonlySet<string> = new Set([\"bash-tool\", \"ci-runner\", \"mcp\", \"internal\"]);\nconst ISO_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/;\n\n/** Combined stdout/stderr bytes accepted from an already-redacted caller. */\nexport const MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES = 64 * 1024;\n\nexport interface AlreadyRedactedOutputBytes {\n\treadonly redactionPolicyId: string;\n\treadonly stdout: Uint8Array;\n\treadonly stderr: Uint8Array;\n}\n\nexport interface EvidenceReceiptCoreInputFields {\n\treadonly receiptId: string;\n\treadonly goalId: string;\n\treadonly laneId?: string;\n\treadonly claim: string;\n\treadonly command: EvidenceCommandDescriptor;\n\treadonly cwd: string;\n\treadonly timeoutMs: number | null;\n\treadonly startedAt: string;\n\treadonly finishedAt: string;\n\treadonly durationMs: number;\n\treadonly workspaceBefore: WorkspaceFingerprint;\n\treadonly workspaceAfter: WorkspaceFingerprint;\n\treadonly alreadyRedactedOutput: AlreadyRedactedOutputBytes;\n\treadonly executor: EvidenceExecutor;\n\treadonly toolCallId?: string;\n\treadonly commandRedaction?: CommandRedactionSummary;\n\treadonly commandBinding?: CommandHmacBinding;\n}\n\nexport type CreateEvidenceReceiptInput = EvidenceReceiptCoreInputFields &\n\tEvidenceReceiptDisposition & {\n\t\treadonly ledgerBinding?: EvidenceReceiptLedgerBinding;\n\t\treadonly trustedAttestation?: TrustedEvidenceAttestation;\n\t};\n\nexport interface EvidenceReceiptEnvelopeMetadata {\n\treadonly ledgerBinding?: EvidenceReceiptLedgerBinding;\n\treadonly trustedAttestation?: TrustedEvidenceAttestation;\n}\n\nexport function isSha256Hex(value: unknown): value is Sha256Hex {\n\treturn typeof value === \"string\" && SHA256_HEX.test(value);\n}\n\nexport function parseSha256Hex(value: unknown, label = \"digest\"): Sha256Hex {\n\tif (!isSha256Hex(value)) {\n\t\tthrow new Error(`${label} must be exactly 64 lowercase SHA-256 hexadecimal characters`);\n\t}\n\treturn value;\n}\n\n/** Decode validated hex before comparing, avoiding string timing and malformed-buffer pitfalls. */\nexport function constantTimeSha256Equal(left: unknown, right: unknown): boolean {\n\tif (!isSha256Hex(left) || !isSha256Hex(right)) return false;\n\treturn timingSafeEqual(Buffer.from(left, \"hex\"), Buffer.from(right, \"hex\"));\n}\n\nexport function isSafeEvidenceReceiptId(value: unknown): value is string {\n\treturn typeof value === \"string\" && SAFE_RECEIPT_ID.test(value);\n}\n\nfunction exactObject(\n\tvalue: unknown,\n\tlabel: string,\n\trequiredKeys: readonly string[],\n\toptionalKeys: readonly string[] = [],\n): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\tconst record = value as Record<string, unknown>;\n\tconst descriptors = Object.getOwnPropertyDescriptors(record);\n\tconst actual = Object.entries(descriptors)\n\t\t.filter(([, descriptor]) => descriptor.enumerable)\n\t\t.map(([key]) => key)\n\t\t.sort();\n\tconst allowed = new Set([...requiredKeys, ...optionalKeys]);\n\tconst isOwnEnumerableDataProperty = (key: string): boolean => {\n\t\tconst descriptor = descriptors[key];\n\t\treturn descriptor !== undefined && descriptor.enumerable === true && \"value\" in descriptor;\n\t};\n\tif (\n\t\tactual.some((key) => !allowed.has(key)) ||\n\t\trequiredKeys.some((key) => !isOwnEnumerableDataProperty(key)) ||\n\t\toptionalKeys.some((key) => key in record && !isOwnEnumerableDataProperty(key))\n\t) {\n\t\tthrow new Error(`${label} has an invalid key set`);\n\t}\n\tconst snapshot: Record<string, unknown> = {};\n\tfor (const key of actual) {\n\t\tconst descriptor = descriptors[key];\n\t\tif (descriptor === undefined || !(\"value\" in descriptor)) {\n\t\t\tthrow new Error(`${label} has an invalid key set`);\n\t\t}\n\t\tsnapshot[key] = descriptor.value;\n\t}\n\treturn snapshot;\n}\n\nfunction nonEmptyString(value: unknown, label: string): string {\n\tif (typeof value !== \"string\" || value.length === 0 || value.includes(\"\\0\")) {\n\t\tthrow new Error(`${label} must be a non-empty string without NUL bytes`);\n\t}\n\treturn value;\n}\n\nfunction optionalNonEmptyString(value: unknown, label: string): string | undefined {\n\treturn value === undefined ? undefined : nonEmptyString(value, label);\n}\n\nfunction safeNonNegativeInteger(value: unknown, label: string): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new Error(`${label} must be a non-negative safe integer`);\n\t}\n\treturn value as number;\n}\n\nfunction positiveInteger(value: unknown, label: string): number {\n\tif (!Number.isSafeInteger(value) || (value as number) <= 0) {\n\t\tthrow new Error(`${label} must be a positive safe integer`);\n\t}\n\treturn value as number;\n}\n\nfunction timestamp(value: unknown, label: string): string {\n\tconst parsed = nonEmptyString(value, label);\n\tif (!ISO_TIMESTAMP.test(parsed) || Number.isNaN(Date.parse(parsed))) {\n\t\tthrow new Error(`${label} must be a canonical ISO-8601 timestamp`);\n\t}\n\treturn parsed;\n}\n\n/** Strict structural parse plus the credential-free policy for persisted commands. */\nfunction parseCommand(value: unknown): EvidenceCommandDescriptor {\n\tconst command = parseEvidenceCommandShape(value);\n\tassertCredentialFreeEvidenceCommand(command);\n\treturn command;\n}\n\nfunction parseOutputDigest(value: unknown, label: string): EvidenceOutputCapture[\"stdout\"] {\n\tconst raw = exactObject(value, label, [\"sha256\", \"byteCount\"]);\n\treturn Object.freeze({\n\t\tsha256: parseSha256Hex(raw.sha256, `${label}.sha256`),\n\t\tbyteCount: safeNonNegativeInteger(raw.byteCount, `${label}.byteCount`),\n\t});\n}\n\nfunction parseOutput(value: unknown): EvidenceOutputCapture {\n\tconst raw = exactObject(value, \"receipt output\", [\"redactionPolicyId\", \"stdout\", \"stderr\"]);\n\tconst redactionPolicyId = nonEmptyString(raw.redactionPolicyId, \"receipt output redactionPolicyId\");\n\tif (redactionPolicyId.length > 256) {\n\t\tthrow new Error(\"receipt output redactionPolicyId is too long\");\n\t}\n\tconst stdout = parseOutputDigest(raw.stdout, \"receipt stdout\");\n\tconst stderr = parseOutputDigest(raw.stderr, \"receipt stderr\");\n\tif (stdout.byteCount + stderr.byteCount > MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES) {\n\t\tthrow new Error(`receipt output exceeds ${MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES} bytes`);\n\t}\n\treturn Object.freeze({ redactionPolicyId, stdout, stderr });\n}\n\nfunction parseDisposition(status: unknown, exitCode: unknown): EvidenceReceiptDisposition {\n\tif (status === \"passed\") {\n\t\tif (exitCode !== 0) throw new Error(\"passed receipt status requires exitCode 0\");\n\t\treturn Object.freeze({ status, exitCode: 0 });\n\t}\n\tif (status === \"failed\") {\n\t\tif (!Number.isSafeInteger(exitCode) || exitCode === 0) {\n\t\t\tthrow new Error(\"failed receipt status requires a non-zero integer exitCode\");\n\t\t}\n\t\treturn Object.freeze({ status, exitCode: exitCode as number });\n\t}\n\tif (status === \"timeout\" || status === \"aborted\") {\n\t\tif (exitCode !== null) throw new Error(`${status} receipt status requires a null exitCode`);\n\t\treturn Object.freeze({ status, exitCode: null });\n\t}\n\tthrow new Error(\"receipt status must be passed, failed, timeout, or aborted\");\n}\n\n/** Strict shape parser for immutable receipt core data. */\nexport function parseEvidenceReceiptCore(value: unknown): EvidenceReceiptCore {\n\tconst raw = exactObject(\n\t\tvalue,\n\t\t\"receipt core\",\n\t\t[\n\t\t\t\"schemaVersion\",\n\t\t\t\"receiptId\",\n\t\t\t\"goalId\",\n\t\t\t\"claim\",\n\t\t\t\"command\",\n\t\t\t\"cwd\",\n\t\t\t\"timeoutMs\",\n\t\t\t\"startedAt\",\n\t\t\t\"finishedAt\",\n\t\t\t\"durationMs\",\n\t\t\t\"status\",\n\t\t\t\"exitCode\",\n\t\t\t\"workspaceBefore\",\n\t\t\t\"workspaceAfter\",\n\t\t\t\"output\",\n\t\t\t\"executor\",\n\t\t],\n\t\t[\"laneId\", \"toolCallId\", \"commandRedaction\", \"commandBinding\"],\n\t);\n\tif (raw.schemaVersion !== 3) throw new Error(\"receipt core schemaVersion must be 3\");\n\tif (!isSafeEvidenceReceiptId(raw.receiptId)) throw new Error(\"receiptId is not safe\");\n\tconst goalId = nonEmptyString(raw.goalId, \"receipt core goalId\");\n\tconst laneId = optionalNonEmptyString(raw.laneId, \"receipt core laneId\");\n\tconst claim = nonEmptyString(raw.claim, \"receipt core claim\");\n\tconst command = parseCommand(raw.command);\n\tconst cwd = nonEmptyString(raw.cwd, \"receipt core cwd\");\n\tconst timeoutMs = raw.timeoutMs === null ? null : positiveInteger(raw.timeoutMs, \"receipt core timeoutMs\");\n\tconst startedAt = timestamp(raw.startedAt, \"receipt core startedAt\");\n\tconst finishedAt = timestamp(raw.finishedAt, \"receipt core finishedAt\");\n\tif (Date.parse(finishedAt) < Date.parse(startedAt)) {\n\t\tthrow new Error(\"receipt core finishedAt must not precede startedAt\");\n\t}\n\tconst durationMs = safeNonNegativeInteger(raw.durationMs, \"receipt core durationMs\");\n\tif (durationMs !== Date.parse(finishedAt) - Date.parse(startedAt)) {\n\t\tthrow new Error(\"receipt core durationMs must match the timestamp interval\");\n\t}\n\tconst workspaceBefore = parseWorkspaceFingerprint(raw.workspaceBefore);\n\tconst workspaceAfter = parseWorkspaceFingerprint(raw.workspaceAfter);\n\tconst output = parseOutput(raw.output);\n\tif (typeof raw.executor !== \"string\" || !EXECUTORS.has(raw.executor)) {\n\t\tthrow new Error(\"receipt core executor is invalid\");\n\t}\n\tconst executor = raw.executor as EvidenceExecutor;\n\tconst toolCallId = optionalNonEmptyString(raw.toolCallId, \"receipt core toolCallId\");\n\tconst commandRedaction =\n\t\traw.commandRedaction === undefined ? undefined : parseCommandRedactionSummary(raw.commandRedaction);\n\tconst commandBinding = raw.commandBinding === undefined ? undefined : parseCommandHmacBinding(raw.commandBinding);\n\tif (commandBinding !== undefined && commandRedaction === undefined) {\n\t\tthrow new Error(\"receipt core commandBinding requires commandRedaction\");\n\t}\n\tif (commandRedaction !== undefined && commandRedaction.placeholders.length > 0 && commandBinding === undefined) {\n\t\tthrow new Error(\"receipt core commandRedaction with applied placeholders requires commandBinding\");\n\t}\n\tconst disposition = parseDisposition(raw.status, raw.exitCode);\n\tconst fields: EvidenceReceiptCoreFields = {\n\t\tschemaVersion: 3,\n\t\treceiptId: raw.receiptId,\n\t\tgoalId,\n\t\t...(laneId !== undefined ? { laneId } : {}),\n\t\tclaim,\n\t\tcommand,\n\t\tcwd,\n\t\ttimeoutMs,\n\t\tstartedAt,\n\t\tfinishedAt,\n\t\tdurationMs,\n\t\tworkspaceBefore,\n\t\tworkspaceAfter,\n\t\toutput,\n\t\texecutor,\n\t\t...(toolCallId !== undefined ? { toolCallId } : {}),\n\t\t...(commandRedaction !== undefined ? { commandRedaction } : {}),\n\t\t...(commandBinding !== undefined ? { commandBinding } : {}),\n\t};\n\treturn Object.freeze({ ...fields, ...disposition });\n}\n\nfunction canonicalJson(value: unknown): string {\n\tif (value === null || typeof value === \"boolean\" || typeof value === \"string\") return JSON.stringify(value);\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isFinite(value) || Object.is(value, -0))\n\t\t\tthrow new Error(\"canonical JSON requires finite non-negative-zero numbers\");\n\t\treturn JSON.stringify(value);\n\t}\n\tif (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(\",\")}]`;\n\tif (typeof value !== \"object\" || value === null) throw new Error(\"value is not canonical JSON data\");\n\tconst record = value as Record<string, unknown>;\n\tconst keys = Object.keys(record).sort();\n\treturn `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(\",\")}}`;\n}\n\nexport function serializeEvidenceReceiptCore(core: EvidenceReceiptCore): string {\n\treturn canonicalJson(parseEvidenceReceiptCore(core));\n}\n\n/** Domain-separated SHA-256 of one exact, credential-free structured command descriptor. */\nexport function computeEvidenceCommandSha256(command: EvidenceCommandDescriptor): Sha256Hex {\n\treturn createHash(\"sha256\")\n\t\t.update(COMMAND_DIGEST_DOMAIN, \"utf8\")\n\t\t.update(canonicalJson(parseCommand(command)), \"utf8\")\n\t\t.digest(\"hex\") as Sha256Hex;\n}\n\n/** Domain-separated SHA-256 of the immutable canonical core only. */\nexport function computeEvidenceReceiptCoreSha256(core: EvidenceReceiptCore): Sha256Hex {\n\treturn createHash(\"sha256\")\n\t\t.update(CORE_DIGEST_DOMAIN, \"utf8\")\n\t\t.update(serializeEvidenceReceiptCore(core), \"utf8\")\n\t\t.digest(\"hex\") as Sha256Hex;\n}\n\nfunction parseLedgerBinding(value: unknown): EvidenceReceiptLedgerBinding {\n\tconst raw = exactObject(value, \"receipt ledger binding\", [\"seq\", \"eventHash\"]);\n\treturn Object.freeze({\n\t\tseq: positiveInteger(raw.seq, \"receipt ledger seq\"),\n\t\teventHash: parseSha256Hex(raw.eventHash, \"receipt ledger eventHash\"),\n\t});\n}\n\nfunction parseTrustedAttestation(value: unknown): TrustedEvidenceAttestation {\n\tconst raw = exactObject(value, \"trusted attestation\", [\"attesterId\", \"keyId\", \"algorithm\", \"signature\", \"issuedAt\"]);\n\tif (raw.algorithm !== \"ed25519\") throw new Error(\"trusted attestation algorithm must be ed25519\");\n\treturn Object.freeze({\n\t\tattesterId: nonEmptyString(raw.attesterId, \"trusted attestation attesterId\"),\n\t\tkeyId: nonEmptyString(raw.keyId, \"trusted attestation keyId\"),\n\t\talgorithm: \"ed25519\",\n\t\tsignature: nonEmptyString(raw.signature, \"trusted attestation signature\"),\n\t\tissuedAt: timestamp(raw.issuedAt, \"trusted attestation issuedAt\"),\n\t});\n}\n\nfunction parseEnvelope(value: unknown): EvidenceReceiptEnvelope {\n\tconst raw = exactObject(value, \"receipt envelope\", [\"coreSha256\"], [\"ledgerBinding\", \"trustedAttestation\"]);\n\tconst coreSha256 = parseSha256Hex(raw.coreSha256, \"receipt envelope coreSha256\");\n\tconst ledgerBinding = raw.ledgerBinding === undefined ? undefined : parseLedgerBinding(raw.ledgerBinding);\n\tconst trustedAttestation =\n\t\traw.trustedAttestation === undefined ? undefined : parseTrustedAttestation(raw.trustedAttestation);\n\treturn Object.freeze({\n\t\tcoreSha256,\n\t\t...(ledgerBinding !== undefined ? { ledgerBinding } : {}),\n\t\t...(trustedAttestation !== undefined ? { trustedAttestation } : {}),\n\t});\n}\n\n/** Strict shape and digest validation for an already-decoded receipt value. */\nexport function validateEvidenceReceipt(value: unknown): EvidenceReceipt {\n\tconst raw = exactObject(value, \"evidence receipt\", [\"core\", \"envelope\"]);\n\tconst core = parseEvidenceReceiptCore(raw.core);\n\tconst envelope = parseEnvelope(raw.envelope);\n\tconst expected = computeEvidenceReceiptCoreSha256(core);\n\tif (!constantTimeSha256Equal(expected, envelope.coreSha256)) {\n\t\tthrow new Error(\"evidence receipt core digest mismatch\");\n\t}\n\treturn Object.freeze({ core, envelope });\n}\n\nexport function parseEvidenceReceipt(serialized: string): EvidenceReceipt {\n\tif (typeof serialized !== \"string\") throw new Error(\"serialized evidence receipt must be a string\");\n\tlet value: unknown;\n\ttry {\n\t\tvalue = JSON.parse(serialized);\n\t} catch {\n\t\tthrow new Error(\"evidence receipt contains invalid JSON\");\n\t}\n\treturn validateEvidenceReceipt(value);\n}\n\nexport function serializeEvidenceReceipt(receipt: EvidenceReceipt): string {\n\treturn canonicalJson(validateEvidenceReceipt(receipt));\n}\n\nexport function verifyEvidenceReceiptCoreDigest(receipt: EvidenceReceipt): boolean {\n\ttry {\n\t\tconst validated = validateEvidenceReceipt(receipt);\n\t\treturn constantTimeSha256Equal(computeEvidenceReceiptCoreSha256(validated.core), validated.envelope.coreSha256);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction assertByteArray(value: unknown, label: string): asserts value is Uint8Array {\n\tif (!(value instanceof Uint8Array)) throw new Error(`${label} must be a Uint8Array of already-redacted bytes`);\n}\n\nfunction outputCapture(output: AlreadyRedactedOutputBytes): EvidenceOutputCapture {\n\tif (typeof output !== \"object\" || output === null || Array.isArray(output)) {\n\t\tthrow new Error(\"alreadyRedactedOutput must be an object\");\n\t}\n\tconst redactionPolicyId = nonEmptyString(output.redactionPolicyId, \"redaction policy id\");\n\tif (redactionPolicyId.length > 256) throw new Error(\"redaction policy id is too long\");\n\tassertByteArray(output.stdout, \"already-redacted stdout\");\n\tassertByteArray(output.stderr, \"already-redacted stderr\");\n\tconst byteCount = output.stdout.byteLength + output.stderr.byteLength;\n\tif (!Number.isSafeInteger(byteCount) || byteCount > MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES) {\n\t\tthrow new Error(`receipt output exceeds ${MAX_EVIDENCE_RECEIPT_OUTPUT_BYTES} bytes`);\n\t}\n\treturn Object.freeze({\n\t\tredactionPolicyId,\n\t\tstdout: Object.freeze({\n\t\t\tsha256: createHash(\"sha256\").update(output.stdout).digest(\"hex\") as Sha256Hex,\n\t\t\tbyteCount: output.stdout.byteLength,\n\t\t}),\n\t\tstderr: Object.freeze({\n\t\t\tsha256: createHash(\"sha256\").update(output.stderr).digest(\"hex\") as Sha256Hex,\n\t\t\tbyteCount: output.stderr.byteLength,\n\t\t}),\n\t});\n}\n\n/** Build a receipt from caller-supplied execution facts without executing a command or activating a gate. */\nexport function createEvidenceReceipt(input: CreateEvidenceReceiptInput): EvidenceReceipt {\n\tconst output = outputCapture(input.alreadyRedactedOutput);\n\tconst core = parseEvidenceReceiptCore({\n\t\tschemaVersion: 3,\n\t\treceiptId: input.receiptId,\n\t\tgoalId: input.goalId,\n\t\t...(input.laneId !== undefined ? { laneId: input.laneId } : {}),\n\t\tclaim: input.claim,\n\t\tcommand: input.command,\n\t\tcwd: input.cwd,\n\t\ttimeoutMs: input.timeoutMs,\n\t\tstartedAt: input.startedAt,\n\t\tfinishedAt: input.finishedAt,\n\t\tdurationMs: input.durationMs,\n\t\tstatus: input.status,\n\t\texitCode: input.exitCode,\n\t\tworkspaceBefore: input.workspaceBefore,\n\t\tworkspaceAfter: input.workspaceAfter,\n\t\toutput,\n\t\texecutor: input.executor,\n\t\t...(input.toolCallId !== undefined ? { toolCallId: input.toolCallId } : {}),\n\t\t...(input.commandRedaction !== undefined ? { commandRedaction: input.commandRedaction } : {}),\n\t\t...(input.commandBinding !== undefined ? { commandBinding: input.commandBinding } : {}),\n\t});\n\tconst envelope: EvidenceReceiptEnvelope = {\n\t\tcoreSha256: computeEvidenceReceiptCoreSha256(core),\n\t\t...(input.ledgerBinding !== undefined ? { ledgerBinding: input.ledgerBinding } : {}),\n\t\t...(input.trustedAttestation !== undefined ? { trustedAttestation: input.trustedAttestation } : {}),\n\t};\n\treturn validateEvidenceReceipt({ core, envelope });\n}\n\n/** Attach non-core metadata after a ledger commits {receiptId, coreSha256}. */\nexport function withEvidenceReceiptEnvelope(\n\treceipt: EvidenceReceipt,\n\tmetadata: EvidenceReceiptEnvelopeMetadata,\n): EvidenceReceipt {\n\tconst validated = validateEvidenceReceipt(receipt);\n\treturn validateEvidenceReceipt({\n\t\tcore: validated.core,\n\t\tenvelope: {\n\t\t\tcoreSha256: validated.envelope.coreSha256,\n\t\t\t...(metadata.ledgerBinding !== undefined ? { ledgerBinding: metadata.ledgerBinding } : {}),\n\t\t\t...(metadata.trustedAttestation !== undefined ? { trustedAttestation: metadata.trustedAttestation } : {}),\n\t\t},\n\t});\n}\n\nexport function evidenceReceiptReplayPayload(receipt: EvidenceReceipt): EvidenceReceiptReplayPayload {\n\tconst validated = validateEvidenceReceipt(receipt);\n\treturn Object.freeze({\n\t\treceiptId: validated.core.receiptId,\n\t\tcoreSha256: validated.envelope.coreSha256,\n\t});\n}\n"]}