@vauban-org/agent-sdk 1.0.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (513) hide show
  1. package/CONTRACT.md +6918 -742
  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 +41 -3
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +31 -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/agent.d.ts.map +1 -1
  98. package/dist/orchestration/ooda/agent.js +36 -0
  99. package/dist/orchestration/ooda/agent.js.map +1 -1
  100. package/dist/orchestration/ooda/skills.d.ts +104 -0
  101. package/dist/orchestration/ooda/skills.d.ts.map +1 -1
  102. package/dist/orchestration/ooda/skills.js +106 -0
  103. package/dist/orchestration/ooda/skills.js.map +1 -1
  104. package/dist/orchestration/ooda/types.d.ts +11 -0
  105. package/dist/orchestration/ooda/types.d.ts.map +1 -1
  106. package/dist/ports/bastion-action.contract.test.d.ts +11 -0
  107. package/dist/ports/bastion-action.contract.test.d.ts.map +1 -0
  108. package/dist/ports/bastion-action.contract.test.js +238 -0
  109. package/dist/ports/bastion-action.contract.test.js.map +1 -0
  110. package/dist/ports/bastion-action.d.ts +133 -0
  111. package/dist/ports/bastion-action.d.ts.map +1 -0
  112. package/dist/ports/bastion-action.js +73 -0
  113. package/dist/ports/bastion-action.js.map +1 -0
  114. package/dist/ports/brain.d.ts +31 -0
  115. package/dist/ports/brain.d.ts.map +1 -1
  116. package/dist/ports/brain.js +115 -1
  117. package/dist/ports/brain.js.map +1 -1
  118. package/dist/ports/citadel-action.contract.test.d.ts +11 -0
  119. package/dist/ports/citadel-action.contract.test.d.ts.map +1 -0
  120. package/dist/ports/citadel-action.contract.test.js +317 -0
  121. package/dist/ports/citadel-action.contract.test.js.map +1 -0
  122. package/dist/ports/citadel-action.d.ts +111 -0
  123. package/dist/ports/citadel-action.d.ts.map +1 -0
  124. package/dist/ports/citadel-action.js +62 -0
  125. package/dist/ports/citadel-action.js.map +1 -0
  126. package/dist/ports/compliance-contract.d.ts +123 -0
  127. package/dist/ports/compliance-contract.d.ts.map +1 -0
  128. package/dist/ports/compliance-contract.js +35 -0
  129. package/dist/ports/compliance-contract.js.map +1 -0
  130. package/dist/ports/db.d.ts +38 -0
  131. package/dist/ports/db.d.ts.map +1 -1
  132. package/dist/ports/db.js +88 -1
  133. package/dist/ports/db.js.map +1 -1
  134. package/dist/ports/delegation.contract.test.d.ts +9 -0
  135. package/dist/ports/delegation.contract.test.d.ts.map +1 -0
  136. package/dist/ports/delegation.contract.test.js +337 -0
  137. package/dist/ports/delegation.contract.test.js.map +1 -0
  138. package/dist/ports/delegation.d.ts +134 -0
  139. package/dist/ports/delegation.d.ts.map +1 -0
  140. package/dist/ports/delegation.js +105 -0
  141. package/dist/ports/delegation.js.map +1 -0
  142. package/dist/ports/event-bus.d.ts +29 -0
  143. package/dist/ports/event-bus.d.ts.map +1 -1
  144. package/dist/ports/event-bus.js +106 -1
  145. package/dist/ports/event-bus.js.map +1 -1
  146. package/dist/ports/federation.contract.test.d.ts +9 -0
  147. package/dist/ports/federation.contract.test.d.ts.map +1 -0
  148. package/dist/ports/federation.contract.test.js +279 -0
  149. package/dist/ports/federation.contract.test.js.map +1 -0
  150. package/dist/ports/federation.d.ts +140 -0
  151. package/dist/ports/federation.d.ts.map +1 -0
  152. package/dist/ports/federation.js +57 -0
  153. package/dist/ports/federation.js.map +1 -0
  154. package/dist/ports/index.d.ts +28 -2
  155. package/dist/ports/index.d.ts.map +1 -1
  156. package/dist/ports/index.js +17 -2
  157. package/dist/ports/index.js.map +1 -1
  158. package/dist/ports/llm-provider.d.ts +37 -0
  159. package/dist/ports/llm-provider.d.ts.map +1 -1
  160. package/dist/ports/llm-provider.js +99 -1
  161. package/dist/ports/llm-provider.js.map +1 -1
  162. package/dist/ports/logger.d.ts +27 -0
  163. package/dist/ports/logger.d.ts.map +1 -1
  164. package/dist/ports/logger.js +87 -0
  165. package/dist/ports/logger.js.map +1 -1
  166. package/dist/ports/manifest-registry.contract.test.d.ts +9 -0
  167. package/dist/ports/manifest-registry.contract.test.d.ts.map +1 -0
  168. package/dist/ports/manifest-registry.contract.test.js +246 -0
  169. package/dist/ports/manifest-registry.contract.test.js.map +1 -0
  170. package/dist/ports/manifest-registry.d.ts +116 -0
  171. package/dist/ports/manifest-registry.d.ts.map +1 -0
  172. package/dist/ports/manifest-registry.js +79 -0
  173. package/dist/ports/manifest-registry.js.map +1 -0
  174. package/dist/ports/observability.contract.test.d.ts +12 -0
  175. package/dist/ports/observability.contract.test.d.ts.map +1 -0
  176. package/dist/ports/observability.contract.test.js +260 -0
  177. package/dist/ports/observability.contract.test.js.map +1 -0
  178. package/dist/ports/observability.d.ts +98 -0
  179. package/dist/ports/observability.d.ts.map +1 -0
  180. package/dist/ports/observability.js +59 -0
  181. package/dist/ports/observability.js.map +1 -0
  182. package/dist/ports/outcome.d.ts +26 -0
  183. package/dist/ports/outcome.d.ts.map +1 -1
  184. package/dist/ports/outcome.js +62 -1
  185. package/dist/ports/outcome.js.map +1 -1
  186. package/dist/ports/privacy.contract.test.d.ts +12 -0
  187. package/dist/ports/privacy.contract.test.d.ts.map +1 -0
  188. package/dist/ports/privacy.contract.test.js +325 -0
  189. package/dist/ports/privacy.contract.test.js.map +1 -0
  190. package/dist/ports/privacy.d.ts +132 -0
  191. package/dist/ports/privacy.d.ts.map +1 -0
  192. package/dist/ports/privacy.js +83 -0
  193. package/dist/ports/privacy.js.map +1 -0
  194. package/dist/ports/tenant-context.contract.test.d.ts +14 -0
  195. package/dist/ports/tenant-context.contract.test.d.ts.map +1 -0
  196. package/dist/ports/tenant-context.contract.test.js +352 -0
  197. package/dist/ports/tenant-context.contract.test.js.map +1 -0
  198. package/dist/ports/tenant-context.d.ts +103 -0
  199. package/dist/ports/tenant-context.d.ts.map +1 -0
  200. package/dist/ports/tenant-context.js +48 -0
  201. package/dist/ports/tenant-context.js.map +1 -0
  202. package/dist/ports/vauban-finance-action.contract.test.d.ts +11 -0
  203. package/dist/ports/vauban-finance-action.contract.test.d.ts.map +1 -0
  204. package/dist/ports/vauban-finance-action.contract.test.js +260 -0
  205. package/dist/ports/vauban-finance-action.contract.test.js.map +1 -0
  206. package/dist/ports/vauban-finance-action.d.ts +106 -0
  207. package/dist/ports/vauban-finance-action.d.ts.map +1 -0
  208. package/dist/ports/vauban-finance-action.js +60 -0
  209. package/dist/ports/vauban-finance-action.js.map +1 -0
  210. package/dist/ports/workflow-runtime.d.ts +204 -0
  211. package/dist/ports/workflow-runtime.d.ts.map +1 -0
  212. package/dist/ports/workflow-runtime.js +72 -0
  213. package/dist/ports/workflow-runtime.js.map +1 -0
  214. package/dist/proof/cert-verify.d.ts +80 -0
  215. package/dist/proof/cert-verify.d.ts.map +1 -0
  216. package/dist/proof/cert-verify.js +178 -0
  217. package/dist/proof/cert-verify.js.map +1 -0
  218. package/dist/replay/replay.d.ts.map +1 -1
  219. package/dist/replay/replay.js +5 -1
  220. package/dist/replay/replay.js.map +1 -1
  221. package/dist/retry/index.d.ts +129 -0
  222. package/dist/retry/index.d.ts.map +1 -0
  223. package/dist/retry/index.js +156 -0
  224. package/dist/retry/index.js.map +1 -0
  225. package/dist/retry/presets.d.ts +39 -0
  226. package/dist/retry/presets.d.ts.map +1 -0
  227. package/dist/retry/presets.js +69 -0
  228. package/dist/retry/presets.js.map +1 -0
  229. package/dist/skill-loop/ab-runner.d.ts +67 -0
  230. package/dist/skill-loop/ab-runner.d.ts.map +1 -0
  231. package/dist/skill-loop/ab-runner.js +160 -0
  232. package/dist/skill-loop/ab-runner.js.map +1 -0
  233. package/dist/skill-loop/adoption.d.ts +67 -0
  234. package/dist/skill-loop/adoption.d.ts.map +1 -0
  235. package/dist/skill-loop/adoption.js +126 -0
  236. package/dist/skill-loop/adoption.js.map +1 -0
  237. package/dist/skill-loop/candidate.d.ts +45 -0
  238. package/dist/skill-loop/candidate.d.ts.map +1 -0
  239. package/dist/skill-loop/candidate.js +43 -0
  240. package/dist/skill-loop/candidate.js.map +1 -0
  241. package/dist/skill-loop/evaluator.d.ts +42 -0
  242. package/dist/skill-loop/evaluator.d.ts.map +1 -0
  243. package/dist/skill-loop/evaluator.js +184 -0
  244. package/dist/skill-loop/evaluator.js.map +1 -0
  245. package/dist/skill-loop/index.d.ts +27 -0
  246. package/dist/skill-loop/index.d.ts.map +1 -0
  247. package/dist/skill-loop/index.js +27 -0
  248. package/dist/skill-loop/index.js.map +1 -0
  249. package/dist/skill-loop/reflexion-replay.d.ts +87 -0
  250. package/dist/skill-loop/reflexion-replay.d.ts.map +1 -0
  251. package/dist/skill-loop/reflexion-replay.js +110 -0
  252. package/dist/skill-loop/reflexion-replay.js.map +1 -0
  253. package/dist/skill-loop/sign-off.d.ts +88 -0
  254. package/dist/skill-loop/sign-off.d.ts.map +1 -0
  255. package/dist/skill-loop/sign-off.js +146 -0
  256. package/dist/skill-loop/sign-off.js.map +1 -0
  257. package/dist/skill-loop/value-metric.d.ts +55 -0
  258. package/dist/skill-loop/value-metric.d.ts.map +1 -0
  259. package/dist/skill-loop/value-metric.js +69 -0
  260. package/dist/skill-loop/value-metric.js.map +1 -0
  261. package/dist/skill-loop/versioning.d.ts +36 -0
  262. package/dist/skill-loop/versioning.d.ts.map +1 -0
  263. package/dist/skill-loop/versioning.js +47 -0
  264. package/dist/skill-loop/versioning.js.map +1 -0
  265. package/dist/skill-manifest/anchor.d.ts +91 -0
  266. package/dist/skill-manifest/anchor.d.ts.map +1 -0
  267. package/dist/skill-manifest/anchor.js +331 -0
  268. package/dist/skill-manifest/anchor.js.map +1 -0
  269. package/dist/skill-manifest/builder.d.ts +47 -0
  270. package/dist/skill-manifest/builder.d.ts.map +1 -0
  271. package/dist/skill-manifest/builder.js +93 -0
  272. package/dist/skill-manifest/builder.js.map +1 -0
  273. package/dist/skill-manifest/index.d.ts +13 -0
  274. package/dist/skill-manifest/index.d.ts.map +1 -0
  275. package/dist/skill-manifest/index.js +9 -0
  276. package/dist/skill-manifest/index.js.map +1 -0
  277. package/dist/skill-manifest/types.d.ts +67 -0
  278. package/dist/skill-manifest/types.d.ts.map +1 -0
  279. package/dist/skill-manifest/types.js +16 -0
  280. package/dist/skill-manifest/types.js.map +1 -0
  281. package/dist/skill-manifest/verifier.d.ts +42 -0
  282. package/dist/skill-manifest/verifier.d.ts.map +1 -0
  283. package/dist/skill-manifest/verifier.js +136 -0
  284. package/dist/skill-manifest/verifier.js.map +1 -0
  285. package/dist/skills/_secrets.d.ts +16 -0
  286. package/dist/skills/_secrets.d.ts.map +1 -0
  287. package/dist/skills/_secrets.js +20 -0
  288. package/dist/skills/_secrets.js.map +1 -0
  289. package/dist/skills/alpaca-quote.d.ts +2 -2
  290. package/dist/skills/alpaca-quote.d.ts.map +1 -1
  291. package/dist/skills/alpaca-quote.js +51 -20
  292. package/dist/skills/alpaca-quote.js.map +1 -1
  293. package/dist/skills/brain-query.d.ts +4 -4
  294. package/dist/skills/brain-store.d.ts +6 -6
  295. package/dist/skills/errors.d.ts +15 -0
  296. package/dist/skills/errors.d.ts.map +1 -1
  297. package/dist/skills/errors.js +21 -0
  298. package/dist/skills/errors.js.map +1 -1
  299. package/dist/skills/hitl-request.d.ts +2 -2
  300. package/dist/skills/index.d.ts +3 -1
  301. package/dist/skills/index.d.ts.map +1 -1
  302. package/dist/skills/index.js +4 -1
  303. package/dist/skills/index.js.map +1 -1
  304. package/dist/skills/markdown/loader.d.ts +52 -0
  305. package/dist/skills/markdown/loader.d.ts.map +1 -0
  306. package/dist/skills/markdown/loader.js +93 -0
  307. package/dist/skills/markdown/loader.js.map +1 -0
  308. package/dist/skills/markdown/schema.d.ts +432 -0
  309. package/dist/skills/markdown/schema.d.ts.map +1 -0
  310. package/dist/skills/markdown/schema.js +121 -0
  311. package/dist/skills/markdown/schema.js.map +1 -0
  312. package/dist/skills/poc-md-loader/markdown-loader.d.ts +77 -0
  313. package/dist/skills/poc-md-loader/markdown-loader.d.ts.map +1 -0
  314. package/dist/skills/poc-md-loader/markdown-loader.js +125 -0
  315. package/dist/skills/poc-md-loader/markdown-loader.js.map +1 -0
  316. package/dist/skills/poc-md-loader/runner.d.ts +24 -0
  317. package/dist/skills/poc-md-loader/runner.d.ts.map +1 -0
  318. package/dist/skills/poc-md-loader/runner.js +57 -0
  319. package/dist/skills/poc-md-loader/runner.js.map +1 -0
  320. package/dist/skills/poc-md-loader/vitest.poc.config.d.ts +3 -0
  321. package/dist/skills/poc-md-loader/vitest.poc.config.d.ts.map +1 -0
  322. package/dist/skills/poc-md-loader/vitest.poc.config.js +13 -0
  323. package/dist/skills/poc-md-loader/vitest.poc.config.js.map +1 -0
  324. package/dist/skills/poc-md-loader/web-search/script.d.ts +33 -0
  325. package/dist/skills/poc-md-loader/web-search/script.d.ts.map +1 -0
  326. package/dist/skills/poc-md-loader/web-search/script.js +75 -0
  327. package/dist/skills/poc-md-loader/web-search/script.js.map +1 -0
  328. package/dist/skills/record-outcome.d.ts +4 -4
  329. package/dist/skills/send-email.d.ts +2 -2
  330. package/dist/skills/send-email.d.ts.map +1 -1
  331. package/dist/skills/send-email.js +4 -3
  332. package/dist/skills/send-email.js.map +1 -1
  333. package/dist/skills/slack-notify.d.ts +4 -4
  334. package/dist/skills/slack-notify.d.ts.map +1 -1
  335. package/dist/skills/slack-notify.js +52 -21
  336. package/dist/skills/slack-notify.js.map +1 -1
  337. package/dist/skills/starknet-balance.d.ts +1 -1
  338. package/dist/skills/telegram-notify.d.ts +4 -4
  339. package/dist/skills/telegram-notify.d.ts.map +1 -1
  340. package/dist/skills/telegram-notify.js +48 -19
  341. package/dist/skills/telegram-notify.js.map +1 -1
  342. package/dist/skills/web-search.d.ts +1 -1
  343. package/dist/skills/web-search.d.ts.map +1 -1
  344. package/dist/skills/web-search.js +85 -40
  345. package/dist/skills/web-search.js.map +1 -1
  346. package/dist/telemetry/bus.d.ts +54 -0
  347. package/dist/telemetry/bus.d.ts.map +1 -0
  348. package/dist/telemetry/bus.js +159 -0
  349. package/dist/telemetry/bus.js.map +1 -0
  350. package/dist/telemetry/index.d.ts +35 -0
  351. package/dist/telemetry/index.d.ts.map +1 -0
  352. package/dist/telemetry/index.js +30 -0
  353. package/dist/telemetry/index.js.map +1 -0
  354. package/dist/telemetry/port.d.ts +121 -0
  355. package/dist/telemetry/port.d.ts.map +1 -0
  356. package/dist/telemetry/port.js +48 -0
  357. package/dist/telemetry/port.js.map +1 -0
  358. package/dist/telemetry/sinks/otlp.d.ts +45 -0
  359. package/dist/telemetry/sinks/otlp.d.ts.map +1 -0
  360. package/dist/telemetry/sinks/otlp.js +195 -0
  361. package/dist/telemetry/sinks/otlp.js.map +1 -0
  362. package/dist/telemetry/sinks/sqlite.d.ts +32 -0
  363. package/dist/telemetry/sinks/sqlite.d.ts.map +1 -0
  364. package/dist/telemetry/sinks/sqlite.js +170 -0
  365. package/dist/telemetry/sinks/sqlite.js.map +1 -0
  366. package/dist/telemetry/sinks/stdout.d.ts +22 -0
  367. package/dist/telemetry/sinks/stdout.d.ts.map +1 -0
  368. package/dist/telemetry/sinks/stdout.js +38 -0
  369. package/dist/telemetry/sinks/stdout.js.map +1 -0
  370. package/dist/testing/index.d.ts +3 -0
  371. package/dist/testing/test-brain-port.d.ts +4 -0
  372. package/dist/testing/test-brain-port.d.ts.map +1 -1
  373. package/dist/testing/test-brain-port.js +75 -20
  374. package/dist/testing/test-brain-port.js.map +1 -1
  375. package/dist/testing/test-event-bus.d.ts.map +1 -1
  376. package/dist/testing/test-event-bus.js +89 -36
  377. package/dist/testing/test-event-bus.js.map +1 -1
  378. package/dist/trace/schema.d.ts +1 -1
  379. package/dist/trace/schema.d.ts.map +1 -1
  380. package/dist/trace/schema.js +1 -1
  381. package/dist/trace/schema.js.map +1 -1
  382. package/dist/verify/formal/index.d.ts +44 -0
  383. package/dist/verify/formal/index.d.ts.map +1 -0
  384. package/dist/verify/formal/index.js +98 -0
  385. package/dist/verify/formal/index.js.map +1 -0
  386. package/dist/verify/formal/policy.d.ts +105 -0
  387. package/dist/verify/formal/policy.d.ts.map +1 -0
  388. package/dist/verify/formal/policy.js +159 -0
  389. package/dist/verify/formal/policy.js.map +1 -0
  390. package/dist/verify/formal/result.d.ts +50 -0
  391. package/dist/verify/formal/result.d.ts.map +1 -0
  392. package/dist/verify/formal/result.js +21 -0
  393. package/dist/verify/formal/result.js.map +1 -0
  394. package/dist/verify/formal/solver.d.ts +67 -0
  395. package/dist/verify/formal/solver.d.ts.map +1 -0
  396. package/dist/verify/formal/solver.js +184 -0
  397. package/dist/verify/formal/solver.js.map +1 -0
  398. package/dist/verify/formal/spec-language.d.ts +80 -0
  399. package/dist/verify/formal/spec-language.d.ts.map +1 -0
  400. package/dist/verify/formal/spec-language.js +219 -0
  401. package/dist/verify/formal/spec-language.js.map +1 -0
  402. package/docs/attestation.md +199 -0
  403. package/docs/identity.md +193 -0
  404. package/docs/telemetry/migration.md +155 -0
  405. package/docs/telemetry/overview.md +154 -0
  406. package/docs/telemetry/privacy.md +127 -0
  407. package/docs/telemetry/sinks/cc.md +155 -0
  408. package/docs/telemetry/sinks/otlp.md +146 -0
  409. package/docs/telemetry/sinks/sqlite.md +126 -0
  410. package/docs/telemetry/sinks/stdout.md +82 -0
  411. package/package.json +18 -2
  412. package/src/adapters/llm/anthropic-direct.ts +51 -0
  413. package/src/adapters/llm/cascade.ts +64 -19
  414. package/src/adapters/llm/litellm.ts +49 -0
  415. package/src/compute/difficulty-estimator.ts +111 -0
  416. package/src/compute/strategies/mixture-of-agents.ts +150 -0
  417. package/src/compute/strategies/tree-of-thoughts.ts +293 -0
  418. package/src/compute/strategies/two-phase-orient.ts +147 -0
  419. package/src/container/protocol.ts +243 -0
  420. package/src/container/runtime.ts +424 -0
  421. package/src/db/migrations/026_formal_verify_results.sql +30 -0
  422. package/src/identity/agent-persona.ts +203 -0
  423. package/src/identity/persona-prompt.ts +84 -0
  424. package/src/identity/persona-schema.ts +127 -0
  425. package/src/index.ts +368 -2
  426. package/src/memory/episodic-rrf.ts +224 -0
  427. package/src/mesh/attenuation.ts +190 -0
  428. package/src/mesh/delegate.ts +254 -0
  429. package/src/mesh/dispatcher.ts +301 -0
  430. package/src/mesh/index.ts +39 -0
  431. package/src/mesh/types.ts +31 -0
  432. package/src/orchestration/ooda/agent.ts +50 -0
  433. package/src/orchestration/ooda/skills.ts +177 -0
  434. package/src/orchestration/ooda/types.ts +12 -0
  435. package/src/ports/bastion-action.contract.test.ts +355 -0
  436. package/src/ports/bastion-action.ts +198 -0
  437. package/src/ports/brain.ts +177 -15
  438. package/src/ports/citadel-action.contract.test.ts +430 -0
  439. package/src/ports/citadel-action.ts +174 -0
  440. package/src/ports/compliance-contract.ts +191 -0
  441. package/src/ports/db.ts +98 -0
  442. package/src/ports/delegation.contract.test.ts +428 -0
  443. package/src/ports/delegation.ts +211 -0
  444. package/src/ports/event-bus.ts +133 -0
  445. package/src/ports/federation.contract.test.ts +355 -0
  446. package/src/ports/federation.ts +190 -0
  447. package/src/ports/index.ts +186 -1
  448. package/src/ports/llm-provider.ts +123 -0
  449. package/src/ports/logger.ts +104 -0
  450. package/src/ports/manifest-registry.contract.test.ts +324 -0
  451. package/src/ports/manifest-registry.ts +188 -0
  452. package/src/ports/observability.contract.test.ts +315 -0
  453. package/src/ports/observability.ts +150 -0
  454. package/src/ports/outcome.ts +69 -0
  455. package/src/ports/privacy.contract.test.ts +413 -0
  456. package/src/ports/privacy.ts +207 -0
  457. package/src/ports/tenant-context.contract.test.ts +454 -0
  458. package/src/ports/tenant-context.ts +150 -0
  459. package/src/ports/vauban-finance-action.contract.test.ts +335 -0
  460. package/src/ports/vauban-finance-action.ts +166 -0
  461. package/src/ports/workflow-runtime.ts +327 -0
  462. package/src/proof/cert-verify.ts +249 -0
  463. package/src/replay/replay.ts +11 -8
  464. package/src/retry/index.ts +227 -0
  465. package/src/retry/presets.ts +75 -0
  466. package/src/skill-loop/ab-runner.ts +196 -0
  467. package/src/skill-loop/adoption.ts +188 -0
  468. package/src/skill-loop/candidate.ts +75 -0
  469. package/src/skill-loop/evaluator.ts +238 -0
  470. package/src/skill-loop/index.ts +51 -0
  471. package/src/skill-loop/reflexion-replay.ts +173 -0
  472. package/src/skill-loop/sign-off.ts +247 -0
  473. package/src/skill-loop/value-metric.ts +120 -0
  474. package/src/skill-loop/versioning.ts +75 -0
  475. package/src/skill-manifest/anchor.ts +401 -0
  476. package/src/skill-manifest/builder.ts +129 -0
  477. package/src/skill-manifest/index.ts +18 -0
  478. package/src/skill-manifest/types.ts +72 -0
  479. package/src/skill-manifest/verifier.ts +198 -0
  480. package/src/skills/_secrets.ts +25 -0
  481. package/src/skills/alpaca-quote.ts +68 -23
  482. package/src/skills/errors.ts +30 -2
  483. package/src/skills/index.ts +19 -0
  484. package/src/skills/markdown/loader.ts +129 -0
  485. package/src/skills/markdown/schema.ts +144 -0
  486. package/src/skills/poc-md-loader/e2e-parity.test.ts +237 -0
  487. package/src/skills/poc-md-loader/markdown-loader.ts +161 -0
  488. package/src/skills/poc-md-loader/runner.ts +82 -0
  489. package/src/skills/poc-md-loader/vitest.poc.config.ts +13 -0
  490. package/src/skills/poc-md-loader/web-search/SKILL.md +42 -0
  491. package/src/skills/poc-md-loader/web-search/script.ts +109 -0
  492. package/src/skills/send-email.ts +4 -3
  493. package/src/skills/slack-notify.ts +73 -30
  494. package/src/skills/telegram-notify.ts +70 -24
  495. package/src/skills/web-search.ts +132 -50
  496. package/src/telemetry/bus.test.ts +231 -0
  497. package/src/telemetry/bus.ts +241 -0
  498. package/src/telemetry/index.ts +49 -0
  499. package/src/telemetry/port.ts +160 -0
  500. package/src/telemetry/sinks/otlp.test.ts +146 -0
  501. package/src/telemetry/sinks/otlp.ts +250 -0
  502. package/src/telemetry/sinks/sqlite.test.ts +121 -0
  503. package/src/telemetry/sinks/sqlite.ts +260 -0
  504. package/src/telemetry/sinks/stdout.test.ts +109 -0
  505. package/src/telemetry/sinks/stdout.ts +59 -0
  506. package/src/testing/test-brain-port.ts +98 -24
  507. package/src/testing/test-event-bus.ts +104 -43
  508. package/src/trace/schema.ts +1 -1
  509. package/src/verify/formal/index.ts +154 -0
  510. package/src/verify/formal/policy.ts +253 -0
  511. package/src/verify/formal/result.ts +52 -0
  512. package/src/verify/formal/solver.ts +235 -0
  513. package/src/verify/formal/spec-language.ts +274 -0
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Retry primitives with exponential backoff and optional jitter.
3
+ *
4
+ * Provides three usage modes:
5
+ * - `retry(fn, opts)` — wrap an async call.
6
+ * - `RetryContext` — manual control flow for granular retry logic.
7
+ * - {@link ./presets.js} — 4 named configurations for common scenarios.
8
+ *
9
+ * Sleep is injectable via `RetryOptions.sleepFn` for deterministic tests.
10
+ * BaseError.retryable flag is honored by default presets (see ./presets.ts).
11
+ *
12
+ * @public @since 1.2.0
13
+ */
14
+
15
+ import { RETRY_TRANSIENT } from "./presets.js";
16
+
17
+ export type SleepFn = (ms: number) => Promise<void>;
18
+
19
+ /**
20
+ * Retry policy. `jitter: true` adds ±25% randomization to each delay
21
+ * (anti-thundering-herd when multiple callers retry simultaneously).
22
+ *
23
+ * Selection precedence on each error:
24
+ * 1. `retryIf` predicate if provided
25
+ * 2. `retryOn` class membership if provided
26
+ * 3. Default: retry on any error
27
+ *
28
+ * @public
29
+ */
30
+ export interface RetryConfig {
31
+ /** Total attempts including the first try. Must be >= 1. */
32
+ readonly maxAttempts: number;
33
+ /** Initial delay before retry 1, in ms. */
34
+ readonly baseDelayMs: number;
35
+ /** Upper bound on any computed delay, in ms. */
36
+ readonly maxDelayMs: number;
37
+ /** Exponent for `baseDelayMs * base^attempt`. Typically 2.0. */
38
+ readonly exponentialBase: number;
39
+ /** Whether to add ±25% random jitter to each delay. */
40
+ readonly jitter: boolean;
41
+ /** If set, only retry when the error is an instance of one of these classes. */
42
+ readonly retryOn?: ReadonlyArray<new (...args: never[]) => Error>;
43
+ /** If set, called per error; return true to retry. Overrides retryOn. */
44
+ readonly retryIf?: (err: unknown) => boolean;
45
+ }
46
+
47
+ /**
48
+ * Options to {@link retry}.
49
+ * @public
50
+ */
51
+ export interface RetryOptions {
52
+ /** Retry policy. Defaults to RETRY_TRANSIENT. */
53
+ config?: RetryConfig;
54
+ /** Called before each retry sleep. Receives the error, the 0-indexed attempt that failed, and the upcoming delay. */
55
+ onRetry?: (err: unknown, attempt: number, delayMs: number) => void;
56
+ /** Sleep injection — defaults to setTimeout. Tests pass a zero-delay or mock. */
57
+ sleepFn?: SleepFn;
58
+ }
59
+
60
+ /**
61
+ * Thrown after all retry attempts have been exhausted.
62
+ * @public
63
+ */
64
+ export class RetryExhaustedError extends Error {
65
+ readonly attempts: number;
66
+ readonly lastError: unknown;
67
+ constructor(attempts: number, lastError: unknown) {
68
+ const reason =
69
+ lastError instanceof Error ? lastError.message : String(lastError);
70
+ super(`All ${attempts} retry attempts exhausted: ${reason}`);
71
+ this.name = "RetryExhaustedError";
72
+ this.attempts = attempts;
73
+ this.lastError = lastError;
74
+ }
75
+ }
76
+
77
+ const defaultSleep: SleepFn = (ms) =>
78
+ new Promise((resolve) => setTimeout(resolve, ms));
79
+
80
+ /**
81
+ * Compute the delay before retry `attempt` (0-indexed). Caps at `maxDelayMs`.
82
+ * Jitter, when enabled, adds a uniform-random ±25% offset.
83
+ *
84
+ * Visible for tests and for callers that need to inspect timing.
85
+ * @public
86
+ */
87
+ export function calculateDelay(config: RetryConfig, attempt: number): number {
88
+ const raw = config.baseDelayMs * Math.pow(config.exponentialBase, attempt);
89
+ const capped = Math.min(raw, config.maxDelayMs);
90
+ if (!config.jitter) return Math.max(0, capped);
91
+ const jitterRange = capped * 0.25;
92
+ const offset = (Math.random() * 2 - 1) * jitterRange;
93
+ return Math.max(0, capped + offset);
94
+ }
95
+
96
+ /**
97
+ * Decide whether to retry after `err` at 0-indexed `attempt`.
98
+ *
99
+ * Returns false on the final attempt (caller should throw exhaustion).
100
+ *
101
+ * @public
102
+ */
103
+ export function shouldRetry(
104
+ config: RetryConfig,
105
+ err: unknown,
106
+ attempt: number
107
+ ): boolean {
108
+ if (attempt >= config.maxAttempts - 1) return false;
109
+ if (config.retryIf) return config.retryIf(err);
110
+ if (config.retryOn && config.retryOn.length > 0) {
111
+ return config.retryOn.some((Ctor) => err instanceof Ctor);
112
+ }
113
+ return true;
114
+ }
115
+
116
+ /**
117
+ * Execute `fn` with retry and exponential backoff.
118
+ *
119
+ * @throws The non-retryable error as-is if `shouldRetry` returns false mid-loop.
120
+ * @throws {@link RetryExhaustedError} when all attempts have failed.
121
+ *
122
+ * @public
123
+ */
124
+ export async function retry<T>(
125
+ fn: () => Promise<T>,
126
+ opts: RetryOptions = {}
127
+ ): Promise<T> {
128
+ const config = opts.config ?? RETRY_TRANSIENT;
129
+ const sleep = opts.sleepFn ?? defaultSleep;
130
+
131
+ let lastError: unknown;
132
+
133
+ for (let attempt = 0; attempt < config.maxAttempts; attempt++) {
134
+ try {
135
+ return await fn();
136
+ } catch (err) {
137
+ lastError = err;
138
+ if (!shouldRetry(config, err, attempt)) {
139
+ if (attempt >= config.maxAttempts - 1) {
140
+ throw new RetryExhaustedError(config.maxAttempts, err);
141
+ }
142
+ throw err;
143
+ }
144
+ const delay = calculateDelay(config, attempt);
145
+ opts.onRetry?.(err, attempt, delay);
146
+ await sleep(delay);
147
+ }
148
+ }
149
+
150
+ throw new RetryExhaustedError(config.maxAttempts, lastError);
151
+ }
152
+
153
+ /**
154
+ * Manual retry control. Use when the operation doesn't fit a single `fn()` —
155
+ * e.g. when multiple endpoints are tried per attempt, or when partial state
156
+ * must be reset between tries.
157
+ *
158
+ * @example
159
+ * ```ts
160
+ * const ctx = new RetryContext({ config: RETRY_AGGRESSIVE });
161
+ * while (ctx.shouldContinue) {
162
+ * try {
163
+ * return await op();
164
+ * } catch (err) {
165
+ * await ctx.handleError(err);
166
+ * }
167
+ * }
168
+ * ```
169
+ *
170
+ * @public
171
+ */
172
+ export class RetryContext {
173
+ private readonly config: RetryConfig;
174
+ private readonly sleep: SleepFn;
175
+ private readonly onRetry?: (
176
+ err: unknown,
177
+ attempt: number,
178
+ delayMs: number
179
+ ) => void;
180
+ private attemptCount = 0;
181
+ private exhausted = false;
182
+
183
+ constructor(opts: RetryOptions & { config: RetryConfig }) {
184
+ this.config = opts.config;
185
+ this.sleep = opts.sleepFn ?? defaultSleep;
186
+ this.onRetry = opts.onRetry;
187
+ }
188
+
189
+ /** True while attempts remain and no non-retryable error has been thrown. */
190
+ get shouldContinue(): boolean {
191
+ return !this.exhausted && this.attemptCount < this.config.maxAttempts;
192
+ }
193
+
194
+ /** 0-indexed count of completed (failed) attempts. */
195
+ get attempt(): number {
196
+ return this.attemptCount;
197
+ }
198
+
199
+ /**
200
+ * Record an error and sleep before the next try.
201
+ *
202
+ * @throws The non-retryable error as-is.
203
+ * @throws {@link RetryExhaustedError} if attempts are exhausted.
204
+ */
205
+ async handleError(err: unknown): Promise<void> {
206
+ const failedAttempt = this.attemptCount;
207
+ this.attemptCount += 1;
208
+ if (this.attemptCount >= this.config.maxAttempts) {
209
+ this.exhausted = true;
210
+ throw new RetryExhaustedError(this.config.maxAttempts, err);
211
+ }
212
+ if (!shouldRetry(this.config, err, failedAttempt)) {
213
+ this.exhausted = true;
214
+ throw err;
215
+ }
216
+ const delay = calculateDelay(this.config, failedAttempt);
217
+ this.onRetry?.(err, failedAttempt, delay);
218
+ await this.sleep(delay);
219
+ }
220
+ }
221
+
222
+ export {
223
+ RETRY_TRANSIENT,
224
+ RETRY_AGGRESSIVE,
225
+ RETRY_PATIENT,
226
+ NO_RETRY,
227
+ } from "./presets.js";
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Named retry presets covering common scenarios.
3
+ *
4
+ * All presets honor a `retryable: boolean` flag on the thrown error when
5
+ * present (compatible with {@link ../errors.js#BaseError}). When the flag is
6
+ * absent, defaults fall back to: transient = retry, no_retry = no retry.
7
+ *
8
+ * @public @since 1.2.0
9
+ */
10
+
11
+ import type { RetryConfig } from "./index.js";
12
+
13
+ function honorsRetryableFlag(err: unknown, defaultIfMissing: boolean): boolean {
14
+ if (typeof err === "object" && err !== null && "retryable" in err) {
15
+ return (err as { retryable: unknown }).retryable === true;
16
+ }
17
+ return defaultIfMissing;
18
+ }
19
+
20
+ /**
21
+ * 3 attempts, 1s base, 10s cap, jitter on. Honors `retryable` flag (defaults
22
+ * to retry when absent). Use for short-lived network calls.
23
+ *
24
+ * @public
25
+ */
26
+ export const RETRY_TRANSIENT: RetryConfig = {
27
+ maxAttempts: 3,
28
+ baseDelayMs: 1000,
29
+ maxDelayMs: 10_000,
30
+ exponentialBase: 2.0,
31
+ jitter: true,
32
+ retryIf: (err) => honorsRetryableFlag(err, true),
33
+ };
34
+
35
+ /**
36
+ * 5 attempts, 500ms base, 30s cap, jitter on. Use for flaky external APIs
37
+ * where rapid recovery is plausible.
38
+ *
39
+ * @public
40
+ */
41
+ export const RETRY_AGGRESSIVE: RetryConfig = {
42
+ maxAttempts: 5,
43
+ baseDelayMs: 500,
44
+ maxDelayMs: 30_000,
45
+ exponentialBase: 2.0,
46
+ jitter: true,
47
+ };
48
+
49
+ /**
50
+ * 10 attempts, 2s base, 120s cap, jitter on. Use for long-recovery
51
+ * dependencies (database failover, restart-storm tolerant calls).
52
+ *
53
+ * @public
54
+ */
55
+ export const RETRY_PATIENT: RetryConfig = {
56
+ maxAttempts: 10,
57
+ baseDelayMs: 2000,
58
+ maxDelayMs: 120_000,
59
+ exponentialBase: 2.0,
60
+ jitter: true,
61
+ };
62
+
63
+ /**
64
+ * Single attempt, no retry. Use as an explicit policy to disable retry at
65
+ * call sites without scattering booleans.
66
+ *
67
+ * @public
68
+ */
69
+ export const NO_RETRY: RetryConfig = {
70
+ maxAttempts: 1,
71
+ baseDelayMs: 0,
72
+ maxDelayMs: 0,
73
+ exponentialBase: 2.0,
74
+ jitter: false,
75
+ };
@@ -0,0 +1,196 @@
1
+ /**
2
+ * src/skill-loop/ab-runner.ts
3
+ *
4
+ * A/B test runner with failure budgets for SkillCandidates.
5
+ *
6
+ * Invariants:
7
+ * - Max 3 candidates simultaneously (4th is rejected).
8
+ * - Traffic fraction cap: 10% per candidate.
9
+ * - Rollback triggered when a candidate's mean score drops >5% vs incumbent.
10
+ * - Winner declared when: p < 0.05 AND delta >= 10% AND no rollback triggered.
11
+ *
12
+ * Statistical significance uses the same Welch approach as evaluator.ts.
13
+ *
14
+ * @module skill-loop/ab-runner
15
+ */
16
+
17
+ import type { SkillCandidate } from "./candidate.js";
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Types
21
+ // ---------------------------------------------------------------------------
22
+
23
+ export interface ABConfig {
24
+ /** Maximum concurrent candidates. Hard cap: 3. */
25
+ maxCandidates: number;
26
+ /** Traffic fraction routed to each candidate. Hard cap: 0.10. */
27
+ trafficPct: number;
28
+ /** Rollback threshold: if candidate mean drops by this fraction vs incumbent. */
29
+ rollbackThresholdPct: number;
30
+ }
31
+
32
+ export interface ABSlot {
33
+ candidateId: string;
34
+ trafficFraction: number;
35
+ outcomes: number[];
36
+ rollbackTriggered: boolean;
37
+ }
38
+
39
+ // ---------------------------------------------------------------------------
40
+ // Statistical helpers (inline — avoid circular dep with evaluator)
41
+ // ---------------------------------------------------------------------------
42
+
43
+ function mean(xs: number[]): number {
44
+ if (xs.length === 0) return 0;
45
+ return xs.reduce((a, b) => a + b, 0) / xs.length;
46
+ }
47
+
48
+ function variance(xs: number[], m: number): number {
49
+ if (xs.length < 2) return 0;
50
+ return xs.reduce((acc, x) => acc + (x - m) ** 2, 0) / (xs.length - 1);
51
+ }
52
+
53
+ function normalCdf(z: number): number {
54
+ const t = 1 / (1 + 0.2316419 * z);
55
+ const d = 0.3989423 * Math.exp((-z * z) / 2);
56
+ const p =
57
+ d *
58
+ t *
59
+ (0.3193815 +
60
+ t * (-0.3565638 + t * (1.7814779 + t * (-1.8212559 + t * 1.3302744))));
61
+ return 1 - p;
62
+ }
63
+
64
+ function welchPValue(a: number[], b: number[]): number {
65
+ const n1 = a.length;
66
+ const n2 = b.length;
67
+ if (n1 < 2 || n2 < 2) return 1.0;
68
+
69
+ const m1 = mean(a);
70
+ const m2 = mean(b);
71
+ const v1 = variance(a, m1);
72
+ const v2 = variance(b, m2);
73
+ const se = Math.sqrt(v1 / n1 + v2 / n2);
74
+ if (se === 0) return m1 === m2 ? 1.0 : 0.0;
75
+
76
+ const tStat = Math.abs((m1 - m2) / se);
77
+ const df =
78
+ (v1 / n1 + v2 / n2) ** 2 /
79
+ ((v1 / n1) ** 2 / (n1 - 1) + (v2 / n2) ** 2 / (n2 - 1));
80
+
81
+ if (df >= 30) {
82
+ return Math.max(0, Math.min(1, 2 * (1 - normalCdf(tStat))));
83
+ }
84
+ // Conservative: approximate p via critical value at df ≈ 30 (t-crit = 2.042)
85
+ return tStat > 2.042 ? 0.04 : 0.1;
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // ABRunner
90
+ // ---------------------------------------------------------------------------
91
+
92
+ export class ABRunner {
93
+ private readonly config: Readonly<ABConfig>;
94
+ private readonly slots = new Map<string, ABSlot>();
95
+ private readonly candidates = new Map<string, SkillCandidate>();
96
+ private readonly incumbentOutcomes: number[] = [];
97
+
98
+ constructor(config: ABConfig) {
99
+ // Enforce hard caps
100
+ this.config = {
101
+ maxCandidates: Math.min(config.maxCandidates, 3),
102
+ trafficPct: Math.min(config.trafficPct, 0.1),
103
+ rollbackThresholdPct: config.rollbackThresholdPct,
104
+ };
105
+ }
106
+
107
+ /**
108
+ * Register a new candidate for A/B testing.
109
+ *
110
+ * @returns true if added, false if maxCandidates already reached.
111
+ */
112
+ addCandidate(candidate: SkillCandidate): boolean {
113
+ if (this.slots.size >= this.config.maxCandidates) {
114
+ return false;
115
+ }
116
+ this.candidates.set(candidate.id, candidate);
117
+ this.slots.set(candidate.id, {
118
+ candidateId: candidate.id,
119
+ trafficFraction: this.config.trafficPct,
120
+ outcomes: [],
121
+ rollbackTriggered: false,
122
+ });
123
+ return true;
124
+ }
125
+
126
+ /**
127
+ * Record an outcome score for a candidate or the incumbent.
128
+ *
129
+ * @param candidateId - Candidate ID or "incumbent".
130
+ * @param score - Outcome score [0, 1].
131
+ */
132
+ recordOutcome(candidateId: string | "incumbent", score: number): void {
133
+ if (candidateId === "incumbent") {
134
+ this.incumbentOutcomes.push(score);
135
+ return;
136
+ }
137
+ const slot = this.slots.get(candidateId);
138
+ if (!slot) return;
139
+
140
+ slot.outcomes.push(score);
141
+
142
+ // Check rollback condition after each new data point
143
+ if (!slot.rollbackTriggered && this.shouldRollback(candidateId)) {
144
+ slot.rollbackTriggered = true;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Returns true if the candidate's mean score has dropped >rollbackThresholdPct
150
+ * vs the incumbent mean.
151
+ */
152
+ shouldRollback(candidateId: string): boolean {
153
+ const slot = this.slots.get(candidateId);
154
+ if (!slot || slot.outcomes.length === 0) return false;
155
+ if (this.incumbentOutcomes.length === 0) return false;
156
+
157
+ const candMean = mean(slot.outcomes);
158
+ const incMean = mean(this.incumbentOutcomes);
159
+
160
+ // Degradation: candidate is worse than incumbent by more than threshold
161
+ const degradation = incMean - candMean;
162
+ return degradation > this.config.rollbackThresholdPct;
163
+ }
164
+
165
+ /**
166
+ * Return the winning candidate if statistical significance is established.
167
+ *
168
+ * Winner criteria:
169
+ * - p < 0.05 (Welch's t-test vs incumbent)
170
+ * - delta >= 10% vs incumbent mean
171
+ * - rollback NOT triggered
172
+ *
173
+ * Returns null if no candidate meets all criteria.
174
+ */
175
+ getWinner(): SkillCandidate | null {
176
+ if (this.incumbentOutcomes.length < 2) return null;
177
+
178
+ const incMean = mean(this.incumbentOutcomes);
179
+
180
+ for (const [id, slot] of this.slots) {
181
+ if (slot.rollbackTriggered) continue;
182
+ if (slot.outcomes.length < 2) continue;
183
+
184
+ const candMean = mean(slot.outcomes);
185
+ const delta = candMean - incMean;
186
+ if (delta < 0.1) continue;
187
+
188
+ const p = welchPValue(slot.outcomes, this.incumbentOutcomes);
189
+ if (p < 0.05) {
190
+ return this.candidates.get(id) ?? null;
191
+ }
192
+ }
193
+
194
+ return null;
195
+ }
196
+ }
@@ -0,0 +1,188 @@
1
+ /**
2
+ * src/skill-loop/adoption.ts
3
+ *
4
+ * Skill adoption — auto-promote and deprecate logic gated by sign-off.
5
+ *
6
+ * Promotion conditions (ALL must hold):
7
+ * 1. EvalResult.pValue < 0.05 (statistical significance)
8
+ * 2. EvalResult.deltaVsIncumbent >= 0.10 (>=10% improvement)
9
+ * 3. A valid approved SignOffRecord exists for the candidateId
10
+ *
11
+ * Violation of condition 3 → throws PromotionWithoutSignOffError.
12
+ *
13
+ * Underperformers (deltaVsIncumbent < -0.05 OR pValue > 0.5) are
14
+ * automatically deprecated via deprecateUnderperformers().
15
+ *
16
+ * @module skill-loop/adoption
17
+ */
18
+
19
+ import type { SkillCandidate } from "./candidate.js";
20
+ import type { EvalResult } from "./evaluator.js";
21
+ import type { SignOffRecord } from "./sign-off.js";
22
+
23
+ // ---------------------------------------------------------------------------
24
+ // Errors
25
+ // ---------------------------------------------------------------------------
26
+
27
+ export class PromotionWithoutSignOffError extends Error {
28
+ readonly candidateId: string;
29
+
30
+ constructor(candidateId: string) {
31
+ super(
32
+ `adoption: cannot promote candidate "${candidateId}" without a valid approved sign-off. ` +
33
+ "Request human approval via SignOffManager.requestSignOff() first."
34
+ );
35
+ this.name = "PromotionWithoutSignOffError";
36
+ this.candidateId = candidateId;
37
+ }
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Types
42
+ // ---------------------------------------------------------------------------
43
+
44
+ export type AdoptionStatus =
45
+ | "promoted"
46
+ | "rejected"
47
+ | "deprecated"
48
+ | "pending_signoff"
49
+ | "insufficient_signal";
50
+
51
+ export interface AdoptionRecord {
52
+ candidateId: string;
53
+ status: AdoptionStatus;
54
+ evalResult: EvalResult;
55
+ signOff: SignOffRecord | null;
56
+ promotedAt: Date | null;
57
+ deprecatedAt: Date | null;
58
+ reason: string;
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Adoption helpers
63
+ // ---------------------------------------------------------------------------
64
+
65
+ /**
66
+ * Attempt to promote a candidate to production.
67
+ *
68
+ * @param candidate - The skill candidate.
69
+ * @param evalResult - Evaluation result against the verifier set.
70
+ * @param signOff - Approved sign-off record (must be decision === 'approved').
71
+ * @param nowDate - Optional date override for deterministic testing.
72
+ *
73
+ * @throws PromotionWithoutSignOffError if signOff is null or not approved.
74
+ */
75
+ export function promoteCandidate(
76
+ candidate: SkillCandidate,
77
+ evalResult: EvalResult,
78
+ signOff: SignOffRecord | null,
79
+ nowDate?: Date
80
+ ): AdoptionRecord {
81
+ // Gate 1: sign-off required — never auto-promote without human approval
82
+ if (!signOff || signOff.decision !== "approved") {
83
+ throw new PromotionWithoutSignOffError(candidate.id);
84
+ }
85
+
86
+ // Gate 2: statistical significance
87
+ if (!evalResult.significant) {
88
+ return {
89
+ candidateId: candidate.id,
90
+ status: "insufficient_signal",
91
+ evalResult,
92
+ signOff,
93
+ promotedAt: null,
94
+ deprecatedAt: null,
95
+ reason: `insufficient signal: p=${evalResult.pValue.toFixed(
96
+ 4
97
+ )} delta=${evalResult.deltaVsIncumbent.toFixed(
98
+ 4
99
+ )} (need p<0.05 AND delta>=0.10)`,
100
+ };
101
+ }
102
+
103
+ // All gates passed → promote
104
+ return {
105
+ candidateId: candidate.id,
106
+ status: "promoted",
107
+ evalResult,
108
+ signOff,
109
+ promotedAt: nowDate ?? new Date(),
110
+ deprecatedAt: null,
111
+ reason: `promoted: p=${evalResult.pValue.toFixed(4)} delta=${(
112
+ evalResult.deltaVsIncumbent * 100
113
+ ).toFixed(1)}% sign-off by ${signOff.approverId}`,
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Reject a candidate explicitly (e.g. sign-off decision = rejected).
119
+ */
120
+ export function rejectCandidate(
121
+ candidate: SkillCandidate,
122
+ evalResult: EvalResult,
123
+ signOff: SignOffRecord | null,
124
+ reason: string
125
+ ): AdoptionRecord {
126
+ return {
127
+ candidateId: candidate.id,
128
+ status: "rejected",
129
+ evalResult,
130
+ signOff,
131
+ promotedAt: null,
132
+ deprecatedAt: null,
133
+ reason,
134
+ };
135
+ }
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Underperformer deprecation
139
+ // ---------------------------------------------------------------------------
140
+
141
+ /**
142
+ * Deprecation threshold: candidate's delta < -5% OR p-value > 0.5 with negative delta.
143
+ */
144
+ const UNDERPERFORMER_DELTA_THRESHOLD = -0.05;
145
+ const UNDERPERFORMER_PVALUE_THRESHOLD = 0.5;
146
+
147
+ /**
148
+ * Automatically deprecate underperforming candidates from a pool.
149
+ *
150
+ * A candidate is an underperformer if:
151
+ * - deltaVsIncumbent < -0.05 (degrades by more than 5%)
152
+ * - OR (deltaVsIncumbent < 0 AND pValue > 0.5) — statistically noisy & negative
153
+ *
154
+ * Deprecation is automatic — no sign-off required (sign-off is only for promotion).
155
+ *
156
+ * @param candidates - Pool of (candidate, evalResult) pairs to assess.
157
+ * @param nowDate - Optional date override.
158
+ * @returns AdoptionRecords for every deprecated candidate.
159
+ */
160
+ export function deprecateUnderperformers(
161
+ candidates: Array<{ candidate: SkillCandidate; evalResult: EvalResult }>,
162
+ nowDate?: Date
163
+ ): AdoptionRecord[] {
164
+ const deprecated: AdoptionRecord[] = [];
165
+
166
+ for (const { candidate, evalResult } of candidates) {
167
+ const isUnderperformer =
168
+ evalResult.deltaVsIncumbent < UNDERPERFORMER_DELTA_THRESHOLD ||
169
+ (evalResult.deltaVsIncumbent < 0 &&
170
+ evalResult.pValue > UNDERPERFORMER_PVALUE_THRESHOLD);
171
+
172
+ if (isUnderperformer) {
173
+ deprecated.push({
174
+ candidateId: candidate.id,
175
+ status: "deprecated",
176
+ evalResult,
177
+ signOff: null,
178
+ promotedAt: null,
179
+ deprecatedAt: nowDate ?? new Date(),
180
+ reason: `auto-deprecated: delta=${(
181
+ evalResult.deltaVsIncumbent * 100
182
+ ).toFixed(1)}% p=${evalResult.pValue.toFixed(4)}`,
183
+ });
184
+ }
185
+ }
186
+
187
+ return deprecated;
188
+ }