@vauban-org/agent-sdk 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/CONTRACT.md +6401 -813
  2. package/dist/adapters/llm/anthropic-direct.d.ts +1 -0
  3. package/dist/adapters/llm/anthropic-direct.d.ts.map +1 -1
  4. package/dist/adapters/llm/anthropic-direct.js +43 -0
  5. package/dist/adapters/llm/anthropic-direct.js.map +1 -1
  6. package/dist/adapters/llm/cascade.d.ts.map +1 -1
  7. package/dist/adapters/llm/cascade.js +57 -14
  8. package/dist/adapters/llm/cascade.js.map +1 -1
  9. package/dist/adapters/llm/litellm.d.ts +2 -0
  10. package/dist/adapters/llm/litellm.d.ts.map +1 -1
  11. package/dist/adapters/llm/litellm.js +44 -0
  12. package/dist/adapters/llm/litellm.js.map +1 -1
  13. package/dist/compute/difficulty-estimator.d.ts +53 -0
  14. package/dist/compute/difficulty-estimator.d.ts.map +1 -0
  15. package/dist/compute/difficulty-estimator.js +82 -0
  16. package/dist/compute/difficulty-estimator.js.map +1 -0
  17. package/dist/compute/strategies/mixture-of-agents.d.ts +40 -0
  18. package/dist/compute/strategies/mixture-of-agents.d.ts.map +1 -0
  19. package/dist/compute/strategies/mixture-of-agents.js +110 -0
  20. package/dist/compute/strategies/mixture-of-agents.js.map +1 -0
  21. package/dist/compute/strategies/tree-of-thoughts.d.ts +48 -0
  22. package/dist/compute/strategies/tree-of-thoughts.d.ts.map +1 -0
  23. package/dist/compute/strategies/tree-of-thoughts.js +242 -0
  24. package/dist/compute/strategies/tree-of-thoughts.js.map +1 -0
  25. package/dist/compute/strategies/two-phase-orient.d.ts +72 -0
  26. package/dist/compute/strategies/two-phase-orient.d.ts.map +1 -0
  27. package/dist/compute/strategies/two-phase-orient.js +85 -0
  28. package/dist/compute/strategies/two-phase-orient.js.map +1 -0
  29. package/dist/constitution/types.d.ts +10 -10
  30. package/dist/container/protocol.d.ts +134 -0
  31. package/dist/container/protocol.d.ts.map +1 -0
  32. package/dist/container/protocol.js +157 -0
  33. package/dist/container/protocol.js.map +1 -0
  34. package/dist/container/runtime.d.ts +140 -0
  35. package/dist/container/runtime.d.ts.map +1 -0
  36. package/dist/container/runtime.js +256 -0
  37. package/dist/container/runtime.js.map +1 -0
  38. package/dist/events/catalogue.d.ts +46 -46
  39. package/dist/events/schemas/agent.completed.v1.d.ts +4 -4
  40. package/dist/events/schemas/agent.failed.v1.d.ts +2 -2
  41. package/dist/events/schemas/agent.hitl_resolved.v1.d.ts +2 -2
  42. package/dist/events/schemas/agent.started.v1.d.ts +2 -2
  43. package/dist/events/schemas/brain.skill.extracted.v1.d.ts +4 -4
  44. package/dist/events/schemas/cc.cost.anomaly_detected.v1.d.ts +2 -2
  45. package/dist/events/schemas/cc.cost.recorded.v1.d.ts +4 -4
  46. package/dist/events/schemas/citadel.sprint.analyzed.v1.d.ts +6 -6
  47. package/dist/events/schemas/citadel.sprint.closed.v1.d.ts +2 -2
  48. package/dist/events/schemas/forge.inbox.reply_classified.v1.d.ts +6 -6
  49. package/dist/events/schemas/forge.lead.qualified.v1.d.ts +2 -2
  50. package/dist/events/schemas/forge.outreach.sent.v1.d.ts +4 -4
  51. package/dist/events/schemas/incident.detected.v1.d.ts +2 -2
  52. package/dist/events/schemas/vauban.goal.checked.v1.d.ts +2 -2
  53. package/dist/events/schemas/vauban.rebalancing.checked.v1.d.ts +2 -2
  54. package/dist/events/schemas/vauban.tax.checked.v1.d.ts +2 -2
  55. package/dist/events/schemas/vauban.vault.analyzed.v1.d.ts +6 -6
  56. package/dist/identity/agent-persona.d.ts +73 -0
  57. package/dist/identity/agent-persona.d.ts.map +1 -0
  58. package/dist/identity/agent-persona.js +165 -0
  59. package/dist/identity/agent-persona.js.map +1 -0
  60. package/dist/identity/persona-prompt.d.ts +25 -0
  61. package/dist/identity/persona-prompt.d.ts.map +1 -0
  62. package/dist/identity/persona-prompt.js +71 -0
  63. package/dist/identity/persona-prompt.js.map +1 -0
  64. package/dist/identity/persona-schema.d.ts +120 -0
  65. package/dist/identity/persona-schema.d.ts.map +1 -0
  66. package/dist/identity/persona-schema.js +103 -0
  67. package/dist/identity/persona-schema.js.map +1 -0
  68. package/dist/index.d.ts +37 -2
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +29 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/loop/minimal-loop.js +293 -287
  73. package/dist/memory/episodic-rrf.d.ts +114 -0
  74. package/dist/memory/episodic-rrf.d.ts.map +1 -0
  75. package/dist/memory/episodic-rrf.js +148 -0
  76. package/dist/memory/episodic-rrf.js.map +1 -0
  77. package/dist/mesh/attenuation.d.ts +78 -0
  78. package/dist/mesh/attenuation.d.ts.map +1 -0
  79. package/dist/mesh/attenuation.js +141 -0
  80. package/dist/mesh/attenuation.js.map +1 -0
  81. package/dist/mesh/delegate.d.ts +96 -0
  82. package/dist/mesh/delegate.d.ts.map +1 -0
  83. package/dist/mesh/delegate.js +172 -0
  84. package/dist/mesh/delegate.js.map +1 -0
  85. package/dist/mesh/dispatcher.d.ts +119 -0
  86. package/dist/mesh/dispatcher.d.ts.map +1 -0
  87. package/dist/mesh/dispatcher.js +207 -0
  88. package/dist/mesh/dispatcher.js.map +1 -0
  89. package/dist/mesh/index.d.ts +12 -0
  90. package/dist/mesh/index.d.ts.map +1 -0
  91. package/dist/mesh/index.js +11 -0
  92. package/dist/mesh/index.js.map +1 -0
  93. package/dist/mesh/types.d.ts +30 -0
  94. package/dist/mesh/types.d.ts.map +1 -0
  95. package/dist/mesh/types.js +11 -0
  96. package/dist/mesh/types.js.map +1 -0
  97. package/dist/orchestration/ooda/skills.d.ts +104 -0
  98. package/dist/orchestration/ooda/skills.d.ts.map +1 -1
  99. package/dist/orchestration/ooda/skills.js +106 -0
  100. package/dist/orchestration/ooda/skills.js.map +1 -1
  101. package/dist/ports/bastion-action.contract.test.d.ts +11 -0
  102. package/dist/ports/bastion-action.contract.test.d.ts.map +1 -0
  103. package/dist/ports/bastion-action.contract.test.js +238 -0
  104. package/dist/ports/bastion-action.contract.test.js.map +1 -0
  105. package/dist/ports/bastion-action.d.ts +133 -0
  106. package/dist/ports/bastion-action.d.ts.map +1 -0
  107. package/dist/ports/bastion-action.js +73 -0
  108. package/dist/ports/bastion-action.js.map +1 -0
  109. package/dist/ports/brain.d.ts +31 -0
  110. package/dist/ports/brain.d.ts.map +1 -1
  111. package/dist/ports/brain.js +115 -1
  112. package/dist/ports/brain.js.map +1 -1
  113. package/dist/ports/citadel-action.contract.test.d.ts +11 -0
  114. package/dist/ports/citadel-action.contract.test.d.ts.map +1 -0
  115. package/dist/ports/citadel-action.contract.test.js +317 -0
  116. package/dist/ports/citadel-action.contract.test.js.map +1 -0
  117. package/dist/ports/citadel-action.d.ts +111 -0
  118. package/dist/ports/citadel-action.d.ts.map +1 -0
  119. package/dist/ports/citadel-action.js +62 -0
  120. package/dist/ports/citadel-action.js.map +1 -0
  121. package/dist/ports/compliance-contract.d.ts +123 -0
  122. package/dist/ports/compliance-contract.d.ts.map +1 -0
  123. package/dist/ports/compliance-contract.js +35 -0
  124. package/dist/ports/compliance-contract.js.map +1 -0
  125. package/dist/ports/db.d.ts +38 -0
  126. package/dist/ports/db.d.ts.map +1 -1
  127. package/dist/ports/db.js +88 -1
  128. package/dist/ports/db.js.map +1 -1
  129. package/dist/ports/delegation.contract.test.d.ts +9 -0
  130. package/dist/ports/delegation.contract.test.d.ts.map +1 -0
  131. package/dist/ports/delegation.contract.test.js +337 -0
  132. package/dist/ports/delegation.contract.test.js.map +1 -0
  133. package/dist/ports/delegation.d.ts +134 -0
  134. package/dist/ports/delegation.d.ts.map +1 -0
  135. package/dist/ports/delegation.js +105 -0
  136. package/dist/ports/delegation.js.map +1 -0
  137. package/dist/ports/event-bus.d.ts +29 -0
  138. package/dist/ports/event-bus.d.ts.map +1 -1
  139. package/dist/ports/event-bus.js +106 -1
  140. package/dist/ports/event-bus.js.map +1 -1
  141. package/dist/ports/federation.contract.test.d.ts +9 -0
  142. package/dist/ports/federation.contract.test.d.ts.map +1 -0
  143. package/dist/ports/federation.contract.test.js +279 -0
  144. package/dist/ports/federation.contract.test.js.map +1 -0
  145. package/dist/ports/federation.d.ts +140 -0
  146. package/dist/ports/federation.d.ts.map +1 -0
  147. package/dist/ports/federation.js +57 -0
  148. package/dist/ports/federation.js.map +1 -0
  149. package/dist/ports/index.d.ts +28 -2
  150. package/dist/ports/index.d.ts.map +1 -1
  151. package/dist/ports/index.js +17 -2
  152. package/dist/ports/index.js.map +1 -1
  153. package/dist/ports/llm-provider.d.ts +37 -0
  154. package/dist/ports/llm-provider.d.ts.map +1 -1
  155. package/dist/ports/llm-provider.js +99 -1
  156. package/dist/ports/llm-provider.js.map +1 -1
  157. package/dist/ports/logger.d.ts +27 -0
  158. package/dist/ports/logger.d.ts.map +1 -1
  159. package/dist/ports/logger.js +87 -0
  160. package/dist/ports/logger.js.map +1 -1
  161. package/dist/ports/manifest-registry.contract.test.d.ts +9 -0
  162. package/dist/ports/manifest-registry.contract.test.d.ts.map +1 -0
  163. package/dist/ports/manifest-registry.contract.test.js +246 -0
  164. package/dist/ports/manifest-registry.contract.test.js.map +1 -0
  165. package/dist/ports/manifest-registry.d.ts +116 -0
  166. package/dist/ports/manifest-registry.d.ts.map +1 -0
  167. package/dist/ports/manifest-registry.js +79 -0
  168. package/dist/ports/manifest-registry.js.map +1 -0
  169. package/dist/ports/observability.contract.test.d.ts +12 -0
  170. package/dist/ports/observability.contract.test.d.ts.map +1 -0
  171. package/dist/ports/observability.contract.test.js +260 -0
  172. package/dist/ports/observability.contract.test.js.map +1 -0
  173. package/dist/ports/observability.d.ts +98 -0
  174. package/dist/ports/observability.d.ts.map +1 -0
  175. package/dist/ports/observability.js +59 -0
  176. package/dist/ports/observability.js.map +1 -0
  177. package/dist/ports/outcome.d.ts +26 -0
  178. package/dist/ports/outcome.d.ts.map +1 -1
  179. package/dist/ports/outcome.js +62 -1
  180. package/dist/ports/outcome.js.map +1 -1
  181. package/dist/ports/privacy.contract.test.d.ts +12 -0
  182. package/dist/ports/privacy.contract.test.d.ts.map +1 -0
  183. package/dist/ports/privacy.contract.test.js +325 -0
  184. package/dist/ports/privacy.contract.test.js.map +1 -0
  185. package/dist/ports/privacy.d.ts +132 -0
  186. package/dist/ports/privacy.d.ts.map +1 -0
  187. package/dist/ports/privacy.js +83 -0
  188. package/dist/ports/privacy.js.map +1 -0
  189. package/dist/ports/tenant-context.contract.test.d.ts +14 -0
  190. package/dist/ports/tenant-context.contract.test.d.ts.map +1 -0
  191. package/dist/ports/tenant-context.contract.test.js +352 -0
  192. package/dist/ports/tenant-context.contract.test.js.map +1 -0
  193. package/dist/ports/tenant-context.d.ts +103 -0
  194. package/dist/ports/tenant-context.d.ts.map +1 -0
  195. package/dist/ports/tenant-context.js +48 -0
  196. package/dist/ports/tenant-context.js.map +1 -0
  197. package/dist/ports/vauban-finance-action.contract.test.d.ts +11 -0
  198. package/dist/ports/vauban-finance-action.contract.test.d.ts.map +1 -0
  199. package/dist/ports/vauban-finance-action.contract.test.js +260 -0
  200. package/dist/ports/vauban-finance-action.contract.test.js.map +1 -0
  201. package/dist/ports/vauban-finance-action.d.ts +106 -0
  202. package/dist/ports/vauban-finance-action.d.ts.map +1 -0
  203. package/dist/ports/vauban-finance-action.js +60 -0
  204. package/dist/ports/vauban-finance-action.js.map +1 -0
  205. package/dist/ports/workflow-runtime.d.ts +204 -0
  206. package/dist/ports/workflow-runtime.d.ts.map +1 -0
  207. package/dist/ports/workflow-runtime.js +72 -0
  208. package/dist/ports/workflow-runtime.js.map +1 -0
  209. package/dist/proof/cert-verify.d.ts +80 -0
  210. package/dist/proof/cert-verify.d.ts.map +1 -0
  211. package/dist/proof/cert-verify.js +178 -0
  212. package/dist/proof/cert-verify.js.map +1 -0
  213. package/dist/replay/replay.d.ts.map +1 -1
  214. package/dist/replay/replay.js +5 -1
  215. package/dist/replay/replay.js.map +1 -1
  216. package/dist/retry/index.d.ts +129 -0
  217. package/dist/retry/index.d.ts.map +1 -0
  218. package/dist/retry/index.js +156 -0
  219. package/dist/retry/index.js.map +1 -0
  220. package/dist/retry/presets.d.ts +39 -0
  221. package/dist/retry/presets.d.ts.map +1 -0
  222. package/dist/retry/presets.js +69 -0
  223. package/dist/retry/presets.js.map +1 -0
  224. package/dist/skill-loop/ab-runner.d.ts +67 -0
  225. package/dist/skill-loop/ab-runner.d.ts.map +1 -0
  226. package/dist/skill-loop/ab-runner.js +160 -0
  227. package/dist/skill-loop/ab-runner.js.map +1 -0
  228. package/dist/skill-loop/adoption.d.ts +67 -0
  229. package/dist/skill-loop/adoption.d.ts.map +1 -0
  230. package/dist/skill-loop/adoption.js +126 -0
  231. package/dist/skill-loop/adoption.js.map +1 -0
  232. package/dist/skill-loop/candidate.d.ts +45 -0
  233. package/dist/skill-loop/candidate.d.ts.map +1 -0
  234. package/dist/skill-loop/candidate.js +43 -0
  235. package/dist/skill-loop/candidate.js.map +1 -0
  236. package/dist/skill-loop/evaluator.d.ts +42 -0
  237. package/dist/skill-loop/evaluator.d.ts.map +1 -0
  238. package/dist/skill-loop/evaluator.js +184 -0
  239. package/dist/skill-loop/evaluator.js.map +1 -0
  240. package/dist/skill-loop/index.d.ts +27 -0
  241. package/dist/skill-loop/index.d.ts.map +1 -0
  242. package/dist/skill-loop/index.js +27 -0
  243. package/dist/skill-loop/index.js.map +1 -0
  244. package/dist/skill-loop/reflexion-replay.d.ts +87 -0
  245. package/dist/skill-loop/reflexion-replay.d.ts.map +1 -0
  246. package/dist/skill-loop/reflexion-replay.js +110 -0
  247. package/dist/skill-loop/reflexion-replay.js.map +1 -0
  248. package/dist/skill-loop/sign-off.d.ts +88 -0
  249. package/dist/skill-loop/sign-off.d.ts.map +1 -0
  250. package/dist/skill-loop/sign-off.js +146 -0
  251. package/dist/skill-loop/sign-off.js.map +1 -0
  252. package/dist/skill-loop/value-metric.d.ts +55 -0
  253. package/dist/skill-loop/value-metric.d.ts.map +1 -0
  254. package/dist/skill-loop/value-metric.js +69 -0
  255. package/dist/skill-loop/value-metric.js.map +1 -0
  256. package/dist/skill-loop/versioning.d.ts +36 -0
  257. package/dist/skill-loop/versioning.d.ts.map +1 -0
  258. package/dist/skill-loop/versioning.js +47 -0
  259. package/dist/skill-loop/versioning.js.map +1 -0
  260. package/dist/skill-manifest/anchor.d.ts +91 -0
  261. package/dist/skill-manifest/anchor.d.ts.map +1 -0
  262. package/dist/skill-manifest/anchor.js +331 -0
  263. package/dist/skill-manifest/anchor.js.map +1 -0
  264. package/dist/skill-manifest/builder.d.ts +47 -0
  265. package/dist/skill-manifest/builder.d.ts.map +1 -0
  266. package/dist/skill-manifest/builder.js +93 -0
  267. package/dist/skill-manifest/builder.js.map +1 -0
  268. package/dist/skill-manifest/index.d.ts +13 -0
  269. package/dist/skill-manifest/index.d.ts.map +1 -0
  270. package/dist/skill-manifest/index.js +9 -0
  271. package/dist/skill-manifest/index.js.map +1 -0
  272. package/dist/skill-manifest/types.d.ts +67 -0
  273. package/dist/skill-manifest/types.d.ts.map +1 -0
  274. package/dist/skill-manifest/types.js +16 -0
  275. package/dist/skill-manifest/types.js.map +1 -0
  276. package/dist/skill-manifest/verifier.d.ts +42 -0
  277. package/dist/skill-manifest/verifier.d.ts.map +1 -0
  278. package/dist/skill-manifest/verifier.js +136 -0
  279. package/dist/skill-manifest/verifier.js.map +1 -0
  280. package/dist/skills/brain-query.d.ts +4 -4
  281. package/dist/skills/brain-store.d.ts +6 -6
  282. package/dist/skills/errors.d.ts +15 -0
  283. package/dist/skills/errors.d.ts.map +1 -1
  284. package/dist/skills/errors.js +21 -0
  285. package/dist/skills/errors.js.map +1 -1
  286. package/dist/skills/hitl-request.d.ts +2 -2
  287. package/dist/skills/index.d.ts +3 -1
  288. package/dist/skills/index.d.ts.map +1 -1
  289. package/dist/skills/index.js +4 -1
  290. package/dist/skills/index.js.map +1 -1
  291. package/dist/skills/markdown/loader.d.ts +52 -0
  292. package/dist/skills/markdown/loader.d.ts.map +1 -0
  293. package/dist/skills/markdown/loader.js +93 -0
  294. package/dist/skills/markdown/loader.js.map +1 -0
  295. package/dist/skills/markdown/schema.d.ts +432 -0
  296. package/dist/skills/markdown/schema.d.ts.map +1 -0
  297. package/dist/skills/markdown/schema.js +121 -0
  298. package/dist/skills/markdown/schema.js.map +1 -0
  299. package/dist/skills/poc-md-loader/markdown-loader.d.ts +77 -0
  300. package/dist/skills/poc-md-loader/markdown-loader.d.ts.map +1 -0
  301. package/dist/skills/poc-md-loader/markdown-loader.js +125 -0
  302. package/dist/skills/poc-md-loader/markdown-loader.js.map +1 -0
  303. package/dist/skills/poc-md-loader/runner.d.ts +24 -0
  304. package/dist/skills/poc-md-loader/runner.d.ts.map +1 -0
  305. package/dist/skills/poc-md-loader/runner.js +57 -0
  306. package/dist/skills/poc-md-loader/runner.js.map +1 -0
  307. package/dist/skills/poc-md-loader/vitest.poc.config.d.ts +3 -0
  308. package/dist/skills/poc-md-loader/vitest.poc.config.d.ts.map +1 -0
  309. package/dist/skills/poc-md-loader/vitest.poc.config.js +13 -0
  310. package/dist/skills/poc-md-loader/vitest.poc.config.js.map +1 -0
  311. package/dist/skills/poc-md-loader/web-search/script.d.ts +33 -0
  312. package/dist/skills/poc-md-loader/web-search/script.d.ts.map +1 -0
  313. package/dist/skills/poc-md-loader/web-search/script.js +75 -0
  314. package/dist/skills/poc-md-loader/web-search/script.js.map +1 -0
  315. package/dist/skills/record-outcome.d.ts +4 -4
  316. package/dist/skills/send-email.d.ts.map +1 -1
  317. package/dist/skills/send-email.js +15 -3
  318. package/dist/skills/send-email.js.map +1 -1
  319. package/dist/skills/slack-notify.d.ts +4 -4
  320. package/dist/skills/starknet-balance.d.ts +1 -1
  321. package/dist/skills/telegram-notify.d.ts +4 -4
  322. package/dist/skills/web-search.d.ts +1 -1
  323. package/dist/testing/index.d.ts +3 -0
  324. package/dist/testing/test-brain-port.d.ts +4 -0
  325. package/dist/testing/test-brain-port.d.ts.map +1 -1
  326. package/dist/testing/test-brain-port.js +75 -20
  327. package/dist/testing/test-brain-port.js.map +1 -1
  328. package/dist/testing/test-event-bus.d.ts.map +1 -1
  329. package/dist/testing/test-event-bus.js +89 -36
  330. package/dist/testing/test-event-bus.js.map +1 -1
  331. package/dist/trace/schema.d.ts +1 -1
  332. package/dist/trace/schema.d.ts.map +1 -1
  333. package/dist/trace/schema.js +1 -1
  334. package/dist/trace/schema.js.map +1 -1
  335. package/dist/verify/formal/index.d.ts +44 -0
  336. package/dist/verify/formal/index.d.ts.map +1 -0
  337. package/dist/verify/formal/index.js +98 -0
  338. package/dist/verify/formal/index.js.map +1 -0
  339. package/dist/verify/formal/policy.d.ts +105 -0
  340. package/dist/verify/formal/policy.d.ts.map +1 -0
  341. package/dist/verify/formal/policy.js +159 -0
  342. package/dist/verify/formal/policy.js.map +1 -0
  343. package/dist/verify/formal/result.d.ts +50 -0
  344. package/dist/verify/formal/result.d.ts.map +1 -0
  345. package/dist/verify/formal/result.js +21 -0
  346. package/dist/verify/formal/result.js.map +1 -0
  347. package/dist/verify/formal/solver.d.ts +67 -0
  348. package/dist/verify/formal/solver.d.ts.map +1 -0
  349. package/dist/verify/formal/solver.js +184 -0
  350. package/dist/verify/formal/solver.js.map +1 -0
  351. package/dist/verify/formal/spec-language.d.ts +80 -0
  352. package/dist/verify/formal/spec-language.d.ts.map +1 -0
  353. package/dist/verify/formal/spec-language.js +219 -0
  354. package/dist/verify/formal/spec-language.js.map +1 -0
  355. package/docs/attestation.md +199 -0
  356. package/docs/identity.md +193 -0
  357. package/package.json +34 -17
  358. package/src/adapters/llm/anthropic-direct.ts +51 -0
  359. package/src/adapters/llm/cascade.ts +64 -19
  360. package/src/adapters/llm/litellm.ts +49 -0
  361. package/src/compute/difficulty-estimator.ts +111 -0
  362. package/src/compute/strategies/mixture-of-agents.ts +150 -0
  363. package/src/compute/strategies/tree-of-thoughts.ts +293 -0
  364. package/src/compute/strategies/two-phase-orient.ts +147 -0
  365. package/src/container/protocol.ts +243 -0
  366. package/src/container/runtime.ts +424 -0
  367. package/src/db/migrations/026_formal_verify_results.sql +30 -0
  368. package/src/identity/agent-persona.ts +203 -0
  369. package/src/identity/persona-prompt.ts +84 -0
  370. package/src/identity/persona-schema.ts +127 -0
  371. package/src/index.ts +338 -1
  372. package/src/memory/episodic-rrf.ts +224 -0
  373. package/src/mesh/attenuation.ts +190 -0
  374. package/src/mesh/delegate.ts +254 -0
  375. package/src/mesh/dispatcher.ts +301 -0
  376. package/src/mesh/index.ts +39 -0
  377. package/src/mesh/types.ts +31 -0
  378. package/src/orchestration/ooda/skills.ts +177 -0
  379. package/src/ports/bastion-action.contract.test.ts +355 -0
  380. package/src/ports/bastion-action.ts +198 -0
  381. package/src/ports/brain.ts +177 -15
  382. package/src/ports/citadel-action.contract.test.ts +430 -0
  383. package/src/ports/citadel-action.ts +174 -0
  384. package/src/ports/compliance-contract.ts +191 -0
  385. package/src/ports/db.ts +98 -0
  386. package/src/ports/delegation.contract.test.ts +428 -0
  387. package/src/ports/delegation.ts +211 -0
  388. package/src/ports/event-bus.ts +133 -0
  389. package/src/ports/federation.contract.test.ts +355 -0
  390. package/src/ports/federation.ts +190 -0
  391. package/src/ports/index.ts +186 -1
  392. package/src/ports/llm-provider.ts +123 -0
  393. package/src/ports/logger.ts +104 -0
  394. package/src/ports/manifest-registry.contract.test.ts +324 -0
  395. package/src/ports/manifest-registry.ts +188 -0
  396. package/src/ports/observability.contract.test.ts +315 -0
  397. package/src/ports/observability.ts +150 -0
  398. package/src/ports/outcome.ts +69 -0
  399. package/src/ports/privacy.contract.test.ts +413 -0
  400. package/src/ports/privacy.ts +207 -0
  401. package/src/ports/tenant-context.contract.test.ts +454 -0
  402. package/src/ports/tenant-context.ts +150 -0
  403. package/src/ports/vauban-finance-action.contract.test.ts +335 -0
  404. package/src/ports/vauban-finance-action.ts +166 -0
  405. package/src/ports/workflow-runtime.ts +327 -0
  406. package/src/proof/cert-verify.ts +249 -0
  407. package/src/replay/replay.ts +11 -8
  408. package/src/retry/index.ts +227 -0
  409. package/src/retry/presets.ts +75 -0
  410. package/src/skill-loop/ab-runner.ts +196 -0
  411. package/src/skill-loop/adoption.ts +188 -0
  412. package/src/skill-loop/candidate.ts +75 -0
  413. package/src/skill-loop/evaluator.ts +238 -0
  414. package/src/skill-loop/index.ts +51 -0
  415. package/src/skill-loop/reflexion-replay.ts +173 -0
  416. package/src/skill-loop/sign-off.ts +247 -0
  417. package/src/skill-loop/value-metric.ts +120 -0
  418. package/src/skill-loop/versioning.ts +75 -0
  419. package/src/skill-manifest/anchor.ts +401 -0
  420. package/src/skill-manifest/builder.ts +129 -0
  421. package/src/skill-manifest/index.ts +18 -0
  422. package/src/skill-manifest/types.ts +72 -0
  423. package/src/skill-manifest/verifier.ts +198 -0
  424. package/src/skills/errors.ts +30 -2
  425. package/src/skills/index.ts +19 -0
  426. package/src/skills/markdown/loader.ts +129 -0
  427. package/src/skills/markdown/schema.ts +144 -0
  428. package/src/skills/poc-md-loader/e2e-parity.test.ts +237 -0
  429. package/src/skills/poc-md-loader/markdown-loader.ts +161 -0
  430. package/src/skills/poc-md-loader/runner.ts +82 -0
  431. package/src/skills/poc-md-loader/vitest.poc.config.ts +13 -0
  432. package/src/skills/poc-md-loader/web-search/SKILL.md +42 -0
  433. package/src/skills/poc-md-loader/web-search/script.ts +109 -0
  434. package/src/skills/send-email.ts +15 -3
  435. package/src/testing/test-brain-port.ts +98 -24
  436. package/src/testing/test-event-bus.ts +104 -43
  437. package/src/trace/schema.ts +1 -1
  438. package/src/verify/formal/index.ts +154 -0
  439. package/src/verify/formal/policy.ts +253 -0
  440. package/src/verify/formal/result.ts +52 -0
  441. package/src/verify/formal/solver.ts +235 -0
  442. package/src/verify/formal/spec-language.ts +274 -0
@@ -0,0 +1,327 @@
1
+ /**
2
+ * WorkflowRuntimePort — durable workflow execution port (S3 spec).
3
+ *
4
+ * Defines: 9-state machine, journal invariants I-S3-1..8, replay determinism,
5
+ * ctx.* primitives, lease takeover pattern, handler versioning.
6
+ *
7
+ * Default adapter: DBOSWorkflowAdapter (DBOS-TS Apache 2.0, Postgres-native).
8
+ * Swap-able to Temporal/Restate via this port + test contract T-S3-1..12.
9
+ */
10
+
11
+ // ─── Workflow run state machine (S3 §3.1) ─────────────────────────────────────
12
+
13
+ export type WorkflowStatus =
14
+ | "PENDING"
15
+ | "RUNNING"
16
+ | "SLEEPING"
17
+ | "WAITING_SIGNAL"
18
+ | "AWAITING_BLOCK"
19
+ | "AWAITING_EVENT"
20
+ | "DONE"
21
+ | "FAILED"
22
+ | "CANCELLED";
23
+
24
+ // ─── Journal types (S3 §4) ────────────────────────────────────────────────────
25
+
26
+ export type StepKind =
27
+ | "tool_call"
28
+ | "sleep"
29
+ | "wait_signal"
30
+ | "await_block"
31
+ | "await_event"
32
+ | "submit_tx"
33
+ | "human"
34
+ | "ctx_now"
35
+ | "ctx_random"
36
+ | "ctx_uuid"
37
+ | "child_workflow";
38
+
39
+ export type StepStatus = "pending" | "running" | "done" | "failed";
40
+
41
+ export interface JournalEntry {
42
+ readonly journal_id: bigint;
43
+ readonly run_id: string; // UUID
44
+ readonly step_index: number; // monotone, I-S3-1
45
+ readonly step_name: string;
46
+ readonly step_kind: StepKind;
47
+ readonly status: StepStatus;
48
+ readonly input: unknown; // CBOR-encoded at rest
49
+ readonly output?: unknown;
50
+ readonly error?: { message: string; stack?: string };
51
+ readonly idempotency_key?: string; // 32-byte hex, I-S3-3
52
+ readonly started_at: Date;
53
+ readonly finished_at?: Date;
54
+ }
55
+
56
+ // ─── Workflow run (S3 §3.1 + §7) ─────────────────────────────────────────────
57
+
58
+ export interface WorkflowRun<TOutput = unknown> {
59
+ readonly run_id: string;
60
+ readonly workflow_name: string;
61
+ readonly workflow_version: string; // locked per I-S3-6
62
+ readonly status: WorkflowStatus;
63
+ readonly input: unknown;
64
+ readonly output?: TOutput;
65
+ readonly error?: string;
66
+ readonly lease_owner?: string;
67
+ readonly lease_expires_at?: Date;
68
+ readonly wake_at?: Date;
69
+ readonly started_at: Date;
70
+ readonly finished_at?: Date;
71
+ readonly tenant_id: string;
72
+ readonly manifest_hash?: string; // crosslink S1
73
+ }
74
+
75
+ // ─── Step options ─────────────────────────────────────────────────────────────
76
+
77
+ export interface StepOpts {
78
+ readonly maxAttempts?: number; // default 3, EC-S3-6
79
+ readonly backoffMs?: number; // default 1000 (1s/2s/4s)
80
+ readonly idempotencyKey?: string;
81
+ readonly timeoutMs?: number;
82
+ }
83
+
84
+ // ─── WorkflowContext — ctx.* primitives (S3 §5.3) ────────────────────────────
85
+
86
+ export interface WorkflowContext {
87
+ readonly runId: string;
88
+ readonly tenantId: string;
89
+ readonly workflowName: string;
90
+ readonly workflowVersion: string;
91
+ readonly abortSignal: AbortSignal;
92
+
93
+ /**
94
+ * Wrap any side-effectful operation. Journaled + replayed from journal.
95
+ * FORBIDDEN outside ctx.step(): Date.now, Math.random, fetch, fs.*, etc. (S3 §5.4)
96
+ */
97
+ step<T>(name: string, fn: () => Promise<T>, opts?: StepOpts): Promise<T>;
98
+
99
+ /** Deterministic timestamp — journaled (Q3.2). Never use Date.now() in handlers. */
100
+ now(): Promise<Date>;
101
+
102
+ /** Deterministic random [0,1) — journaled (Q3.2). Never use Math.random(). */
103
+ random(): Promise<number>;
104
+
105
+ /** Deterministic UUID v4 — journaled (Q3.2). Never use crypto.randomUUID(). */
106
+ uuid(): Promise<string>;
107
+
108
+ /** Durable sleep until absolute timestamp. EC-S3-8: t < now() → no-op. */
109
+ sleepUntil(t: Date): Promise<void>;
110
+
111
+ /** Durable sleep for duration in ms. */
112
+ sleepFor(durationMs: number): Promise<void>;
113
+
114
+ /**
115
+ * Wait for a named signal. Returns signal payload.
116
+ * Times out → WorkflowSignalTimeoutError (S3 §3.3: FAILED, 'signal_timeout').
117
+ */
118
+ waitForSignal<T = unknown>(name: string, timeoutMs?: number): Promise<T>;
119
+
120
+ /** Await Starknet block height ≥ n. Transitions: RUNNING → AWAITING_BLOCK → RUNNING. */
121
+ awaitStarknetBlock(blockHeight: number): Promise<void>;
122
+
123
+ /** Await matching event from event bus. Transitions: RUNNING → AWAITING_EVENT → RUNNING. */
124
+ awaitEvent<T = unknown>(filter: EventFilter): Promise<T>;
125
+
126
+ /** Spawn a child workflow. Parent waits for child completion. */
127
+ childWorkflow<TInput, TOutput>(
128
+ name: string,
129
+ input: TInput,
130
+ opts?: ChildWorkflowOpts
131
+ ): Promise<TOutput>;
132
+
133
+ /** Cooperative cancellation. Step handlers should poll ctx.abortSignal. */
134
+ cancel(reason: string): Promise<void>;
135
+ }
136
+
137
+ export interface EventFilter {
138
+ readonly source?: string;
139
+ readonly type?: string;
140
+ readonly tenantId?: string;
141
+ readonly [key: string]: unknown;
142
+ }
143
+
144
+ export interface ChildWorkflowOpts {
145
+ readonly tenantId?: string;
146
+ readonly version?: string;
147
+ readonly idempotencyKey?: string;
148
+ }
149
+
150
+ // ─── Handler type ─────────────────────────────────────────────────────────────
151
+
152
+ export type WorkflowHandler<TInput = unknown, TOutput = unknown> = (
153
+ ctx: WorkflowContext,
154
+ input: TInput
155
+ ) => Promise<TOutput>;
156
+
157
+ // ─── Migration (S3 §6.3) ──────────────────────────────────────────────────────
158
+
159
+ export type JournalMigrator = (journal: JournalEntry[]) => JournalEntry[];
160
+
161
+ export interface MigrationResult {
162
+ readonly ok: boolean;
163
+ readonly run_id: string;
164
+ readonly from_version: string;
165
+ readonly to_version: string;
166
+ readonly entries_migrated: number;
167
+ readonly error?: string;
168
+ }
169
+
170
+ // ─── WorkflowRuntimePort ──────────────────────────────────────────────────────
171
+
172
+ export interface StartWorkflowOpts {
173
+ readonly workflowName: string;
174
+ readonly workflowVersion: string;
175
+ readonly tenantId: string;
176
+ readonly input: unknown;
177
+ readonly idempotencyKey?: string;
178
+ readonly manifestHash?: string;
179
+ }
180
+
181
+ export interface ResumeWorkflowOpts {
182
+ readonly workerId: string;
183
+ readonly leaseTtlSeconds?: number; // default 60
184
+ }
185
+
186
+ export interface SendSignalOpts {
187
+ readonly payload?: unknown;
188
+ }
189
+
190
+ export interface WorkflowRuntimePort {
191
+ /**
192
+ * Register a handler for a workflow name + version.
193
+ * Must be called before start() for that workflow type.
194
+ */
195
+ register<TInput, TOutput>(
196
+ name: string,
197
+ version: string,
198
+ handler: WorkflowHandler<TInput, TOutput>
199
+ ): void;
200
+
201
+ /**
202
+ * Start a new workflow run. Returns run_id.
203
+ * Idempotent if idempotencyKey provided and run already exists.
204
+ */
205
+ start(opts: StartWorkflowOpts): Promise<string>;
206
+
207
+ /**
208
+ * Attempt to acquire lease and execute a pending/sleeping run.
209
+ * Returns null if no eligible run found (no work to do).
210
+ * Runs handler to completion (or suspension) before returning.
211
+ */
212
+ executeNext(opts: ResumeWorkflowOpts): Promise<WorkflowRun | null>;
213
+
214
+ /** Get current state of a run. */
215
+ getStatus(runId: string): Promise<WorkflowRun | null>;
216
+
217
+ /**
218
+ * Wait for a run to reach DONE or FAILED.
219
+ * Polls internally — do not call in a tight loop.
220
+ */
221
+ waitForCompletion<TOutput>(
222
+ runId: string,
223
+ opts?: { timeoutMs?: number; pollIntervalMs?: number }
224
+ ): Promise<WorkflowRun<TOutput>>;
225
+
226
+ /** Send a named signal to a waiting run (S3 §3.3). */
227
+ sendSignal(
228
+ runId: string,
229
+ signalName: string,
230
+ opts?: SendSignalOpts
231
+ ): Promise<void>;
232
+
233
+ /**
234
+ * Cancel a run cooperatively (S3 §3.3, EC-S3-7).
235
+ * Sets AbortSignal on next step boundary. Hard abort after 30s.
236
+ */
237
+ cancel(runId: string, reason: string): Promise<void>;
238
+
239
+ /**
240
+ * Migrate a suspended run to a new handler version (S3 §6.3).
241
+ * Only valid for runs in SLEEPING | WAITING_SIGNAL | AWAITING_* states.
242
+ */
243
+ migrateRun(
244
+ runId: string,
245
+ fromVersion: string,
246
+ toVersion: string,
247
+ migrator: JournalMigrator
248
+ ): Promise<MigrationResult>;
249
+
250
+ /** Read the journal entries for a run (read-only, audit). */
251
+ getJournal(runId: string): Promise<JournalEntry[]>;
252
+
253
+ /**
254
+ * Start the worker polling loop.
255
+ * Worker polls for PENDING runs and calls executeNext() in a loop.
256
+ */
257
+ startWorker(opts?: {
258
+ workerId?: string;
259
+ pollIntervalMs?: number;
260
+ maxConcurrent?: number;
261
+ }): Promise<void>;
262
+
263
+ /** Stop the worker polling loop gracefully. */
264
+ stopWorker(): Promise<void>;
265
+ }
266
+
267
+ // ─── Typed errors ─────────────────────────────────────────────────────────────
268
+
269
+ export class WorkflowNotFoundError extends Error {
270
+ constructor(public readonly runId: string) {
271
+ super(`Workflow run not found: ${runId}`);
272
+ this.name = "WorkflowNotFoundError";
273
+ Object.setPrototypeOf(this, new.target.prototype);
274
+ }
275
+ }
276
+
277
+ export class WorkflowNonDeterminismError extends Error {
278
+ constructor(
279
+ public readonly runId: string,
280
+ public readonly stepIndex: number,
281
+ public readonly expected: string,
282
+ public readonly actual: string
283
+ ) {
284
+ super(
285
+ `Non-determinism at step ${stepIndex}: expected ${expected}, got ${actual} (run=${runId})`
286
+ );
287
+ this.name = "WorkflowNonDeterminismError";
288
+ Object.setPrototypeOf(this, new.target.prototype);
289
+ }
290
+ }
291
+
292
+ export class WorkflowSignalTimeoutError extends Error {
293
+ constructor(
294
+ public readonly runId: string,
295
+ public readonly signalName: string,
296
+ public readonly timeoutMs: number
297
+ ) {
298
+ super(`Signal '${signalName}' timeout after ${timeoutMs}ms (run=${runId})`);
299
+ this.name = "WorkflowSignalTimeoutError";
300
+ Object.setPrototypeOf(this, new.target.prototype);
301
+ }
302
+ }
303
+
304
+ export class WorkflowLeaseConflictError extends Error {
305
+ constructor(
306
+ public readonly runId: string,
307
+ public readonly currentOwner: string
308
+ ) {
309
+ super(`Cannot acquire lease for run ${runId}: held by ${currentOwner}`);
310
+ this.name = "WorkflowLeaseConflictError";
311
+ Object.setPrototypeOf(this, new.target.prototype);
312
+ }
313
+ }
314
+
315
+ export class WorkflowVersionMismatchError extends Error {
316
+ constructor(
317
+ public readonly runId: string,
318
+ public readonly expected: string,
319
+ public readonly actual: string
320
+ ) {
321
+ super(
322
+ `Version mismatch for run ${runId}: expected ${expected}, got ${actual}`
323
+ );
324
+ this.name = "WorkflowVersionMismatchError";
325
+ Object.setPrototypeOf(this, new.target.prototype);
326
+ }
327
+ }
@@ -0,0 +1,249 @@
1
+ /**
2
+ * Run Certificate verifier — standalone surface for external clients (CLI, dashboards).
3
+ *
4
+ * This module is the **single source of truth** for offline verification of a
5
+ * `SignedRunProofCertificate`. The CC server (`src/proof/ed25519-{signer,verifier}.ts`)
6
+ * holds the signing side (DB-coupled assembly + signing); this module holds the
7
+ * verification side, which is intentionally dependency-free (no DB, no MCP).
8
+ *
9
+ * Pipeline (mirrors server signer):
10
+ * 1. Strip embedded `signature` field
11
+ * 2. JCS-canonicalize (RFC 8785 subset: sorted keys, -0 → 0)
12
+ * 3. SHA-256 → first 31 bytes → felt252-safe
13
+ * 4. Poseidon([0x1, sha_felt, CERT_MARKER_FELT]) → cert_hash_felt252
14
+ * 5. Felt → 32-byte buffer → Ed25519 verify
15
+ *
16
+ * Domain separator: `CERT_MARKER_FELT` = UTF-8 "run_cert" felt252 (right-aligned).
17
+ *
18
+ * @see ../../../src/proof/ed25519-signer.ts (signer, CC server)
19
+ * @see docs/ietf/draft-vauban-skill-attestation-00.md (RFC profile)
20
+ * @public
21
+ */
22
+
23
+ import {
24
+ createHash,
25
+ createPublicKey,
26
+ verify as cryptoVerify,
27
+ type KeyObject,
28
+ } from "node:crypto";
29
+ import { hash } from "starknet";
30
+
31
+ // ─── Types ────────────────────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Ed25519 signature payload embedded in `SignedRunProofCertificate`.
35
+ * Mirrors the server-side `SignaturePayload` (single field schema).
36
+ */
37
+ export interface SignaturePayload {
38
+ alg: "Ed25519";
39
+ kid: string;
40
+ value: string;
41
+ pubkey_spki_b64: string;
42
+ cert_hash_felt252: string;
43
+ signed_at: string;
44
+ }
45
+
46
+ /**
47
+ * A `SignedRunProofCertificate` for verification purposes — only the fields
48
+ * the verifier touches are typed. The cert may carry additional fields
49
+ * (decision_chain, merkle_root, etc.) which the verifier preserves but does
50
+ * not inspect.
51
+ */
52
+ export interface SignedRunProofCertificateLike {
53
+ signature?: SignaturePayload;
54
+ // ... plus arbitrary other fields, opaque to the verifier
55
+ [key: string]: unknown;
56
+ }
57
+
58
+ export type CertVerifyFailReason =
59
+ | "missing_signature"
60
+ | "wrong_alg"
61
+ | "hash_mismatch"
62
+ | "pubkey_unresolvable"
63
+ | "kid_mismatch"
64
+ | "signature_invalid"
65
+ | "malformed_signature";
66
+
67
+ export interface CertVerifyResult {
68
+ valid: boolean;
69
+ reason?: CertVerifyFailReason;
70
+ details?: string;
71
+ recomputed_cert_hash_felt252: string;
72
+ }
73
+
74
+ export interface CertVerifyOptions {
75
+ expectedPublicKey?: KeyObject;
76
+ expectedKid?: string;
77
+ }
78
+
79
+ // ─── Constants ────────────────────────────────────────────────────────────────
80
+
81
+ /** Domain separator: UTF-8 "run_cert" → felt252 right-aligned, zero-padded. */
82
+ export const CERT_MARKER_FELT: string =
83
+ "0x" + Buffer.from("run_cert", "utf8").toString("hex").padStart(62, "0");
84
+
85
+ // ─── JCS canonicalization (RFC 8785 subset) ──────────────────────────────────
86
+
87
+ function normalizeValue(value: unknown): unknown {
88
+ if (value === null) return null;
89
+ if (typeof value === "number") {
90
+ if (Object.is(value, -0)) return 0;
91
+ return value;
92
+ }
93
+ if (Array.isArray(value)) return value.map(normalizeValue);
94
+ if (typeof value === "object") {
95
+ const obj = value as Record<string, unknown>;
96
+ const sorted: Record<string, unknown> = {};
97
+ for (const key of Object.keys(obj).sort()) {
98
+ sorted[key] = normalizeValue(obj[key]);
99
+ }
100
+ return sorted;
101
+ }
102
+ return value;
103
+ }
104
+
105
+ function canonicalizeJcs(data: Record<string, unknown>): string {
106
+ return JSON.stringify(normalizeValue(data));
107
+ }
108
+
109
+ // ─── Felt helpers ─────────────────────────────────────────────────────────────
110
+
111
+ function felt252ToBytes(felt: string): Buffer {
112
+ const hex = felt.replace(/^0x/, "").padStart(64, "0");
113
+ return Buffer.from(hex, "hex");
114
+ }
115
+
116
+ /**
117
+ * Compute the felt252 hash that Ed25519 signs over for a Run Certificate.
118
+ * Strips any embedded `signature` field before hashing — verification is
119
+ * idempotent.
120
+ */
121
+ export function computeCertHashFelt252(
122
+ cert: SignedRunProofCertificateLike
123
+ ): string {
124
+ const { signature: _stripped, ...unsigned } = cert;
125
+ void _stripped;
126
+ const canonical = canonicalizeJcs(unsigned as Record<string, unknown>);
127
+ const sha = createHash("sha256").update(canonical, "utf8").digest("hex");
128
+ const shaFelt = "0x" + sha.substring(0, 62);
129
+ return hash.computePoseidonHashOnElements(["0x1", shaFelt, CERT_MARKER_FELT]);
130
+ }
131
+
132
+ // ─── Public-key reconstruction ────────────────────────────────────────────────
133
+
134
+ /**
135
+ * Reconstruct an Ed25519 `KeyObject` from base64 SPKI DER (as embedded in
136
+ * `signature.pubkey_spki_b64`).
137
+ *
138
+ * @throws if base64 is malformed or the key is not Ed25519.
139
+ */
140
+ export function publicKeyFromSpkiB64(pubkeySpkiB64: string): KeyObject {
141
+ const der = Buffer.from(pubkeySpkiB64, "base64");
142
+ if (der.length === 0) {
143
+ throw new Error("[cert-verify] empty SPKI buffer");
144
+ }
145
+ const key = createPublicKey({ key: der, format: "der", type: "spki" });
146
+ if (key.asymmetricKeyType !== "ed25519") {
147
+ throw new Error(
148
+ `[cert-verify] SPKI is not Ed25519 (got ${key.asymmetricKeyType})`
149
+ );
150
+ }
151
+ return key;
152
+ }
153
+
154
+ // ─── Verifier ─────────────────────────────────────────────────────────────────
155
+
156
+ /**
157
+ * Verify a signed Run Certificate. Returns a structured result — never throws
158
+ * on verification failure (only on malformed input).
159
+ *
160
+ * The 7 possible failure reasons are typed via `CertVerifyFailReason` so callers
161
+ * can branch precisely. `recomputed_cert_hash_felt252` is always returned for
162
+ * audit trail / debug inspection.
163
+ */
164
+ export function verifyRunCertificate(
165
+ cert: SignedRunProofCertificateLike,
166
+ opts: CertVerifyOptions = {}
167
+ ): CertVerifyResult {
168
+ const recomputed = computeCertHashFelt252(cert);
169
+
170
+ const sig = cert.signature;
171
+ if (!sig) {
172
+ return {
173
+ valid: false,
174
+ reason: "missing_signature",
175
+ recomputed_cert_hash_felt252: recomputed,
176
+ };
177
+ }
178
+ if (sig.alg !== "Ed25519") {
179
+ return {
180
+ valid: false,
181
+ reason: "wrong_alg",
182
+ details: `expected Ed25519, got ${String(sig.alg)}`,
183
+ recomputed_cert_hash_felt252: recomputed,
184
+ };
185
+ }
186
+ if (sig.cert_hash_felt252 !== recomputed) {
187
+ return {
188
+ valid: false,
189
+ reason: "hash_mismatch",
190
+ details: `embedded=${sig.cert_hash_felt252} recomputed=${recomputed}`,
191
+ recomputed_cert_hash_felt252: recomputed,
192
+ };
193
+ }
194
+ if (opts.expectedKid && opts.expectedKid !== sig.kid) {
195
+ return {
196
+ valid: false,
197
+ reason: "kid_mismatch",
198
+ details: `expected kid=${opts.expectedKid}, got ${sig.kid}`,
199
+ recomputed_cert_hash_felt252: recomputed,
200
+ };
201
+ }
202
+
203
+ let pubkey: KeyObject;
204
+ if (opts.expectedPublicKey) {
205
+ pubkey = opts.expectedPublicKey;
206
+ } else {
207
+ try {
208
+ pubkey = publicKeyFromSpkiB64(sig.pubkey_spki_b64);
209
+ } catch (err) {
210
+ return {
211
+ valid: false,
212
+ reason: "pubkey_unresolvable",
213
+ details: err instanceof Error ? err.message : String(err),
214
+ recomputed_cert_hash_felt252: recomputed,
215
+ };
216
+ }
217
+ }
218
+
219
+ let sigBytes: Buffer;
220
+ try {
221
+ sigBytes = Buffer.from(sig.value, "base64");
222
+ } catch {
223
+ return {
224
+ valid: false,
225
+ reason: "malformed_signature",
226
+ details: "signature.value is not valid base64",
227
+ recomputed_cert_hash_felt252: recomputed,
228
+ };
229
+ }
230
+ if (sigBytes.length !== 64) {
231
+ return {
232
+ valid: false,
233
+ reason: "malformed_signature",
234
+ details: `Ed25519 signature must be 64 bytes (got ${sigBytes.length})`,
235
+ recomputed_cert_hash_felt252: recomputed,
236
+ };
237
+ }
238
+
239
+ const msgBytes = felt252ToBytes(recomputed);
240
+ const ok = cryptoVerify(null, msgBytes, pubkey, sigBytes);
241
+ if (!ok) {
242
+ return {
243
+ valid: false,
244
+ reason: "signature_invalid",
245
+ recomputed_cert_hash_felt252: recomputed,
246
+ };
247
+ }
248
+ return { valid: true, recomputed_cert_hash_felt252: recomputed };
249
+ }
@@ -158,7 +158,7 @@ export interface ReplayRunner {
158
158
  export class NonDeterministicReplayError extends Error {
159
159
  constructor(
160
160
  public readonly stepIndex: number,
161
- public readonly reason: string,
161
+ public readonly reason: string
162
162
  ) {
163
163
  super(`Non-deterministic replay at step ${stepIndex}: ${reason}`);
164
164
  this.name = "NonDeterministicReplayError";
@@ -171,10 +171,7 @@ export class NonDeterministicReplayError extends Error {
171
171
  * Compute a step-level diff between two traces.
172
172
  * Returns an empty array if the traces are identical at the step level.
173
173
  */
174
- function diffTraces(
175
- original: Trace,
176
- replayed: Trace,
177
- ): ReplayResult["diff"] {
174
+ function diffTraces(original: Trace, replayed: Trace): ReplayResult["diff"] {
178
175
  const diffs: NonNullable<ReplayResult["diff"]> = [];
179
176
 
180
177
  const minLen = Math.min(original.steps.length, replayed.steps.length);
@@ -223,16 +220,22 @@ export async function replayFrom(
223
220
  runId: string,
224
221
  loader: ReplayLoader,
225
222
  runner: ReplayRunner,
226
- opts?: { mode?: ReplayMode; fromStepIndex?: number },
223
+ opts?: { mode?: ReplayMode; fromStepIndex?: number }
227
224
  ): Promise<ReplayResult> {
228
225
  const mode = opts?.mode ?? "strict";
229
226
 
230
227
  // 1. Load original artifacts
231
- const [originalTrace, artifacts] = await Promise.all([
228
+ const [rawTrace, artifacts] = await Promise.all([
232
229
  loader.loadOriginalTrace(runId),
233
230
  loader.loadCacheEntries(runId),
234
231
  ]);
235
232
 
233
+ // Auto-migrate traces recorded under the draft schema to the frozen 1.0.0
234
+ const originalTrace =
235
+ (rawTrace.schemaVersion as string) === "0.1.0-draft"
236
+ ? { ...rawTrace, schemaVersion: "1.0.0" as const }
237
+ : rawTrace;
238
+
236
239
  // 2. Import concrete implementations lazily to avoid circular references
237
240
  const { RecordedClock } = await import("./clock.js");
238
241
  const { RecordedRandom } = await import("./random.js");
@@ -241,7 +244,7 @@ export async function replayFrom(
241
244
  const random = new RecordedRandom(
242
245
  artifacts.recordedNext,
243
246
  artifacts.recordedUuids,
244
- mode,
247
+ mode
245
248
  );
246
249
 
247
250
  // 3. Build the replay context