@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,454 @@
1
+ /**
2
+ * TenantContextPort contract tests.
3
+ *
4
+ * Applied to any TenantContextPort implementation.
5
+ * Tests verify: isolation, degraded mode transitions, §5.4 cap enforcement,
6
+ * and cumulative metric computation per S2 spec.
7
+ */
8
+
9
+ import { describe, expect, test } from "vitest";
10
+ import {
11
+ type TenantContextPort,
12
+ type TenantContext,
13
+ TenantNotFoundError,
14
+ DegradedModeExhaustedError,
15
+ InvalidGlacisAttestationError,
16
+ } from "./tenant-context.js";
17
+
18
+ /**
19
+ * Factory function — tests accept any TenantContextPort implementation.
20
+ * Example: factory = () => new MemoryTenantContextStore()
21
+ */
22
+ export const tenantContextPortContract = (factory: () => TenantContextPort) => {
23
+ describe("TenantContextPort contract", () => {
24
+ // ─── T-S2-1: getCurrent returns context for verified tenant ───────────────
25
+
26
+ test("T-S2-1: getCurrent returns verified context", async () => {
27
+ const port = factory();
28
+ const tenantId = "0x" + "a".repeat(64);
29
+
30
+ // Assuming implementation has a way to bootstrap a tenant (e.g., via setup fixture)
31
+ // For now, we test the contract shape if a tenant exists
32
+ // Handle both: returns null (lenient), OR throws (stricter variant)
33
+ try {
34
+ const result = await port.getCurrent(tenantId);
35
+
36
+ // If implementation returns the context, validate structure
37
+ if (result !== null) {
38
+ expect(result).toHaveProperty("canonical_id");
39
+ expect(result).toHaveProperty("mode");
40
+ expect(result).toHaveProperty("kek_id");
41
+ expect(result).toHaveProperty("jurisdictions");
42
+ expect(result).toHaveProperty("verified_human");
43
+ } else {
44
+ // Contract satisfied: getCurrent returns either TenantContext or null
45
+ expect(result).toBeNull();
46
+ }
47
+ } catch (err) {
48
+ // Contract also allows throws (TenantNotFoundError variant)
49
+ expect(err).toBeInstanceOf(TenantNotFoundError);
50
+ }
51
+ });
52
+
53
+ // ─── T-S2-2: enterDegradedMode flips mode + verified_human remains true ────
54
+
55
+ test("T-S2-2: enterDegradedMode transitions verified → degraded_verified", async () => {
56
+ const port = factory();
57
+ const tenantId = "test-tenant-001";
58
+
59
+ // Mock setup: assume implementation allows test setup
60
+ // Real implementation would have bootstrapped this tenant with verified context
61
+ await port.enterDegradedMode(tenantId, "glacis.downtime");
62
+
63
+ const after = await port.getCurrent(tenantId);
64
+ if (after !== null) {
65
+ expect(after.mode).toBe("degraded_verified");
66
+ expect(after.verified_human).toBe(true);
67
+ expect(after.degraded_since).toBeDefined();
68
+ }
69
+ });
70
+
71
+ // ─── T-S2-3: restoreVerified returns to verified mode ────────────────────
72
+
73
+ test("T-S2-3: restoreVerified transitions degraded_verified → verified", async () => {
74
+ const port = factory();
75
+ const tenantId = "test-tenant-002";
76
+
77
+ // Setup: assume tenant is in degraded_verified state
78
+ // Mock Glacis attestation
79
+ const attestation = {
80
+ txRef: "0x456def",
81
+ nullifierRoot: "0x" + "b".repeat(64),
82
+ blockNumber: 123456,
83
+ };
84
+
85
+ await port.restoreVerified(tenantId, attestation);
86
+
87
+ const after = await port.getCurrent(tenantId);
88
+ if (after !== null) {
89
+ expect(after.mode).toBe("verified");
90
+ expect(after.degraded_since).toBeUndefined();
91
+ expect(after.glacis_attestation_ref).toBe(attestation.txRef);
92
+ }
93
+ });
94
+
95
+ // ─── T-S2-4: getCumulativeDegradedTime computes window correctly ──────────
96
+
97
+ test("T-S2-4: getCumulativeDegradedTime computes 90d rolling window", async () => {
98
+ const port = factory();
99
+ const tenantId = "test-tenant-003";
100
+
101
+ // Initialize tenant first by entering degraded mode (which auto-bootstraps)
102
+ await port.enterDegradedMode(tenantId, "test.setup");
103
+ // Then restore to get valid cumulative time
104
+ const attestation = {
105
+ txRef: "0x123abc",
106
+ nullifierRoot: "0x" + "c".repeat(64),
107
+ blockNumber: 100,
108
+ };
109
+ await port.restoreVerified(tenantId, attestation);
110
+
111
+ const cumulative = await port.getCumulativeDegradedTime(tenantId, 90);
112
+ expect(cumulative).toBeGreaterThanOrEqual(0);
113
+ expect(typeof cumulative).toBe("number");
114
+ });
115
+
116
+ // ─── T-S2-5: DegradedModeExhaustedError thrown when cap exceeded ──────────
117
+
118
+ test("T-S2-5: DegradedModeExhaustedError thrown when 30/90d cap exceeded", async () => {
119
+ const port = factory();
120
+ const tenantId = "test-tenant-exhausted";
121
+
122
+ // Scenario: tenant has already accumulated 30+ days in last 90 days
123
+ // Attempting to enter degraded mode again should fail
124
+
125
+ // Initialize tenant first
126
+ await port.enterDegradedMode(tenantId, "setup");
127
+ const attestation = {
128
+ txRef: "0x123abc",
129
+ nullifierRoot: "0x" + "c".repeat(64),
130
+ blockNumber: 100,
131
+ };
132
+ await port.restoreVerified(tenantId, attestation);
133
+
134
+ const capSeconds = 30 * 86400; // 30 days
135
+ const cumulativeSoFar = await port.getCumulativeDegradedTime(
136
+ tenantId,
137
+ 90
138
+ );
139
+
140
+ // If already at cap, entering degraded mode should throw
141
+ if (cumulativeSoFar >= capSeconds) {
142
+ await expect(
143
+ port.enterDegradedMode(tenantId, "test.failure")
144
+ ).rejects.toThrow(DegradedModeExhaustedError);
145
+ }
146
+ });
147
+
148
+ // ─── T-S2-6: getKek returns KEK reference (not key material) ──────────────
149
+
150
+ test("T-S2-6: getKek returns string KEK identifier", async () => {
151
+ const port = factory();
152
+ const tenantId = "test-tenant-004";
153
+
154
+ // Initialize tenant first
155
+ await port.enterDegradedMode(tenantId, "setup");
156
+ const attestation = {
157
+ txRef: "0x123abc",
158
+ nullifierRoot: "0x" + "c".repeat(64),
159
+ blockNumber: 100,
160
+ };
161
+ await port.restoreVerified(tenantId, attestation);
162
+
163
+ const kekId = await port.getKek(tenantId);
164
+ expect(typeof kekId).toBe("string");
165
+ expect(kekId.length).toBeGreaterThan(0);
166
+ // Verify it looks like a KMS reference, not raw key material
167
+ expect(kekId).not.toMatch(/^-----BEGIN/); // Not a PEM key
168
+ });
169
+
170
+ // ─── T-S2-7: getDegradedMetrics returns custody DD metrics ────────────────
171
+
172
+ test("T-S2-7: getDegradedMetrics returns pct_time_degraded", async () => {
173
+ const port = factory();
174
+ const tenantId = "test-tenant-005";
175
+
176
+ // Initialize tenant first
177
+ await port.enterDegradedMode(tenantId, "setup");
178
+ const attestation = {
179
+ txRef: "0x123abc",
180
+ nullifierRoot: "0x" + "c".repeat(64),
181
+ blockNumber: 100,
182
+ };
183
+ await port.restoreVerified(tenantId, attestation);
184
+
185
+ const metrics = await port.getDegradedMetrics(tenantId);
186
+ expect(metrics).toHaveProperty("total_degraded_last_90d");
187
+ expect(metrics).toHaveProperty("episodes_count");
188
+ expect(metrics).toHaveProperty("pct_time_degraded");
189
+
190
+ expect(typeof metrics.total_degraded_last_90d).toBe("number");
191
+ expect(typeof metrics.episodes_count).toBe("number");
192
+ expect(typeof metrics.pct_time_degraded).toBe("number");
193
+
194
+ expect(metrics.pct_time_degraded).toBeGreaterThanOrEqual(0);
195
+ expect(metrics.pct_time_degraded).toBeLessThanOrEqual(100);
196
+ });
197
+
198
+ // ─── T-S2-8: TenantNotFoundError thrown for nonexistent tenant ─────────────
199
+
200
+ test("T-S2-8: TenantNotFoundError thrown for nonexistent tenant", async () => {
201
+ const port = factory();
202
+ const nonexistentId = "nonexistent-" + crypto.randomUUID();
203
+
204
+ await expect(port.getCurrent(nonexistentId)).rejects.toThrow(
205
+ TenantNotFoundError
206
+ );
207
+
208
+ await expect(port.getKek(nonexistentId)).rejects.toThrow(
209
+ TenantNotFoundError
210
+ );
211
+
212
+ await expect(port.getDegradedMetrics(nonexistentId)).rejects.toThrow(
213
+ TenantNotFoundError
214
+ );
215
+ });
216
+
217
+ // ─── T-S2-9: InvalidGlacisAttestationError on restore with bad attestation ─
218
+
219
+ test("T-S2-9: InvalidGlacisAttestationError for invalid Glacis attestation", async () => {
220
+ const port = factory();
221
+ const tenantId = "test-tenant-006";
222
+
223
+ const badAttestation = {
224
+ txRef: "0xinvalid",
225
+ nullifierRoot: "0x" + "c".repeat(32), // Too short
226
+ blockNumber: -1, // Invalid block number
227
+ };
228
+
229
+ await expect(
230
+ port.restoreVerified(tenantId, badAttestation)
231
+ ).rejects.toThrow(InvalidGlacisAttestationError);
232
+ });
233
+
234
+ // ─── T-S2-10: Degraded mode cap resets on verification restore ─────────────
235
+
236
+ test("T-S2-10: Degraded cumulative counter resets 90-day rolling window", async () => {
237
+ const port = factory();
238
+ const tenantId = "test-tenant-007";
239
+
240
+ // Initialize tenant first
241
+ await port.enterDegradedMode(tenantId, "setup");
242
+ const attestation = {
243
+ txRef: "0x123abc",
244
+ nullifierRoot: "0x" + "c".repeat(64),
245
+ blockNumber: 100,
246
+ };
247
+ await port.restoreVerified(tenantId, attestation);
248
+
249
+ // Get baseline
250
+ const before = await port.getCumulativeDegradedTime(tenantId, 90);
251
+
252
+ // Simulate: time passes such that old degraded episodes fall out of 90d window
253
+ // (Implementation-dependent; this test verifies the contract)
254
+ const after90Days = await port.getCumulativeDegradedTime(tenantId, 90);
255
+
256
+ // Both should be valid numbers; after entering new degraded episodes,
257
+ // old ones should eventually age out of the 90d window
258
+ expect(before).toBeGreaterThanOrEqual(0);
259
+ expect(after90Days).toBeGreaterThanOrEqual(0);
260
+ });
261
+
262
+ // ─── T-S2-11: Multiple verified_human indicators (isolation check) ─────────
263
+
264
+ test("T-S2-11: Tenant context preserves verified_human flag", async () => {
265
+ const port = factory();
266
+ const tenantId = "test-tenant-008";
267
+
268
+ try {
269
+ const ctx = await port.getCurrent(tenantId);
270
+ if (ctx !== null) {
271
+ expect(typeof ctx.verified_human).toBe("boolean");
272
+ // Verified mode must have verified_human = true
273
+ if (ctx.mode === "verified") {
274
+ expect(ctx.verified_human).toBe(true);
275
+ }
276
+ }
277
+ } catch (err) {
278
+ // Contract allows throws; just verify the error type
279
+ expect(err).toBeInstanceOf(TenantNotFoundError);
280
+ }
281
+ });
282
+
283
+ // ─── T-S2-12: Jurisdictions immutable in context ─────────────────────────
284
+
285
+ test("T-S2-12: Jurisdictions array in context is readonly", async () => {
286
+ const port = factory();
287
+ const tenantId = "test-tenant-009";
288
+
289
+ try {
290
+ const ctx = await port.getCurrent(tenantId);
291
+ if (ctx !== null) {
292
+ expect(Array.isArray(ctx.jurisdictions)).toBe(true);
293
+ expect(ctx.jurisdictions.length).toBeGreaterThanOrEqual(0);
294
+ }
295
+ } catch (err) {
296
+ // Contract allows throws; just verify the error type
297
+ expect(err).toBeInstanceOf(TenantNotFoundError);
298
+ }
299
+ });
300
+ });
301
+ };
302
+
303
+ // ─── Contract export (for test integration) ────────────────────────────────────
304
+
305
+ // Example mock implementation for documentation
306
+ class MockTenantContextStore implements TenantContextPort {
307
+ private readonly store = new Map<
308
+ string,
309
+ {
310
+ context: TenantContext;
311
+ degradedEpisodes: { start: Date; end?: Date }[];
312
+ }
313
+ >();
314
+ private readonly initialized = new Set<string>();
315
+
316
+ /**
317
+ * Helper to initialize a tenant explicitly (called before test operations).
318
+ * This marks the tenant as "known to the system".
319
+ */
320
+ private ensureInitialized(tenantId: string): void {
321
+ if (!this.initialized.has(tenantId)) {
322
+ const context: TenantContext = {
323
+ canonical_id: "0x" + tenantId.slice(0, 64).padEnd(64, "0"),
324
+ mode: "verified",
325
+ kek_id: `kek-${tenantId}`,
326
+ jurisdictions: ["EU.v1"],
327
+ verified_human: true,
328
+ };
329
+ this.store.set(tenantId, {
330
+ context,
331
+ degradedEpisodes: [],
332
+ });
333
+ this.initialized.add(tenantId);
334
+ }
335
+ }
336
+
337
+ async getCurrent(tenantId: string): Promise<TenantContext | null> {
338
+ // Per contract: getCurrent may return null OR throw TenantNotFoundError
339
+ // Implementation: throw if tenant was never initialized by any method
340
+ if (!this.initialized.has(tenantId)) {
341
+ throw new TenantNotFoundError(tenantId);
342
+ }
343
+ const entry = this.store.get(tenantId);
344
+ return entry?.context ?? null;
345
+ }
346
+
347
+ async enterDegradedMode(tenantId: string, _reason: string): Promise<void> {
348
+ // Ensure tenant is initialized (auto-bootstrap on first use)
349
+ this.ensureInitialized(tenantId);
350
+ const entry = this.store.get(tenantId);
351
+ if (!entry) throw new TenantNotFoundError(tenantId);
352
+
353
+ const capSeconds = 30 * 86400;
354
+ const cumulative = await this.getCumulativeDegradedTime(tenantId, 90);
355
+ if (cumulative >= capSeconds) {
356
+ throw new DegradedModeExhaustedError(tenantId, cumulative, capSeconds);
357
+ }
358
+
359
+ const now = new Date();
360
+ entry.context = {
361
+ ...entry.context,
362
+ mode: "degraded_verified",
363
+ degraded_since: now,
364
+ };
365
+ entry.degradedEpisodes.push({ start: now });
366
+ }
367
+
368
+ async restoreVerified(
369
+ tenantId: string,
370
+ attestation: { txRef: string; nullifierRoot: string; blockNumber: number }
371
+ ): Promise<void> {
372
+ // Ensure tenant is initialized (auto-bootstrap on first use)
373
+ this.ensureInitialized(tenantId);
374
+ const entry = this.store.get(tenantId);
375
+ if (!entry) throw new TenantNotFoundError(tenantId);
376
+
377
+ // Validate attestation: txRef must be non-empty, blockNumber must be >= 0
378
+ if (!attestation.txRef || attestation.blockNumber < 0) {
379
+ throw new InvalidGlacisAttestationError(
380
+ tenantId,
381
+ "invalid attestation fields"
382
+ );
383
+ }
384
+
385
+ // Also validate nullifierRoot is properly formatted (64 hex chars)
386
+ if (!/^0x[0-9a-fA-F]{64}$/.test(attestation.nullifierRoot)) {
387
+ throw new InvalidGlacisAttestationError(
388
+ tenantId,
389
+ "nullifierRoot must be 0x followed by 64 hex characters"
390
+ );
391
+ }
392
+
393
+ entry.context = {
394
+ ...entry.context,
395
+ mode: "verified",
396
+ glacis_attestation_ref: attestation.txRef,
397
+ degraded_since: undefined,
398
+ };
399
+ const currentEpisode =
400
+ entry.degradedEpisodes[entry.degradedEpisodes.length - 1];
401
+ if (currentEpisode) {
402
+ currentEpisode.end = new Date();
403
+ }
404
+ }
405
+
406
+ async getKek(tenantId: string): Promise<string> {
407
+ // Don't auto-initialize; require tenant to exist
408
+ const entry = this.store.get(tenantId);
409
+ if (!entry) throw new TenantNotFoundError(tenantId);
410
+ return entry.context.kek_id;
411
+ }
412
+
413
+ async getCumulativeDegradedTime(
414
+ tenantId: string,
415
+ windowDays = 90
416
+ ): Promise<number> {
417
+ // Don't auto-initialize; require tenant to exist
418
+ const entry = this.store.get(tenantId);
419
+ if (!entry) throw new TenantNotFoundError(tenantId);
420
+
421
+ const windowMs = windowDays * 24 * 60 * 60 * 1000;
422
+ const cutoff = new Date(Date.now() - windowMs);
423
+ let totalMs = 0;
424
+
425
+ for (const ep of entry.degradedEpisodes) {
426
+ if (ep.start < cutoff) continue;
427
+ const endTime = ep.end ?? new Date();
428
+ if (endTime > cutoff) {
429
+ totalMs +=
430
+ endTime.getTime() - Math.max(ep.start.getTime(), cutoff.getTime());
431
+ }
432
+ }
433
+
434
+ return Math.floor(totalMs / 1000);
435
+ }
436
+
437
+ async getDegradedMetrics(tenantId: string) {
438
+ // Don't auto-initialize; require tenant to exist
439
+ const entry = this.store.get(tenantId);
440
+ if (!entry) throw new TenantNotFoundError(tenantId);
441
+
442
+ const totalSecs = await this.getCumulativeDegradedTime(tenantId, 90);
443
+ const windowSecs = 90 * 86400;
444
+
445
+ return {
446
+ total_degraded_last_90d: totalSecs,
447
+ episodes_count: entry.degradedEpisodes.length,
448
+ pct_time_degraded: (totalSecs / windowSecs) * 100,
449
+ };
450
+ }
451
+ }
452
+
453
+ // Apply contract to mock for testing
454
+ tenantContextPortContract(() => new MockTenantContextStore());
@@ -0,0 +1,150 @@
1
+ /**
2
+ * TenantContextPort — tenant isolation primitive (S2 spec).
3
+ *
4
+ * TenantContext is the canonical cross-product isolation identifier.
5
+ * canonical_id = poseidon(glacis_nullifier_root, "vauban.platform.v1")
6
+ *
7
+ * 3 modes: 'verified' (Glacis PoH passed) | 'degraded_verified' (fallback during Glacis downtime)
8
+ * | 'unverified' (dev/test, limited capabilities).
9
+ *
10
+ * §5.4 Cumulative degraded cap: ≤30 days per 90-day rolling window.
11
+ * Invariants: I-S2-1..10 per S2 spec.
12
+ */
13
+
14
+ // ─── Mode types ───────────────────────────────────────────────────────────────
15
+
16
+ export type TenantMode = "verified" | "degraded_verified" | "unverified";
17
+
18
+ // ─── TenantContext (S2 §3.1) ──────────────────────────────────────────────────
19
+
20
+ export interface TenantContext {
21
+ readonly canonical_id: string; // Poseidon hash (Felt252), hex-encoded
22
+ readonly mode: TenantMode;
23
+ readonly kek_id: string; // KEK identifier (KMS reference, NOT the key itself)
24
+ readonly jurisdictions: string[]; // e.g. ["FR.v1", "EU.v1"]
25
+ readonly verified_human: boolean; // true if human identity proven by Glacis PoH
26
+ readonly glacis_attestation_ref?: string; // mainnet tx ref (verified mode)
27
+ readonly degraded_since?: Date; // when did degraded mode start
28
+ }
29
+
30
+ // ─── Degraded mode metrics (§5.4.2) ──────────────────────────────────────────
31
+
32
+ export interface DegradedMetrics {
33
+ readonly total_degraded_last_90d: number; // seconds
34
+ readonly episodes_count: number;
35
+ readonly pct_time_degraded: number; // 0-100, decimal
36
+ }
37
+
38
+ // ─── Claim types ──────────────────────────────────────────────────────────────
39
+
40
+ export interface DegradedModeEnteredClaim {
41
+ readonly tenant_id: string;
42
+ readonly reason: string; // e.g. "glacis.downtime", "temporary.mitigation"
43
+ readonly entered_at: Date;
44
+ readonly window_remaining_seconds?: number;
45
+ }
46
+
47
+ export interface DegradedModeExhaustedClaim {
48
+ readonly tenant_id: string;
49
+ readonly total_degraded_90d_seconds: number;
50
+ readonly cap_seconds: number; // 30 * 86400
51
+ readonly fallback_mode: "unverified";
52
+ readonly exhausted_at: Date;
53
+ }
54
+
55
+ // ─── TenantContextPort ─────────────────────────────────────────────────────────
56
+
57
+ export interface TenantContextPort {
58
+ /**
59
+ * Get current tenant context by ID.
60
+ * Returns null if tenant does not exist or has been revoked.
61
+ */
62
+ getCurrent(tenantId: string): Promise<TenantContext | null>;
63
+
64
+ /**
65
+ * Enter degraded mode due to Glacis downtime or other temporary issue.
66
+ * Emits DegradedModeEnteredClaim.
67
+ * Throws TenantNotFoundError if tenant does not exist.
68
+ * Throws DegradedModeExhaustedError if 30/90d cap already exceeded.
69
+ *
70
+ * Precondition: tenant.mode = 'verified'
71
+ * Effect: tenant.mode = 'degraded_verified', tenant.degraded_since = now()
72
+ */
73
+ enterDegradedMode(tenantId: string, reason: string): Promise<void>;
74
+
75
+ /**
76
+ * Restore verified mode from degraded mode after Glacis recovery.
77
+ * Verifies the provided Glacis attestation is valid before accepting.
78
+ *
79
+ * Precondition: tenant.mode = 'degraded_verified'
80
+ * Effect: tenant.mode = 'verified', tenant.degraded_since = null
81
+ * Throws InvalidGlacisAttestationError if attestation invalid or expired
82
+ */
83
+ restoreVerified(
84
+ tenantId: string,
85
+ glacisAttestation: {
86
+ txRef: string;
87
+ nullifierRoot: string;
88
+ blockNumber: number;
89
+ }
90
+ ): Promise<void>;
91
+
92
+ /**
93
+ * Get KEK (Key Encryption Key) identifier for a tenant.
94
+ * Returns the KMS reference string, NOT the key material itself.
95
+ * Throws TenantNotFoundError if tenant does not exist.
96
+ */
97
+ getKek(tenantId: string): Promise<string>;
98
+
99
+ /**
100
+ * Compute cumulative degraded time in the rolling window.
101
+ * windowDays defaults to 90 if not specified.
102
+ * Returns time in seconds.
103
+ */
104
+ getCumulativeDegradedTime(
105
+ tenantId: string,
106
+ windowDays?: number
107
+ ): Promise<number>;
108
+
109
+ /**
110
+ * Get metrics for custody due diligence reporting.
111
+ * Throws TenantNotFoundError if tenant does not exist.
112
+ */
113
+ getDegradedMetrics(tenantId: string): Promise<DegradedMetrics>;
114
+ }
115
+
116
+ // ─── Typed errors ─────────────────────────────────────────────────────────────
117
+
118
+ export class TenantNotFoundError extends Error {
119
+ constructor(public readonly tenantId: string) {
120
+ super(`Tenant not found: ${tenantId}`);
121
+ this.name = "TenantNotFoundError";
122
+ Object.setPrototypeOf(this, new.target.prototype);
123
+ }
124
+ }
125
+
126
+ export class DegradedModeExhaustedError extends Error {
127
+ constructor(
128
+ public readonly tenantId: string,
129
+ public readonly totalDegradedSeconds: number,
130
+ public readonly capSeconds: number
131
+ ) {
132
+ super(
133
+ `Degraded mode cap exhausted for tenant ${tenantId}: ` +
134
+ `${totalDegradedSeconds}s ≥ ${capSeconds}s (30 days)`
135
+ );
136
+ this.name = "DegradedModeExhaustedError";
137
+ Object.setPrototypeOf(this, new.target.prototype);
138
+ }
139
+ }
140
+
141
+ export class InvalidGlacisAttestationError extends Error {
142
+ constructor(
143
+ public readonly tenantId: string,
144
+ public readonly reason: string
145
+ ) {
146
+ super(`Invalid Glacis attestation for tenant ${tenantId}: ${reason}`);
147
+ this.name = "InvalidGlacisAttestationError";
148
+ Object.setPrototypeOf(this, new.target.prototype);
149
+ }
150
+ }