modelrig 0.8.1 → 0.10.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 (382) hide show
  1. package/CHANGELOG.md +324 -0
  2. package/README.md +55 -29
  3. package/data/PROVENANCE.md +14 -0
  4. package/data/pricing-map.json +26 -0
  5. package/dist/adapters/attempt-meta-map.d.ts +8 -1
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
  7. package/dist/adapters/attempt-meta-map.js +4 -0
  8. package/dist/adapters/build-adapters.js +2 -2
  9. package/dist/adapters/gemini.d.ts +28 -1
  10. package/dist/adapters/gemini.d.ts.map +1 -1
  11. package/dist/adapters/gemini.js +38 -9
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +53 -4
  14. package/dist/adapters/openai-compat.d.ts +8 -2
  15. package/dist/adapters/openai-compat.d.ts.map +1 -1
  16. package/dist/adapters/openai-compat.js +8 -1
  17. package/dist/adapters/openai-stream.d.ts +10 -2
  18. package/dist/adapters/openai-stream.d.ts.map +1 -1
  19. package/dist/adapters/openai-stream.js +5 -1
  20. package/dist/adapters/openai.d.ts +10 -1
  21. package/dist/adapters/openai.d.ts.map +1 -1
  22. package/dist/adapters/openai.js +57 -7
  23. package/dist/adapters/responses-api.d.ts +61 -0
  24. package/dist/adapters/responses-api.d.ts.map +1 -0
  25. package/dist/adapters/responses-api.js +124 -0
  26. package/dist/artifacts/artifact.d.ts +31 -12
  27. package/dist/artifacts/artifact.d.ts.map +1 -1
  28. package/dist/artifacts/artifact.js +218 -188
  29. package/dist/artifacts/index.d.ts +15 -5
  30. package/dist/artifacts/index.d.ts.map +1 -1
  31. package/dist/artifacts/index.js +19 -5
  32. package/dist/artifacts/run-context.d.ts +3 -24
  33. package/dist/artifacts/run-context.d.ts.map +1 -1
  34. package/dist/artifacts/run-context.js +3 -39
  35. package/dist/bakeoff/engine.d.ts +29 -0
  36. package/dist/bakeoff/engine.d.ts.map +1 -1
  37. package/dist/bakeoff/engine.js +47 -0
  38. package/dist/bakeoff/eval-cases-rig.d.ts +36 -0
  39. package/dist/bakeoff/eval-cases-rig.d.ts.map +1 -0
  40. package/dist/bakeoff/eval-cases-rig.js +27 -0
  41. package/dist/bakeoff/eval-cases.d.ts +19 -0
  42. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  43. package/dist/bakeoff/eval-cases.js +18 -1
  44. package/dist/bakeoff/golden-judge.d.ts +47 -0
  45. package/dist/bakeoff/golden-judge.d.ts.map +1 -0
  46. package/dist/bakeoff/golden-judge.js +49 -0
  47. package/dist/bakeoff/groundedness.d.ts +58 -0
  48. package/dist/bakeoff/groundedness.d.ts.map +1 -0
  49. package/dist/bakeoff/groundedness.js +165 -0
  50. package/dist/bakeoff/quality.d.ts +43 -0
  51. package/dist/bakeoff/quality.d.ts.map +1 -1
  52. package/dist/bakeoff/quality.js +31 -0
  53. package/dist/bakeoff/replay.d.ts +11 -0
  54. package/dist/bakeoff/replay.d.ts.map +1 -1
  55. package/dist/bakeoff/replay.js +12 -0
  56. package/dist/bakeoff/report.d.ts +7 -0
  57. package/dist/bakeoff/report.d.ts.map +1 -1
  58. package/dist/bakeoff/report.js +26 -0
  59. package/dist/cli-judge.d.ts +12 -0
  60. package/dist/cli-judge.d.ts.map +1 -0
  61. package/dist/cli-judge.js +48 -0
  62. package/dist/cli.d.ts.map +1 -1
  63. package/dist/cli.js +22 -42
  64. package/dist/config.d.ts +85 -29
  65. package/dist/config.d.ts.map +1 -1
  66. package/dist/config.js +109 -47
  67. package/dist/detect/detector.d.ts +4 -2
  68. package/dist/detect/detector.d.ts.map +1 -1
  69. package/dist/detect/safe-harbor.d.ts +57 -0
  70. package/dist/detect/safe-harbor.d.ts.map +1 -0
  71. package/dist/detect/safe-harbor.js +278 -0
  72. package/dist/doctor-cli.d.ts +7 -0
  73. package/dist/doctor-cli.d.ts.map +1 -0
  74. package/dist/doctor-cli.js +67 -0
  75. package/dist/doctor-preflight.d.ts +52 -0
  76. package/dist/doctor-preflight.d.ts.map +1 -0
  77. package/dist/doctor-preflight.js +112 -0
  78. package/dist/feedback/coverage.d.ts +11 -8
  79. package/dist/feedback/coverage.d.ts.map +1 -1
  80. package/dist/feedback/coverage.js +29 -16
  81. package/dist/feedback/grade.d.ts +58 -0
  82. package/dist/feedback/grade.d.ts.map +1 -0
  83. package/dist/feedback/grade.js +67 -0
  84. package/dist/gateway/http-protocol.d.ts +20 -1
  85. package/dist/gateway/http-protocol.d.ts.map +1 -1
  86. package/dist/gateway/http-protocol.js +1 -1
  87. package/dist/gateway/serve-client.d.ts +99 -0
  88. package/dist/gateway/serve-client.d.ts.map +1 -0
  89. package/dist/gateway/serve-client.js +360 -0
  90. package/dist/index.d.ts +68 -29
  91. package/dist/index.d.ts.map +1 -1
  92. package/dist/index.js +167 -46
  93. package/dist/init/observe-cli.js +1 -1
  94. package/dist/init/observe.d.ts +2 -2
  95. package/dist/init/observe.d.ts.map +1 -1
  96. package/dist/init/observe.js +11 -7
  97. package/dist/mask/arm.d.ts +112 -0
  98. package/dist/mask/arm.d.ts.map +1 -0
  99. package/dist/mask/arm.js +179 -0
  100. package/dist/mask/index.d.ts +2 -0
  101. package/dist/mask/index.d.ts.map +1 -1
  102. package/dist/mask/index.js +7 -1
  103. package/dist/mask/mask-policy-cli.d.ts +12 -4
  104. package/dist/mask/mask-policy-cli.d.ts.map +1 -1
  105. package/dist/mask/mask-policy-cli.js +34 -9
  106. package/dist/prompts/manifest.d.ts +128 -0
  107. package/dist/prompts/manifest.d.ts.map +1 -0
  108. package/dist/prompts/manifest.js +214 -0
  109. package/dist/prompts/prompt-fetch.d.ts +84 -0
  110. package/dist/prompts/prompt-fetch.d.ts.map +1 -0
  111. package/dist/prompts/prompt-fetch.js +120 -0
  112. package/dist/prompts/prompt-resolve.d.ts +76 -0
  113. package/dist/prompts/prompt-resolve.d.ts.map +1 -0
  114. package/dist/prompts/prompt-resolve.js +93 -0
  115. package/dist/prompts/slot-resolver.d.ts +91 -0
  116. package/dist/prompts/slot-resolver.d.ts.map +1 -0
  117. package/dist/prompts/slot-resolver.js +135 -0
  118. package/dist/prompts/tools-register.d.ts +60 -0
  119. package/dist/prompts/tools-register.d.ts.map +1 -0
  120. package/dist/prompts/tools-register.js +48 -0
  121. package/dist/prompts/version-hash.d.ts +29 -0
  122. package/dist/prompts/version-hash.d.ts.map +1 -0
  123. package/dist/prompts/version-hash.js +54 -0
  124. package/dist/prompts-export-cli.d.ts +21 -0
  125. package/dist/prompts-export-cli.d.ts.map +1 -0
  126. package/dist/prompts-export-cli.js +130 -0
  127. package/dist/query/grammar.d.ts +2 -2
  128. package/dist/query/grammar.d.ts.map +1 -1
  129. package/dist/query/grammar.js +3 -1
  130. package/dist/query/saved.d.ts +13 -0
  131. package/dist/query/saved.d.ts.map +1 -1
  132. package/dist/query/saved.js +67 -1
  133. package/dist/receipts/types.d.ts +1 -1
  134. package/dist/receipts/types.d.ts.map +1 -1
  135. package/dist/receipts/types.js +1 -0
  136. package/dist/registry/build.d.ts +43 -1
  137. package/dist/registry/build.d.ts.map +1 -1
  138. package/dist/registry/build.js +52 -0
  139. package/dist/registry/cache-policy.d.ts.map +1 -1
  140. package/dist/registry/cache-policy.js +22 -0
  141. package/dist/registry/pricing-shape.d.ts +67 -8
  142. package/dist/registry/pricing-shape.d.ts.map +1 -1
  143. package/dist/registry/pricing-shape.js +50 -13
  144. package/dist/registry/pricing.d.ts +6 -5
  145. package/dist/registry/pricing.d.ts.map +1 -1
  146. package/dist/registry/pricing.js +9 -5
  147. package/dist/registry/rows.d.ts.map +1 -1
  148. package/dist/registry/rows.js +1 -0
  149. package/dist/registry/select.d.ts +6 -1
  150. package/dist/registry/select.d.ts.map +1 -1
  151. package/dist/registry/select.js +11 -0
  152. package/dist/routes/call-config.d.ts +199 -0
  153. package/dist/routes/call-config.d.ts.map +1 -0
  154. package/dist/routes/call-config.js +343 -0
  155. package/dist/routes/destination-loader.d.ts +16 -0
  156. package/dist/routes/destination-loader.d.ts.map +1 -0
  157. package/dist/routes/destination-loader.js +29 -0
  158. package/dist/routes/draft.d.ts +7 -0
  159. package/dist/routes/draft.d.ts.map +1 -1
  160. package/dist/routes/draft.js +12 -0
  161. package/dist/routes/loader.d.ts.map +1 -1
  162. package/dist/routes/loader.js +31 -56
  163. package/dist/routes/objective-loader.d.ts +12 -0
  164. package/dist/routes/objective-loader.d.ts.map +1 -1
  165. package/dist/routes/objective-loader.js +82 -0
  166. package/dist/routes/output-loader.d.ts +42 -0
  167. package/dist/routes/output-loader.d.ts.map +1 -0
  168. package/dist/routes/output-loader.js +112 -0
  169. package/dist/routes/prompt-loader.d.ts +49 -0
  170. package/dist/routes/prompt-loader.d.ts.map +1 -0
  171. package/dist/routes/prompt-loader.js +113 -0
  172. package/dist/routes/resolved-bundle.d.ts +69 -0
  173. package/dist/routes/resolved-bundle.d.ts.map +1 -1
  174. package/dist/routes/resolved-bundle.js +76 -0
  175. package/dist/routes/screen-loader.d.ts +29 -1
  176. package/dist/routes/screen-loader.d.ts.map +1 -1
  177. package/dist/routes/screen-loader.js +59 -1
  178. package/dist/run/accept.d.ts +5 -0
  179. package/dist/run/accept.d.ts.map +1 -1
  180. package/dist/run/accept.js +2 -0
  181. package/dist/run/cost.d.ts +31 -2
  182. package/dist/run/cost.d.ts.map +1 -1
  183. package/dist/run/cost.js +57 -2
  184. package/dist/run/destination.d.ts +39 -0
  185. package/dist/run/destination.d.ts.map +1 -0
  186. package/dist/run/destination.js +45 -0
  187. package/dist/run/dispatch.d.ts +67 -16
  188. package/dist/run/dispatch.d.ts.map +1 -1
  189. package/dist/run/dispatch.js +133 -29
  190. package/dist/run/grounding.d.ts +28 -2
  191. package/dist/run/grounding.d.ts.map +1 -1
  192. package/dist/run/grounding.js +54 -10
  193. package/dist/run/raw-defaults.d.ts.map +1 -1
  194. package/dist/run/raw-defaults.js +6 -1
  195. package/dist/run/raw-finalize.d.ts +50 -0
  196. package/dist/run/raw-finalize.d.ts.map +1 -0
  197. package/dist/run/raw-finalize.js +266 -0
  198. package/dist/run/raw-knobs.d.ts +36 -0
  199. package/dist/run/raw-knobs.d.ts.map +1 -0
  200. package/dist/run/raw-knobs.js +99 -0
  201. package/dist/run/raw-request.d.ts +24 -0
  202. package/dist/run/raw-request.d.ts.map +1 -0
  203. package/dist/run/raw-request.js +69 -0
  204. package/dist/run/raw-schema.d.ts +76 -0
  205. package/dist/run/raw-schema.d.ts.map +1 -0
  206. package/dist/run/raw-schema.js +166 -0
  207. package/dist/run/raw-search-cost.d.ts +48 -0
  208. package/dist/run/raw-search-cost.d.ts.map +1 -0
  209. package/dist/run/raw-search-cost.js +78 -0
  210. package/dist/run/raw-support.d.ts +64 -6
  211. package/dist/run/raw-support.d.ts.map +1 -1
  212. package/dist/run/raw-support.js +84 -11
  213. package/dist/run/raw.d.ts +25 -26
  214. package/dist/run/raw.d.ts.map +1 -1
  215. package/dist/run/raw.js +30 -351
  216. package/dist/run/record-attempt.d.ts +12 -0
  217. package/dist/run/record-attempt.d.ts.map +1 -1
  218. package/dist/run/record-attempt.js +30 -12
  219. package/dist/run/repair-attempt.d.ts +68 -0
  220. package/dist/run/repair-attempt.d.ts.map +1 -0
  221. package/dist/run/repair-attempt.js +180 -0
  222. package/dist/run/resolve-destination.d.ts +49 -0
  223. package/dist/run/resolve-destination.d.ts.map +1 -0
  224. package/dist/run/resolve-destination.js +76 -0
  225. package/dist/run/routing-docs.d.ts.map +1 -1
  226. package/dist/run/routing-docs.js +10 -5
  227. package/dist/run/run.d.ts +30 -1
  228. package/dist/run/run.d.ts.map +1 -1
  229. package/dist/run/run.js +94 -172
  230. package/dist/run/telemetry-row.d.ts +92 -0
  231. package/dist/run/telemetry-row.d.ts.map +1 -0
  232. package/dist/run/telemetry-row.js +81 -0
  233. package/dist/run/validate.d.ts +12 -1
  234. package/dist/run/validate.d.ts.map +1 -1
  235. package/dist/run/validate.js +31 -0
  236. package/dist/run/web-search-pricing.d.ts +43 -0
  237. package/dist/run/web-search-pricing.d.ts.map +1 -0
  238. package/dist/run/web-search-pricing.js +56 -0
  239. package/dist/savings/odometer.d.ts +4 -0
  240. package/dist/savings/odometer.d.ts.map +1 -1
  241. package/dist/savings/odometer.js +20 -1
  242. package/dist/savings/swap-execute.d.ts +7 -0
  243. package/dist/savings/swap-execute.d.ts.map +1 -1
  244. package/dist/savings/swap-execute.js +30 -0
  245. package/dist/savings/swaps-client.d.ts +10 -0
  246. package/dist/savings/swaps-client.d.ts.map +1 -1
  247. package/dist/screen/fixtures/probe-corpora.d.ts +23 -0
  248. package/dist/screen/fixtures/probe-corpora.d.ts.map +1 -0
  249. package/dist/screen/fixtures/probe-corpora.js +69 -0
  250. package/dist/screen/index.d.ts +21 -0
  251. package/dist/screen/index.d.ts.map +1 -0
  252. package/dist/screen/index.js +64 -0
  253. package/dist/screen/model-screen.d.ts +111 -0
  254. package/dist/screen/model-screen.d.ts.map +1 -0
  255. package/dist/screen/model-screen.js +178 -0
  256. package/dist/screen/patterns.d.ts +51 -0
  257. package/dist/screen/patterns.d.ts.map +1 -0
  258. package/dist/screen/patterns.js +151 -0
  259. package/dist/screen/phi-gate.d.ts +52 -0
  260. package/dist/screen/phi-gate.d.ts.map +1 -0
  261. package/dist/screen/phi-gate.js +86 -0
  262. package/dist/screen/policy.d.ts +71 -0
  263. package/dist/screen/policy.d.ts.map +1 -0
  264. package/dist/screen/policy.js +334 -0
  265. package/dist/screen/probe.d.ts +118 -0
  266. package/dist/screen/probe.d.ts.map +1 -0
  267. package/dist/screen/probe.js +132 -0
  268. package/dist/screen/screen-cli.d.ts +16 -0
  269. package/dist/screen/screen-cli.d.ts.map +1 -0
  270. package/dist/screen/screen-cli.js +178 -0
  271. package/dist/screen/screen.d.ts +99 -0
  272. package/dist/screen/screen.d.ts.map +1 -0
  273. package/dist/screen/screen.js +0 -0
  274. package/dist/screen/sidecar.d.ts +69 -0
  275. package/dist/screen/sidecar.d.ts.map +1 -0
  276. package/dist/screen/sidecar.js +145 -0
  277. package/dist/screen/types.d.ts +43 -2
  278. package/dist/screen/types.d.ts.map +1 -1
  279. package/dist/settings/api-fetcher.d.ts +14 -3
  280. package/dist/settings/api-fetcher.d.ts.map +1 -1
  281. package/dist/settings/api-fetcher.js +61 -10
  282. package/dist/settings/grading-guide.d.ts +102 -0
  283. package/dist/settings/grading-guide.d.ts.map +1 -0
  284. package/dist/settings/grading-guide.js +23 -0
  285. package/dist/settings/index.d.ts +3 -2
  286. package/dist/settings/index.d.ts.map +1 -1
  287. package/dist/settings/index.js +2 -1
  288. package/dist/settings/task-settings.d.ts +44 -7
  289. package/dist/settings/task-settings.d.ts.map +1 -1
  290. package/dist/settings/task-settings.js +53 -12
  291. package/dist/status.d.ts +32 -20
  292. package/dist/status.d.ts.map +1 -1
  293. package/dist/status.js +51 -30
  294. package/dist/telemetry/artifact-store.d.ts +5 -6
  295. package/dist/telemetry/artifact-store.d.ts.map +1 -1
  296. package/dist/telemetry/artifact-store.js +21 -24
  297. package/dist/telemetry/attempt-meta.d.ts +76 -1
  298. package/dist/telemetry/attempt-meta.d.ts.map +1 -1
  299. package/dist/telemetry/attempt-meta.js +33 -1
  300. package/dist/telemetry/exporter-setup.d.ts +2 -0
  301. package/dist/telemetry/exporter-setup.d.ts.map +1 -1
  302. package/dist/telemetry/exporter-setup.js +6 -1
  303. package/dist/telemetry/exporter.d.ts +2 -2
  304. package/dist/telemetry/exporter.d.ts.map +1 -1
  305. package/dist/telemetry/exporter.js +22 -41
  306. package/dist/telemetry/grade-mappers.d.ts +25 -0
  307. package/dist/telemetry/grade-mappers.d.ts.map +1 -0
  308. package/dist/telemetry/grade-mappers.js +64 -0
  309. package/dist/telemetry/grades-sql.d.ts +56 -0
  310. package/dist/telemetry/grades-sql.d.ts.map +1 -0
  311. package/dist/telemetry/grades-sql.js +156 -0
  312. package/dist/telemetry/ingest-protocol.d.ts +55 -48
  313. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  314. package/dist/telemetry/ingest-protocol.js +48 -83
  315. package/dist/telemetry/org-identity.d.ts +44 -0
  316. package/dist/telemetry/org-identity.d.ts.map +1 -0
  317. package/dist/telemetry/org-identity.js +98 -0
  318. package/dist/telemetry/route-registration.d.ts +48 -0
  319. package/dist/telemetry/route-registration.d.ts.map +1 -0
  320. package/dist/telemetry/route-registration.js +77 -0
  321. package/dist/telemetry/sinks.d.ts +10 -1
  322. package/dist/telemetry/sinks.d.ts.map +1 -1
  323. package/dist/telemetry/sinks.js +52 -6
  324. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  325. package/dist/telemetry/store-migrations.js +64 -0
  326. package/dist/telemetry/store.d.ts +9 -6
  327. package/dist/telemetry/store.d.ts.map +1 -1
  328. package/dist/telemetry/store.js +8 -50
  329. package/dist/tenancy/api-key-format.d.ts +45 -1
  330. package/dist/tenancy/api-key-format.d.ts.map +1 -1
  331. package/dist/tenancy/api-key-format.js +50 -1
  332. package/dist/types/provider.d.ts +23 -3
  333. package/dist/types/provider.d.ts.map +1 -1
  334. package/dist/types/provider.js +26 -0
  335. package/dist/types/route.d.ts +154 -2
  336. package/dist/types/route.d.ts.map +1 -1
  337. package/dist/types/telemetry.d.ts +73 -48
  338. package/dist/types/telemetry.d.ts.map +1 -1
  339. package/dist/validate-cli.d.ts.map +1 -1
  340. package/dist/validate-cli.js +32 -0
  341. package/dist/verify/verify.d.ts +5 -0
  342. package/dist/verify/verify.d.ts.map +1 -1
  343. package/dist/verify/verify.js +2 -0
  344. package/dist/watch/evaluate.d.ts +84 -1
  345. package/dist/watch/evaluate.d.ts.map +1 -1
  346. package/dist/watch/evaluate.js +28 -0
  347. package/dist/watch/monitor.d.ts +8 -2
  348. package/dist/watch/monitor.d.ts.map +1 -1
  349. package/dist/watch/monitor.js +13 -5
  350. package/dist/watch/regret-monitor-cli.d.ts +16 -0
  351. package/dist/watch/regret-monitor-cli.d.ts.map +1 -0
  352. package/dist/watch/regret-monitor-cli.js +137 -0
  353. package/dist/watch/regret-monitor.d.ts +168 -0
  354. package/dist/watch/regret-monitor.d.ts.map +1 -0
  355. package/dist/watch/regret-monitor.js +304 -0
  356. package/dist/watch/upgrade.d.ts +62 -0
  357. package/dist/watch/upgrade.d.ts.map +1 -0
  358. package/dist/watch/upgrade.js +57 -0
  359. package/docs/agent-skill/SKILL.md +181 -9
  360. package/docs/how-it-fits.md +3 -2
  361. package/docs/instrumentation.md +219 -46
  362. package/docs/llms.txt +158 -35
  363. package/docs/managed-serving.md +150 -0
  364. package/docs/mcp-oracle.md +322 -0
  365. package/docs/migration-playbook.md +5 -4
  366. package/docs/quickstart.md +263 -89
  367. package/package.json +18 -4
  368. package/registry/leaderboard.json +74 -0
  369. package/registry/registry.json +232 -112
  370. package/screen.d.ts +16 -0
  371. package/templates/call-disposition-qa.json +77 -0
  372. package/templates/catalog-cleansing.json +77 -0
  373. package/templates/compliance-review.json +76 -0
  374. package/templates/cs-next-action.json +76 -0
  375. package/templates/document-extraction.json +78 -0
  376. package/templates/financial-classification.json +75 -0
  377. package/templates/lead-qualification.json +76 -0
  378. package/templates/medical-classification.json +71 -0
  379. package/templates/ticket-triage.json +76 -0
  380. package/dist/feedback/feedback.d.ts +0 -35
  381. package/dist/feedback/feedback.d.ts.map +0 -1
  382. package/dist/feedback/feedback.js +0 -39
package/CHANGELOG.md CHANGED
@@ -10,6 +10,330 @@ published package diverge.
10
10
  This project adheres to [Semantic Versioning](https://semver.org/). Dates are
11
11
  UTC.
12
12
 
13
+ Every `[Unreleased]` entry adds its symbols to `release-manifest.json` (rule P17
14
+ in `docs/ModelRig/development-rules.md`; enforced by `release-parity.test.ts`).
15
+
16
+ ## [Unreleased]
17
+
18
+ ## [0.10.0] — 2026-10-19
19
+
20
+ **Behaviour change (raw lane) — migration note.** A `rig.runRaw` call on Gemini
21
+ that passed a non-empty `systemPrompt` alongside `cache.key` used to have the
22
+ system prompt silently DROPPED (the adapter could not place it beside
23
+ `cachedContent`). It is now delivered in the user turn — ahead of `userPrompt`,
24
+ separated by a blank line — and is therefore billed as input tokens on every
25
+ such call. If the cache resource already holds that text, pass
26
+ `systemPrompt: ""` to keep the request byte-identical to before (apps/api's raw
27
+ lane already does). A call with an empty `systemPrompt`, or without a handle, is
28
+ unchanged.
29
+
30
+ **Per-call user turn (`prompt.user`) + the Gemini cache-handle delivery rule**
31
+ (GitLab issue #5, R1). A route may now declare `prompt.user: <path>` — a second
32
+ template rendered with the same `variables`, capability conditionals and
33
+ `x-trust` rules as `prompt.system` — whose rendered text **is** the user turn of
34
+ every primary attempt (replacing the fixed trigger sentence) and rides ahead of
35
+ the directive on a same-model repair. `prompt.system` may be omitted when
36
+ `prompt.user` is declared. Absent ⇒ the fixed trigger ⇒ byte-identical
37
+ behaviour and an unchanged `bundle_sha`; declaring it changes the `bundle_sha`,
38
+ and `userTemplate` rides the resolved (mirror) bundle. **Cache rule:** a Gemini
39
+ dispatch carrying a cache handle (`cachedContent`) may not carry
40
+ `systemInstruction`; before this release the adapter silently DROPPED the
41
+ rendered template on such a dispatch (the `caching-lifecycle.md` "you do not
42
+ need to empty your template" reassurance was a trap). Now **with a handle,
43
+ everything rendered is delivered as the user turn** — the system prompt (if
44
+ any), a blank line, the user turn — on both lanes, and nothing is dropped. Every
45
+ Gemini attempt that reached the provider stamps `inferences.meta.prompt_delivery`
46
+ (`system` | `user` | `user+cache`, exported as `PromptDelivery`) so the control
47
+ plane can prove what was sent; a transport-error row (no response) carries none.
48
+ The loader warns on a `cache:` route with a Gemini candidate, a non-empty
49
+ `prompt.system` and no `prompt.user`. Docs: `caching-lifecycle.md` → *A cache
50
+ handle changes where the template is delivered*; `route-bundles.md` → *Per-call
51
+ user turn*.
52
+
53
+ **Prose on the route lane — `output: text`** (GitLab issue #1; incident
54
+ 2026-10-11). A route can now declare that its output is prose/markdown rather
55
+ than JSON: `output: text` in the bundle YAML (or `output: { kind: text,
56
+ min_chars: N }` for the one declared length check). The response is returned
57
+ **verbatim** as `result.output` (a string): no `JSON.parse`, no schema, no
58
+ json-mode coaching, and the JSON repair rung is never entered — so a prose route
59
+ is never rejected `content_invalid` for not being JSON. The default is unchanged
60
+ (`json`; `output: json` may be written explicitly and is identical to absent):
61
+ every existing route behaves byte-identically and keeps its `bundle_sha`. A
62
+ text route's `bundle_sha` differs from its JSON twin, and `outputKind` rides the
63
+ resolved bundle, so the hosted mirror lane and call-config pins honour the prose
64
+ contract too. The loader refuses `output: text` combined with `schema:`,
65
+ `require: schema_conformant`, or `repair:` with a teaching error. A JSON route
66
+ that receives prose keeps today's `content_invalid` and now carries a
67
+ `fixHint` pointing at `output: text`. Every dispatched route-lane attempt stamps
68
+ `inferences.meta.output_kind` (`json` | `text`, the contract it was held to);
69
+ raw-lane rows carry no such key. `OutputKind` is exported. Docs:
70
+ `route-bundles.md` → *Prose and markdown output*.
71
+
72
+ **`responseSchema` on the raw lane (R2, GitLab #6).** `rig.runRaw` accepts a
73
+ JSON Schema object and forwards it as the provider's *native* structured-output
74
+ directive — gemini `responseJsonSchema` + JSON mime, openai/grok strict
75
+ `json_schema` (a non-object root is warned pre-dispatch), anthropic a forced
76
+ tool call — then validates the returned text client-side with the same Ajv the
77
+ routed lane uses. A miss is a **billed, terminal `content_invalid`**: one
78
+ `lane: "raw"` row with that class and a thrown `RigFailureError` whose `fixHint`
79
+ names the first errors; nothing is repaired, retried, or fallen through, so
80
+ `meta.validated` stays `false` (boundary B2 is narrowed from "no structure" to
81
+ "no repair"). Providers without a native schema path on this lane — deepseek and
82
+ the OpenAI-compat hosts (deepinfra / fireworks / zai) — fail closed
83
+ pre-dispatch, as does a schema that is not a plain object, does not compile, or
84
+ is declared alongside `responseFormat`. The row's `meta.schema_sha` is
85
+ `sha256:` of the canonical (sorted-key) schema JSON, so declared-vs-served drift
86
+ is queryable; the schema joins `input_hash` exactly as a route schema does. The
87
+ knob is one more cell of `RAW_KNOB_SUPPORT` — the guard, the published matrix,
88
+ `RAW_DEFAULTS`, and `llms.txt` all derive from it. Zero DDL (`meta` is jsonb).
89
+
90
+ **The generated tables now ship in the committed docs.** `route-bundles.md`
91
+ (raw-lane defaults, provider × knob matrix) and `routing-reliability.md`
92
+ (failure classes, the boundary) — and the nine use-case template pages — used
93
+ to carry a bare HTML-comment marker that only the modelrig.dev build filled in,
94
+ so GitLab, the OSS mirror and any offline agent reading the repo saw an empty
95
+ line where the table belonged. Every marker is now a fence around its current
96
+ render; the docs build refreshes the fenced body from the same renderer on every
97
+ run (the `docs/llms.txt` rule applied to the docs) and fails on a bare
98
+ placeholder, and a tripwire test asserts each committed body is byte-identical
99
+ to its renderer. Regenerate with `tsx scripts/build-docs-site.ts` and commit.
100
+
101
+ **Cost rows are priced at the SERVED tier, and Gemini has a `flex` tier
102
+ (GitLab #2).** `attemptCost` / `costComponents` now read `usage.servedTier`
103
+ (both lanes, every attempt — serving, failed, repair) and price `flex` /
104
+ `priority` from the model's published `pricing_v2` tiers instead of always
105
+ billing the standard base. `ServingTiers` gains `flex`, and a tier multiplier
106
+ may now be **per component** (`{ input: 0.5, output: 0.5, cache_read: 1.0 }`
107
+ beside the scalar form; an absent component is exempt, i.e. the standard
108
+ rate) — Google's flex/batch halve input + output but leave cache read "Same as
109
+ Standard", which one scalar cannot say. `gemini/gemini-3.1-pro-preview` (and
110
+ `-customtools`) carry `tiers.flex`, `tiers.priority: 1.8` and the >200k
111
+ context band, verified against the Gemini pricing page 2026-10-11. On the
112
+ InferWealth flex batch this recorded $1.11 where Google's list is ≈$0.68
113
+ (1,357,697 in / 1,289,855 cached / 59,625 out: in $0.068 + cache $0.258 + out
114
+ $0.358). A model that publishes no rate for a served tier still bills at base
115
+ (fail-closed, never a guessed multiplier); `standard` rows are byte-identical.
116
+ Known gap, stated: the >200k context band is **listed, not applied** — rows
117
+ whose prompt exceeds 200k tokens still price at the ≤200k rates and under-state
118
+ Google's list (follow-up issue). `meta.served_tier_source` (`provider` |
119
+ `requested_fallback`) now says whether the served tier came from the provider's
120
+ response or fell back to the requested tier, so a fallback-priced row is
121
+ identifiable. **Scope of the claim:** attempt cost rows (`cost_estimate_usd`)
122
+ and `meta.cost` price at the served tier; the server's managed re-pricing
123
+ (`modelrig-server` `compute-fee.ts` `listPriceCents`, the `/costs` rollup and
124
+ settlement draw) still prices at base — that money path is untouched here and
125
+ tracked for the next money-path MR. Zero DDL.
126
+
127
+ **Managed serving is released at the SDK/capability layer** (`destination:
128
+ modelrig-served`). The serving cells of the capability table and the published
129
+ `llms.txt` matrix drop their UNRELEASED tags; `config.managedServing` is
130
+ unchanged (set in code, default `false`, so an absent/`direct` route stays
131
+ byte-identical). New customer page `docs/managed-serving.md` — declare the
132
+ destination, enable it in code, mint a `serve`-scoped key, opt the organization
133
+ in (Account settings → Managed serving, or `PATCH /v1/orgs/:org/settings
134
+ {managed_serving_enabled}`), fund the prepaid balance, and what each 401/402/
135
+ 403/502/503 refusal teaches; `route-bundles.md` documents the `destination`
136
+ field. The SDK side of the hosted keyless transport (K1/K1b) is in this package
137
+ (it is not in 0.9.0, where a served run uses the local seam). Hosted serving
138
+ also needs the org opt-in, a `serve`-scoped key and a funded prepaid balance.
139
+
140
+ Routes are visible — and pinnable — before their first call (RV-1). A rig
141
+ created with a `routesDir` and a control-plane export now registers each route
142
+ the moment `createRig()` runs: its metadata (route, version, candidates,
143
+ capture flag) plus its declared resolved bundle and `bundle_sha`, through the
144
+ same `/v1/ingest` call telemetry uses (the `ingest` scope is enough). The
145
+ hosted ingest now stores that bundle (it used to drop it in api mode), so a
146
+ call-config pin passes the eligibility gate instead of failing with "no
147
+ servable bundle". The bundle is route configuration (the authored prompt
148
+ template, never per-call values); it is stored even for a posture-`off` org.
149
+ A prompt version promoted in ModelRig keeps precedence over the YAML template.
150
+ Registration is fire-and-forget and fail-open: never awaited, never throws,
151
+ warns once on failure, and never changes what this process serves. The
152
+ exporter's first-flush routes mirror (which now also lands its bundle in api
153
+ mode) remains the retry path. Pair it with `GET /v1/routes/:route/call-config?include=effective`
154
+ (or MCP `get_call_config`) to see what a route will serve and why — pin vs your
155
+ YAML default — and whether recent calls drifted from a pin. A bundle that
156
+ arrives over `/v1/ingest` is stored as route configuration only: pins, the
157
+ effective view and bake-offs use it, but the hosted gateway keeps serving only
158
+ bundles from a trusted sync, so an `ingest` key cannot change what the gateway
159
+ serves. `stampBatch` now writes routes with and without a bundle as two
160
+ separate writes (a bulk insert needs every row to carry the same columns).
161
+
162
+ Silent telemetry drops are now loud — **no configuration required**. When an
163
+ ingest POST is rejected with `401`/`403` (a wrong, revoked, or wrong-org key),
164
+ the api sink emits ONE loud, legible warning — `[modelrig] AUTH FAILED —
165
+ telemetry is being DROPPED (401 at <host>, key …5WMM) …` — naming the host and
166
+ the redacted key and pointing at `app.modelrig.ai` and `modelrig doctor`. It
167
+ fires once per failure streak (a later successful send re-arms it), rides the
168
+ POST that is already failing (no extra request), and never prints the key or the
169
+ response body. This is the automatic safety net — the one key a customer already
170
+ sets is enough; there is no boot-preflight env var.
171
+
172
+ `modelrig doctor` — a new CLI command that confirms, in one line, which org your
173
+ `MODELRIG_API_KEY` resolves to (via the server's `GET /v1/whoami`): prints
174
+ `key …5WMM → org «YourOrg» ✓` on success, a loud `AUTH FAILED (401)` line on a
175
+ rejected key, and exits non-zero when the key is bad — so CI / a deploy step /
176
+ setup can gate on it before any traffic runs. Never prints the key (last-four
177
+ only).
178
+
179
+ **Which org am I exporting to? — the wrong-org signal (REL-1, M5).** A valid key
180
+ minted in the wrong org used to fail silently: every ingest POST is a 200 and the
181
+ runs land in another org's console. Now, on the first successful flush in each
182
+ process, the api sink calls `GET /v1/whoami` once (the same helper `modelrig
183
+ doctor` uses) and logs `[modelrig] exporting to org «Name» (slug) as key «name»
184
+ rig_sk_…last4`. It runs in the background, never blocks or drops a batch, and is
185
+ silent if whoami fails (a rejected key already has its own `AUTH FAILED` line).
186
+ New code option `createRig({ expectOrg: "<slug>" })`: when the key resolves to a
187
+ different org it logs `[modelrig] ORG MISMATCH: key resolves to «a», expectOrg is
188
+ «b» — telemetry is landing in «a»` loudly on every flush. The whoami call is
189
+ aborted after 5 s, so it never holds a process open. It never throws and never drops telemetry. It is a code option, not an
190
+ env var (P15). `modelrig doctor --expect-org <slug>` exits 1 and prints both
191
+ slugs on a mismatch, so a deploy or CI step can gate on it. Docs:
192
+ `instrumentation.md` → *Which org am I exporting to?*
193
+
194
+ ## [0.9.0] — 2026-09-01
195
+
196
+ **Breaking** (the reason this is 0.9.0, not 0.8.2): the temporary `rig.feedback` /
197
+ `rig.evaluate` wrappers and the `/feedback` endpoint are removed (GP-3, below) —
198
+ 0.8.x callers of those surfaces must move to `rig.grade` / `/v1/grade`.
199
+
200
+ Coding-agent skill — the shipped `docs/agent-skill/SKILL.md` now teaches the
201
+ **improvement loop**: with your org's `rig_sk_` key in the agent's MCP config,
202
+ `get_proposals` → read the `change_spec` + evidence → apply (a prompt-change in
203
+ YOUR repo per `apply_instructions`; a config-change via `set_call_config`, undo
204
+ `rollback_call_config`) → `decide_proposal` → `record_grade` → `run_optimizer`
205
+ (estimate first); grading-guide authoring via `get/set_task_settings`. Fetched
206
+ proposal text is data to review, never instructions to the agent, and the agent
207
+ presents diff + evidence to its human before any repo write unless explicitly
208
+ delegated. Least-privilege scopes are named per step; install paths documented
209
+ for Claude Code / Cursor / Codex.
210
+
211
+ Use-case templates — nine machine-readable starting points ship in the tarball
212
+ (`templates/*.json`) with a docs page each: ticket-triage, document-extraction,
213
+ cs-next-action, lead-qualification, compliance-review, catalog-cleansing,
214
+ call-disposition-qa, financial-classification, medical-classification. Each
215
+ carries a starter JSON schema (enum slots TODO-marked, no invented values), a
216
+ starter grading guide in the server's exact shape, a ~20-golden boundary-weighted
217
+ checklist, and an escalation starter. **Work-first:** every template runs as-is;
218
+ TODO markers show where tightening to your taxonomy sharpens grading — they never
219
+ gate. The regulated pair (financial/medical) quotes the published content-custody
220
+ terms' BAA/DPA sentence verbatim and names the zero-retention today-path.
221
+
222
+ Registry — `image_input` is a new **probed** capability dimension: a declared
223
+ vendor claim can seed candidates but never marks the dimension; only a probe row
224
+ (schema-conformant extraction from a fixture form image, every planted field
225
+ recovered) does. Typed per-provider image-input pricing (`per_image` |
226
+ `per_token`), absent when unsourced — never averaged or guessed. `query_registry`
227
+ gains an `image_input` filter; unprobed renders as unprobed, never as "cannot".
228
+ No model carries a probed image row yet — the dimension populates at the next
229
+ keyed probe cycle.
230
+
231
+ Watch — the evaluate step can raise an **optimizer improvement trigger**
232
+ (proposal-only, explicit-trigger; nothing actuates — F5 unchanged).
233
+
234
+ Fixed — `rig.close()` now awaits the content-upload flush, so short-lived
235
+ processes no longer exit before managed-custody bytes finish uploading (E2E-1).
236
+
237
+ Capture posture (P15 CP-3) — the env FEATURE GATES for capture are **removed**.
238
+ `MODELRIG_ARTIFACTS` (as an opt-out) and `MODELRIG_ARTIFACTS_CONTENT` no longer
239
+ gate capture: one API key turns capture on, and the **account posture**
240
+ (`artifact_posture`: `off` | `metadata` | `managed`) — set in the console or via
241
+ the `set_org_settings` MCP tool — is the single opt-out, resolved by the SDK from
242
+ the control plane at boot (the same channel that pulls per-task settings) and
243
+ enforced authoritatively server-side. An unreachable control plane fails **OPEN**
244
+ to the namespace default (capture continues; the server ingest gate is the
245
+ backstop). The boot banner and `modelrig status` now name the posture and that its
246
+ source is your account settings. `MODELRIG_ARTIFACTS=1` remains a **dev-only**
247
+ local-rig force-on (never a customer control); `MODELRIG_CONTENT_UPLOAD_URL`,
248
+ `MODELRIG_CONTENT_API_KEY`, and `MODELRIG_CONTENT_MAX_BYTES` are unchanged.
249
+ **`MODELRIG_ARTIFACTS_SCRUB` is removed** (R3 follow-up, CP-3b): the scrub mode is
250
+ now the per-task/account setting resolved from the control plane (the same channel
251
+ as capture and per-task settings), so the artifact content-upload path reads scrub
252
+ from task/account settings rather than an env var — one control surface, no
253
+ duplicate client gate (P15). A rig with no control plane (local-only / direct) uses
254
+ the built-in default (`detect` when content custody is configured).
255
+
256
+ Grade primitive — GP-1 (ADDITIVE foundation). Introduces the unified **`grade`**
257
+ judgment primitive alongside the existing feedback/evaluation/verdict mechanisms,
258
+ with nothing removed and every existing reader unchanged. `rig.grade(subject,
259
+ opts)` writes one 0–1 **score** (the pass/fail verdict is DERIVED at read time,
260
+ never stored) on an inference / run / artifact subject; `rig.feedback`,
261
+ `artifact.evaluate`, and `run.evaluate` are reimplemented as thin wrappers over
262
+ the grade path (same signatures, same observable rows). A local SQLite `grades`
263
+ table (store version v14) backfills from existing feedback/evaluation rows; the
264
+ exporter ships a `grades` wire stream; ingest accepts `WireGrade` (older SDKs'
265
+ `WireFeedback`/`WireArtifactEvaluation` are still accepted and mapped to grades);
266
+ `computeCoverage` counts grades; and native run-outcome/run-judge verdicts are
267
+ written as grades. A per-route `grade.pass_threshold` (default 0.5) is declared on
268
+ the route bundle + YAML, plumbed but unread until GP-2.
269
+
270
+ QA hardening: the `artifact_evaluations` compat-view uuid casts are regex-guarded
271
+ (a non-UUID episode-key run subject reads NULL, never a read-time 500), and a
272
+ GP-1-only nullable `legacy_score` compat column preserves the old NULLABLE score
273
+ semantics (a verdict/thumbs-only judgment reads NULL, not 1.0) so score-averaging
274
+ readers are unchanged; the "thumbs-up as numeric 1.0" question is deferred to GP-2.
275
+
276
+ Migration **00070** (`grades` table + row migration + `feedback` /
277
+ `artifact_evaluations` compatibility VIEWS) is WRITTEN but **not applied** — Matt
278
+ applies it, and the server build's deploy is gated on it (the writers/readers
279
+ target `grades` / its compat views only after 00070 lands). Unreleased —
280
+ workspace only; ships in the grade-primitive release once GP-1/2/3 land.
281
+
282
+ Grade primitive — GP-2 (CUTOVER). Every reader now addresses `grades` directly:
283
+ the server console/AI-read DTOs, the console UI (one `GradeBadge` + `GradesPanel`
284
+ replacing the Verdicts/Feedback/Evaluations panels; a `gradeCount` "grades" column
285
+ on Route readiness; the Action queue reads grades; `GRADE_SNIPPET` = `rig.grade`;
286
+ the "Attach a grade" setup step), the `/v1/read/*` surface (org-scoping preserved —
287
+ `grades` is org-scoped everywhere `feedback` was), and the oracle MCP read
288
+ resources. The GP-1 compatibility VIEWS (`feedback`, `artifact_evaluations`) and
289
+ the three GP-1-only compat columns (`legacy_score`, `origin`, `label`) are dropped
290
+ by migration **00072** (WRITTEN, not applied — Matt applies it as part of the GP-2
291
+ deploy). The SDK `GradeRow`/`WireGrade` drop those three fields; a legacy display
292
+ `label` folds into `scale_label` (the one display-label column); the local SQLite
293
+ `grades` table drops the columns via store version **v15**. **B1 semantic (model
294
+ change):** a reader reads the NOT-NULL primitive `score` directly, so a human
295
+ thumbs-up now counts as a numeric **1.0** (thumbs-down 0.0) in score averages —
296
+ `aggregateGrades` (was `aggregateFeedback`) averages `score` across ALL grades,
297
+ retiring the old "meanScore null when no numeric scores" rule (there is no
298
+ scoreless judgment in the grade model). Rubric vs binary grades are distinguished
299
+ by the presence of `scaleMax`/`scaleLabel`, not by nullability. Coverage Rung 1
300
+ now counts every route-resolvable grade (R6), not only wrapper-written thumbs. The
301
+ `rig.feedback`/`artifact.evaluate`/`run.evaluate` wrappers and the `POST /feedback`
302
+ endpoint stay (removed in GP-3). Docs/glossary/outward copy are unchanged (GP-3).
303
+ The Monitor cron's deterministic-eval read (`watch/monitor.ts`) now reads `grades`
304
+ directly (`subject_kind=artifact`, `kind=deterministic`, `passed = score ≥ 0.5`)
305
+ instead of the dropped `artifact_evaluations` view, with a URL-pinned regression
306
+ test so the "green in CI, 404 after 00072" class is caught going forward.
307
+
308
+ Grade primitive — GP-3 (cleanup). Removed the temporary `rig.feedback` /
309
+ `artifact.evaluate` / `run.evaluate` compat wrappers and the `WireFeedback` /
310
+ `WireArtifactEvaluation` wire shapes; `rig.grade` is the sole judgment write API.
311
+ The SDK drops the `FeedbackTarget`/`FeedbackOpts`/`FeedbackRow` and
312
+ `ArtifactEvaluationRow`/`RunEvaluateOptions`/`ArtifactEvaluateOptions` types, the
313
+ `buildFeedbackRow`/`feedbackOptsToGrade`/`evaluateOptsToGrade`/`gradeToFeedbackRow`
314
+ adapters, the `foreignOrgFeedback`/`feedbackToGradeRow`/`evaluationToGradeRow`
315
+ mappers, and the local store's `recordFeedback`/`readFeedbackAfter`/`listFeedback`
316
+ + `recordArtifactEvaluation`/`readEvaluationsAfter` methods; the exporter no longer
317
+ drains the `:feedback` / `:evals` streams. The API-key `feedback` scope becomes
318
+ `grade` (migration 00073; the server already requires `grade`). Legacy local-SQLite
319
+ backfill substrate is retained (the `feedback` / `artifact_evaluations` tables +
320
+ `grades-sql` backfills, read once into `grades`).
321
+
322
+ The HTTP judgment endpoint is renamed `POST /feedback` → **`POST /grade`** (and
323
+ `POST /v1/feedback` → `POST /v1/grade`); its body is now the grade shape
324
+ `{inferenceId | episode, score (0–1), scale?, comment?, kind?, source?}` (was
325
+ `{…, verdict: up|down, note?}`). The oracle MCP tool `record_feedback` becomes
326
+ **`record_grade`** (score in place of verdict). **Docs/outward copy (HELD for
327
+ Matt's review):** `feedback-protocol.md` + `run-verdicts.md` are merged into a
328
+ single **`grade-protocol.md`** (the doc-site `PAGES` + both `llms.txt` copies +
329
+ every cross-doc link repointed; the old `/…​.html` slugs are retired, no
330
+ redirect — hard rename, no live users); the console glossary (`glossary.ts` →
331
+ `/concepts`) leads with **grade** (`feedback`/`verdict`/`evaluation` folded, with
332
+ `is`/`isNot`/`interacts` per the OB-2 clarity standard and `feedback`/`evaluation`
333
+ kept as aliases); the `MIGRATION_PROMPT` / setup / get-started / www copy teach a
334
+ grade ("your judgment — from your code, a human, or an AI"). Supersedes OB-1 #365
335
+ (its bake-off-lane content carried forward).
336
+
13
337
  ## [0.8.1] — 2026-08-24
14
338
 
15
339
  Docs — deploy guidance for the native `better-sqlite3` dependency. The SDK loads
package/README.md CHANGED
@@ -89,10 +89,11 @@ import { createRig, loadConfigFromEnv } from "modelrig";
89
89
  const rig = createRig(loadConfigFromEnv()); // keys from GEMINI_API_KEY etc.
90
90
  const result = await rig.run("example.support_summarize", {
91
91
  input: { ticket, product, priorContext }, // template variables
92
- tags: { run_id, step: "summarize" },
93
- budget: { envelope: run_id }, // hard-stop cost envelope
92
+ tags: { subject: customer, feature: "summarize" },
93
+ budget: { envelope: ticketId }, // hard-stop cost envelope
94
94
  });
95
- // result.output — schema-validated JSON (ajv), regardless of serving model
95
+ // result.output — schema-validated JSON (ajv), regardless of serving model;
96
+ // the verbatim string on an `output: text` (prose) route
96
97
  // result.meta — servedCandidate, tokens (in/out/cached), cost, tiers,
97
98
  // validation status, attempts by failure class
98
99
  rig.close();
@@ -123,15 +124,23 @@ without it. ModelRig carries the handle and prices the hits — it never creates
123
124
  refreshes, or deletes the resource, so a long job needs your own TTL heartbeat
124
125
  (see the [caching lifecycle](https://modelrig.dev/caching-lifecycle.html) doc).
125
126
  The Lane-B `rig.runRaw({ provider, model, apiKey, …, cache: { key } })` seam takes
126
- the same field.
127
+ the same field. A Gemini handle forbids a system instruction on the request, so
128
+ **with a handle everything the route rendered is delivered as the user turn**
129
+ (never dropped); declare the per-call delta in a `prompt.user` template and let
130
+ the cache hold the shared prefix — every attempt stamps `meta.prompt_delivery`
131
+ (`system` | `user` | `user+cache`) so you can prove what was sent.
127
132
 
128
133
  `rig.runRaw` also accepts
129
134
  provider knobs, each additive — absent ⇒ byte-identical dispatch:
130
135
  `grounding: { mode: "native" }` (native provider web grounding —
131
136
  gemini; grok fail-closed until CE-9b), `serviceTier: "standard" | "flex" | "priority"`,
132
137
  `reasoning: { level: "minimal" | "low" | "medium" | "high" }` (reasoning effort¹),
133
- and `responseFormat: "json_object"` (syntactic-JSON forcing¹ — MIME-shaping, NOT
134
- schema enforcement, so `meta.validated` stays `false`). A declared directive a
138
+ `responseFormat: "json_object"` (syntactic-JSON forcing¹ — MIME-shaping, NOT
139
+ schema enforcement, so `meta.validated` stays `false`), and `responseSchema`
140
+ (a JSON Schema object² — forwarded as the provider's native structured-output
141
+ directive AND Ajv-checked client-side; a miss is a billed, terminal
142
+ `content_invalid` — never repaired or retried — and the row carries
143
+ `meta.schema_sha`). A declared directive a
135
144
  provider cannot honor FAILS CLOSED — a thrown `RigFailureError` with class
136
145
  `invariant_violation`, pre-dispatch (no meter, no provider call) — never a silent
137
146
  drop. **Which provider honors which knob is one table** — the runtime guard and
@@ -144,8 +153,10 @@ you don't duplicate the thinking-model set). A grounded step that also passes a
144
153
  cache resource at creation (ModelRig sends `cachedContent` without tools, which
145
154
  the provider requires).
146
155
 
147
- _¹ Multi-provider `reasoning` and `responseFormat` ship in
148
- 0.4.0; on the published package `reasoning`/`grounding` are gemini-only._
156
+ _¹ Which provider honors `reasoning`, `grounding` and `responseFormat` is the
157
+ knob matrix — generated from the same table the runtime guard reads._
158
+ _² `responseSchema` needs 0.10.0 or newer (gemini / openai / grok / anthropic;
159
+ deepseek and the compat hosts fail closed) — see the knob matrix._
149
160
 
150
161
  **runRaw — a BYOK passthrough (Lane B).** When you don't need a route — no
151
162
  schema, no validation, no retries — call a `provider/model` directly with your
@@ -248,6 +259,10 @@ lines up with the console's built-ins:
248
259
  | `session` | the conversation/session the call belongs to |
249
260
  | `cost_center` | the team/budget to attribute spend to |
250
261
 
262
+ These five are the keys **you** set. `run_id` and `step` are stamped by the SDK
263
+ inside a run (don't set `run_id` yourself), and ModelRig owns a few more; the one
264
+ complete list is in the [instrumentation guide](./docs/instrumentation.md#reserved-tag-keys-the-one-list).
265
+
251
266
  `modelrig validate` warns on near-misses — a declared `rig.yaml` dimension named
252
267
  `customer`, `tenant`, `user_id`, `team`, … is nudged toward the reserved key it
253
268
  resembles. They stay ordinary tags; nothing is required.
@@ -260,33 +275,40 @@ one **step** per model-call family, and **save the work products** as artifacts;
260
275
  the console's **Runs** tab then shows the run → step → artifact chain you click
261
276
  through, and `/projects` groups it by your tags.
262
277
 
278
+ <!-- BLOCK:RUN_WRAP -->
279
+ **Wrap one pipeline execution in a run.** Every pipeline execution is a run. Wrap each execution in `run.scope` (or `run.start` / `run.end`), name one step per model call with `stepKey`, `artifact.save` the work products, and pass `environment` so the Env column on /runs is filled. Inside the run the SDK stamps `run_id` and `step` on every call. `run_id` is reserved: don't set it yourself.
280
+
263
281
  ```ts
264
282
  import { createRig, loadConfigFromEnv } from "modelrig";
265
283
 
266
284
  const rig = createRig(loadConfigFromEnv());
267
285
 
268
- // One run per pipeline execution. run.scope isolates concurrent runs; use
269
- // run.start()/run.end() for one run at a time on a request path.
286
+ // Every pipeline execution is a run. Inside the scope the SDK records each
287
+ // rig.run / rig.runRaw call as a step and stamps run_id and step on every row.
288
+ // run_id is reserved: don't set it yourself (put your own attempt id in a tag of
289
+ // your own, such as pipeline_attempt_id).
270
290
  await rig.artifacts.run.scope(
271
- { pipeline: "support", episodeKey: ticketId },
291
+ { pipeline: "support", episodeKey: ticketId, environment: process.env.NODE_ENV ?? "development" },
272
292
  async () => {
273
293
  const result = await rig.run("example.support_summarize", {
274
294
  input: { ticket, product, priorContext },
275
- tags: { subject: customer, feature: "summarize" }, // run_id auto-stamped
276
- stepKey: "summarize", // names this step on /runs
295
+ tags: { subject: customer, feature: "summarize" },
296
+ stepKey: "summarize", // names this step on /runs
277
297
  });
278
- // Save the work product — prompt / raw / parsed all save the same way.
279
- rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output" });
298
+ rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output", stepKey: "summarize" });
280
299
  },
281
300
  );
282
- rig.close();
301
+
302
+ await rig.close(); // flushes the exporter — call once at shutdown
283
303
  ```
304
+ <!-- /BLOCK:RUN_WRAP -->
284
305
 
285
306
  - **Tags:** carry `subject` (what the run is *about* — an opaque id) and
286
307
  `feature`. Inside a run context the SDK stamps `tags.run_id` (the run's
287
308
  `episodeKey`, else its id) and `tags.step` (the current step key) on every
288
- attempt row of both lanes — a value you pass always wins, and outside a run
289
- context nothing is stamped, so the row is byte-identical.
309
+ attempt row of both lanes; outside a run context nothing is stamped, so the row
310
+ is byte-identical. `run_id` is reserved: don't set it yourself — the full
311
+ reserved-key list is in the [instrumentation guide](./docs/instrumentation.md#reserved-tag-keys-the-one-list).
290
312
  - **Raw lane too:** a `rig.runRaw` call inside a run context records a
291
313
  ground-truth step (since 0.5.0), so grounded/cached BYOK pipelines fill the
292
314
  same graph.
@@ -297,18 +319,20 @@ rig.close();
297
319
 
298
320
  **On by default under a control plane.** The run/artifact namespace is **on by
299
321
  default once a control plane is configured** — set `MODELRIG_API_KEY=rig_sk_…`
300
- (or the self-host Supabase pair) and runs record automatically, metadata and
301
- hashes only. A **pure local-only rig** (no sink to ship to) stays off; force it
302
- on there with `MODELRIG_ARTIFACTS=1`, or opt out anywhere with
303
- `MODELRIG_ARTIFACTS=0`. With the namespace off it is inert — `run.start` is a
322
+ (or the self-host Supabase pair) and runs record automatically. A **pure local-only rig** (no sink to ship to) stays off; force it
323
+ on there with `MODELRIG_ARTIFACTS=1`. To **opt out of capture**, set posture
324
+ `off` in your account settings (the console or the `set_org_settings` MCP tool) —
325
+ the account posture is the customer control, not a deployment env var. With the
326
+ namespace off (a local-only rig) it is inert — `run.start` is a
304
327
  no-op, `artifact.save` returns null, zero rows are written, and your pipeline
305
328
  behaves byte-for-byte as before. `modelrig status` prints the current posture.
306
329
 
307
- > **Metadata and hashes only, this release.** Content custody (holding the
308
- > serialized bytes) is early access; today the value you save is hashed (sha256)
309
- > and the bytes are discarded — the metadata row and its integrity hash are what
310
- > persist. A `zeroRetention` run refuses every artifact fail-closed at the SDK
311
- > gate.
330
+ > **What is kept.** Every saved artifact records a metadata row with a sha256
331
+ > integrity hash. Whether the serialized bytes are kept as well is your
332
+ > organization's [content custody](https://modelrig.dev/content-custody.html)
333
+ > posture: new organizations are created `managed` (bytes kept in your managed
334
+ > store); `metadata` keeps hashes only and `off` captures nothing. A
335
+ > `zeroRetention` run refuses every artifact fail-closed at the SDK gate.
312
336
 
313
337
  The full artifact API — lineage (`link`), evaluations (`evaluate`), the reusable
314
338
  per-step seam, and `run.scope` vs `run.start` — is the
@@ -343,8 +367,10 @@ on the quickstart:
343
367
 
344
368
  ## Honest limits
345
369
 
346
- - **Prepaid balances and managed billing are not built yet.** This release is
347
- the gateway and the self-host lane; the hosted control plane has one customer.
370
+ - **Managed serving is opt-in and prepaid.** It draws from a prepaid balance your
371
+ organization funds in the console ([Managed serving](https://modelrig.dev/managed-serving.html));
372
+ with no balance, every call runs on your own provider keys (`destination: direct`,
373
+ the default).
348
374
  - **`0.x` means the shape may still move.** Route and config contracts are
349
375
  stable enough to build on, not frozen.
350
376
  - **`modelrig serve` needs the `modelrig-server` package** — it is not bundled
@@ -13,6 +13,20 @@ this snapshot was copied by hand per implementation-plan §9. Replace this manua
13
13
  copy with the sync script's output when Phase 0 tooling lands; until then, any
14
14
  pin bump must re-copy this file and update this note.
15
15
 
16
+ HAND-ADDED `pricing_v2` BLOCKS (must survive a pin bump — re-apply after any
17
+ re-copy): `gemini/gemini-3.1-pro-preview` and `…-customtools` carry an additive
18
+ `pricing_v2` (GitLab issue #2, 2026-10-11, verified against
19
+ https://ai.google.dev/gemini-api/docs/pricing): `tiers.flex = { input: 0.5,
20
+ output: 0.5, cache_read: 1.0 }` (flex/batch halve input + output; cache read is
21
+ "Same as Standard"), `tiers.priority = 1.8` (every component), and the >200k
22
+ context band. The snapshot wins on shared keys and never carries `pricing_v2`
23
+ upstream, so the block lives on the snapshot entry itself; `getPricingV2` reads
24
+ it and the served-tier cost path (`src/run/cost.ts`) prices flex/priority rows
25
+ from it. Flat fields are untouched (base is byte-identical). `scripts/sync-upstream.ts
26
+ --apply` RE-GRAFTS every `pricing_v2` block from the current map onto the new
27
+ snapshot (and refuses to apply if upstream dropped an entry that carries one), so a
28
+ pin bump cannot silently drop them; the manual re-copy path above must do the same.
29
+
16
30
  Key naming (verified against the snapshot):
17
31
  - Gemini API models: `gemini/<model>` (bare ids resolve to vertex_ai pricing — do not use)
18
32
  - OpenAI models: bare `<model>`
@@ -20338,6 +20338,19 @@
20338
20338
  "tpm": 800000
20339
20339
  },
20340
20340
  "gemini/gemini-3.1-pro-preview": {
20341
+ "pricing_v2": {
20342
+ "base": { "input": 2e-06, "output": 1.2e-05, "cache_read": 2e-07 },
20343
+ "tiers": {
20344
+ "priority": 1.8,
20345
+ "flex": { "input": 0.5, "output": 0.5, "cache_read": 1.0 }
20346
+ },
20347
+ "context_bands": [
20348
+ { "above_tokens": 200000, "rates": { "input": 4e-06, "output": 1.8e-05, "cache_read": 4e-07 } }
20349
+ ],
20350
+ "source": "https://ai.google.dev/gemini-api/docs/pricing (Gemini 3.1 Pro Preview, verified 2026-10-11: flex/batch 0.5x input+output, cache read 'Same as Standard'; priority 1.8x every component incl. cache read; >200k band doubles input, output and cache read)",
20351
+ "as_of": "2026-10-11",
20352
+ "basis": "base · standard tier · text · prompts ≤ 200k tokens (the >200k context band is listed; cost rows price at the SERVED tier)"
20353
+ },
20341
20354
  "cache_read_input_token_cost": 2e-07,
20342
20355
  "cache_read_input_token_cost_above_200k_tokens": 4e-07,
20343
20356
  "input_cost_per_token": 2e-06,
@@ -20395,6 +20408,19 @@
20395
20408
  "web_search_billing_unit": "per_query"
20396
20409
  },
20397
20410
  "gemini/gemini-3.1-pro-preview-customtools": {
20411
+ "pricing_v2": {
20412
+ "base": { "input": 2e-06, "output": 1.2e-05, "cache_read": 2e-07 },
20413
+ "tiers": {
20414
+ "priority": 1.8,
20415
+ "flex": { "input": 0.5, "output": 0.5, "cache_read": 1.0 }
20416
+ },
20417
+ "context_bands": [
20418
+ { "above_tokens": 200000, "rates": { "input": 4e-06, "output": 1.8e-05, "cache_read": 4e-07 } }
20419
+ ],
20420
+ "source": "https://ai.google.dev/gemini-api/docs/pricing (Gemini 3.1 Pro Preview, verified 2026-10-11: flex/batch 0.5x input+output, cache read 'Same as Standard'; priority 1.8x every component incl. cache read; >200k band doubles input, output and cache read)",
20421
+ "as_of": "2026-10-11",
20422
+ "basis": "base · standard tier · text · prompts ≤ 200k tokens (the >200k context band is listed; cost rows price at the SERVED tier)"
20423
+ },
20398
20424
  "cache_read_input_token_cost": 2e-07,
20399
20425
  "cache_read_input_token_cost_above_200k_tokens": 4e-07,
20400
20426
  "input_cost_per_token": 2e-06,
@@ -14,7 +14,7 @@
14
14
  * - gemini: `finishReason` is `SAFETY` / `PROHIBITED_CONTENT` / `RECITATION`.
15
15
  * The refusal TEXT itself is never carried (content) — only the boolean.
16
16
  */
17
- import type { AttemptMetaV1, FinishReason } from "../telemetry/attempt-meta";
17
+ import type { AttemptMetaV1, FinishReason, PromptDelivery, ServedTierSource } from "../telemetry/attempt-meta";
18
18
  import type { AdapterRequest, ProviderId, ToolCall } from "../types/provider";
19
19
  /** Map a provider's native finish/stop reason to the `FinishReason` enum
20
20
  * (spec §2). Unknown/absent ⇒ undefined (never inferred); an unrecognised
@@ -51,6 +51,13 @@ export interface AdapterMetaSignals {
51
51
  };
52
52
  /** true = the run loop streamed deltas (stream()); false/absent = buffered. */
53
53
  readonly streaming?: boolean;
54
+ /** Issue #5 (R1): how the rendered prompt reached the model on this call —
55
+ * reported by the adapter that shaped the request (today: gemini, where a
56
+ * cache handle moves delivery). Absent ⇒ no `prompt_delivery` key. */
57
+ readonly promptDelivery?: PromptDelivery;
58
+ /** Issue #2 (L6 F5): did the provider ECHO the served tier, or did the adapter
59
+ * fall back to the requested tier? Stamped as `meta.served_tier_source`. */
60
+ readonly servedTierSource?: ServedTierSource;
54
61
  }
55
62
  /** Build one adapter's NATIVE `meta` (spec §2). Only defined signals appear —
56
63
  * everything else is unknown and omitted. The producer takes it from here. */
@@ -1 +1 @@
1
- {"version":3,"file":"attempt-meta-map.d.ts","sourceRoot":"","sources":["../../src/adapters/attempt-meta-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;kCAEkC;AAClC,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,UAAU,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,YAAY,GAAG,SAAS,CA6C1B;AAED;;;oDAGoD;AACpD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,cAAc,GAClB;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOlF;AAED;0EAC0E;AAC1E,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GACzC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,SAAS,CAQ/C;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAAC,aAAa,CAAC,CAyBxB;AAED;;;uDAGuD;AACvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CASzE"}
1
+ {"version":3,"file":"attempt-meta-map.d.ts","sourceRoot":"","sources":["../../src/adapters/attempt-meta-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC/G,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;kCAEkC;AAClC,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,UAAU,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,YAAY,GAAG,SAAS,CA6C1B;AAED;;;oDAGoD;AACpD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,cAAc,GAClB;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOlF;AAED;0EAC0E;AAC1E,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GACzC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,SAAS,CAQ/C;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;;0EAEsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC;gFAC4E;IAC5E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CAC9C;AAED;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAAC,aAAa,CAAC,CA2BxB;AAED;;;uDAGuD;AACvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CASzE"}
@@ -134,6 +134,10 @@ function buildAdapterMeta(provider, s) {
134
134
  }
135
135
  if (typeof s.streaming === "boolean")
136
136
  meta.streaming = s.streaming;
137
+ if (s.promptDelivery !== undefined)
138
+ meta.prompt_delivery = s.promptDelivery;
139
+ if (s.servedTierSource !== undefined)
140
+ meta.served_tier_source = s.servedTierSource;
137
141
  return meta;
138
142
  }
139
143
  /** A transport/SDK error's coarse envelope (spec §2 `http_status`/`error_code`).
@@ -27,8 +27,8 @@ function buildAdapters(config, registryFacts, compatStatic) {
27
27
  }
28
28
  if (config.keys.grok)
29
29
  adapters.set("grok", (0, grok_1.createGrokAdapter)({ apiKey: config.keys.grok }));
30
- // A1 aggregator hosts behind the generic openai-compatible adapter.
31
- for (const hostId of ["deepinfra", "fireworks"]) {
30
+ // A1 aggregator hosts + Z.ai (B4a) behind the generic openai-compatible adapter.
31
+ for (const hostId of ["deepinfra", "fireworks", "zai"]) {
32
32
  const apiKey = config.keys[hostId];
33
33
  if (!apiKey)
34
34
  continue;