modelrig 0.1.0 → 0.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 (310) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +53 -11
  3. package/data/PROVENANCE.md +19 -0
  4. package/data/pricing-manual.json +22 -0
  5. package/data/pricing-map.json +46362 -0
  6. package/data/pricing-overlay.json +419 -0
  7. package/dist/adapters/anthropic.d.ts +50 -6
  8. package/dist/adapters/anthropic.d.ts.map +1 -1
  9. package/dist/adapters/anthropic.js +123 -29
  10. package/dist/adapters/build-adapters.d.ts +11 -0
  11. package/dist/adapters/build-adapters.d.ts.map +1 -0
  12. package/dist/adapters/build-adapters.js +45 -0
  13. package/dist/adapters/deepseek.d.ts +6 -0
  14. package/dist/adapters/deepseek.d.ts.map +1 -1
  15. package/dist/adapters/deepseek.js +54 -1
  16. package/dist/adapters/gemini.d.ts +10 -0
  17. package/dist/adapters/gemini.d.ts.map +1 -1
  18. package/dist/adapters/gemini.js +78 -7
  19. package/dist/adapters/grok.d.ts +9 -0
  20. package/dist/adapters/grok.d.ts.map +1 -1
  21. package/dist/adapters/grok.js +67 -3
  22. package/dist/adapters/openai-compat.d.ts +6 -0
  23. package/dist/adapters/openai-compat.d.ts.map +1 -1
  24. package/dist/adapters/openai-compat.js +58 -1
  25. package/dist/adapters/openai-stream.d.ts +58 -0
  26. package/dist/adapters/openai-stream.d.ts.map +1 -0
  27. package/dist/adapters/openai-stream.js +120 -0
  28. package/dist/adapters/openai.d.ts +6 -0
  29. package/dist/adapters/openai.d.ts.map +1 -1
  30. package/dist/adapters/openai.js +60 -1
  31. package/dist/adapters/sampling.d.ts +28 -0
  32. package/dist/adapters/sampling.d.ts.map +1 -0
  33. package/dist/adapters/sampling.js +34 -0
  34. package/dist/adapters/tool-mapping.d.ts +92 -0
  35. package/dist/adapters/tool-mapping.d.ts.map +1 -0
  36. package/dist/adapters/tool-mapping.js +143 -0
  37. package/dist/artifacts/artifact.d.ts +137 -0
  38. package/dist/artifacts/artifact.d.ts.map +1 -0
  39. package/dist/artifacts/artifact.js +377 -0
  40. package/dist/artifacts/content-uploader.d.ts +87 -0
  41. package/dist/artifacts/content-uploader.d.ts.map +1 -0
  42. package/dist/artifacts/content-uploader.js +124 -0
  43. package/dist/artifacts/index.d.ts +63 -0
  44. package/dist/artifacts/index.d.ts.map +1 -0
  45. package/dist/artifacts/index.js +80 -0
  46. package/dist/artifacts/run-context.d.ts +129 -0
  47. package/dist/artifacts/run-context.d.ts.map +1 -0
  48. package/dist/artifacts/run-context.js +208 -0
  49. package/dist/artifacts/scrub.d.ts +60 -0
  50. package/dist/artifacts/scrub.d.ts.map +1 -0
  51. package/dist/artifacts/scrub.js +139 -0
  52. package/dist/artifacts/step.d.ts +26 -0
  53. package/dist/artifacts/step.d.ts.map +1 -0
  54. package/dist/artifacts/step.js +37 -0
  55. package/dist/artifacts/taint.d.ts +63 -0
  56. package/dist/artifacts/taint.d.ts.map +1 -0
  57. package/dist/artifacts/taint.js +76 -0
  58. package/dist/bakeoff/cache-sim.d.ts +99 -0
  59. package/dist/bakeoff/cache-sim.d.ts.map +1 -0
  60. package/dist/bakeoff/cache-sim.js +126 -0
  61. package/dist/bakeoff/eval-cases.d.ts +96 -0
  62. package/dist/bakeoff/eval-cases.d.ts.map +1 -0
  63. package/dist/bakeoff/eval-cases.js +105 -0
  64. package/dist/bakeoff/report.d.ts +41 -0
  65. package/dist/bakeoff/report.d.ts.map +1 -0
  66. package/dist/bakeoff/report.js +142 -0
  67. package/dist/bakeoff/samples.d.ts +15 -0
  68. package/dist/bakeoff/samples.d.ts.map +1 -1
  69. package/dist/bakeoff/samples.js +20 -0
  70. package/dist/cli.d.ts +1 -3
  71. package/dist/cli.d.ts.map +1 -1
  72. package/dist/cli.js +134 -116
  73. package/dist/config.d.ts +100 -0
  74. package/dist/config.d.ts.map +1 -1
  75. package/dist/config.js +104 -0
  76. package/dist/deprecations.d.ts +32 -0
  77. package/dist/deprecations.d.ts.map +1 -0
  78. package/dist/deprecations.js +59 -0
  79. package/dist/gateway/http-protocol.d.ts +175 -0
  80. package/dist/gateway/http-protocol.d.ts.map +1 -0
  81. package/dist/gateway/http-protocol.js +105 -0
  82. package/dist/index.d.ts +106 -19
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +266 -138
  85. package/dist/init/finder.d.ts +15 -0
  86. package/dist/init/finder.d.ts.map +1 -1
  87. package/dist/init/finder.js +46 -0
  88. package/dist/init/init-cli.d.ts +10 -0
  89. package/dist/init/init-cli.d.ts.map +1 -0
  90. package/dist/init/init-cli.js +86 -0
  91. package/dist/init/observe-cli.d.ts +19 -0
  92. package/dist/init/observe-cli.d.ts.map +1 -0
  93. package/dist/init/observe-cli.js +73 -0
  94. package/dist/init/observe.d.ts +81 -0
  95. package/dist/init/observe.d.ts.map +1 -0
  96. package/dist/init/observe.js +245 -0
  97. package/dist/registry/build.d.ts +94 -1
  98. package/dist/registry/build.d.ts.map +1 -1
  99. package/dist/registry/build.js +125 -4
  100. package/dist/registry/cache-policy.d.ts +94 -0
  101. package/dist/registry/cache-policy.d.ts.map +1 -0
  102. package/dist/registry/cache-policy.js +238 -0
  103. package/dist/registry/call-notes.d.ts.map +1 -1
  104. package/dist/registry/call-notes.js +40 -2
  105. package/dist/registry/effective-rate.d.ts +151 -0
  106. package/dist/registry/effective-rate.d.ts.map +1 -0
  107. package/dist/registry/effective-rate.js +156 -0
  108. package/dist/registry/facts.d.ts.map +1 -1
  109. package/dist/registry/facts.js +16 -0
  110. package/dist/registry/parity.d.ts +26 -0
  111. package/dist/registry/parity.d.ts.map +1 -1
  112. package/dist/registry/parity.js +43 -2
  113. package/dist/registry/pricing-shape.d.ts +113 -0
  114. package/dist/registry/pricing-shape.d.ts.map +1 -0
  115. package/dist/registry/pricing-shape.js +123 -0
  116. package/dist/registry/pricing.d.ts +25 -1
  117. package/dist/registry/pricing.d.ts.map +1 -1
  118. package/dist/registry/pricing.js +64 -6
  119. package/dist/rigindex/bootstrap.d.ts +36 -0
  120. package/dist/rigindex/bootstrap.d.ts.map +1 -0
  121. package/dist/rigindex/bootstrap.js +97 -0
  122. package/dist/rigindex/fit.d.ts +24 -0
  123. package/dist/rigindex/fit.d.ts.map +1 -0
  124. package/dist/rigindex/fit.js +76 -0
  125. package/dist/rigindex/gates.d.ts +52 -0
  126. package/dist/rigindex/gates.d.ts.map +1 -0
  127. package/dist/rigindex/gates.js +109 -0
  128. package/dist/rigindex/graph.d.ts +31 -0
  129. package/dist/rigindex/graph.d.ts.map +1 -0
  130. package/dist/rigindex/graph.js +120 -0
  131. package/dist/rigindex/index.d.ts +23 -0
  132. package/dist/rigindex/index.d.ts.map +1 -0
  133. package/dist/rigindex/index.js +112 -0
  134. package/dist/rigindex/types.d.ts +76 -0
  135. package/dist/rigindex/types.d.ts.map +1 -0
  136. package/dist/rigindex/types.js +8 -0
  137. package/dist/routes/cache-lint.d.ts +41 -0
  138. package/dist/routes/cache-lint.d.ts.map +1 -0
  139. package/dist/routes/cache-lint.js +81 -0
  140. package/dist/routes/loader.d.ts +48 -0
  141. package/dist/routes/loader.d.ts.map +1 -1
  142. package/dist/routes/loader.js +167 -0
  143. package/dist/routes/resolved-bundle.d.ts +79 -0
  144. package/dist/routes/resolved-bundle.d.ts.map +1 -0
  145. package/dist/routes/resolved-bundle.js +148 -0
  146. package/dist/routes/sampling-config.d.ts +32 -0
  147. package/dist/routes/sampling-config.d.ts.map +1 -0
  148. package/dist/routes/sampling-config.js +91 -0
  149. package/dist/run/ajv.d.ts +22 -0
  150. package/dist/run/ajv.d.ts.map +1 -0
  151. package/dist/run/ajv.js +32 -0
  152. package/dist/run/cache-handle.d.ts +58 -0
  153. package/dist/run/cache-handle.d.ts.map +1 -0
  154. package/dist/run/cache-handle.js +54 -0
  155. package/dist/run/cache-stamp.d.ts +65 -0
  156. package/dist/run/cache-stamp.d.ts.map +1 -0
  157. package/dist/run/cache-stamp.js +0 -0
  158. package/dist/run/capture-write.d.ts +33 -0
  159. package/dist/run/capture-write.d.ts.map +1 -0
  160. package/dist/run/capture-write.js +55 -0
  161. package/dist/run/cost.d.ts.map +1 -1
  162. package/dist/run/cost.js +1 -1
  163. package/dist/run/dispatch.d.ts +99 -0
  164. package/dist/run/dispatch.d.ts.map +1 -0
  165. package/dist/run/dispatch.js +191 -0
  166. package/dist/run/raw.d.ts +130 -0
  167. package/dist/run/raw.d.ts.map +1 -0
  168. package/dist/run/raw.js +363 -0
  169. package/dist/run/record-attempt.d.ts +40 -0
  170. package/dist/run/record-attempt.d.ts.map +1 -0
  171. package/dist/run/record-attempt.js +99 -0
  172. package/dist/run/resolve.d.ts +75 -2
  173. package/dist/run/resolve.d.ts.map +1 -1
  174. package/dist/run/resolve.js +126 -4
  175. package/dist/run/run.d.ts +14 -0
  176. package/dist/run/run.d.ts.map +1 -1
  177. package/dist/run/run.js +0 -0
  178. package/dist/run/stream.d.ts +49 -0
  179. package/dist/run/stream.d.ts.map +1 -0
  180. package/dist/run/stream.js +87 -0
  181. package/dist/run/validate.d.ts.map +1 -1
  182. package/dist/run/validate.js +6 -7
  183. package/dist/savings/cache-ledger.d.ts +108 -0
  184. package/dist/savings/cache-ledger.d.ts.map +1 -0
  185. package/dist/savings/cache-ledger.js +174 -0
  186. package/dist/savings/cache-reconcile.d.ts +64 -0
  187. package/dist/savings/cache-reconcile.d.ts.map +1 -0
  188. package/dist/savings/cache-reconcile.js +59 -0
  189. package/dist/savings/cache-stats.d.ts +63 -0
  190. package/dist/savings/cache-stats.d.ts.map +1 -0
  191. package/dist/savings/cache-stats.js +108 -0
  192. package/dist/savings/emit.d.ts +13 -0
  193. package/dist/savings/emit.d.ts.map +1 -1
  194. package/dist/savings/emit.js +25 -0
  195. package/dist/savings/swap-execute.d.ts +14 -0
  196. package/dist/savings/swap-execute.d.ts.map +1 -1
  197. package/dist/savings/swap-execute.js +44 -1
  198. package/dist/savings/swaps-client.d.ts +14 -0
  199. package/dist/savings/swaps-client.d.ts.map +1 -1
  200. package/dist/savings/swaps-client.js +16 -2
  201. package/dist/settings/api-fetcher.d.ts +39 -0
  202. package/dist/settings/api-fetcher.d.ts.map +1 -0
  203. package/dist/settings/api-fetcher.js +92 -0
  204. package/dist/settings/index.d.ts +10 -0
  205. package/dist/settings/index.d.ts.map +1 -0
  206. package/dist/settings/index.js +15 -0
  207. package/dist/settings/task-settings.d.ts +91 -0
  208. package/dist/settings/task-settings.d.ts.map +1 -0
  209. package/dist/settings/task-settings.js +116 -0
  210. package/dist/status.d.ts +77 -0
  211. package/dist/status.d.ts.map +1 -0
  212. package/dist/status.js +113 -0
  213. package/dist/telemetry/artifact-store.d.ts +48 -0
  214. package/dist/telemetry/artifact-store.d.ts.map +1 -0
  215. package/dist/telemetry/artifact-store.js +189 -0
  216. package/dist/telemetry/cache-miss-streak.d.ts +79 -0
  217. package/dist/telemetry/cache-miss-streak.d.ts.map +1 -0
  218. package/dist/telemetry/cache-miss-streak.js +99 -0
  219. package/dist/telemetry/capture.d.ts +3 -0
  220. package/dist/telemetry/capture.d.ts.map +1 -1
  221. package/dist/telemetry/capture.js +15 -8
  222. package/dist/telemetry/exporter-setup.d.ts +28 -0
  223. package/dist/telemetry/exporter-setup.d.ts.map +1 -0
  224. package/dist/telemetry/exporter-setup.js +34 -0
  225. package/dist/telemetry/exporter.d.ts +75 -0
  226. package/dist/telemetry/exporter.d.ts.map +1 -1
  227. package/dist/telemetry/exporter.js +324 -47
  228. package/dist/telemetry/health.d.ts +76 -0
  229. package/dist/telemetry/health.d.ts.map +1 -0
  230. package/dist/telemetry/health.js +92 -0
  231. package/dist/telemetry/ingest-protocol.d.ts +152 -3
  232. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  233. package/dist/telemetry/ingest-protocol.js +204 -4
  234. package/dist/telemetry/load-error-report.d.ts +24 -0
  235. package/dist/telemetry/load-error-report.d.ts.map +1 -0
  236. package/dist/telemetry/load-error-report.js +50 -0
  237. package/dist/telemetry/otel-map.d.ts +121 -0
  238. package/dist/telemetry/otel-map.d.ts.map +1 -0
  239. package/dist/telemetry/otel-map.js +224 -0
  240. package/dist/telemetry/otel-normalize.d.ts +104 -0
  241. package/dist/telemetry/otel-normalize.d.ts.map +1 -0
  242. package/dist/telemetry/otel-normalize.js +530 -0
  243. package/dist/telemetry/otlp-emitter.d.ts +52 -0
  244. package/dist/telemetry/otlp-emitter.d.ts.map +1 -0
  245. package/dist/telemetry/otlp-emitter.js +144 -0
  246. package/dist/telemetry/otlp-proto.d.ts +29 -0
  247. package/dist/telemetry/otlp-proto.d.ts.map +1 -0
  248. package/dist/telemetry/otlp-proto.js +266 -0
  249. package/dist/telemetry/sinks.d.ts +37 -1
  250. package/dist/telemetry/sinks.d.ts.map +1 -1
  251. package/dist/telemetry/sinks.js +121 -30
  252. package/dist/telemetry/store.d.ts +28 -1
  253. package/dist/telemetry/store.d.ts.map +1 -1
  254. package/dist/telemetry/store.js +218 -3
  255. package/dist/tenancy/api-key-format.d.ts +15 -5
  256. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  257. package/dist/tenancy/api-key-format.js +24 -5
  258. package/dist/types/config-teaching.d.ts +62 -0
  259. package/dist/types/config-teaching.d.ts.map +1 -0
  260. package/dist/types/config-teaching.js +183 -0
  261. package/dist/types/provider.d.ts +147 -1
  262. package/dist/types/provider.d.ts.map +1 -1
  263. package/dist/types/provider.js +21 -1
  264. package/dist/types/route.d.ts +79 -1
  265. package/dist/types/route.d.ts.map +1 -1
  266. package/dist/types/telemetry.d.ts +117 -0
  267. package/dist/types/telemetry.d.ts.map +1 -1
  268. package/dist/validate-cli.d.ts +12 -0
  269. package/dist/validate-cli.d.ts.map +1 -0
  270. package/dist/validate-cli.js +57 -0
  271. package/dist/verify/capture.d.ts +66 -0
  272. package/dist/verify/capture.d.ts.map +1 -0
  273. package/dist/verify/capture.js +180 -0
  274. package/dist/verify/cli-run.d.ts +23 -0
  275. package/dist/verify/cli-run.d.ts.map +1 -0
  276. package/dist/verify/cli-run.js +57 -0
  277. package/dist/verify/diff.d.ts +48 -0
  278. package/dist/verify/diff.d.ts.map +1 -0
  279. package/dist/verify/diff.js +157 -0
  280. package/dist/verify/index.d.ts +17 -0
  281. package/dist/verify/index.d.ts.map +1 -0
  282. package/dist/verify/index.js +24 -0
  283. package/dist/verify/report.d.ts +14 -0
  284. package/dist/verify/report.d.ts.map +1 -0
  285. package/dist/verify/report.js +88 -0
  286. package/dist/verify/types.d.ts +101 -0
  287. package/dist/verify/types.d.ts.map +1 -0
  288. package/dist/verify/types.js +17 -0
  289. package/dist/verify/verify.d.ts +29 -0
  290. package/dist/verify/verify.d.ts.map +1 -0
  291. package/dist/verify/verify.js +148 -0
  292. package/dist/watch/evaluate.d.ts +162 -3
  293. package/dist/watch/evaluate.d.ts.map +1 -1
  294. package/dist/watch/evaluate.js +61 -0
  295. package/dist/watch/monitor.d.ts +116 -0
  296. package/dist/watch/monitor.d.ts.map +1 -0
  297. package/dist/watch/monitor.js +199 -0
  298. package/dist/watch/proposals.d.ts +20 -4
  299. package/dist/watch/proposals.d.ts.map +1 -1
  300. package/dist/watch/proposals.js +31 -3
  301. package/dist/watch/run.d.ts +33 -0
  302. package/dist/watch/run.d.ts.map +1 -1
  303. package/dist/watch/run.js +39 -0
  304. package/package.json +7 -1
  305. package/registry/LICENSE +20 -0
  306. package/registry/leaderboard.json +3639 -0
  307. package/registry/registry.json +7263 -0
  308. package/dist/telemetry/supabase-exporter.d.ts +0 -64
  309. package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
  310. package/dist/telemetry/supabase-exporter.js +0 -211
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bake-off terminal reporting — the matrix printer (moved here from cli.ts for
3
+ * the LOC cap, P1) and the `--from-eval-cases` runner (E4 chunk A) with its
4
+ * HONEST-REFUSAL report: every eval case that could not be rehydrated is NAMED
5
+ * with a reason before any bake-off runs — never a silent subset (spec §3.3).
6
+ */
7
+ import type { RigConfig } from "../config";
8
+ import type { CacheSimAssumptions } from "./cache-sim";
9
+ import type { KeepOutputsMode } from "./samples";
10
+ import type { BakeoffArtifact } from "./types";
11
+ /**
12
+ * One variant's measured-cold vs cache-adjusted input cost (cache-aware-routing
13
+ * spec §5.4). Built by the caller from `simulateCache` + the candidate's rates;
14
+ * this module only RENDERS it, so the report stays pure of pricing lookups.
15
+ */
16
+ export interface CacheAdjustedRow {
17
+ readonly variant: string;
18
+ /** Input cost the way replay actually paid it — every token at list (§5.4). */
19
+ readonly measuredColdUsd: number;
20
+ /** Input cost at production cadence — reads/writes priced per policy (§5.4). */
21
+ readonly cacheAdjustedUsd: number;
22
+ readonly assumptions: CacheSimAssumptions;
23
+ }
24
+ /**
25
+ * Render the cache-adjusted section (spec §5.4) as lines — measured-cold AND
26
+ * cache-adjusted input cost side by side, with the simulator's assumptions
27
+ * PRINTED so the numbers are never mistaken for measured savings. Pure (returns
28
+ * strings; `printMatrix` does the console I/O) so it is unit-tested directly.
29
+ * Empty input ⇒ no lines (the section is omitted entirely — back-compat).
30
+ */
31
+ export declare function formatCacheAdjustedSection(rows: readonly CacheAdjustedRow[]): string[];
32
+ export declare function printMatrix(artifact: BakeoffArtifact, cacheAdjusted?: readonly CacheAdjustedRow[]): void;
33
+ /**
34
+ * `modelrig bakeoff --from-eval-cases <task> --variants a,b,c` — replay a task's
35
+ * promoted eval cases through each variant. Prints the honest-refusal report,
36
+ * then (if anything rehydrated) runs the bake-off and persists/mirrors/prints it.
37
+ * Returns the process exit code: 0 on a run (or an empty suite), 1 when cases
38
+ * existed but NONE could be rehydrated (an actionable non-zero, not a crash).
39
+ */
40
+ export declare function runEvalCaseBakeoffCli(config: RigConfig, task: string, variants: readonly string[], envelope: number | undefined, keep: KeepOutputsMode | undefined, asJson: boolean): Promise<number>;
41
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/bakeoff/report.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAM/D;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,gFAAgF;IAChF,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAgCtF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,aAAa,GAAE,SAAS,gBAAgB,EAAO,GAAG,IAAI,CAgC5G;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,eAAe,GAAG,SAAS,EACjC,MAAM,EAAE,OAAO,GACd,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ /**
3
+ * Bake-off terminal reporting — the matrix printer (moved here from cli.ts for
4
+ * the LOC cap, P1) and the `--from-eval-cases` runner (E4 chunk A) with its
5
+ * HONEST-REFUSAL report: every eval case that could not be rehydrated is NAMED
6
+ * with a reason before any bake-off runs — never a silent subset (spec §3.3).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.formatCacheAdjustedSection = formatCacheAdjustedSection;
10
+ exports.printMatrix = printMatrix;
11
+ exports.runEvalCaseBakeoffCli = runEvalCaseBakeoffCli;
12
+ const node_fs_1 = require("node:fs");
13
+ const node_os_1 = require("node:os");
14
+ const node_path_1 = require("node:path");
15
+ const index_1 = require("../index");
16
+ const engine_1 = require("./engine");
17
+ function fmtEff(usd) {
18
+ return Number.isFinite(usd) ? `$${usd.toFixed(4)}` : "∞ (0 conformant)";
19
+ }
20
+ /**
21
+ * Render the cache-adjusted section (spec §5.4) as lines — measured-cold AND
22
+ * cache-adjusted input cost side by side, with the simulator's assumptions
23
+ * PRINTED so the numbers are never mistaken for measured savings. Pure (returns
24
+ * strings; `printMatrix` does the console I/O) so it is unit-tested directly.
25
+ * Empty input ⇒ no lines (the section is omitted entirely — back-compat).
26
+ */
27
+ function formatCacheAdjustedSection(rows) {
28
+ if (rows.length === 0)
29
+ return [];
30
+ const lines = [
31
+ "cache-adjusted input cost (SIMULATED — not measured savings; the odometer stays measured-only):",
32
+ ];
33
+ const header = ["variant", "measured-cold", "cache-adjusted", "sim-savings"];
34
+ const body = rows.map((r) => {
35
+ const savings = r.measuredColdUsd > 0
36
+ ? `${(((r.measuredColdUsd - r.cacheAdjustedUsd) / r.measuredColdUsd) * 100).toFixed(1)}%`
37
+ : "n/a";
38
+ return [r.variant, `$${r.measuredColdUsd.toFixed(4)}`, `$${r.cacheAdjustedUsd.toFixed(4)}`, savings];
39
+ });
40
+ const widths = header.map((h, c) => Math.max(h.length, ...body.map((row) => row[c].length)));
41
+ const line = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(" ");
42
+ lines.push(line(header));
43
+ for (const row of body)
44
+ lines.push(line(row));
45
+ // Assumptions basis — one line per distinct policy so a mixed-provider matrix
46
+ // is honest about which TTL/min-cacheable each arm assumed.
47
+ const seen = new Set();
48
+ for (const r of rows) {
49
+ const a = r.assumptions;
50
+ const key = `${a.provider}/${a.model}`;
51
+ if (seen.has(key))
52
+ continue;
53
+ seen.add(key);
54
+ lines.push(` assumptions ${key}: ${a.cacheSupported ? "" : "cache unsupported; "}ttl=${a.ttlSeconds}s` +
55
+ `${a.ttlSourced ? "" : " (fallback — undocumented)"}, min-cacheable=${a.minCacheableTokens}tok, ` +
56
+ `refresh-on-read=${a.refreshOnRead}`);
57
+ }
58
+ return lines;
59
+ }
60
+ function printMatrix(artifact, cacheAdjusted = []) {
61
+ console.log(`bakeoff ${artifact.id}\nroute ${artifact.route}@${artifact.routeVersion} · replayed ${artifact.replayN} captured inputs · ${artifact.ranAt}`);
62
+ const header = ["variant", "n", "conform", "ci95", "repair", "repair$", "lat(ms)", "eff-$/1K-conf"];
63
+ const rows = artifact.variants.map((m) => [
64
+ m.variant,
65
+ String(m.samples),
66
+ `${(m.conformRate * 100).toFixed(0)}%`,
67
+ `[${(m.conformCi95[0] * 100).toFixed(0)},${(m.conformCi95[1] * 100).toFixed(0)}]`,
68
+ `${(m.repairRate * 100).toFixed(0)}%`,
69
+ `$${m.meanRepairCostUsd.toFixed(4)}`,
70
+ m.meanLatencyMs.toFixed(0),
71
+ m.pricingMissing ? "UNPRICED (no ranking)" : fmtEff(m.effectiveUsdPer1kConformant),
72
+ ]);
73
+ const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => r[c].length)));
74
+ const line = (cells) => cells.map((cell, c) => cell.padEnd(widths[c])).join(" ");
75
+ console.log(line(header));
76
+ for (const row of rows)
77
+ console.log(line(row));
78
+ if (artifact.proposal !== null) {
79
+ console.log(`PROPOSAL: variant "${artifact.proposal.variant}" beats the incumbent by ` +
80
+ `${artifact.proposal.vsIncumbentSavingsPct.toFixed(1)}% effective cost — human decision required; ` +
81
+ "nothing is executed.");
82
+ }
83
+ else {
84
+ console.log("proposal: none (no variant beats the incumbent's gates)");
85
+ }
86
+ // Cache-adjusted section (spec §5.4) — printed only when the caller supplied
87
+ // simulator rows; absent ⇒ the matrix output is byte-identical to pre-WS-B.
88
+ for (const l of formatCacheAdjustedSection(cacheAdjusted))
89
+ console.log(l);
90
+ }
91
+ /**
92
+ * `modelrig bakeoff --from-eval-cases <task> --variants a,b,c` — replay a task's
93
+ * promoted eval cases through each variant. Prints the honest-refusal report,
94
+ * then (if anything rehydrated) runs the bake-off and persists/mirrors/prints it.
95
+ * Returns the process exit code: 0 on a run (or an empty suite), 1 when cases
96
+ * existed but NONE could be rehydrated (an actionable non-zero, not a crash).
97
+ */
98
+ async function runEvalCaseBakeoffCli(config, task, variants, envelope, keep, asJson) {
99
+ const mirrorTarget = config.export?.mode === "direct" ? config.export : undefined;
100
+ const rig = (0, index_1.createRig)(config);
101
+ try {
102
+ const { selection, artifact } = await rig.bakeoffFromEvalCases(task, {
103
+ variants,
104
+ ...(envelope !== undefined ? { envelopeUsdPerVariant: envelope } : {}),
105
+ ...(keep !== undefined ? { keepOutputs: keep } : {}),
106
+ });
107
+ console.log(`eval cases for "${task}": ${selection.total} total · ` +
108
+ `${selection.rehydratable.length} rehydratable · ${selection.refusals.length} refused`);
109
+ // NEVER a silent subset: name every refused case with its reason.
110
+ for (const r of selection.refusals) {
111
+ console.log(` refused ${r.caseId} (source artifact ${r.sourceArtifactId}): ${r.reason}`);
112
+ }
113
+ if (artifact === null) {
114
+ console.log(selection.total === 0
115
+ ? `no eval cases promoted for "${task}" yet — promote a failure first (artifact.promoteToEval).`
116
+ : "nothing rehydratable — no bake-off run (see the refusals above).");
117
+ return selection.total === 0 ? 0 : 1;
118
+ }
119
+ const outDir = (0, node_path_1.join)((0, node_path_1.dirname)(config.telemetryDbPath), "bakeoffs");
120
+ (0, node_fs_1.mkdirSync)(outDir, { recursive: true });
121
+ const artifactPath = (0, node_path_1.join)(outDir, `${artifact.id}.json`);
122
+ (0, node_fs_1.writeFileSync)(artifactPath, `${JSON.stringify(artifact, null, 2)}\n`);
123
+ let mirrored = false;
124
+ if (mirrorTarget !== undefined) {
125
+ await (0, engine_1.mirrorBakeoff)(artifact, {
126
+ supabaseUrl: mirrorTarget.supabaseUrl,
127
+ serviceRoleKey: mirrorTarget.serviceRoleKey,
128
+ sourceId: mirrorTarget.sourceId ?? (0, node_os_1.hostname)(),
129
+ });
130
+ mirrored = true;
131
+ }
132
+ if (asJson)
133
+ console.log(JSON.stringify(artifact, null, 2));
134
+ else
135
+ printMatrix(artifact);
136
+ console.log(`artifact → ${artifactPath}${mirrored ? " · mirrored to bakeoffs" : ""}`);
137
+ return 0;
138
+ }
139
+ finally {
140
+ await rig.close();
141
+ }
142
+ }
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * Transport is raw PostgREST over fetch (same pattern as the exporter).
11
11
  */
12
+ import type { BakeoffSamplesSink } from "./engine";
12
13
  /** 256KB per output, per the architect's caps; TOAST compresses at rest. */
13
14
  export declare const OUTPUT_MAX_BYTES: number;
14
15
  export type KeepOutputsMode = "winner" | "all";
@@ -59,4 +60,18 @@ export interface BakeoffSamplesClient {
59
60
  }): Promise<number>;
60
61
  }
61
62
  export declare function createBakeoffSamplesClient(config: BakeoffSamplesClientConfig): BakeoffSamplesClient;
63
+ /**
64
+ * Build the direct-mode bake-off review-sample sink (extracted from index.ts for
65
+ * the LOC cap, P1). Persists retained outputs to the SEPARATE `bakeoff_samples`
66
+ * store (service role; deny-anon RLS); captures never touch it.
67
+ *
68
+ * DIRECT MODE ONLY, and that is a real limitation, not an oversight:
69
+ * `bakeoff_samples` has no ingest route (telemetry-ingestion-boundary.md §2
70
+ * covers telemetry, not review samples), so an api-mode rig keeps its retained
71
+ * outputs local. Callers pass `undefined` for a non-direct rig.
72
+ */
73
+ export declare function buildDirectSamplesSink(cfg: {
74
+ readonly supabaseUrl: string;
75
+ readonly serviceRoleKey: string;
76
+ }): BakeoffSamplesSink;
62
77
  //# sourceMappingURL=samples.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;CACJ;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,EAAE,CAKV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAoBjB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAoEnG"}
1
+ {"version":3,"file":"samples.d.ts","sourceRoot":"","sources":["../../src/bakeoff/samples.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD,4EAA4E;AAC5E,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,qDAAqD;AACrD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;QAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;QAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;KAC5B,CAAC,CAAC;CACJ;AAED,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,EAAE,CAKV;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;+DAC+D;AAC/D,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,SAAK,GAAG,MAAM,CAEhE;AAED,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,UAAU,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,aAAa,EAAE,CAoBjB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,2EAA2E;IAC3E,KAAK,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAoEnG;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,GAAG,kBAAkB,CAQrB"}
@@ -17,6 +17,7 @@ exports.truncateOutput = truncateOutput;
17
17
  exports.retentionExpiry = retentionExpiry;
18
18
  exports.buildBakeoffSamples = buildBakeoffSamples;
19
19
  exports.createBakeoffSamplesClient = createBakeoffSamplesClient;
20
+ exports.buildDirectSamplesSink = buildDirectSamplesSink;
20
21
  /** 256KB per output, per the architect's caps; TOAST compresses at rest. */
21
22
  exports.OUTPUT_MAX_BYTES = 256 * 1024;
22
23
  /** Default persists incumbent + winner only; "all" persists every arm. */
@@ -126,3 +127,22 @@ function createBakeoffSamplesClient(config) {
126
127
  },
127
128
  };
128
129
  }
130
+ /**
131
+ * Build the direct-mode bake-off review-sample sink (extracted from index.ts for
132
+ * the LOC cap, P1). Persists retained outputs to the SEPARATE `bakeoff_samples`
133
+ * store (service role; deny-anon RLS); captures never touch it.
134
+ *
135
+ * DIRECT MODE ONLY, and that is a real limitation, not an oversight:
136
+ * `bakeoff_samples` has no ingest route (telemetry-ingestion-boundary.md §2
137
+ * covers telemetry, not review samples), so an api-mode rig keeps its retained
138
+ * outputs local. Callers pass `undefined` for a non-direct rig.
139
+ */
140
+ function buildDirectSamplesSink(cfg) {
141
+ return async ({ bakeoffId, route, arms }) => {
142
+ const rows = buildBakeoffSamples(bakeoffId, route, arms, retentionExpiry(Date.now()));
143
+ await createBakeoffSamplesClient({
144
+ supabaseUrl: cfg.supabaseUrl,
145
+ serviceRoleKey: cfg.serviceRoleKey,
146
+ }).insert(rows);
147
+ };
148
+ }
package/dist/cli.d.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
4
4
  * modelrig bakeoff --route X --variants a,b,c --replay-last N
5
- * [--envelope usd] [--json] [--no-mirror]
5
+ * [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
6
6
  * modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
7
7
  * modelrig watch run [--events fixtures.json] [--json]
8
8
  *
@@ -15,13 +15,11 @@
15
15
  * executes a swap — that is `modelrig swap execute --approve`, a separate,
16
16
  * human command.
17
17
  */
18
- import type { BakeoffArtifact } from "./bakeoff/types";
19
18
  export interface Flags {
20
19
  readonly positional: readonly string[];
21
20
  readonly named: ReadonlyMap<string, string>;
22
21
  readonly bare: ReadonlySet<string>;
23
22
  }
24
23
  export declare function parseArgs(argv: readonly string[]): Flags;
25
- export declare function printMatrix(artifact: BakeoffArtifact): void;
26
24
  export declare function main(argv: readonly string[]): Promise<number>;
27
25
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AAgBH,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,iBAAiB,CAAC;AAcvE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAaD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AAMD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI,CA6B3D;AAqfD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCnE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;GAeG;AAiCH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACpC;AAeD,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,KAAK,CAuBxD;AA0gBD,wBAAsB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCnE"}
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  /**
4
4
  * modelrig CLI — Phase 3 plan §3 + Phase 4 plan §3:
5
5
  * modelrig bakeoff --route X --variants a,b,c --replay-last N
6
- * [--envelope usd] [--json] [--no-mirror]
6
+ * [--budget-usd usd] [--json] [--no-mirror] (--envelope is a kept alias)
7
7
  * modelrig bakeoff --from-proposal <id> [--replay-last N] [--json]
8
8
  * modelrig watch run [--events fixtures.json] [--json]
9
9
  *
@@ -18,21 +18,24 @@
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.parseArgs = parseArgs;
21
- exports.printMatrix = printMatrix;
22
21
  exports.main = main;
23
22
  const node_crypto_1 = require("node:crypto");
24
23
  const node_fs_1 = require("node:fs");
25
24
  const node_os_1 = require("node:os");
26
25
  const node_path_1 = require("node:path");
27
- const run_1 = require("./init/run");
26
+ const init_cli_1 = require("./init/init-cli");
27
+ const observe_cli_1 = require("./init/observe-cli");
28
28
  const failures_1 = require("./types/failures");
29
29
  const failures_teaching_1 = require("./types/failures-teaching");
30
- const rows_1 = require("./registry/rows");
31
30
  const config_1 = require("./config");
32
31
  const index_1 = require("./index");
32
+ const status_1 = require("./status");
33
33
  const engine_1 = require("./bakeoff/engine");
34
34
  const samples_1 = require("./bakeoff/samples");
35
+ const report_1 = require("./bakeoff/report");
36
+ const cli_run_1 = require("./verify/cli-run");
35
37
  const loader_1 = require("./routes/loader");
38
+ const validate_cli_1 = require("./validate-cli");
36
39
  const store_1 = require("./telemetry/store");
37
40
  const budget_1 = require("./watch/budget");
38
41
  const proposals_1 = require("./watch/proposals");
@@ -50,6 +53,8 @@ const BARE_FLAGS = new Set([
50
53
  // modelrig init (Phase 5 WS2)
51
54
  "dry-run",
52
55
  "include-tests",
56
+ // modelrig observe / init --observe (T0/T1 scaffold)
57
+ "observe",
53
58
  ]);
54
59
  function parseArgs(argv) {
55
60
  const positional = [];
@@ -76,36 +81,6 @@ function parseArgs(argv) {
76
81
  }
77
82
  return { positional, named, bare };
78
83
  }
79
- function fmtEff(usd) {
80
- return Number.isFinite(usd) ? `$${usd.toFixed(4)}` : "∞ (0 conformant)";
81
- }
82
- function printMatrix(artifact) {
83
- console.log(`bakeoff ${artifact.id}\nroute ${artifact.route}@${artifact.routeVersion} · replayed ${artifact.replayN} captured inputs · ${artifact.ranAt}`);
84
- const header = ["variant", "n", "conform", "ci95", "repair", "repair$", "lat(ms)", "eff-$/1K-conf"];
85
- const rows = artifact.variants.map((m) => [
86
- m.variant,
87
- String(m.samples),
88
- `${(m.conformRate * 100).toFixed(0)}%`,
89
- `[${(m.conformCi95[0] * 100).toFixed(0)},${(m.conformCi95[1] * 100).toFixed(0)}]`,
90
- `${(m.repairRate * 100).toFixed(0)}%`,
91
- `$${m.meanRepairCostUsd.toFixed(4)}`,
92
- m.meanLatencyMs.toFixed(0),
93
- m.pricingMissing ? "UNPRICED (no ranking)" : fmtEff(m.effectiveUsdPer1kConformant),
94
- ]);
95
- const widths = header.map((h, c) => Math.max(h.length, ...rows.map((r) => r[c].length)));
96
- const line = (cells) => cells.map((cell, c) => cell.padEnd(widths[c])).join(" ");
97
- console.log(line(header));
98
- for (const row of rows)
99
- console.log(line(row));
100
- if (artifact.proposal !== null) {
101
- console.log(`PROPOSAL: variant "${artifact.proposal.variant}" beats the incumbent by ` +
102
- `${artifact.proposal.vsIncumbentSavingsPct.toFixed(1)}% effective cost — human decision required; ` +
103
- "nothing is executed.");
104
- }
105
- else {
106
- console.log("proposal: none (no variant beats the incumbent's gates)");
107
- }
108
- }
109
84
  /** Control-plane client from config.export — the watch/proposal surfaces
110
85
  * need it; a clear error beats a silent no-op. */
111
86
  function proposalsClientFrom(config) {
@@ -153,7 +128,7 @@ async function runAndReportBakeoff(config, flags, route, variants, replayLastN,
153
128
  console.log(JSON.stringify(artifact, null, 2));
154
129
  }
155
130
  else {
156
- printMatrix(artifact);
131
+ (0, report_1.printMatrix)(artifact);
157
132
  }
158
133
  console.log(`artifact → ${artifactPath}${mirrored ? " · mirrored to bakeoffs" : ""}` +
159
134
  (keep !== undefined
@@ -162,7 +137,9 @@ async function runAndReportBakeoff(config, flags, route, variants, replayLastN,
162
137
  return artifact;
163
138
  }
164
139
  finally {
165
- rig.close();
140
+ // One-shot CLI: await the settled drain so the exporter's final batch is
141
+ // guaranteed to land before the process exits (H1).
142
+ await rig.close();
166
143
  }
167
144
  }
168
145
  async function cmdBakeoff(flags) {
@@ -225,6 +202,19 @@ async function cmdBakeoff(flags) {
225
202
  budgetStore.close();
226
203
  }
227
204
  }
205
+ // Replay a task's promoted eval cases instead of the last-N captures (E4 chunk A).
206
+ const evalTask = flags.named.get("from-eval-cases");
207
+ if (evalTask !== undefined) {
208
+ const vRaw = flags.named.get("variants");
209
+ if (vRaw === undefined)
210
+ throw new Error("bakeoff --from-eval-cases <task> requires --variants a,b,c");
211
+ const vs = vRaw.split(",").map((v) => v.trim()).filter((v) => v !== "");
212
+ const envRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
213
+ const env = envRaw !== undefined ? Number(envRaw) : undefined;
214
+ if (env !== undefined && (!Number.isFinite(env) || env <= 0))
215
+ throw new Error("--budget-usd must be positive");
216
+ return (0, report_1.runEvalCaseBakeoffCli)(config, evalTask, vs, env, keepOutputsMode(flags), flags.bare.has("json"));
217
+ }
228
218
  const route = flags.named.get("route");
229
219
  const variantsRaw = flags.named.get("variants");
230
220
  const lastRaw = flags.named.get("replay-last");
@@ -236,10 +226,11 @@ async function cmdBakeoff(flags) {
236
226
  if (!Number.isInteger(replayLastN) || replayLastN < 1) {
237
227
  throw new Error("--replay-last must be a positive integer");
238
228
  }
239
- const envelopeRaw = flags.named.get("envelope");
229
+ // §4: --budget-usd is the named envelope knob; --envelope is kept as an alias.
230
+ const envelopeRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
240
231
  const envelope = envelopeRaw !== undefined ? Number(envelopeRaw) : undefined;
241
232
  if (envelope !== undefined && (!Number.isFinite(envelope) || envelope <= 0)) {
242
- throw new Error("--envelope must be positive");
233
+ throw new Error("--budget-usd must be a positive number of dollars (per replay variant)");
243
234
  }
244
235
  await runAndReportBakeoff(config, flags, route, variants, replayLastN, envelope);
245
236
  return 0;
@@ -413,75 +404,30 @@ async function cmdSamples(flags) {
413
404
  return 0;
414
405
  }
415
406
  /**
416
- * `modelrig init [path]` — Phase 5 WS2. Scans a repo for provider call
417
- * sites and drafts a route bundle per site, plus a MIGRATION.md map.
418
- *
419
- * It writes ONLY under <routes-dir>/drafts/. Your source is never edited;
420
- * applying the migration is a human's diff to make.
421
- */
422
- async function cmdInit(flags) {
423
- const root = (0, node_path_1.resolve)(flags.positional[0] ?? ".");
424
- const config = (0, config_1.loadConfigFromEnv)();
425
- const draftsDir = flags.named.get("out") ?? (0, node_path_1.join)(config.routesDir, "drafts");
426
- // Registry rows seed the challenger candidates. A missing/unbuilt registry
427
- // is not fatal — drafts then carry the incumbent alone, and say so.
428
- let registryRows = [];
429
- try {
430
- registryRows = (0, rows_1.loadRegistryRows)(config.registryPath);
407
+ * `modelrig verify-swap` — the autonomy-ladder Tier-2 verify step (Expansion
408
+ * §9). Replays a LOCAL capture fixture through the drafted SAME-MODEL route,
409
+ * diffs each output against the original, and emits the report the Tier-2 PR
410
+ * embeds. A plumbing proof; refuses uncapturable (streaming/dynamic) sites. */
411
+ async function cmdVerifySwap(flags) {
412
+ const route = flags.named.get("route");
413
+ const fixturePath = flags.named.get("captures");
414
+ if (route === undefined || fixturePath === undefined) {
415
+ throw new Error("verify-swap requires --route <route> and --captures <fixture.json> " +
416
+ "(the local capture of the original call's inputs/outputs)");
431
417
  }
432
- catch {
433
- registryRows = [];
418
+ const envRaw = flags.named.get("budget-usd") ?? flags.named.get("envelope");
419
+ const envelopeUsd = envRaw !== undefined ? Number(envRaw) : undefined;
420
+ if (envelopeUsd !== undefined && (!Number.isFinite(envelopeUsd) || envelopeUsd <= 0)) {
421
+ throw new Error("--budget-usd must be a positive number of dollars (the replay-pass envelope)");
434
422
  }
435
- const result = (0, run_1.runInit)({
436
- root,
437
- draftsDir,
438
- fs: { readdirSync: node_fs_1.readdirSync, readFileSync: node_fs_1.readFileSync, statSync: node_fs_1.statSync, existsSync: node_fs_1.existsSync, mkdirSync: node_fs_1.mkdirSync, writeFileSync: node_fs_1.writeFileSync },
439
- path: { join: node_path_1.join, relative: node_path_1.relative, dirname: node_path_1.dirname, basename: node_path_1.basename },
440
- }, {
441
- registryRows,
442
- ...(flags.bare.has("dry-run") ? { dryRun: true } : {}),
443
- ...(flags.bare.has("include-tests") ? { includeTests: true } : {}),
423
+ const config = (0, config_1.loadConfigFromEnv)();
424
+ return (0, cli_run_1.runVerifySwapCli)(config, {
425
+ route,
426
+ fixturePath,
427
+ ...(envelopeUsd !== undefined ? { envelopeUsd } : {}),
428
+ ...(flags.bare.has("json") ? { asJson: true } : {}),
429
+ ...(flags.named.get("out") !== undefined ? { outPath: flags.named.get("out") } : {}),
444
430
  });
445
- if (flags.bare.has("json")) {
446
- console.log(JSON.stringify({
447
- sitesFound: result.sitesFound,
448
- filesScanned: result.filesScanned,
449
- filesSkipped: result.filesSkipped,
450
- routes: result.drafts.map((d) => ({
451
- route: d.route,
452
- file: d.site.file,
453
- line: d.site.line,
454
- provider: d.site.provider,
455
- model: d.site.model,
456
- notes: d.site.notes,
457
- })),
458
- written: result.written,
459
- skipped: result.skipped,
460
- }, null, 2));
461
- return 0;
462
- }
463
- console.log(`modelrig init — scanned ${result.filesScanned} file(s) under ${root}\n` +
464
- `found ${result.sitesFound} provider call site(s)`);
465
- if (result.sitesFound === 0) {
466
- console.log("\nNothing to draft. If you know there are model calls here, they may sit behind a helper\n" +
467
- "that hides the SDK — point init at the file that actually calls the provider.");
468
- return 0;
469
- }
470
- for (const draft of result.drafts) {
471
- console.log(` ${draft.site.file}:${draft.site.line} (${draft.site.provider}` +
472
- `${draft.site.model !== null ? ` ${draft.site.model}` : ""}) → ${draft.route}`);
473
- }
474
- if (result.filesSkipped.length > 0) {
475
- console.log(`\n${result.filesSkipped.length} file(s) skipped (too large or unreadable).`);
476
- }
477
- if (flags.bare.has("dry-run")) {
478
- console.log("\n--dry-run: nothing written.");
479
- return 0;
480
- }
481
- console.log(`\nwrote ${result.written.length} file(s) to ${draftsDir}` +
482
- (result.skipped.length > 0 ? ` (${result.skipped.length} left alone — already there)` : "") +
483
- `\nread ${(0, node_path_1.join)(draftsDir, "MIGRATION.md")} next. Your source was not touched.`);
484
- return 0;
485
431
  }
486
432
  /**
487
433
  * `modelrig serve` — Phase 5 WS4's zero-config path. The HTTP surface lives
@@ -519,10 +465,86 @@ async function cmdServe() {
519
465
  // The server owns the process from here (it holds the listener open).
520
466
  return 0;
521
467
  }
468
+ /** `modelrig status` — print the runtime posture (§2): export mode, artifact
469
+ * namespace on/off + WHY, content custody, OTLP. Reads config only (no rig, no
470
+ * routes dir), so it works anywhere the env is set. */
471
+ function cmdStatus() {
472
+ console.log((0, status_1.formatStatusReport)((0, status_1.buildRigStatus)((0, config_1.loadConfigFromEnv)())));
473
+ return 0;
474
+ }
475
+ /** `modelrig judge <run>` — E5 V.4: explicitly ask the bounded model-judge to
476
+ * score a run. Goes through the API boundary (T12: the CLI never holds a DB
477
+ * client) — POST /v1/runs/:id/judge on the configured server, authed with a
478
+ * rig_sk key. The daily cap + metering are the SERVER's; this just triggers one. */
479
+ async function cmdJudge(flags) {
480
+ const runId = flags.positional[0] ?? "";
481
+ // Config is the only env reader (package-invariants rule). The judge endpoint
482
+ // lives on the same server as the api-mode ingest sink, so the base is that
483
+ // URL's origin and the credential is the api key.
484
+ const exp = (0, config_1.loadConfigFromEnv)().export;
485
+ const api = exp !== undefined && exp.mode === "api" ? exp : undefined;
486
+ const base = api !== undefined ? new URL(api.ingestUrl).origin : "";
487
+ const key = api?.apiKey ?? "";
488
+ if (runId === "" || base === "" || key === "") {
489
+ console.error([
490
+ "usage: modelrig judge <runId>",
491
+ "",
492
+ "Needs api-mode telemetry configured (the judge is on the same server):",
493
+ " MODELRIG_API_KEY a rig_sk_ key with the `oracle-act` scope",
494
+ " MODELRIG_INGEST_URL optional — the server base; defaults to the hosted API",
495
+ "",
496
+ "The judge is EXPLICIT-only and metered; scoring rides the gateway (a route),",
497
+ "so the org's daily cap and spend-stops apply. Nothing schedules it.",
498
+ ].join("\n"));
499
+ return 1;
500
+ }
501
+ const res = await fetch(`${base}/v1/runs/${encodeURIComponent(runId)}/judge`, {
502
+ method: "POST",
503
+ headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
504
+ body: "{}",
505
+ });
506
+ const body = (await res.json().catch(() => ({})));
507
+ if (!res.ok) {
508
+ console.error(`judge ${res.status}: ${typeof body.error === "string" ? body.error : "failed"}`);
509
+ return 1;
510
+ }
511
+ console.log(JSON.stringify(body, null, 2));
512
+ return 0;
513
+ }
514
+ /** The one usage block, printed to stdout on `--help` (exit 0) and to stderr on
515
+ * an unknown command (exit 1). Kept as a const so the two paths can never
516
+ * drift. */
517
+ const USAGE = [
518
+ "usage:",
519
+ " modelrig init [path] [--scope glob,glob] [--out dir] [--dry-run] [--include-tests] [--json]",
520
+ " modelrig observe [path] [--scope glob,glob] [--out dir] [--dry-run] [--json] (T0/T1: see your runs first — no migration)",
521
+ " modelrig validate (structurally check rig.yaml + every route bundle — no provider key needed)",
522
+ " modelrig status (print export/artifact/otlp posture)",
523
+ " modelrig serve (needs the modelrig-server package)",
524
+ " modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--budget-usd usd] [--keep-outputs[=all]] [--json] [--no-mirror]",
525
+ " modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
526
+ " modelrig watch run [--events fixtures.json] [--json]",
527
+ " modelrig verify-swap --route <route> --captures <fixture.json> [--budget-usd usd] [--out report.md] [--json]",
528
+ " modelrig swap execute <proposalId> --approve [--approved-by name]",
529
+ " modelrig swap revert <swapId>",
530
+ " modelrig judge <runId> (E5: score a run with the bounded model-judge; explicit + metered)",
531
+ " modelrig samples purge [--bakeoff <id>]",
532
+ ].join("\n");
522
533
  async function main(argv) {
523
534
  const [command, ...rest] = argv;
535
+ // `--help`/`-h`/`help` is a success: print usage to stdout and exit 0. (An
536
+ // unknown command still prints usage to stderr and exits 1, below.) The
537
+ // clean-room tarball test asserts the shipped bin exits 0 on --help.
538
+ if (command === "--help" || command === "-h" || command === "help") {
539
+ console.log(USAGE);
540
+ return 0;
541
+ }
524
542
  const flags = parseArgs(rest);
525
543
  switch (command) {
544
+ case "status":
545
+ return cmdStatus();
546
+ case "validate":
547
+ return (0, validate_cli_1.runValidateCli)();
526
548
  case "bakeoff":
527
549
  return cmdBakeoff(flags);
528
550
  case "watch":
@@ -532,21 +554,17 @@ async function main(argv) {
532
554
  case "samples":
533
555
  return cmdSamples(flags);
534
556
  case "init":
535
- return cmdInit(flags);
557
+ return (0, init_cli_1.cmdInit)(flags);
558
+ case "observe":
559
+ return (0, observe_cli_1.cmdObserve)(flags);
560
+ case "verify-swap":
561
+ return cmdVerifySwap(flags);
562
+ case "judge":
563
+ return cmdJudge(flags);
536
564
  case "serve":
537
565
  return cmdServe();
538
566
  default:
539
- console.error([
540
- "usage:",
541
- " modelrig init [path] [--out dir] [--dry-run] [--include-tests] [--json]",
542
- " modelrig serve (needs the modelrig-server package)",
543
- " modelrig bakeoff --route <route> --variants a,b,c --replay-last N [--envelope usd] [--keep-outputs[=all]] [--json] [--no-mirror]",
544
- " modelrig bakeoff --from-proposal <id> [--replay-last N] [--keep-outputs[=all]] [--json]",
545
- " modelrig watch run [--events fixtures.json] [--json]",
546
- " modelrig swap execute <proposalId> --approve [--approved-by name]",
547
- " modelrig swap revert <swapId>",
548
- " modelrig samples purge [--bakeoff <id>]",
549
- ].join("\n"));
567
+ console.error(USAGE);
550
568
  return 1;
551
569
  }
552
570
  }