modelrig 0.0.1 → 0.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 (420) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +201 -0
  3. package/README.md +145 -8
  4. package/data/PROVENANCE.md +19 -0
  5. package/data/pricing-manual.json +22 -0
  6. package/data/pricing-map.json +46362 -0
  7. package/data/pricing-overlay.json +419 -0
  8. package/dist/adapters/anthropic.d.ts +90 -0
  9. package/dist/adapters/anthropic.d.ts.map +1 -0
  10. package/dist/adapters/anthropic.js +296 -0
  11. package/dist/adapters/build-adapters.d.ts +11 -0
  12. package/dist/adapters/build-adapters.d.ts.map +1 -0
  13. package/dist/adapters/build-adapters.js +45 -0
  14. package/dist/adapters/deepseek.d.ts +47 -0
  15. package/dist/adapters/deepseek.d.ts.map +1 -0
  16. package/dist/adapters/deepseek.js +216 -0
  17. package/dist/adapters/gemini.d.ts +44 -0
  18. package/dist/adapters/gemini.d.ts.map +1 -0
  19. package/dist/adapters/gemini.js +262 -0
  20. package/dist/adapters/grok.d.ts +53 -0
  21. package/dist/adapters/grok.d.ts.map +1 -0
  22. package/dist/adapters/grok.js +232 -0
  23. package/dist/adapters/openai-compat.d.ts +80 -0
  24. package/dist/adapters/openai-compat.d.ts.map +1 -0
  25. package/dist/adapters/openai-compat.js +244 -0
  26. package/dist/adapters/openai-stream.d.ts +58 -0
  27. package/dist/adapters/openai-stream.d.ts.map +1 -0
  28. package/dist/adapters/openai-stream.js +120 -0
  29. package/dist/adapters/openai.d.ts +55 -0
  30. package/dist/adapters/openai.d.ts.map +1 -0
  31. package/dist/adapters/openai.js +281 -0
  32. package/dist/adapters/sampling.d.ts +28 -0
  33. package/dist/adapters/sampling.d.ts.map +1 -0
  34. package/dist/adapters/sampling.js +34 -0
  35. package/dist/adapters/tool-mapping.d.ts +92 -0
  36. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  37. package/dist/adapters/tool-mapping.js +143 -0
  38. package/dist/artifacts/artifact.d.ts +137 -0
  39. package/dist/artifacts/artifact.d.ts.map +1 -0
  40. package/dist/artifacts/artifact.js +377 -0
  41. package/dist/artifacts/content-uploader.d.ts +87 -0
  42. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  43. package/dist/artifacts/content-uploader.js +124 -0
  44. package/dist/artifacts/index.d.ts +63 -0
  45. package/dist/artifacts/index.d.ts.map +1 -0
  46. package/dist/artifacts/index.js +80 -0
  47. package/dist/artifacts/run-context.d.ts +129 -0
  48. package/dist/artifacts/run-context.d.ts.map +1 -0
  49. package/dist/artifacts/run-context.js +208 -0
  50. package/dist/artifacts/scrub.d.ts +60 -0
  51. package/dist/artifacts/scrub.d.ts.map +1 -0
  52. package/dist/artifacts/scrub.js +139 -0
  53. package/dist/artifacts/step.d.ts +26 -0
  54. package/dist/artifacts/step.d.ts.map +1 -0
  55. package/dist/artifacts/step.js +37 -0
  56. package/dist/artifacts/taint.d.ts +63 -0
  57. package/dist/artifacts/taint.d.ts.map +1 -0
  58. package/dist/artifacts/taint.js +76 -0
  59. package/dist/bakeoff/cache-sim.d.ts +99 -0
  60. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  61. package/dist/bakeoff/cache-sim.js +126 -0
  62. package/dist/bakeoff/engine.d.ts +52 -0
  63. package/dist/bakeoff/engine.d.ts.map +1 -0
  64. package/dist/bakeoff/engine.js +163 -0
  65. package/dist/bakeoff/eval-cases.d.ts +96 -0
  66. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  67. package/dist/bakeoff/eval-cases.js +105 -0
  68. package/dist/bakeoff/replay.d.ts +52 -0
  69. package/dist/bakeoff/replay.d.ts.map +1 -0
  70. package/dist/bakeoff/replay.js +94 -0
  71. package/dist/bakeoff/report.d.ts +41 -0
  72. package/dist/bakeoff/report.d.ts.map +1 -0
  73. package/dist/bakeoff/report.js +142 -0
  74. package/dist/bakeoff/samples.d.ts +77 -0
  75. package/dist/bakeoff/samples.d.ts.map +1 -0
  76. package/dist/bakeoff/samples.js +148 -0
  77. package/dist/bakeoff/stats.d.ts +13 -0
  78. package/dist/bakeoff/stats.d.ts.map +1 -0
  79. package/dist/bakeoff/stats.js +48 -0
  80. package/dist/bakeoff/types.d.ts +37 -0
  81. package/dist/bakeoff/types.d.ts.map +1 -0
  82. package/dist/bakeoff/types.js +10 -0
  83. package/dist/cli.d.ts +25 -0
  84. package/dist/cli.d.ts.map +1 -0
  85. package/dist/cli.js +590 -0
  86. package/dist/config.d.ts +204 -0
  87. package/dist/config.d.ts.map +1 -0
  88. package/dist/config.js +252 -0
  89. package/dist/deprecations.d.ts +32 -0
  90. package/dist/deprecations.d.ts.map +1 -0
  91. package/dist/deprecations.js +59 -0
  92. package/dist/feedback/coverage.d.ts +32 -0
  93. package/dist/feedback/coverage.d.ts.map +1 -0
  94. package/dist/feedback/coverage.js +95 -0
  95. package/dist/feedback/feedback.d.ts +35 -0
  96. package/dist/feedback/feedback.d.ts.map +1 -0
  97. package/dist/feedback/feedback.js +39 -0
  98. package/dist/gateway/http-protocol.d.ts +175 -0
  99. package/dist/gateway/http-protocol.d.ts.map +1 -0
  100. package/dist/gateway/http-protocol.js +105 -0
  101. package/dist/index.d.ts +242 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +576 -0
  104. package/dist/init/finder.d.ts +94 -0
  105. package/dist/init/finder.d.ts.map +1 -0
  106. package/dist/init/finder.js +343 -0
  107. package/dist/init/init-cli.d.ts +10 -0
  108. package/dist/init/init-cli.d.ts.map +1 -0
  109. package/dist/init/init-cli.js +86 -0
  110. package/dist/init/observe-cli.d.ts +19 -0
  111. package/dist/init/observe-cli.d.ts.map +1 -0
  112. package/dist/init/observe-cli.js +73 -0
  113. package/dist/init/observe.d.ts +81 -0
  114. package/dist/init/observe.d.ts.map +1 -0
  115. package/dist/init/observe.js +245 -0
  116. package/dist/init/run.d.ts +99 -0
  117. package/dist/init/run.d.ts.map +1 -0
  118. package/dist/init/run.js +281 -0
  119. package/dist/init/signatures.d.ts +39 -0
  120. package/dist/init/signatures.d.ts.map +1 -0
  121. package/dist/init/signatures.js +116 -0
  122. package/dist/matrix/aggregate.d.ts +89 -0
  123. package/dist/matrix/aggregate.d.ts.map +1 -0
  124. package/dist/matrix/aggregate.js +186 -0
  125. package/dist/query/grammar.d.ts +35 -0
  126. package/dist/query/grammar.d.ts.map +1 -0
  127. package/dist/query/grammar.js +279 -0
  128. package/dist/query/run.d.ts +8 -0
  129. package/dist/query/run.d.ts.map +1 -0
  130. package/dist/query/run.js +21 -0
  131. package/dist/registry/build.d.ts +219 -0
  132. package/dist/registry/build.d.ts.map +1 -0
  133. package/dist/registry/build.js +313 -0
  134. package/dist/registry/cache-policy.d.ts +94 -0
  135. package/dist/registry/cache-policy.d.ts.map +1 -0
  136. package/dist/registry/cache-policy.js +238 -0
  137. package/dist/registry/call-notes.d.ts +11 -0
  138. package/dist/registry/call-notes.d.ts.map +1 -0
  139. package/dist/registry/call-notes.js +110 -0
  140. package/dist/registry/claims.d.ts +63 -0
  141. package/dist/registry/claims.d.ts.map +1 -0
  142. package/dist/registry/claims.js +107 -0
  143. package/dist/registry/effective-rate.d.ts +151 -0
  144. package/dist/registry/effective-rate.d.ts.map +1 -0
  145. package/dist/registry/effective-rate.js +156 -0
  146. package/dist/registry/facts.d.ts +32 -0
  147. package/dist/registry/facts.d.ts.map +1 -0
  148. package/dist/registry/facts.js +136 -0
  149. package/dist/registry/fingerprint.d.ts +20 -0
  150. package/dist/registry/fingerprint.d.ts.map +1 -0
  151. package/dist/registry/fingerprint.js +150 -0
  152. package/dist/registry/parity.d.ts +52 -0
  153. package/dist/registry/parity.d.ts.map +1 -0
  154. package/dist/registry/parity.js +82 -0
  155. package/dist/registry/pricing-shape.d.ts +113 -0
  156. package/dist/registry/pricing-shape.d.ts.map +1 -0
  157. package/dist/registry/pricing-shape.js +123 -0
  158. package/dist/registry/pricing.d.ts +43 -0
  159. package/dist/registry/pricing.d.ts.map +1 -0
  160. package/dist/registry/pricing.js +127 -0
  161. package/dist/registry/rows.d.ts +16 -0
  162. package/dist/registry/rows.d.ts.map +1 -0
  163. package/dist/registry/rows.js +57 -0
  164. package/dist/registry/seed.d.ts +43 -0
  165. package/dist/registry/seed.d.ts.map +1 -0
  166. package/dist/registry/seed.js +97 -0
  167. package/dist/registry/select.d.ts +68 -0
  168. package/dist/registry/select.d.ts.map +1 -0
  169. package/dist/registry/select.js +141 -0
  170. package/dist/registry/staleness.d.ts +18 -0
  171. package/dist/registry/staleness.d.ts.map +1 -0
  172. package/dist/registry/staleness.js +39 -0
  173. package/dist/rigindex/bootstrap.d.ts +36 -0
  174. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  175. package/dist/rigindex/bootstrap.js +97 -0
  176. package/dist/rigindex/fit.d.ts +24 -0
  177. package/dist/rigindex/fit.d.ts.map +1 -0
  178. package/dist/rigindex/fit.js +76 -0
  179. package/dist/rigindex/gates.d.ts +52 -0
  180. package/dist/rigindex/gates.d.ts.map +1 -0
  181. package/dist/rigindex/gates.js +109 -0
  182. package/dist/rigindex/graph.d.ts +31 -0
  183. package/dist/rigindex/graph.d.ts.map +1 -0
  184. package/dist/rigindex/graph.js +120 -0
  185. package/dist/rigindex/index.d.ts +23 -0
  186. package/dist/rigindex/index.d.ts.map +1 -0
  187. package/dist/rigindex/index.js +112 -0
  188. package/dist/rigindex/types.d.ts +76 -0
  189. package/dist/rigindex/types.d.ts.map +1 -0
  190. package/dist/rigindex/types.js +8 -0
  191. package/dist/routes/cache-lint.d.ts +41 -0
  192. package/dist/routes/cache-lint.d.ts.map +1 -0
  193. package/dist/routes/cache-lint.js +81 -0
  194. package/dist/routes/draft.d.ts +83 -0
  195. package/dist/routes/draft.d.ts.map +1 -0
  196. package/dist/routes/draft.js +135 -0
  197. package/dist/routes/loader.d.ts +63 -0
  198. package/dist/routes/loader.d.ts.map +1 -0
  199. package/dist/routes/loader.js +574 -0
  200. package/dist/routes/manifest.d.ts +24 -0
  201. package/dist/routes/manifest.d.ts.map +1 -0
  202. package/dist/routes/manifest.js +118 -0
  203. package/dist/routes/resolved-bundle.d.ts +79 -0
  204. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  205. package/dist/routes/resolved-bundle.js +148 -0
  206. package/dist/routes/sampling-config.d.ts +32 -0
  207. package/dist/routes/sampling-config.d.ts.map +1 -0
  208. package/dist/routes/sampling-config.js +91 -0
  209. package/dist/routes/serveability.d.ts +52 -0
  210. package/dist/routes/serveability.d.ts.map +1 -0
  211. package/dist/routes/serveability.js +73 -0
  212. package/dist/routes/template.d.ts +18 -0
  213. package/dist/routes/template.d.ts.map +1 -0
  214. package/dist/routes/template.js +94 -0
  215. package/dist/run/ajv.d.ts +22 -0
  216. package/dist/run/ajv.d.ts.map +1 -0
  217. package/dist/run/ajv.js +32 -0
  218. package/dist/run/cache-handle.d.ts +58 -0
  219. package/dist/run/cache-handle.d.ts.map +1 -0
  220. package/dist/run/cache-handle.js +54 -0
  221. package/dist/run/cache-stamp.d.ts +65 -0
  222. package/dist/run/cache-stamp.d.ts.map +1 -0
  223. package/dist/run/cache-stamp.js +0 -0
  224. package/dist/run/capture-write.d.ts +33 -0
  225. package/dist/run/capture-write.d.ts.map +1 -0
  226. package/dist/run/capture-write.js +55 -0
  227. package/dist/run/cost.d.ts +26 -0
  228. package/dist/run/cost.d.ts.map +1 -0
  229. package/dist/run/cost.js +41 -0
  230. package/dist/run/dispatch.d.ts +99 -0
  231. package/dist/run/dispatch.d.ts.map +1 -0
  232. package/dist/run/dispatch.js +191 -0
  233. package/dist/run/envelope.d.ts +21 -0
  234. package/dist/run/envelope.d.ts.map +1 -0
  235. package/dist/run/envelope.js +45 -0
  236. package/dist/run/grounding.d.ts +56 -0
  237. package/dist/run/grounding.d.ts.map +1 -0
  238. package/dist/run/grounding.js +89 -0
  239. package/dist/run/raw.d.ts +88 -0
  240. package/dist/run/raw.d.ts.map +1 -0
  241. package/dist/run/raw.js +286 -0
  242. package/dist/run/record-attempt.d.ts +40 -0
  243. package/dist/run/record-attempt.d.ts.map +1 -0
  244. package/dist/run/record-attempt.js +99 -0
  245. package/dist/run/repair.d.ts +36 -0
  246. package/dist/run/repair.d.ts.map +1 -0
  247. package/dist/run/repair.js +61 -0
  248. package/dist/run/resolve.d.ts +99 -0
  249. package/dist/run/resolve.d.ts.map +1 -0
  250. package/dist/run/resolve.js +203 -0
  251. package/dist/run/retry.d.ts +19 -0
  252. package/dist/run/retry.d.ts.map +1 -0
  253. package/dist/run/retry.js +50 -0
  254. package/dist/run/run.d.ts +55 -0
  255. package/dist/run/run.d.ts.map +1 -0
  256. package/dist/run/run.js +540 -0
  257. package/dist/run/stream.d.ts +49 -0
  258. package/dist/run/stream.d.ts.map +1 -0
  259. package/dist/run/stream.js +87 -0
  260. package/dist/run/validate.d.ts +15 -0
  261. package/dist/run/validate.d.ts.map +1 -0
  262. package/dist/run/validate.js +55 -0
  263. package/dist/savings/cache-ledger.d.ts +108 -0
  264. package/dist/savings/cache-ledger.d.ts.map +1 -0
  265. package/dist/savings/cache-ledger.js +174 -0
  266. package/dist/savings/cache-reconcile.d.ts +64 -0
  267. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  268. package/dist/savings/cache-reconcile.js +59 -0
  269. package/dist/savings/cache-stats.d.ts +63 -0
  270. package/dist/savings/cache-stats.d.ts.map +1 -0
  271. package/dist/savings/cache-stats.js +108 -0
  272. package/dist/savings/emit.d.ts +38 -0
  273. package/dist/savings/emit.d.ts.map +1 -0
  274. package/dist/savings/emit.js +100 -0
  275. package/dist/savings/odometer.d.ts +32 -0
  276. package/dist/savings/odometer.d.ts.map +1 -0
  277. package/dist/savings/odometer.js +70 -0
  278. package/dist/savings/swap-execute.d.ts +58 -0
  279. package/dist/savings/swap-execute.d.ts.map +1 -0
  280. package/dist/savings/swap-execute.js +130 -0
  281. package/dist/savings/swaps-client.d.ts +55 -0
  282. package/dist/savings/swaps-client.d.ts.map +1 -0
  283. package/dist/savings/swaps-client.js +95 -0
  284. package/dist/settings/api-fetcher.d.ts +39 -0
  285. package/dist/settings/api-fetcher.d.ts.map +1 -0
  286. package/dist/settings/api-fetcher.js +92 -0
  287. package/dist/settings/index.d.ts +10 -0
  288. package/dist/settings/index.d.ts.map +1 -0
  289. package/dist/settings/index.js +15 -0
  290. package/dist/settings/task-settings.d.ts +91 -0
  291. package/dist/settings/task-settings.d.ts.map +1 -0
  292. package/dist/settings/task-settings.js +116 -0
  293. package/dist/status.d.ts +77 -0
  294. package/dist/status.d.ts.map +1 -0
  295. package/dist/status.js +113 -0
  296. package/dist/telemetry/artifact-store.d.ts +48 -0
  297. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  298. package/dist/telemetry/artifact-store.js +189 -0
  299. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  300. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  301. package/dist/telemetry/cache-miss-streak.js +99 -0
  302. package/dist/telemetry/capture.d.ts +31 -0
  303. package/dist/telemetry/capture.d.ts.map +1 -0
  304. package/dist/telemetry/capture.js +81 -0
  305. package/dist/telemetry/exporter-setup.d.ts +28 -0
  306. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  307. package/dist/telemetry/exporter-setup.js +34 -0
  308. package/dist/telemetry/exporter.d.ts +137 -0
  309. package/dist/telemetry/exporter.d.ts.map +1 -0
  310. package/dist/telemetry/exporter.js +510 -0
  311. package/dist/telemetry/health.d.ts +76 -0
  312. package/dist/telemetry/health.d.ts.map +1 -0
  313. package/dist/telemetry/health.js +92 -0
  314. package/dist/telemetry/ingest-protocol.d.ts +310 -0
  315. package/dist/telemetry/ingest-protocol.d.ts.map +1 -0
  316. package/dist/telemetry/ingest-protocol.js +363 -0
  317. package/dist/telemetry/load-error-report.d.ts +24 -0
  318. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  319. package/dist/telemetry/load-error-report.js +50 -0
  320. package/dist/telemetry/otel-map.d.ts +121 -0
  321. package/dist/telemetry/otel-map.d.ts.map +1 -0
  322. package/dist/telemetry/otel-map.js +224 -0
  323. package/dist/telemetry/otel-normalize.d.ts +104 -0
  324. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  325. package/dist/telemetry/otel-normalize.js +530 -0
  326. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  327. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  328. package/dist/telemetry/otlp-emitter.js +144 -0
  329. package/dist/telemetry/otlp-proto.d.ts +29 -0
  330. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  331. package/dist/telemetry/otlp-proto.js +266 -0
  332. package/dist/telemetry/sinks.d.ts +90 -0
  333. package/dist/telemetry/sinks.d.ts.map +1 -0
  334. package/dist/telemetry/sinks.js +180 -0
  335. package/dist/telemetry/store.d.ts +65 -0
  336. package/dist/telemetry/store.d.ts.map +1 -0
  337. package/dist/telemetry/store.js +547 -0
  338. package/dist/tenancy/api-key-format.d.ts +77 -0
  339. package/dist/tenancy/api-key-format.d.ts.map +1 -0
  340. package/dist/tenancy/api-key-format.js +110 -0
  341. package/dist/tenancy/constants.d.ts +23 -0
  342. package/dist/tenancy/constants.d.ts.map +1 -0
  343. package/dist/tenancy/constants.js +25 -0
  344. package/dist/tenancy/scrub.d.ts +47 -0
  345. package/dist/tenancy/scrub.d.ts.map +1 -0
  346. package/dist/tenancy/scrub.js +85 -0
  347. package/dist/types/config-teaching.d.ts +62 -0
  348. package/dist/types/config-teaching.d.ts.map +1 -0
  349. package/dist/types/config-teaching.js +183 -0
  350. package/dist/types/failures-teaching.d.ts +56 -0
  351. package/dist/types/failures-teaching.d.ts.map +1 -0
  352. package/dist/types/failures-teaching.js +177 -0
  353. package/dist/types/failures.d.ts +27 -0
  354. package/dist/types/failures.d.ts.map +1 -0
  355. package/dist/types/failures.js +41 -0
  356. package/dist/types/provider.d.ts +200 -0
  357. package/dist/types/provider.d.ts.map +1 -0
  358. package/dist/types/provider.js +40 -0
  359. package/dist/types/route.d.ts +205 -0
  360. package/dist/types/route.d.ts.map +1 -0
  361. package/dist/types/route.js +6 -0
  362. package/dist/types/telemetry.d.ts +206 -0
  363. package/dist/types/telemetry.d.ts.map +1 -0
  364. package/dist/types/telemetry.js +6 -0
  365. package/dist/validate-cli.d.ts +12 -0
  366. package/dist/validate-cli.d.ts.map +1 -0
  367. package/dist/validate-cli.js +57 -0
  368. package/dist/verify/capture.d.ts +66 -0
  369. package/dist/verify/capture.d.ts.map +1 -0
  370. package/dist/verify/capture.js +180 -0
  371. package/dist/verify/cli-run.d.ts +23 -0
  372. package/dist/verify/cli-run.d.ts.map +1 -0
  373. package/dist/verify/cli-run.js +57 -0
  374. package/dist/verify/diff.d.ts +48 -0
  375. package/dist/verify/diff.d.ts.map +1 -0
  376. package/dist/verify/diff.js +157 -0
  377. package/dist/verify/index.d.ts +17 -0
  378. package/dist/verify/index.d.ts.map +1 -0
  379. package/dist/verify/index.js +24 -0
  380. package/dist/verify/report.d.ts +14 -0
  381. package/dist/verify/report.d.ts.map +1 -0
  382. package/dist/verify/report.js +88 -0
  383. package/dist/verify/types.d.ts +101 -0
  384. package/dist/verify/types.d.ts.map +1 -0
  385. package/dist/verify/types.js +17 -0
  386. package/dist/verify/verify.d.ts +29 -0
  387. package/dist/verify/verify.d.ts.map +1 -0
  388. package/dist/verify/verify.js +148 -0
  389. package/dist/watch/budget.d.ts +19 -0
  390. package/dist/watch/budget.d.ts.map +1 -0
  391. package/dist/watch/budget.js +35 -0
  392. package/dist/watch/evaluate.d.ts +225 -0
  393. package/dist/watch/evaluate.d.ts.map +1 -0
  394. package/dist/watch/evaluate.js +155 -0
  395. package/dist/watch/monitor.d.ts +116 -0
  396. package/dist/watch/monitor.d.ts.map +1 -0
  397. package/dist/watch/monitor.js +199 -0
  398. package/dist/watch/proposals.d.ts +89 -0
  399. package/dist/watch/proposals.d.ts.map +1 -0
  400. package/dist/watch/proposals.js +197 -0
  401. package/dist/watch/run-record.d.ts +64 -0
  402. package/dist/watch/run-record.d.ts.map +1 -0
  403. package/dist/watch/run-record.js +86 -0
  404. package/dist/watch/run-reported.d.ts +20 -0
  405. package/dist/watch/run-reported.d.ts.map +1 -0
  406. package/dist/watch/run-reported.js +53 -0
  407. package/dist/watch/run.d.ts +73 -0
  408. package/dist/watch/run.d.ts.map +1 -0
  409. package/dist/watch/run.js +83 -0
  410. package/dist/watch/state-store.d.ts +48 -0
  411. package/dist/watch/state-store.d.ts.map +1 -0
  412. package/dist/watch/state-store.js +86 -0
  413. package/dist/watch/state.d.ts +21 -0
  414. package/dist/watch/state.d.ts.map +1 -0
  415. package/dist/watch/state.js +93 -0
  416. package/package.json +56 -14
  417. package/registry/LICENSE +20 -0
  418. package/registry/leaderboard.json +3639 -0
  419. package/registry/registry.json +7263 -0
  420. package/index.js +0 -3
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * Cache-hit-rate rollup — cache-aware-routing spec §5.1, WS-B. PURE: a function
4
+ * over telemetry rows, no store/clock/network (an injected `now` bounds the
5
+ * window). Feeds the effective-rate blend (§4.2), the derived ledger (§5.2), and
6
+ * the console (WS-D).
7
+ *
8
+ * ── THE ODOMETER RULE (replay exclusion) ──────────────────────────────────
9
+ * Trailing-window measurements count PRODUCTION serving only: a bake-off replay
10
+ * pass runs real (billed) calls tagged `replay`, and those must NEVER inflate a
11
+ * route's measured hit rate any more than they inflate measured savings. This is
12
+ * the SAME 5-line filter the savings odometer uses — see
13
+ * `savings/odometer.ts` (`JSON.parse(r.tagsJson).replay !== undefined`). It is
14
+ * duplicated (not imported) deliberately: the two live in different modules with
15
+ * different row shapes, and a shared 5-line helper would couple them for no real
16
+ * gain. If the replay tag convention ever changes, change BOTH.
17
+ *
18
+ * ── UNPRICED ROWS ARE KEPT ────────────────────────────────────────────────
19
+ * Unlike the odometer (which drops `pricingMissing` rows so a $0 cannot inflate
20
+ * dollar savings), the hit-rate rollup is a TOKEN measurement, not a dollar one:
21
+ * an unpriced row still carries honest token counts, so it counts toward h/w.
22
+ * Pricing enters later, in the effective-rate blend, where a missing rate
23
+ * fail-closes on its own.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.DEFAULT_CACHE_WINDOW_DAYS = void 0;
27
+ exports.rollupCacheStats = rollupCacheStats;
28
+ /** Trailing window for the rollup — 7 days by default (spec §5.1). */
29
+ exports.DEFAULT_CACHE_WINDOW_DAYS = 7;
30
+ /** The replay-exclusion predicate — see the module header (odometer parity).
31
+ * A row is production unless its tags carry a `replay` key. Unparseable tags
32
+ * are treated as production (a malformed tag never hides real traffic). */
33
+ function isProductionRow(row) {
34
+ try {
35
+ return JSON.parse(row.tagsJson).replay === undefined;
36
+ }
37
+ catch {
38
+ return true;
39
+ }
40
+ }
41
+ // Grouping key: none of route / provider / model contains a newline, so a
42
+ // newline separator splits back unambiguously.
43
+ const KEY_SEP = "\n";
44
+ /**
45
+ * Roll telemetry rows up into per-(route, provider, model) cache stats over the
46
+ * trailing window. Rows outside `[now − windowDays, now]`, with an unparseable
47
+ * ts, or tagged `replay` are excluded. Output is sorted (route, provider, model)
48
+ * for deterministic downstream rendering and stable snapshots.
49
+ */
50
+ function rollupCacheStats(rows, opts) {
51
+ const windowDays = opts.windowDays ?? exports.DEFAULT_CACHE_WINDOW_DAYS;
52
+ const windowStartMs = opts.now - windowDays * 86400000;
53
+ const byKey = new Map();
54
+ for (const row of rows) {
55
+ if (!isProductionRow(row))
56
+ continue;
57
+ const t = Date.parse(row.ts);
58
+ if (Number.isNaN(t) || t < windowStartMs || t > opts.now)
59
+ continue;
60
+ const key = `${row.route}${KEY_SEP}${row.provider}${KEY_SEP}${row.model}`;
61
+ let acc = byKey.get(key);
62
+ if (acc === undefined) {
63
+ acc = {
64
+ sampleN: 0,
65
+ tokensIn: 0,
66
+ tokensCached: 0,
67
+ tokensCacheWrite: 0,
68
+ lastSeenAtMs: -1,
69
+ lastSeenAt: null,
70
+ fingerprints: new Set(),
71
+ };
72
+ byKey.set(key, acc);
73
+ }
74
+ acc.sampleN += 1;
75
+ acc.tokensIn += Math.max(row.tokensIn, 0);
76
+ acc.tokensCached += Math.max(row.tokensCached, 0);
77
+ acc.tokensCacheWrite += Math.max(row.tokensCacheWrite ?? 0, 0);
78
+ if (t > acc.lastSeenAtMs) {
79
+ acc.lastSeenAtMs = t;
80
+ acc.lastSeenAt = row.ts;
81
+ }
82
+ if (row.prefixFingerprint != null && row.prefixFingerprint !== "") {
83
+ acc.fingerprints.add(row.prefixFingerprint);
84
+ }
85
+ }
86
+ const out = [];
87
+ for (const [key, acc] of byKey) {
88
+ const [route, provider, model] = key.split(KEY_SEP);
89
+ out.push({
90
+ route,
91
+ provider,
92
+ model,
93
+ sampleN: acc.sampleN,
94
+ tokensIn: acc.tokensIn,
95
+ tokensCached: acc.tokensCached,
96
+ tokensCacheWrite: acc.tokensCacheWrite,
97
+ h: acc.tokensIn > 0 ? acc.tokensCached / acc.tokensIn : 0,
98
+ w: acc.tokensIn > 0 ? acc.tokensCacheWrite / acc.tokensIn : 0,
99
+ lastSeenAt: acc.lastSeenAt,
100
+ prefixFingerprints: [...acc.fingerprints].sort(),
101
+ });
102
+ }
103
+ return out.sort((a, b) => a.route !== b.route
104
+ ? a.route.localeCompare(b.route)
105
+ : a.provider !== b.provider
106
+ ? a.provider.localeCompare(b.provider)
107
+ : a.model.localeCompare(b.model));
108
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Swap-change emission — Phase 4 WS4 (plan §4/§7). The executor NEVER edits
3
+ * route files: it renders the exact YAML the human applies via git, plus the
4
+ * revert block. This file deliberately imports no filesystem API — the
5
+ * "actions live in your code" invariant is structural, and emit.test.ts
6
+ * greps for it.
7
+ */
8
+ import type { CandidateSpec, RouteBundle } from "../types/route";
9
+ export interface SwapChange {
10
+ readonly route: string;
11
+ readonly file: string;
12
+ readonly fromModel: string;
13
+ readonly toModel: string;
14
+ readonly newVersion: number;
15
+ /** Paste-ready YAML fragment applying the swap (complete, no elisions). */
16
+ readonly yaml: string;
17
+ /** Paste-ready YAML fragment reverting it (version bumps again — history
18
+ * moves forward, never rewrites). */
19
+ readonly revertYaml: string;
20
+ /** Original candidate order — snapshotted into swap_events.baseline so a
21
+ * revert can be emitted even without the pre-swap file. */
22
+ readonly incumbentCandidates: readonly CandidateSpec[];
23
+ }
24
+ /**
25
+ * Restore-YAML for a REVERT — managed-loop §1 (`POST /v1/swaps/:id/revert`).
26
+ *
27
+ * A revert has no bake-off winner; it restores the swap's snapshotted incumbent
28
+ * candidates (`swap_events.baseline.incumbent_candidates`) at a version above the
29
+ * route's current one — history moves forward, never rewrites. Same
30
+ * no-filesystem, paste-ready shape as `buildSwapChange`; the server emits it and
31
+ * a human applies it via a PR (never-silent-swap extends to the revert).
32
+ */
33
+ export declare function buildRestoreYaml(route: string, incumbent: readonly {
34
+ readonly provider: string;
35
+ readonly model: string;
36
+ }[], currentVersion: number): string;
37
+ export declare function buildSwapChange(bundle: RouteBundle, winningVariant: string): SwapChange;
38
+ //# sourceMappingURL=emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../../src/savings/emit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAgB,MAAM,gBAAgB,CAAC;AAE/E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;yCACqC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;+DAC2D;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,SAAS,aAAa,EAAE,CAAC;CACxD;AAmCD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,EAC3E,cAAc,EAAE,MAAM,GACrB,MAAM,CAgBR;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CA6CvF"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * Swap-change emission — Phase 4 WS4 (plan §4/§7). The executor NEVER edits
4
+ * route files: it renders the exact YAML the human applies via git, plus the
5
+ * revert block. This file deliberately imports no filesystem API — the
6
+ * "actions live in your code" invariant is structural, and emit.test.ts
7
+ * greps for it.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.buildRestoreYaml = buildRestoreYaml;
11
+ exports.buildSwapChange = buildSwapChange;
12
+ function candidateLines(candidates) {
13
+ return candidates.flatMap((c) => [` - provider: ${c.provider}`, ` model: ${c.model}`]);
14
+ }
15
+ function overrideLines(variant) {
16
+ const lines = [];
17
+ if (variant.json !== undefined)
18
+ lines.push(`json: ${variant.json}`);
19
+ if (variant.repair !== undefined) {
20
+ lines.push("repair:");
21
+ lines.push(` max_repairs: ${variant.repair.maxRepairs}`);
22
+ if (variant.repair.repairModel !== undefined) {
23
+ lines.push(` repair_model: ${variant.repair.repairModel.provider}/${variant.repair.repairModel.model}`);
24
+ }
25
+ }
26
+ return lines;
27
+ }
28
+ /** Winner-first candidate order: the variant's candidates, then every other
29
+ * declared candidate in original order. Nothing is deleted — fallbacks and
30
+ * bake-off challengers keep their standing. */
31
+ function promotedOrder(declared, winners) {
32
+ const key = (c) => `${c.provider}/${c.model}`;
33
+ const winnerKeys = new Set(winners.map(key));
34
+ return [...winners, ...declared.filter((c) => !winnerKeys.has(key(c)))];
35
+ }
36
+ /**
37
+ * Restore-YAML for a REVERT — managed-loop §1 (`POST /v1/swaps/:id/revert`).
38
+ *
39
+ * A revert has no bake-off winner; it restores the swap's snapshotted incumbent
40
+ * candidates (`swap_events.baseline.incumbent_candidates`) at a version above the
41
+ * route's current one — history moves forward, never rewrites. Same
42
+ * no-filesystem, paste-ready shape as `buildSwapChange`; the server emits it and
43
+ * a human applies it via a PR (never-silent-swap extends to the revert).
44
+ */
45
+ function buildRestoreYaml(route, incumbent, currentVersion) {
46
+ if (incumbent.length === 0) {
47
+ throw new Error(`cannot emit a restore for "${route}" — the swap recorded no incumbent candidates to restore ` +
48
+ "(an older swap predating the baseline snapshot; revert it by hand from git history)");
49
+ }
50
+ const file = `modelrig/routes/${route}.yaml`;
51
+ return [
52
+ `# revert: restore ${route} to ${incumbent[0].provider}/${incumbent[0].model}`,
53
+ `# apply to ${file} — replace the version and candidates fields; nothing else changes`,
54
+ `version: ${currentVersion + 1}`,
55
+ "candidates:",
56
+ ...candidateLines(incumbent),
57
+ "",
58
+ ].join("\n");
59
+ }
60
+ function buildSwapChange(bundle, winningVariant) {
61
+ const variant = bundle.variants.find((v) => v.name === winningVariant);
62
+ if (variant === undefined) {
63
+ throw new Error(`route "${bundle.route}" has no variant "${winningVariant}" — cannot emit a swap for it`);
64
+ }
65
+ const winners = variant.candidates ?? bundle.candidates;
66
+ if (winners.length === 0) {
67
+ throw new Error(`variant "${winningVariant}" has no candidates to promote`);
68
+ }
69
+ const incumbent = bundle.candidates[0];
70
+ const promoted = promotedOrder(bundle.candidates, winners);
71
+ const file = `modelrig/routes/${bundle.route}.yaml`;
72
+ const newVersion = bundle.version + 1;
73
+ const yaml = [
74
+ `# swap: serve ${bundle.route} on ${winners[0].provider}/${winners[0].model}`,
75
+ `# apply to ${file} — replace the version and candidates fields; nothing else changes`,
76
+ `version: ${newVersion}`,
77
+ "candidates:",
78
+ ...candidateLines(promoted),
79
+ ...overrideLines(variant),
80
+ "",
81
+ ].join("\n");
82
+ const revertYaml = [
83
+ `# revert: restore ${bundle.route} to ${incumbent.provider}/${incumbent.model}`,
84
+ `# apply to ${file} — replace the version and candidates fields; nothing else changes`,
85
+ `version: ${newVersion + 1}`,
86
+ "candidates:",
87
+ ...candidateLines(bundle.candidates),
88
+ "",
89
+ ].join("\n");
90
+ return {
91
+ route: bundle.route,
92
+ file,
93
+ fromModel: `${incumbent.provider}/${incumbent.model}`,
94
+ toModel: `${winners[0].provider}/${winners[0].model}`,
95
+ newVersion,
96
+ yaml,
97
+ revertYaml,
98
+ incumbentCandidates: bundle.candidates,
99
+ };
100
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The savings odometer — Phase 4 WS4. Verified savings per executed swap:
3
+ *
4
+ * (baseline effective $/conformant − actual $/conformant since swap)
5
+ * × conformant volume since swap
6
+ *
7
+ * Rules (the odometer's credibility IS the brand — plan §10):
8
+ * - MEASURED only: computed from telemetry rows that actually flowed.
9
+ * Zero volume → $0, never a projection.
10
+ * - Honest costs: failed-attempt spend counts against the actual side;
11
+ * unpriced rows are excluded entirely (their $0 would inflate savings).
12
+ * - Negative results stay negative — a swap that made things worse shows it.
13
+ * - Reverted swaps stop accruing at reverted_at.
14
+ */
15
+ import type { TelemetryRow } from "../types/telemetry";
16
+ import type { SwapEventRecord } from "./swaps-client";
17
+ export interface SwapSavings {
18
+ readonly swapId: string;
19
+ readonly route: string;
20
+ readonly toVariant: string;
21
+ readonly executedAt: string;
22
+ readonly conformantVolume: number;
23
+ readonly actualUsdPerConformant: number | null;
24
+ readonly baselineUsdPerConformant: number;
25
+ readonly verifiedUsd: number;
26
+ }
27
+ export interface Odometer {
28
+ readonly totalVerifiedUsd: number;
29
+ readonly perSwap: readonly SwapSavings[];
30
+ }
31
+ export declare function computeOdometer(swaps: readonly SwapEventRecord[], rows: readonly TelemetryRow[]): Odometer;
32
+ //# sourceMappingURL=odometer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"odometer.d.ts","sourceRoot":"","sources":["../../src/savings/odometer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;CAC1C;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,eAAe,EAAE,EACjC,IAAI,EAAE,SAAS,YAAY,EAAE,GAC5B,QAAQ,CAoDV"}
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * The savings odometer — Phase 4 WS4. Verified savings per executed swap:
4
+ *
5
+ * (baseline effective $/conformant − actual $/conformant since swap)
6
+ * × conformant volume since swap
7
+ *
8
+ * Rules (the odometer's credibility IS the brand — plan §10):
9
+ * - MEASURED only: computed from telemetry rows that actually flowed.
10
+ * Zero volume → $0, never a projection.
11
+ * - Honest costs: failed-attempt spend counts against the actual side;
12
+ * unpriced rows are excluded entirely (their $0 would inflate savings).
13
+ * - Negative results stay negative — a swap that made things worse shows it.
14
+ * - Reverted swaps stop accruing at reverted_at.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.computeOdometer = computeOdometer;
18
+ function computeOdometer(swaps, rows) {
19
+ const perSwap = swaps.map((swap) => {
20
+ const from = Date.parse(swap.executedAt);
21
+ const until = swap.revertedAt !== null ? Date.parse(swap.revertedAt) : Number.POSITIVE_INFINITY;
22
+ const window = rows.filter((r) => {
23
+ if (r.route !== swap.route || r.pricingMissing)
24
+ return false;
25
+ // Replay/shadow traffic is not production serving — never let a
26
+ // bake-off run on the swapped route inflate measured savings.
27
+ try {
28
+ if (JSON.parse(r.tagsJson).replay !== undefined)
29
+ return false;
30
+ }
31
+ catch {
32
+ // Unparseable tags: treat as production (the conservative default
33
+ // here is to keep the row; a malformed tag never hides real spend).
34
+ }
35
+ const t = Date.parse(r.ts);
36
+ return t >= from && t < until;
37
+ });
38
+ const conformant = window.filter((r) => r.failureClass === null).length;
39
+ const spentUsd = window.reduce((a, r) => a + r.costEstimateUsd, 0);
40
+ const baselinePerConformant = swap.baseline.effective_usd_per_1k_conformant / 1000;
41
+ if (conformant === 0) {
42
+ // No conformant volume → nothing verified. $0, not a projection.
43
+ return {
44
+ swapId: swap.id,
45
+ route: swap.route,
46
+ toVariant: swap.toVariant,
47
+ executedAt: swap.executedAt,
48
+ conformantVolume: 0,
49
+ actualUsdPerConformant: null,
50
+ baselineUsdPerConformant: baselinePerConformant,
51
+ verifiedUsd: 0,
52
+ };
53
+ }
54
+ const actualPerConformant = spentUsd / conformant;
55
+ return {
56
+ swapId: swap.id,
57
+ route: swap.route,
58
+ toVariant: swap.toVariant,
59
+ executedAt: swap.executedAt,
60
+ conformantVolume: conformant,
61
+ actualUsdPerConformant: actualPerConformant,
62
+ baselineUsdPerConformant: baselinePerConformant,
63
+ verifiedUsd: (baselinePerConformant - actualPerConformant) * conformant,
64
+ };
65
+ });
66
+ return {
67
+ totalVerifiedUsd: perSwap.reduce((a, s) => a + s.verifiedUsd, 0),
68
+ perSwap,
69
+ };
70
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Swap execution orchestration — Phase 4 WS4, extracted from the CLI so the
3
+ * safety-critical path (the one that must NEVER auto-execute and must record
4
+ * exactly once) is hermetically testable with injected clients. The CLI is
5
+ * thin wiring over this; time, ids, and the approver are injected.
6
+ *
7
+ * Invariants enforced here (each has a test):
8
+ * - `approve` MUST be true — no --approve, no execution, no side effects.
9
+ * - Only an evidenced/approved proposal with a WINNING variant executes.
10
+ * - The incumbent baseline is snapshotted into swap_events (odometer seed),
11
+ * and it MUST be an explicit `default` evidence row — never a fallback.
12
+ * - Control-plane writes happen BEFORE the YAML is emitted (a process death
13
+ * after apply must not leave an unrecorded swap): approved → record →
14
+ * executed. Applying the YAML stays a human/git action.
15
+ */
16
+ import type { SwapChange } from "./emit";
17
+ import type { SwapEventRecord } from "./swaps-client";
18
+ import type { CacheContext } from "../watch/evaluate";
19
+ import type { CacheVerdict } from "../registry/effective-rate";
20
+ import type { ProposalRecord, ProposalStatus } from "../watch/proposals";
21
+ import type { RouteBundle } from "../types/route";
22
+ /** Narrow a proposal's `evidence.cache_context` jsonb (spec §5.5 — additive,
23
+ * present only on cache-aware proposals) to a typed `CacheContext`, or null when
24
+ * absent/malformed. Defensive: an older or non-cache proposal has no block, and
25
+ * a partial block must degrade to "no cache explanation", never throw on the
26
+ * safety-critical execute path. */
27
+ export declare function readCacheContext(evidence: Record<string, unknown> | null): CacheContext | null;
28
+ export interface SwapExecuteDeps {
29
+ readonly proposalId: string;
30
+ /** THE gate — false throws before any read or write. */
31
+ readonly approve: boolean;
32
+ readonly approvedBy: string;
33
+ readonly proposals: {
34
+ get(id: string): Promise<ProposalRecord | null>;
35
+ setStatus(id: string, from: ProposalStatus, to: ProposalStatus): Promise<void>;
36
+ };
37
+ readonly swaps: {
38
+ record(event: SwapEventRecord): Promise<void>;
39
+ };
40
+ readonly loadBundle: (route: string) => RouteBundle;
41
+ readonly newId: () => string;
42
+ readonly now: () => string;
43
+ }
44
+ export interface SwapExecuteResult {
45
+ readonly swapId: string;
46
+ readonly route: string;
47
+ readonly change: SwapChange;
48
+ readonly approvedBy: string;
49
+ readonly executedAt: string;
50
+ /** Cache-aware verdict from the proposal's `cache_context` evidence (spec
51
+ * §5.5), or null on a non-cache proposal. Additive — never gates execution
52
+ * (the human already approved); surfaced so the CLI/console can show WHY. */
53
+ readonly cacheVerdict: CacheVerdict | null;
54
+ /** The [Briefing §30] one-line explanation for the approval prompt, or null. */
55
+ readonly cacheExplanation: string | null;
56
+ }
57
+ export declare function executeSwap(deps: SwapExecuteDeps): Promise<SwapExecuteResult>;
58
+ //# sourceMappingURL=swap-execute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swap-execute.d.ts","sourceRoot":"","sources":["../../src/savings/swap-execute.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;mCAImC;AACnC,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,CAmB9F;AASD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE;QAClB,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;QAChD,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAChF,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE;QAAE,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IAClE,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,WAAW,CAAC;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;iFAE6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAwGnF"}
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * Swap execution orchestration — Phase 4 WS4, extracted from the CLI so the
4
+ * safety-critical path (the one that must NEVER auto-execute and must record
5
+ * exactly once) is hermetically testable with injected clients. The CLI is
6
+ * thin wiring over this; time, ids, and the approver are injected.
7
+ *
8
+ * Invariants enforced here (each has a test):
9
+ * - `approve` MUST be true — no --approve, no execution, no side effects.
10
+ * - Only an evidenced/approved proposal with a WINNING variant executes.
11
+ * - The incumbent baseline is snapshotted into swap_events (odometer seed),
12
+ * and it MUST be an explicit `default` evidence row — never a fallback.
13
+ * - Control-plane writes happen BEFORE the YAML is emitted (a process death
14
+ * after apply must not leave an unrecorded swap): approved → record →
15
+ * executed. Applying the YAML stays a human/git action.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.readCacheContext = readCacheContext;
19
+ exports.executeSwap = executeSwap;
20
+ const emit_1 = require("./emit");
21
+ const evaluate_1 = require("../watch/evaluate");
22
+ /** Narrow a proposal's `evidence.cache_context` jsonb (spec §5.5 — additive,
23
+ * present only on cache-aware proposals) to a typed `CacheContext`, or null when
24
+ * absent/malformed. Defensive: an older or non-cache proposal has no block, and
25
+ * a partial block must degrade to "no cache explanation", never throw on the
26
+ * safety-critical execute path. */
27
+ function readCacheContext(evidence) {
28
+ const raw = evidence?.cache_context;
29
+ if (raw === null || typeof raw !== "object")
30
+ return null;
31
+ const c = raw;
32
+ const verdicts = ["cache_positive", "cache_marginal", "cache_negative", "no_cache_signal"];
33
+ if (typeof c.verdict !== "string" || !verdicts.includes(c.verdict))
34
+ return null;
35
+ const num = (v) => (typeof v === "number" ? v : 0);
36
+ return {
37
+ observed_hit_rate: num(c.observed_hit_rate),
38
+ observed_write_rate: num(c.observed_write_rate),
39
+ window_days: num(c.window_days),
40
+ incumbent_effective_in_per_mtok: num(c.incumbent_effective_in_per_mtok),
41
+ challenger_cold_in_per_mtok: num(c.challenger_cold_in_per_mtok),
42
+ switch_cost_usd: num(c.switch_cost_usd),
43
+ cache_option_value_usd: num(c.cache_option_value_usd),
44
+ breakeven_conformant_calls: typeof c.breakeven_conformant_calls === "number" ? c.breakeven_conformant_calls : Number.POSITIVE_INFINITY,
45
+ verdict: c.verdict,
46
+ };
47
+ }
48
+ async function executeSwap(deps) {
49
+ // The gate first — before any client call, so a non-approved invocation
50
+ // has zero side effects (defense in depth with the CLI's fast-fail check).
51
+ if (!deps.approve) {
52
+ throw new Error("swap execution requires explicit --approve — proposals never auto-execute. " +
53
+ `Re-run: modelrig swap execute ${deps.proposalId} --approve`);
54
+ }
55
+ const proposal = await deps.proposals.get(deps.proposalId);
56
+ if (proposal === null)
57
+ throw new Error(`no proposal "${deps.proposalId}" on the control plane`);
58
+ if (proposal.status !== "evidenced" && proposal.status !== "approved") {
59
+ throw new Error(`proposal ${deps.proposalId} is ${proposal.status} — only evidenced proposals can be approved ` +
60
+ "(run `modelrig bakeoff --from-proposal` first; approval without evidence is not a thing)");
61
+ }
62
+ const winner = proposal.evidence?.proposal;
63
+ if (winner === null || winner === undefined) {
64
+ throw new Error(`proposal ${deps.proposalId} has no winning variant in its evidence — the incumbent stood; ` +
65
+ "nothing to execute");
66
+ }
67
+ // The odometer baseline MUST come from the incumbent — the `default` variant
68
+ // — and nothing else. There is no "close enough" row: seeding from a
69
+ // challenger would report savings against another challenger and quietly
70
+ // inflate every number the odometer ever shows. If in doubt, refuse.
71
+ const matrix = proposal.evidence?.matrix;
72
+ const incumbentRow = matrix?.find((m) => m.variant === "default");
73
+ if (incumbentRow === undefined) {
74
+ const seen = (matrix ?? []).map((m) => m.variant).join(", ") || "none";
75
+ throw new Error(`proposal ${deps.proposalId} evidence has no incumbent (variant "default") row — ` +
76
+ `cannot seed the odometer from a challenger. Variants present: ${seen}. ` +
77
+ "Re-run the bake-off so the incumbent is measured alongside the challengers " +
78
+ `(modelrig bakeoff --from-proposal ${deps.proposalId}).`);
79
+ }
80
+ if (incumbentRow.effective_usd_per_1k_conformant === null) {
81
+ throw new Error(`proposal ${deps.proposalId} evidence has no usable incumbent baseline — cannot seed the odometer`);
82
+ }
83
+ const bundle = deps.loadBundle(proposal.route);
84
+ const change = (0, emit_1.buildSwapChange)(bundle, winner.variant);
85
+ const baseline = {
86
+ effective_usd_per_1k_conformant: incumbentRow.effective_usd_per_1k_conformant,
87
+ conform_rate: incumbentRow.conform_rate,
88
+ samples: incumbentRow.samples,
89
+ bakeoff_id: String(proposal.evidence?.bakeoff_id ?? ""),
90
+ incumbent_candidates: change.incumbentCandidates.map((c) => ({
91
+ provider: c.provider,
92
+ model: c.model,
93
+ })),
94
+ };
95
+ const swapId = deps.newId();
96
+ const executedAt = deps.now();
97
+ // Record on the control plane FIRST — the emitted YAML is worthless as
98
+ // history if the process dies after the operator applies it.
99
+ if (proposal.status === "evidenced") {
100
+ await deps.proposals.setStatus(proposal.id, "evidenced", "approved");
101
+ }
102
+ await deps.swaps.record({
103
+ id: swapId,
104
+ proposalId: proposal.id,
105
+ route: proposal.route,
106
+ fromVariant: "default",
107
+ toVariant: winner.variant,
108
+ baseline,
109
+ executedAt,
110
+ approvedBy: deps.approvedBy,
111
+ revertedAt: null,
112
+ });
113
+ await deps.proposals.setStatus(proposal.id, "approved", "executed");
114
+ // Cache-aware explainability (spec §5.5) — additive, read-only. The verdict +
115
+ // prose ride along for the approval prompt / audit trail; they NEVER gate the
116
+ // swap (the human decision above is the only gate). Absent on non-cache
117
+ // proposals, in which case both are null and behavior is byte-identical.
118
+ const cacheContext = readCacheContext(proposal.evidence);
119
+ const incumbentLabel = change.incumbentCandidates.map((c) => `${c.provider}/${c.model}`).join(", ") || "incumbent";
120
+ const cacheExplanation = cacheContext !== null ? (0, evaluate_1.explainCacheContext)(cacheContext, incumbentLabel, winner.variant) : null;
121
+ return {
122
+ swapId,
123
+ route: proposal.route,
124
+ change,
125
+ approvedBy: deps.approvedBy,
126
+ executedAt,
127
+ cacheVerdict: cacheContext?.verdict ?? null,
128
+ cacheExplanation,
129
+ };
130
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * swap_events control-plane client — Phase 4 WS4. One row per approved,
3
+ * EXECUTED swap; the baseline snapshot is the odometer's fixed comparison
4
+ * point. Same PostgREST transport as proposals/exporter.
5
+ */
6
+ export interface SwapBaseline {
7
+ /** Incumbent's effective $ per 1K conformant from the evidence bake-off. */
8
+ readonly effective_usd_per_1k_conformant: number;
9
+ readonly conform_rate: number;
10
+ readonly samples: number;
11
+ readonly bakeoff_id: string;
12
+ /** Pre-swap candidate order — lets a revert be emitted later. */
13
+ readonly incumbent_candidates?: ReadonlyArray<{
14
+ provider: string;
15
+ model: string;
16
+ }>;
17
+ }
18
+ export interface SwapEventRecord {
19
+ readonly id: string;
20
+ readonly proposalId: string | null;
21
+ readonly route: string;
22
+ readonly fromVariant: string;
23
+ readonly toVariant: string;
24
+ readonly baseline: SwapBaseline;
25
+ readonly executedAt: string;
26
+ readonly approvedBy: string;
27
+ readonly revertedAt: string | null;
28
+ }
29
+ export interface SwapsClientConfig {
30
+ readonly supabaseUrl: string;
31
+ readonly serviceRoleKey: string;
32
+ /**
33
+ * The tenant this client writes/reads for. When set (the hosted managed-loop
34
+ * §1 swap surface + odometer): `record` STAMPS `org_id` on the row (else it
35
+ * falls to the house-org column default — filing a customer's swap under ours)
36
+ * and `list` scopes to this org (the odometer must never sum another tenant's
37
+ * swaps). When omitted, behaviour is unchanged for the CLI/self-host lane.
38
+ * `get`/`orgOf`/`markReverted` are keyed by id and org-agnostic.
39
+ */
40
+ readonly orgId?: string;
41
+ readonly fetchFn?: typeof fetch;
42
+ }
43
+ export interface SwapsClient {
44
+ record(event: SwapEventRecord): Promise<void>;
45
+ /** All executed swaps for this client's org (when `orgId` set) or every org
46
+ * (unscoped) — newest first. The odometer reader's source. */
47
+ list(): Promise<SwapEventRecord[]>;
48
+ get(id: string): Promise<SwapEventRecord | null>;
49
+ /** The org that owns a swap, or null if no such swap — the tenant a revert
50
+ * must be authorized against (T11), mirroring `proposals.orgOf`. */
51
+ orgOf(id: string): Promise<string | null>;
52
+ markReverted(id: string, revertedAt: string): Promise<void>;
53
+ }
54
+ export declare function createSwapsClient(config: SwapsClientConfig): SwapsClient;
55
+ //# sourceMappingURL=swaps-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swaps-client.d.ts","sourceRoot":"","sources":["../../src/savings/swaps-client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,CAAC,+BAA+B,EAAE,MAAM,CAAC;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,aAAa,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AA4BD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;kEAC8D;IAC9D,IAAI,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACnC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IACjD;wEACoE;IACpE,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAiFxE"}