martin-loop 0.1.4 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (286) hide show
  1. package/CODE_OF_CONDUCT.md +32 -0
  2. package/README.md +172 -227
  3. package/demo/seeded-workspace/README.md +35 -0
  4. package/demo/seeded-workspace/TASKS.md +29 -0
  5. package/demo/seeded-workspace/martin.config.yaml +11 -0
  6. package/demo/seeded-workspace/package.json +8 -0
  7. package/demo/seeded-workspace/src/invoice-summary.js +11 -0
  8. package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
  9. package/dist/bin/martin-loop.js +0 -0
  10. package/dist/vendor/adapters/claude-cli.d.ts +19 -4
  11. package/dist/vendor/adapters/claude-cli.js +55 -24
  12. package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
  13. package/dist/vendor/adapters/cli-bridge.js +154 -28
  14. package/dist/vendor/adapters/counter.d.ts +1 -0
  15. package/dist/vendor/adapters/counter.js +4 -0
  16. package/dist/vendor/adapters/git-baseline.d.ts +50 -0
  17. package/dist/vendor/adapters/git-baseline.js +233 -0
  18. package/dist/vendor/adapters/index.d.ts +1 -0
  19. package/dist/vendor/adapters/index.js +1 -0
  20. package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
  21. package/dist/vendor/adapters/openrouter-adapter.js +302 -0
  22. package/dist/vendor/adapters/usage.d.ts +48 -0
  23. package/dist/vendor/adapters/usage.js +66 -0
  24. package/dist/vendor/adapters/verifier-only.d.ts +7 -0
  25. package/dist/vendor/adapters/verifier-only.js +57 -0
  26. package/dist/vendor/cli/bin/exit.d.ts +12 -0
  27. package/dist/vendor/cli/bin/exit.js +28 -0
  28. package/dist/vendor/cli/commands/analyze.d.ts +5 -0
  29. package/dist/vendor/cli/commands/analyze.js +58 -0
  30. package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
  31. package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
  32. package/dist/vendor/cli/commands/audit.d.ts +8 -0
  33. package/dist/vendor/cli/commands/audit.js +199 -0
  34. package/dist/vendor/cli/commands/corpus.d.ts +5 -0
  35. package/dist/vendor/cli/commands/corpus.js +60 -0
  36. package/dist/vendor/cli/commands/doctor.d.ts +8 -0
  37. package/dist/vendor/cli/commands/doctor.js +219 -0
  38. package/dist/vendor/cli/commands/explain.d.ts +17 -0
  39. package/dist/vendor/cli/commands/explain.js +176 -0
  40. package/dist/vendor/cli/commands/export.d.ts +5 -0
  41. package/dist/vendor/cli/commands/export.js +60 -0
  42. package/dist/vendor/cli/commands/governance.d.ts +8 -0
  43. package/dist/vendor/cli/commands/governance.js +95 -0
  44. package/dist/vendor/cli/commands/improve.d.ts +18 -0
  45. package/dist/vendor/cli/commands/improve.js +396 -0
  46. package/dist/vendor/cli/commands/init.d.ts +8 -0
  47. package/dist/vendor/cli/commands/init.js +281 -0
  48. package/dist/vendor/cli/commands/migration.d.ts +8 -0
  49. package/dist/vendor/cli/commands/migration.js +67 -0
  50. package/dist/vendor/cli/commands/prior.d.ts +23 -0
  51. package/dist/vendor/cli/commands/prior.js +145 -0
  52. package/dist/vendor/cli/commands/resume.d.ts +21 -0
  53. package/dist/vendor/cli/commands/resume.js +73 -0
  54. package/dist/vendor/cli/commands/verify.d.ts +6 -0
  55. package/dist/vendor/cli/commands/verify.js +43 -0
  56. package/dist/vendor/cli/index.d.ts +6 -1
  57. package/dist/vendor/cli/index.js +124 -7
  58. package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
  59. package/dist/vendor/cli/research/public-corpus.js +151 -0
  60. package/dist/vendor/cli/ui/error-card.d.ts +38 -0
  61. package/dist/vendor/cli/ui/error-card.js +103 -0
  62. package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
  63. package/dist/vendor/cli/ui/mission-brief.js +173 -0
  64. package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
  65. package/dist/vendor/cli/ui/summary-card.js +102 -0
  66. package/dist/vendor/contracts/audit.d.ts +46 -0
  67. package/dist/vendor/contracts/audit.js +360 -0
  68. package/dist/vendor/contracts/index.d.ts +3 -1
  69. package/dist/vendor/contracts/post-phase15.d.ts +240 -0
  70. package/dist/vendor/contracts/post-phase15.js +166 -0
  71. package/dist/vendor/core/agent/mandates.d.ts +46 -0
  72. package/dist/vendor/core/agent/mandates.js +178 -0
  73. package/dist/vendor/core/agent/receipts.d.ts +38 -0
  74. package/dist/vendor/core/agent/receipts.js +131 -0
  75. package/dist/vendor/core/agent/signing.d.ts +17 -0
  76. package/dist/vendor/core/agent/signing.js +91 -0
  77. package/dist/vendor/core/attestation/sign.d.ts +25 -0
  78. package/dist/vendor/core/attestation/sign.js +216 -0
  79. package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
  80. package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
  81. package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
  82. package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
  83. package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
  84. package/dist/vendor/core/autonomy/envelope.js +27 -0
  85. package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
  86. package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
  87. package/dist/vendor/core/autonomy/resume.d.ts +15 -0
  88. package/dist/vendor/core/autonomy/resume.js +23 -0
  89. package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
  90. package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
  91. package/dist/vendor/core/compiler.d.ts +2 -0
  92. package/dist/vendor/core/compiler.js +10 -4
  93. package/dist/vendor/core/context-distillation.d.ts +3 -0
  94. package/dist/vendor/core/context-distillation.js +44 -0
  95. package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
  96. package/dist/vendor/core/context-flow/compile-context.js +111 -0
  97. package/dist/vendor/core/context-flow/entities.d.ts +2 -0
  98. package/dist/vendor/core/context-flow/entities.js +44 -0
  99. package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
  100. package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
  101. package/dist/vendor/core/context-flow/index.d.ts +11 -0
  102. package/dist/vendor/core/context-flow/index.js +24 -0
  103. package/dist/vendor/core/context-flow/labels.d.ts +3 -0
  104. package/dist/vendor/core/context-flow/labels.js +17 -0
  105. package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
  106. package/dist/vendor/core/context-flow/normalizer.js +69 -0
  107. package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
  108. package/dist/vendor/core/context-flow/profiles.js +36 -0
  109. package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
  110. package/dist/vendor/core/context-flow/redaction.js +6 -0
  111. package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
  112. package/dist/vendor/core/context-flow/sensitivity.js +27 -0
  113. package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
  114. package/dist/vendor/core/context-flow/sync-preview.js +22 -0
  115. package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
  116. package/dist/vendor/core/context-flow/token-estimator.js +13 -0
  117. package/dist/vendor/core/context-flow/types.d.ts +91 -0
  118. package/dist/vendor/core/context-flow/types.js +2 -0
  119. package/dist/vendor/core/context-integrity.d.ts +26 -0
  120. package/dist/vendor/core/context-integrity.js +56 -0
  121. package/dist/vendor/core/context-utility.d.ts +47 -0
  122. package/dist/vendor/core/context-utility.js +405 -0
  123. package/dist/vendor/core/cost/pipeline.d.ts +92 -0
  124. package/dist/vendor/core/cost/pipeline.js +141 -0
  125. package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
  126. package/dist/vendor/core/cost/tagged-cost.js +55 -0
  127. package/dist/vendor/core/cost-governor.d.ts +2 -0
  128. package/dist/vendor/core/cost-governor.js +50 -0
  129. package/dist/vendor/core/cve/cve-check.d.ts +80 -0
  130. package/dist/vendor/core/cve/cve-check.js +172 -0
  131. package/dist/vendor/core/digital-twin/index.d.ts +27 -0
  132. package/dist/vendor/core/digital-twin/index.js +90 -0
  133. package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
  134. package/dist/vendor/core/drift/drift-graph.js +100 -0
  135. package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
  136. package/dist/vendor/core/drift/objective-lock.js +88 -0
  137. package/dist/vendor/core/drift/scope.d.ts +46 -0
  138. package/dist/vendor/core/drift/scope.js +102 -0
  139. package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
  140. package/dist/vendor/core/drift/signature-lock.js +202 -0
  141. package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
  142. package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
  143. package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
  144. package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
  145. package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
  146. package/dist/vendor/core/evidence/claim-audit.js +89 -0
  147. package/dist/vendor/core/exit-intelligence.d.ts +2 -0
  148. package/dist/vendor/core/exit-intelligence.js +58 -0
  149. package/dist/vendor/core/explain/formatter.d.ts +42 -0
  150. package/dist/vendor/core/explain/formatter.js +171 -0
  151. package/dist/vendor/core/explain/timeline.d.ts +29 -0
  152. package/dist/vendor/core/explain/timeline.js +213 -0
  153. package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
  154. package/dist/vendor/core/failure-taxonomy.js +76 -0
  155. package/dist/vendor/core/gateway/index.d.ts +10 -0
  156. package/dist/vendor/core/gateway/index.js +12 -0
  157. package/dist/vendor/core/gateway/registry.d.ts +40 -0
  158. package/dist/vendor/core/gateway/registry.js +97 -0
  159. package/dist/vendor/core/gateway/transport.d.ts +31 -0
  160. package/dist/vendor/core/gateway/transport.js +82 -0
  161. package/dist/vendor/core/gateway/vault.d.ts +19 -0
  162. package/dist/vendor/core/gateway/vault.js +29 -0
  163. package/dist/vendor/core/graph/adapters.d.ts +43 -0
  164. package/dist/vendor/core/graph/adapters.js +91 -0
  165. package/dist/vendor/core/graph/hotspots.d.ts +22 -0
  166. package/dist/vendor/core/graph/hotspots.js +30 -0
  167. package/dist/vendor/core/graph/index.d.ts +1 -0
  168. package/dist/vendor/core/graph/index.js +2 -0
  169. package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
  170. package/dist/vendor/core/honey/honey-tokens.js +44 -0
  171. package/dist/vendor/core/index.d.ts +7 -4
  172. package/dist/vendor/core/index.js +222 -64
  173. package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
  174. package/dist/vendor/core/learning/bayesian-update.js +60 -0
  175. package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
  176. package/dist/vendor/core/learning/prior-sets.js +111 -0
  177. package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
  178. package/dist/vendor/core/learning/promotion-gate.js +23 -0
  179. package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
  180. package/dist/vendor/core/leash/blast-radius.js +156 -0
  181. package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
  182. package/dist/vendor/core/leash/policy-leash.js +117 -0
  183. package/dist/vendor/core/memo/memo.d.ts +63 -0
  184. package/dist/vendor/core/memo/memo.js +97 -0
  185. package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
  186. package/dist/vendor/core/memory/learning-pipeline.js +391 -0
  187. package/dist/vendor/core/memory/palace.d.ts +84 -0
  188. package/dist/vendor/core/memory/palace.js +379 -0
  189. package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
  190. package/dist/vendor/core/merge/ast-merge.js +350 -0
  191. package/dist/vendor/core/merge/text-merge.d.ts +12 -0
  192. package/dist/vendor/core/merge/text-merge.js +182 -0
  193. package/dist/vendor/core/otel/tracer.d.ts +45 -0
  194. package/dist/vendor/core/otel/tracer.js +116 -0
  195. package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
  196. package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
  197. package/dist/vendor/core/parallel/scorer.d.ts +24 -0
  198. package/dist/vendor/core/parallel/scorer.js +65 -0
  199. package/dist/vendor/core/pattern-detection.d.ts +64 -0
  200. package/dist/vendor/core/pattern-detection.js +108 -0
  201. package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
  202. package/dist/vendor/core/persistence/checkpoint.js +156 -0
  203. package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
  204. package/dist/vendor/core/persistence/cleanup.js +131 -0
  205. package/dist/vendor/core/persistence/index.d.ts +2 -0
  206. package/dist/vendor/core/persistence/index.js +1 -0
  207. package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
  208. package/dist/vendor/core/persistence/runs-reader.js +84 -0
  209. package/dist/vendor/core/persistence/store.d.ts +6 -1
  210. package/dist/vendor/core/persistence/store.js +5 -0
  211. package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
  212. package/dist/vendor/core/policy/file-touch-quota.js +105 -0
  213. package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
  214. package/dist/vendor/core/policy/policy-loader.js +170 -0
  215. package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
  216. package/dist/vendor/core/policy/policy-schema.js +78 -0
  217. package/dist/vendor/core/policy.d.ts +6 -0
  218. package/dist/vendor/core/probe/probe.d.ts +49 -0
  219. package/dist/vendor/core/probe/probe.js +115 -0
  220. package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
  221. package/dist/vendor/core/proof/patch-proof.js +84 -0
  222. package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
  223. package/dist/vendor/core/proof/semantic-probe.js +82 -0
  224. package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
  225. package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
  226. package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
  227. package/dist/vendor/core/red-blue/red-phase.js +141 -0
  228. package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
  229. package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
  230. package/dist/vendor/core/replay/replay.d.ts +85 -0
  231. package/dist/vendor/core/replay/replay.js +109 -0
  232. package/dist/vendor/core/router/engine.d.ts +54 -0
  233. package/dist/vendor/core/router/engine.js +131 -0
  234. package/dist/vendor/core/router/index.d.ts +1 -0
  235. package/dist/vendor/core/router/index.js +2 -0
  236. package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
  237. package/dist/vendor/core/router/trust-calibration.js +127 -0
  238. package/dist/vendor/core/run-martin.d.ts +2 -0
  239. package/dist/vendor/core/run-martin.js +287 -0
  240. package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
  241. package/dist/vendor/core/security/cve-scanner.js +178 -0
  242. package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
  243. package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
  244. package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
  245. package/dist/vendor/core/sentinel/progress-guard.js +46 -0
  246. package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
  247. package/dist/vendor/core/siem/siem-emitter.js +157 -0
  248. package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
  249. package/dist/vendor/core/strategy/attempt-brief.js +89 -0
  250. package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
  251. package/dist/vendor/core/summarize/diff-summary.js +204 -0
  252. package/dist/vendor/core/surface-signals.d.ts +21 -0
  253. package/dist/vendor/core/surface-signals.js +139 -0
  254. package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
  255. package/dist/vendor/core/truth/truth-wall.js +69 -0
  256. package/dist/vendor/core/truth-spine.d.ts +26 -0
  257. package/dist/vendor/core/truth-spine.js +62 -0
  258. package/dist/vendor/core/types.d.ts +115 -0
  259. package/dist/vendor/core/types.js +2 -0
  260. package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
  261. package/dist/vendor/core/verification/tiered-verify.js +29 -0
  262. package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
  263. package/dist/vendor/core/verifier-pyramid.js +111 -0
  264. package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
  265. package/dist/vendor/core/workflow-artifacts.js +668 -0
  266. package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
  267. package/dist/vendor/core/wrap/supervised-run.js +178 -0
  268. package/docs/assets/cli-animated.svg +139 -0
  269. package/docs/assets/cli-static.svg +34 -0
  270. package/docs/assets/github-hero-v2.svg +23 -0
  271. package/docs/assets/martin-raplph.png.jpg +0 -0
  272. package/docs/assets/martinloop-logo.png +0 -0
  273. package/docs/assets/nvidia-inception-program-light.png +0 -0
  274. package/docs/assets/nvidia-inception-program.png +0 -0
  275. package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
  276. package/docs/assets/side-by-side.svg +134 -0
  277. package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
  278. package/docs/oss/EXAMPLES.md +9 -1
  279. package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
  280. package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
  281. package/docs/oss/QUICKSTART.md +39 -4
  282. package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
  283. package/docs/oss/README.md +7 -4
  284. package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
  285. package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
  286. package/package.json +129 -49
@@ -0,0 +1,131 @@
1
+ import { agentKeyId, readAgentEd25519Signature, signCanonicalAgentPayload, verifyCanonicalAgentPayload } from "./signing.js";
2
+ const RECEIPT_KEY_NAMESPACE = "agent-receipts";
3
+ export async function signAgentReceipt(payload, options = {}) {
4
+ return {
5
+ ...payload,
6
+ signature: await signCanonicalAgentPayload(payload, RECEIPT_KEY_NAMESPACE, options)
7
+ };
8
+ }
9
+ export function verifyAgentReceipt(receipt, options = {}) {
10
+ const reasons = [];
11
+ if (!receipt || typeof receipt !== "object" || Array.isArray(receipt)) {
12
+ return {
13
+ verified: false,
14
+ receiptId: null,
15
+ runId: null,
16
+ ledgerHash: null,
17
+ reasons: ["Receipt must be an object."]
18
+ };
19
+ }
20
+ const record = receipt;
21
+ const receiptId = readString(record.receiptId);
22
+ const runId = readString(record.runId);
23
+ const quoteId = readString(record.quoteId);
24
+ const mandateId = readString(record.mandateId);
25
+ const status = readReceiptStatus(record.status);
26
+ const ledgerHash = readString(record.ledgerHash);
27
+ const createdAt = readString(record.createdAt);
28
+ const signature = readAgentEd25519Signature(record.signature);
29
+ if (!receiptId) {
30
+ reasons.push("Receipt id is missing.");
31
+ }
32
+ if (!runId) {
33
+ reasons.push("Run id is missing.");
34
+ }
35
+ if (!quoteId) {
36
+ reasons.push("Quote id is missing.");
37
+ }
38
+ if (!mandateId) {
39
+ reasons.push("Mandate id is missing.");
40
+ }
41
+ if (!status) {
42
+ reasons.push("Receipt status is missing.");
43
+ }
44
+ if (!ledgerHash) {
45
+ reasons.push("Ledger hash is missing.");
46
+ }
47
+ if (!createdAt) {
48
+ reasons.push("Receipt timestamp is missing.");
49
+ }
50
+ if (!signature) {
51
+ reasons.push("Receipt signature is missing.");
52
+ }
53
+ if (options.expectedLedgerHash && ledgerHash && options.expectedLedgerHash !== ledgerHash) {
54
+ reasons.push("Ledger hash does not match expected value.");
55
+ }
56
+ if (options.expectedRunId && runId && options.expectedRunId !== runId) {
57
+ reasons.push("Run id does not match expected value.");
58
+ }
59
+ if (options.expectedQuoteId && quoteId && options.expectedQuoteId !== quoteId) {
60
+ reasons.push("Quote id does not match expected value.");
61
+ }
62
+ if (options.expectedMandateId && mandateId && options.expectedMandateId !== mandateId) {
63
+ reasons.push("Mandate id does not match expected value.");
64
+ }
65
+ if (options.expectedStatus && status && options.expectedStatus !== status) {
66
+ reasons.push("Receipt status does not match expected value.");
67
+ }
68
+ if (createdAt && parseTimestamp(createdAt) === null) {
69
+ reasons.push("Receipt timestamp is invalid.");
70
+ }
71
+ if (signature) {
72
+ const expectedKeyId = agentKeyId(signature.publicKeyPem);
73
+ if (signature.keyId !== expectedKeyId) {
74
+ reasons.push("Receipt key id does not match public key.");
75
+ }
76
+ if (!isTrustedSigner(signature, options)) {
77
+ reasons.push("Receipt signer is untrusted.");
78
+ }
79
+ const payload = { ...record };
80
+ delete payload.signature;
81
+ try {
82
+ const verified = verifyCanonicalAgentPayload(payload, signature);
83
+ if (!verified) {
84
+ reasons.push("Receipt signature verification failed.");
85
+ }
86
+ }
87
+ catch {
88
+ reasons.push("Receipt signature verification failed.");
89
+ }
90
+ }
91
+ return {
92
+ verified: reasons.length === 0,
93
+ receiptId,
94
+ runId,
95
+ ledgerHash,
96
+ ...(signature ? { keyId: signature.keyId } : {}),
97
+ reasons
98
+ };
99
+ }
100
+ function readString(value) {
101
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
102
+ }
103
+ function readReceiptStatus(value) {
104
+ return value === "completed" || value === "failed" || value === "denied" || value === "expired"
105
+ ? value
106
+ : null;
107
+ }
108
+ function isTrustedSigner(signature, options) {
109
+ if (options.expectedSignerPublicKeyPem &&
110
+ normalizePem(signature.publicKeyPem) === normalizePem(options.expectedSignerPublicKeyPem)) {
111
+ return true;
112
+ }
113
+ if (options.expectedSignerKeyId) {
114
+ return signature.keyId === options.expectedSignerKeyId;
115
+ }
116
+ if (options.trustedSignerPublicKeys?.some((key) => normalizePem(key) === normalizePem(signature.publicKeyPem))) {
117
+ return true;
118
+ }
119
+ if (options.trustedSignerKeyIds?.includes(signature.keyId)) {
120
+ return true;
121
+ }
122
+ return false;
123
+ }
124
+ function normalizePem(value) {
125
+ return value.replace(/\r\n/g, "\n").trim();
126
+ }
127
+ function parseTimestamp(value) {
128
+ const parsed = Date.parse(value);
129
+ return Number.isFinite(parsed) ? parsed : null;
130
+ }
131
+ //# sourceMappingURL=receipts.js.map
@@ -0,0 +1,17 @@
1
+ export interface AgentEd25519Signature {
2
+ algorithm: "ed25519";
3
+ keyId: string;
4
+ publicKeyPem: string;
5
+ signatureBase64: string;
6
+ signedAt: string;
7
+ }
8
+ export interface AgentSigningOptions {
9
+ martinHome?: string;
10
+ env?: NodeJS.ProcessEnv;
11
+ now?: string;
12
+ }
13
+ export declare function signCanonicalAgentPayload(payload: unknown, namespace: string, options?: AgentSigningOptions): Promise<AgentEd25519Signature>;
14
+ export declare function verifyCanonicalAgentPayload(payload: unknown, signature: AgentEd25519Signature): boolean;
15
+ export declare function readAgentEd25519Signature(value: unknown): AgentEd25519Signature | null;
16
+ export declare function agentKeyId(publicKeyPem: string): string;
17
+ export declare function canonicalJson(value: unknown): string;
@@ -0,0 +1,91 @@
1
+ import { createHash, generateKeyPairSync, sign as signBytes, verify as verifyBytes } from "node:crypto";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { resolveMartinHome } from "../attestation/sign.js";
5
+ const PRIVATE_KEY_FILE = "ed25519-private.pem";
6
+ const PUBLIC_KEY_FILE = "ed25519-public.pem";
7
+ export async function signCanonicalAgentPayload(payload, namespace, options = {}) {
8
+ const keyMaterial = await ensureAgentKeyMaterial(namespace, options);
9
+ const signatureBase64 = signBytes(null, Buffer.from(canonicalJson(payload), "utf8"), keyMaterial.privateKeyPem)
10
+ .toString("base64");
11
+ return {
12
+ algorithm: "ed25519",
13
+ keyId: keyMaterial.keyId,
14
+ publicKeyPem: keyMaterial.publicKeyPem,
15
+ signatureBase64,
16
+ signedAt: options.now ?? new Date().toISOString()
17
+ };
18
+ }
19
+ export function verifyCanonicalAgentPayload(payload, signature) {
20
+ return verifyBytes(null, Buffer.from(canonicalJson(payload), "utf8"), signature.publicKeyPem, Buffer.from(signature.signatureBase64, "base64"));
21
+ }
22
+ export function readAgentEd25519Signature(value) {
23
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
24
+ return null;
25
+ }
26
+ const record = value;
27
+ return record.algorithm === "ed25519" &&
28
+ typeof record.keyId === "string" &&
29
+ typeof record.publicKeyPem === "string" &&
30
+ typeof record.signatureBase64 === "string" &&
31
+ typeof record.signedAt === "string"
32
+ ? {
33
+ algorithm: "ed25519",
34
+ keyId: record.keyId,
35
+ publicKeyPem: record.publicKeyPem,
36
+ signatureBase64: record.signatureBase64,
37
+ signedAt: record.signedAt
38
+ }
39
+ : null;
40
+ }
41
+ export function agentKeyId(publicKeyPem) {
42
+ return createHash("sha256").update(publicKeyPem, "utf8").digest("hex").slice(0, 16);
43
+ }
44
+ export function canonicalJson(value) {
45
+ return JSON.stringify(sortJson(value));
46
+ }
47
+ async function ensureAgentKeyMaterial(namespace, options) {
48
+ const receiptDir = join(options.martinHome ?? resolveMartinHome(options.env), namespace);
49
+ await mkdir(receiptDir, { recursive: true });
50
+ const privateKeyPath = join(receiptDir, PRIVATE_KEY_FILE);
51
+ const publicKeyPath = join(receiptDir, PUBLIC_KEY_FILE);
52
+ try {
53
+ const [privateKeyPem, publicKeyPem] = await Promise.all([
54
+ readFile(privateKeyPath, "utf8"),
55
+ readFile(publicKeyPath, "utf8")
56
+ ]);
57
+ return {
58
+ privateKeyPem,
59
+ publicKeyPem,
60
+ keyId: agentKeyId(publicKeyPem)
61
+ };
62
+ }
63
+ catch {
64
+ const generated = generateKeyPairSync("ed25519", {
65
+ privateKeyEncoding: { format: "pem", type: "pkcs8" },
66
+ publicKeyEncoding: { format: "pem", type: "spki" }
67
+ });
68
+ await Promise.all([
69
+ writeFile(privateKeyPath, generated.privateKey, "utf8"),
70
+ writeFile(publicKeyPath, generated.publicKey, "utf8")
71
+ ]);
72
+ return {
73
+ privateKeyPem: generated.privateKey,
74
+ publicKeyPem: generated.publicKey,
75
+ keyId: agentKeyId(generated.publicKey)
76
+ };
77
+ }
78
+ }
79
+ function sortJson(value) {
80
+ if (Array.isArray(value)) {
81
+ return value.map(sortJson);
82
+ }
83
+ if (value && typeof value === "object") {
84
+ return Object.fromEntries(Object.entries(value)
85
+ .filter(([, entryValue]) => entryValue !== undefined)
86
+ .sort(([left], [right]) => left.localeCompare(right))
87
+ .map(([key, entryValue]) => [key, sortJson(entryValue)]));
88
+ }
89
+ return value;
90
+ }
91
+ //# sourceMappingURL=signing.js.map
@@ -0,0 +1,25 @@
1
+ export interface FileAttestationEnvelope {
2
+ file: string;
3
+ algorithm: "ed25519";
4
+ keyId: string;
5
+ publicKeyFile: string;
6
+ signatureBase64: string;
7
+ signedAt: string;
8
+ }
9
+ export interface AttestationVerificationResult {
10
+ ok: boolean;
11
+ file: string;
12
+ algorithm?: "ed25519";
13
+ keyId?: string;
14
+ reason?: string;
15
+ }
16
+ type AttestationOptions = {
17
+ martinHome?: string;
18
+ env?: NodeJS.ProcessEnv;
19
+ now?: string;
20
+ };
21
+ export declare function resolveMartinHome(env?: NodeJS.ProcessEnv): string;
22
+ export declare function signFileAttestation(filePath: string, options?: AttestationOptions): Promise<FileAttestationEnvelope>;
23
+ export declare function verifyFileAttestation(filePath: string): Promise<AttestationVerificationResult>;
24
+ export declare function verifyLoopRecordAttestation(runRoot: string): Promise<AttestationVerificationResult>;
25
+ export {};
@@ -0,0 +1,216 @@
1
+ import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, sign as signBytes, verify as verifyBytes } from "node:crypto";
2
+ import { mkdir, open, readFile, rm, stat, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { basename, extname, join } from "node:path";
5
+ const PRIVATE_KEY_FILE = "ed25519-private.pem";
6
+ const PUBLIC_KEY_FILE = "ed25519-public.pem";
7
+ const ATTESTATION_LOCK_FILE = ".ed25519.lock";
8
+ const ATTESTATION_LOCK_TIMEOUT_MS = 15_000;
9
+ const ATTESTATION_LOCK_POLL_MS = 50;
10
+ const ATTESTATION_STALE_LOCK_MS = 30_000;
11
+ export function resolveMartinHome(env = process.env) {
12
+ return env["MARTIN_HOME_DIR"]?.trim() || join(homedir(), ".martin");
13
+ }
14
+ export async function signFileAttestation(filePath, options = {}) {
15
+ const keyMaterial = await ensureAttestationKeyMaterial(options);
16
+ const payload = await readFile(filePath);
17
+ const signatureBase64 = signBytes(null, payload, keyMaterial.privateKeyPem).toString("base64");
18
+ const signaturePath = toSignaturePath(filePath);
19
+ const publicKeyPath = toPublicKeyPath(filePath);
20
+ const envelope = {
21
+ file: basename(filePath),
22
+ algorithm: "ed25519",
23
+ keyId: keyMaterial.keyId,
24
+ publicKeyFile: basename(publicKeyPath),
25
+ signatureBase64,
26
+ signedAt: options.now ?? new Date().toISOString()
27
+ };
28
+ await writeFile(publicKeyPath, keyMaterial.publicKeyPem, "utf8");
29
+ await writeFile(signaturePath, `${JSON.stringify(envelope, null, 2)}\n`, "utf8");
30
+ return envelope;
31
+ }
32
+ export async function verifyFileAttestation(filePath) {
33
+ const signaturePath = toSignaturePath(filePath);
34
+ const publicKeyPath = toPublicKeyPath(filePath);
35
+ let envelope;
36
+ try {
37
+ envelope = JSON.parse(await readFile(signaturePath, "utf8"));
38
+ }
39
+ catch {
40
+ return {
41
+ ok: false,
42
+ file: basename(filePath),
43
+ reason: `Missing or unreadable signature artifact for ${basename(filePath)}`
44
+ };
45
+ }
46
+ let publicKeyPem;
47
+ try {
48
+ publicKeyPem = await readFile(publicKeyPath, "utf8");
49
+ }
50
+ catch {
51
+ return {
52
+ ok: false,
53
+ file: basename(filePath),
54
+ algorithm: envelope.algorithm,
55
+ keyId: envelope.keyId,
56
+ reason: `Missing public key verification material for ${basename(filePath)}`
57
+ };
58
+ }
59
+ try {
60
+ const payload = await readFile(filePath);
61
+ const signature = Buffer.from(envelope.signatureBase64, "base64");
62
+ const verified = verifyBytes(null, payload, publicKeyPem, signature);
63
+ if (!verified) {
64
+ return {
65
+ ok: false,
66
+ file: basename(filePath),
67
+ algorithm: envelope.algorithm,
68
+ keyId: envelope.keyId,
69
+ reason: `File signature verification failed for ${basename(filePath)}`
70
+ };
71
+ }
72
+ return {
73
+ ok: true,
74
+ file: basename(filePath),
75
+ algorithm: envelope.algorithm,
76
+ keyId: envelope.keyId
77
+ };
78
+ }
79
+ catch {
80
+ return {
81
+ ok: false,
82
+ file: basename(filePath),
83
+ algorithm: envelope.algorithm,
84
+ keyId: envelope.keyId,
85
+ reason: `Malformed signature or key material for ${basename(filePath)}`
86
+ };
87
+ }
88
+ }
89
+ export async function verifyLoopRecordAttestation(runRoot) {
90
+ return verifyFileAttestation(join(runRoot, "loop-record.json"));
91
+ }
92
+ async function ensureAttestationKeyMaterial(options) {
93
+ const attestationDir = join(options.martinHome ?? resolveMartinHome(options.env), "attestation");
94
+ await mkdir(attestationDir, { recursive: true });
95
+ const privateKeyPath = join(attestationDir, PRIVATE_KEY_FILE);
96
+ const publicKeyPath = join(attestationDir, PUBLIC_KEY_FILE);
97
+ const existingKeyMaterial = await loadKeyMaterial(privateKeyPath, publicKeyPath);
98
+ if (existingKeyMaterial) {
99
+ return existingKeyMaterial;
100
+ }
101
+ const releaseLock = await acquireAttestationLock(join(attestationDir, ATTESTATION_LOCK_FILE));
102
+ try {
103
+ const lockedKeyMaterial = await loadKeyMaterial(privateKeyPath, publicKeyPath);
104
+ if (lockedKeyMaterial) {
105
+ return lockedKeyMaterial;
106
+ }
107
+ const generated = generateKeyPairSync("ed25519", {
108
+ privateKeyEncoding: { format: "pem", type: "pkcs8" },
109
+ publicKeyEncoding: { format: "pem", type: "spki" }
110
+ });
111
+ await writeFile(privateKeyPath, generated.privateKey, "utf8");
112
+ await writeFile(publicKeyPath, generated.publicKey, "utf8");
113
+ const generatedKeyMaterial = normalizeKeyMaterial(generated.privateKey, generated.publicKey);
114
+ if (!generatedKeyMaterial) {
115
+ throw new Error("Generated attestation key material failed validation.");
116
+ }
117
+ return generatedKeyMaterial;
118
+ }
119
+ finally {
120
+ await releaseLock();
121
+ }
122
+ }
123
+ function toKeyId(publicKeyPem) {
124
+ return createHash("sha256").update(publicKeyPem, "utf8").digest("hex").slice(0, 16);
125
+ }
126
+ async function loadKeyMaterial(privateKeyPath, publicKeyPath) {
127
+ try {
128
+ const [privateKeyPem, publicKeyPem] = await Promise.all([
129
+ readFile(privateKeyPath, "utf8"),
130
+ readFile(publicKeyPath, "utf8")
131
+ ]);
132
+ return normalizeKeyMaterial(privateKeyPem, publicKeyPem);
133
+ }
134
+ catch {
135
+ return undefined;
136
+ }
137
+ }
138
+ function normalizeKeyMaterial(privateKeyPem, publicKeyPem) {
139
+ if (privateKeyPem.trim().length === 0 || publicKeyPem.trim().length === 0) {
140
+ return undefined;
141
+ }
142
+ try {
143
+ const privateKey = createPrivateKey(privateKeyPem);
144
+ const derivedPublicKeyPem = createPublicKey(privateKey)
145
+ .export({ format: "pem", type: "spki" })
146
+ .toString();
147
+ if (normalizePem(derivedPublicKeyPem) !== normalizePem(publicKeyPem)) {
148
+ return undefined;
149
+ }
150
+ return {
151
+ privateKeyPem,
152
+ publicKeyPem,
153
+ keyId: toKeyId(publicKeyPem)
154
+ };
155
+ }
156
+ catch {
157
+ return undefined;
158
+ }
159
+ }
160
+ async function acquireAttestationLock(lockPath) {
161
+ const deadline = Date.now() + ATTESTATION_LOCK_TIMEOUT_MS;
162
+ while (true) {
163
+ try {
164
+ const handle = await open(lockPath, "wx");
165
+ return async () => {
166
+ await handle.close();
167
+ await rm(lockPath, { force: true });
168
+ };
169
+ }
170
+ catch (error) {
171
+ if (!isAlreadyExistsError(error)) {
172
+ throw error;
173
+ }
174
+ if (await isStaleLock(lockPath)) {
175
+ await rm(lockPath, { force: true });
176
+ continue;
177
+ }
178
+ if (Date.now() >= deadline) {
179
+ throw new Error(`Timed out waiting for attestation lock: ${lockPath}`);
180
+ }
181
+ await sleep(ATTESTATION_LOCK_POLL_MS);
182
+ }
183
+ }
184
+ }
185
+ async function isStaleLock(lockPath) {
186
+ try {
187
+ const lockStats = await stat(lockPath);
188
+ return Date.now() - lockStats.mtimeMs > ATTESTATION_STALE_LOCK_MS;
189
+ }
190
+ catch {
191
+ return false;
192
+ }
193
+ }
194
+ function isAlreadyExistsError(error) {
195
+ return (typeof error === "object" &&
196
+ error !== null &&
197
+ "code" in error &&
198
+ ["EEXIST", "EPERM", "EACCES"].includes(String(error.code ?? "")));
199
+ }
200
+ function normalizePem(value) {
201
+ return value.replace(/\r\n/g, "\n").trim();
202
+ }
203
+ async function sleep(ms) {
204
+ await new Promise((resolve) => setTimeout(resolve, ms));
205
+ }
206
+ function toSidecarBase(filePath) {
207
+ const extension = extname(filePath);
208
+ return extension.length > 0 ? filePath.slice(0, -extension.length) : filePath;
209
+ }
210
+ function toSignaturePath(filePath) {
211
+ return `${toSidecarBase(filePath)}.signature.json`;
212
+ }
213
+ function toPublicKeyPath(filePath) {
214
+ return `${toSidecarBase(filePath)}.public.pem`;
215
+ }
216
+ //# sourceMappingURL=sign.js.map
@@ -0,0 +1,120 @@
1
+ import { type FileAttestationEnvelope } from "../attestation/sign.js";
2
+ export type AutonomySurface = "heuristic" | "trusted_config" | "trusted_code" | "protected";
3
+ export type AutonomyTripwireReasonCode = "protected_surface" | "untrusted_surface" | "unverifiable_diff" | "missing_rollback_proof" | "attestation_mismatch" | "repeated_grounding" | "verifier_no_improvement" | "budget_pressure_escalation";
4
+ export type AutonomyBudgetPressure = "normal" | "elevated" | "critical";
5
+ export type AutonomousPromotionVerdict = "promote" | "shadow_only" | "rollback_only" | "human_escalation";
6
+ export interface AutonomySurfaceClassification {
7
+ path: string;
8
+ surface: AutonomySurface;
9
+ matchedRule: string;
10
+ }
11
+ export interface AutonomySurfaceSetClassification {
12
+ surfaceClass: AutonomySurface;
13
+ surfaces: AutonomySurfaceClassification[];
14
+ }
15
+ export interface AutonomyTripwireEvent {
16
+ severity: "warning" | "critical";
17
+ reasonCode: AutonomyTripwireReasonCode;
18
+ triggerReason: string;
19
+ actionTaken: "continue" | "block_and_escalate";
20
+ }
21
+ export interface AutonomyTripwireInput {
22
+ changedFiles: string[];
23
+ diffVerified: boolean;
24
+ rollbackProofPresent: boolean;
25
+ attestationMatches: boolean;
26
+ repeatedGroundingCount: number;
27
+ verifierImproved: boolean;
28
+ budgetPressure: AutonomyBudgetPressure;
29
+ }
30
+ export interface AutonomyTripwireVerdict {
31
+ status: "pass" | "fail_closed";
32
+ decision: "continue" | "human_escalation";
33
+ sideEffectsAllowed: boolean;
34
+ reasonCodes: AutonomyTripwireReasonCode[];
35
+ surfaces: AutonomySurfaceClassification[];
36
+ surfaceClass: AutonomySurface;
37
+ events: AutonomyTripwireEvent[];
38
+ }
39
+ export interface AutonomousPromotionInput extends AutonomyTripwireInput {
40
+ requestedMode: "promote" | "shadow" | "rollback";
41
+ confidenceScore?: number;
42
+ shadowEvidencePasses: boolean;
43
+ holdoutEvidencePasses?: boolean;
44
+ rollbackReady: boolean;
45
+ priorSuccessfulPromotions?: number;
46
+ humanApproval: boolean;
47
+ }
48
+ export interface AutonomousPromotionDecision {
49
+ verdict: AutonomousPromotionVerdict;
50
+ sideEffectsAllowed: boolean;
51
+ reasonCodes: Array<AutonomyTripwireReasonCode | "shadow_evidence_pending" | "rollback_not_ready" | "rollback_requested" | "shadow_requested">;
52
+ tripwire: AutonomyTripwireVerdict;
53
+ }
54
+ export interface AutonomousPromotionArtifactInput extends AutonomousPromotionInput {
55
+ runId: string;
56
+ attemptId: string;
57
+ createdAt: string;
58
+ evidence: AutonomousPromotionEvidence;
59
+ }
60
+ export interface AutonomousPromotionEvidence extends Record<string, unknown> {
61
+ diffHash?: string;
62
+ beforeArtifactHash?: string;
63
+ afterArtifactHash?: string;
64
+ rollbackBoundaryRef?: string;
65
+ improvementResultPath?: string;
66
+ }
67
+ export interface AutonomousPromotionSignatureMetadata {
68
+ algorithm: "sha256";
69
+ keyId: "deterministic-local-sha256";
70
+ signedAt: string;
71
+ signature: string;
72
+ }
73
+ export interface AutonomousPromotionArtifact {
74
+ schemaVersion: "martin.autonomous-promotion.v1";
75
+ runId: string;
76
+ attemptId: string;
77
+ createdAt: string;
78
+ requestedMode: AutonomousPromotionInput["requestedMode"];
79
+ surfaceClass: AutonomySurface;
80
+ confidenceScore: number;
81
+ shadowEvidencePasses: boolean;
82
+ holdoutEvidencePasses: boolean;
83
+ rollbackReady: boolean;
84
+ attestationVerified: boolean;
85
+ priorSuccessfulPromotions: number;
86
+ diffHash: string | null;
87
+ beforeArtifactHash: string | null;
88
+ afterArtifactHash: string | null;
89
+ rollbackBoundaryRef: string | null;
90
+ improvementResultPath: string | null;
91
+ promotionPolicyVerdict: AutonomousPromotionVerdict;
92
+ tripwireVerdict: AutonomyTripwireVerdict["status"];
93
+ finalDisposition: AutonomousPromotionVerdict;
94
+ verdict: AutonomousPromotionVerdict;
95
+ sideEffectsAllowed: boolean;
96
+ reasonCodes: AutonomousPromotionDecision["reasonCodes"];
97
+ changedFiles: string[];
98
+ surfaces: AutonomySurfaceClassification[];
99
+ evidence: Record<string, unknown>;
100
+ evidenceHash: string;
101
+ tripwire: AutonomyTripwireVerdict;
102
+ signature: AutonomousPromotionSignatureMetadata;
103
+ }
104
+ export interface PersistAutonomousPromotionArtifactInput {
105
+ root: string;
106
+ artifact: AutonomousPromotionArtifact;
107
+ martinHome?: string;
108
+ }
109
+ export interface PersistedAutonomousPromotionArtifact {
110
+ fileName: "autonomous-promotion.json";
111
+ path: string;
112
+ artifact: AutonomousPromotionArtifact;
113
+ attestation: FileAttestationEnvelope;
114
+ }
115
+ export declare function classifyAutonomySurface(path: string): AutonomySurfaceClassification;
116
+ export declare function classifyAutonomySurfaceSet(paths: string[]): AutonomySurfaceSetClassification;
117
+ export declare function evaluateAutonomyTripwires(input: AutonomyTripwireInput): AutonomyTripwireVerdict;
118
+ export declare function evaluateAutonomousPromotion(input: AutonomousPromotionInput): AutonomousPromotionDecision;
119
+ export declare function buildAutonomousPromotionArtifact(input: AutonomousPromotionArtifactInput): AutonomousPromotionArtifact;
120
+ export declare function persistAutonomousPromotionArtifact(input: PersistAutonomousPromotionArtifactInput): Promise<PersistedAutonomousPromotionArtifact>;