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
+ * Prompt-stability lint — cache-aware-routing WS-C step 4 (spec §5.3,
3
+ * [Briefing §10]). A prefix cache only pays off when the CACHED PREFIX is
4
+ * stable across calls. A route template that interpolates a volatile value
5
+ * (a timestamp, a run id, a random nonce) high in the prompt shifts the prefix
6
+ * on every call, so the provider caches something that never recurs — the route
7
+ * pays the write premium forever and never reads a hit ("paying full price and
8
+ * don't know it", the §5.6 smell, caught statically here instead of after the money).
9
+ *
10
+ * v1 is a pure, conservative NAME heuristic over the declared `{{variable}}`
11
+ * references (the run-loop's stable prefix IS the route template — spec §5.3).
12
+ * It flags variables whose NAME reads volatile AND that appear before the
13
+ * template's stable tail. It does NOT inspect rendered values (it can't — the
14
+ * lint runs at load, before any input) and it never fails the load: a false
15
+ * positive must not block a legitimate route, so this warns. The prompt compiler
16
+ * + Prefix Stability Score (real per-token volatility telemetry) is the deferred
17
+ * successor ([Briefing Phase 5]); this is the cheap static precursor.
18
+ */
19
+ export interface CacheLintWarning {
20
+ /** The offending variable name. */
21
+ readonly variable: string;
22
+ /** Character offset of the reference in the raw template. */
23
+ readonly index: number;
24
+ /** Human-readable, teach-not-scold message. */
25
+ readonly message: string;
26
+ }
27
+ /**
28
+ * Lint a route's raw system template for cache-hostile volatile interpolations.
29
+ * Pure and total. Returns one warning per volatile-named reference that sits
30
+ * before the stable tail; `[]` for a clean template.
31
+ *
32
+ * "Before the stable tail" (v1 approximation): a volatile reference in the first
33
+ * `headFraction` of the template shifts the cacheable prefix; one at the very
34
+ * end (after the bulk of the stable instructions) shifts only the tail and is
35
+ * harmless for prefix caching. We warn only on head references to keep the
36
+ * signal high — a `{{now}}` in a trailing "as of {{now}}" line is fine.
37
+ */
38
+ export declare function lintCacheTemplate(template: string, opts?: {
39
+ readonly headFraction?: number;
40
+ }): CacheLintWarning[];
41
+ //# sourceMappingURL=cache-lint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-lint.d.ts","sourceRoot":"","sources":["../../src/routes/cache-lint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAmCH,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;IAAE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,gBAAgB,EAAE,CAmBpB"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * Prompt-stability lint — cache-aware-routing WS-C step 4 (spec §5.3,
4
+ * [Briefing §10]). A prefix cache only pays off when the CACHED PREFIX is
5
+ * stable across calls. A route template that interpolates a volatile value
6
+ * (a timestamp, a run id, a random nonce) high in the prompt shifts the prefix
7
+ * on every call, so the provider caches something that never recurs — the route
8
+ * pays the write premium forever and never reads a hit ("paying full price and
9
+ * don't know it", the §5.6 smell, caught statically here instead of after the money).
10
+ *
11
+ * v1 is a pure, conservative NAME heuristic over the declared `{{variable}}`
12
+ * references (the run-loop's stable prefix IS the route template — spec §5.3).
13
+ * It flags variables whose NAME reads volatile AND that appear before the
14
+ * template's stable tail. It does NOT inspect rendered values (it can't — the
15
+ * lint runs at load, before any input) and it never fails the load: a false
16
+ * positive must not block a legitimate route, so this warns. The prompt compiler
17
+ * + Prefix Stability Score (real per-token volatility telemetry) is the deferred
18
+ * successor ([Briefing Phase 5]); this is the cheap static precursor.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.lintCacheTemplate = lintCacheTemplate;
22
+ /** Variable names that read as per-call-volatile. Deliberately broad on the
23
+ * volatile side (a warning is cheap; a silently un-cached route is not) but
24
+ * anchored to whole \b tokens so `updated_at_note` matches yet `runtime` does
25
+ * not falsely trip on "run". */
26
+ const VOLATILE_NAME = new RegExp([
27
+ "\\btime(stamp)?\\b",
28
+ "\\bnow\\b",
29
+ "\\bdate(time)?\\b",
30
+ "run[_-]?id",
31
+ "request[_-]?id",
32
+ "trace[_-]?id",
33
+ "session[_-]?id",
34
+ "correlation[_-]?id",
35
+ "\\buuid\\b",
36
+ "\\bguid\\b",
37
+ "\\bnonce\\b",
38
+ "\\brandom\\b",
39
+ "\\brand\\b",
40
+ "\\bepoch\\b",
41
+ "\\bcurrent[_-]",
42
+ "generated[_-]?at",
43
+ "created[_-]?at",
44
+ "updated[_-]?at",
45
+ ].join("|"), "i");
46
+ /** The `{{var}}` scanner from template.ts, duplicated as a global-match here so
47
+ * the lint can read each reference's POSITION (scanTemplateVariables dedupes to
48
+ * names only). Kept in sync with template.ts's VAR_TAG. */
49
+ const VAR_TAG = /\{\{\s*([A-Za-z_][A-Za-z0-9_]*)\s*\}\}/g;
50
+ /**
51
+ * Lint a route's raw system template for cache-hostile volatile interpolations.
52
+ * Pure and total. Returns one warning per volatile-named reference that sits
53
+ * before the stable tail; `[]` for a clean template.
54
+ *
55
+ * "Before the stable tail" (v1 approximation): a volatile reference in the first
56
+ * `headFraction` of the template shifts the cacheable prefix; one at the very
57
+ * end (after the bulk of the stable instructions) shifts only the tail and is
58
+ * harmless for prefix caching. We warn only on head references to keep the
59
+ * signal high — a `{{now}}` in a trailing "as of {{now}}" line is fine.
60
+ */
61
+ function lintCacheTemplate(template, opts = {}) {
62
+ const headFraction = opts.headFraction ?? 0.75;
63
+ const headCutoff = Math.floor(template.length * headFraction);
64
+ const warnings = [];
65
+ for (const m of template.matchAll(VAR_TAG)) {
66
+ const name = m[1];
67
+ const index = m.index;
68
+ if (index >= headCutoff)
69
+ continue; // in the stable tail — harmless
70
+ if (!VOLATILE_NAME.test(name))
71
+ continue;
72
+ warnings.push({
73
+ variable: name,
74
+ index,
75
+ message: `route template interpolates \`{{${name}}}\` before the stable prompt tail — a per-call ` +
76
+ `value here shifts the cacheable prefix and defeats prompt caching (the route would pay ` +
77
+ `full price every call). Move volatile values to the end of the prompt, or drop them.`,
78
+ });
79
+ }
80
+ return warnings;
81
+ }
@@ -4,6 +4,17 @@
4
4
  * resolves schema/prompt paths relative to the bundle file, freezes.
5
5
  */
6
6
  import type { RouteBundle } from "../types/route";
7
+ /**
8
+ * CE-2 A5 co-item: warn (non-fatal) when a route's output schema has an ARRAY
9
+ * root. OpenAI strict structured output (`response_format: json_schema`, strict)
10
+ * requires a top-level OBJECT — an array-root schema cannot use strict
11
+ * enforcement on OpenAI candidates and silently falls back to non-strict there
12
+ * (the walkthrough §10 provider-matrix gap). The route still LOADS and serves as
13
+ * declared; the fix is to wrap the array in an object property so strict
14
+ * enforcement holds across every provider. Returns the warning line, or null.
15
+ * Pure — unit-tested directly.
16
+ */
17
+ export declare function arrayRootSchemaWarning(route: string, outputSchema: object | null): string | null;
7
18
  /**
8
19
  * Structured config error thrown for any invalid bundle (never a raw Error).
9
20
  * Load-time problems are deliberately NOT RigFailures — the failure taxonomy
@@ -22,5 +33,42 @@ export interface LoadedRoutes {
22
33
  get(route: string): RouteBundle;
23
34
  list(): readonly RouteBundle[];
24
35
  }
36
+ /**
37
+ * Merge two loaded route sets into one store — a UNION, collision is a loud
38
+ * error. Used to fold a deployment's bundled INTERNAL infra routes (e.g. the
39
+ * server's run-judge@v1 / analyst-summarize@v1) into the operator's configured
40
+ * routes without replacing them (index.ts createRig, extraRoutesDir). The
41
+ * reserved `@v<n>` infra namespace never collides with customer/demo routes;
42
+ * if it ever did, failing at boot is correct.
43
+ */
44
+ /**
45
+ * Load a routes directory and OPTIONALLY union a second (deployment-bundled
46
+ * infra) directory into it — the one call createRig makes so the extra-routes
47
+ * merge does not grow index.ts. `extraDir` absent/empty ⇒ exactly loadRoutes.
48
+ */
49
+ export declare function loadRoutesWithExtra(routesDir: string, extraDir?: string): LoadedRoutes;
50
+ export declare function mergeLoadedRoutes(primary: LoadedRoutes, extra: LoadedRoutes): LoadedRoutes;
51
+ /** One route file's structural-validation outcome (CE-6, spec §1 / walkthrough
52
+ * §8.7a). `route` is the parsed handle, or null when the bundle failed before a
53
+ * name could be read. */
54
+ export interface RouteValidation {
55
+ readonly file: string;
56
+ readonly route: string | null;
57
+ readonly ok: boolean;
58
+ readonly problems: readonly string[];
59
+ }
60
+ /**
61
+ * Structurally validate every route bundle in a directory WITHOUT requiring any
62
+ * provider key (CE-6 `modelrig validate`). Unlike {@link loadRoutes}, one bad
63
+ * bundle does NOT abort the rest — each file is loaded independently and its
64
+ * outcome collected, so a newbie sees every problem in one pass. Duplicate route
65
+ * handles across files are reported per the later file. This deliberately does
66
+ * NOT run the serveable-candidate gate (serveability.ts) — that needs keys and
67
+ * stays fail-closed for SERVING; validation only checks authoring.
68
+ *
69
+ * A missing directory is still a hard error (nothing to validate) — it throws
70
+ * the same teaching RouteConfigError as loadRoutes.
71
+ */
72
+ export declare function validateRoutesDir(routesDir: string): RouteValidation[];
25
73
  export declare function loadRoutes(routesDir: string): LoadedRoutes;
26
74
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/routes/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAKV,WAAW,EAGZ,MAAM,gBAAgB,CAAC;AAIxB;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;gBAEzB,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAO;CAOxF;AAiCD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,IAAI,IAAI,SAAS,WAAW,EAAE,CAAC;CAChC;AAkXD,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CA2C1D"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/routes/loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAKV,WAAW,EAGZ,MAAM,gBAAgB,CAAC;AAMxB;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAWhG;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;gBAEzB,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAO;CAOxF;AAiCD,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,IAAI,IAAI,SAAS,WAAW,EAAE,CAAC;CAChC;AA4ZD;;;;;;;GAOG;AACH;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CAItF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CAyB1F;AAED;;yBAEyB;AACzB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,eAAe,EAAE,CA6CtE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CA2C1D"}
@@ -6,6 +6,10 @@
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.RouteConfigError = void 0;
9
+ exports.arrayRootSchemaWarning = arrayRootSchemaWarning;
10
+ exports.loadRoutesWithExtra = loadRoutesWithExtra;
11
+ exports.mergeLoadedRoutes = mergeLoadedRoutes;
12
+ exports.validateRoutesDir = validateRoutesDir;
9
13
  exports.loadRoutes = loadRoutes;
10
14
  const node_fs_1 = require("node:fs");
11
15
  const node_path_1 = require("node:path");
@@ -13,6 +17,30 @@ const yaml_1 = require("yaml");
13
17
  const failures_1 = require("../types/failures");
14
18
  const fingerprint_1 = require("../registry/fingerprint");
15
19
  const template_1 = require("./template");
20
+ const cache_lint_1 = require("./cache-lint");
21
+ const sampling_config_1 = require("./sampling-config");
22
+ /**
23
+ * CE-2 A5 co-item: warn (non-fatal) when a route's output schema has an ARRAY
24
+ * root. OpenAI strict structured output (`response_format: json_schema`, strict)
25
+ * requires a top-level OBJECT — an array-root schema cannot use strict
26
+ * enforcement on OpenAI candidates and silently falls back to non-strict there
27
+ * (the walkthrough §10 provider-matrix gap). The route still LOADS and serves as
28
+ * declared; the fix is to wrap the array in an object property so strict
29
+ * enforcement holds across every provider. Returns the warning line, or null.
30
+ * Pure — unit-tested directly.
31
+ */
32
+ function arrayRootSchemaWarning(route, outputSchema) {
33
+ if (outputSchema === null)
34
+ return null;
35
+ const rootType = outputSchema.type;
36
+ if (rootType !== "array")
37
+ return null;
38
+ return (`route "${route}" declares an output schema whose ROOT is type: "array". OpenAI strict ` +
39
+ `structured output requires a top-level object, so strict enforcement cannot apply on OpenAI ` +
40
+ `candidates (they fall back to non-strict there). Wrap the array in an object property ` +
41
+ `(e.g. { "type": "object", "properties": { "items": <your array> } }) to keep strict ` +
42
+ `enforcement across providers. The route loads and serves as declared.`);
43
+ }
16
44
  /**
17
45
  * Structured config error thrown for any invalid bundle (never a raw Error).
18
46
  * Load-time problems are deliberately NOT RigFailures — the failure taxonomy
@@ -136,6 +164,7 @@ function parsePolicy(raw, problems) {
136
164
  if (typeof json !== "string" || !JSON_MODES.has(json)) {
137
165
  problems.push(`policy.json "${String(json)}" is not one of [native, json_mode]`);
138
166
  }
167
+ const sampling = (0, sampling_config_1.parseSampling)(raw.sampling, "policy.sampling", problems);
139
168
  if (problems.length > 0)
140
169
  return fallback;
141
170
  return Object.freeze({
@@ -143,6 +172,7 @@ function parsePolicy(raw, problems) {
143
172
  timeoutMs: timeoutMs,
144
173
  tier: tier,
145
174
  json: json,
175
+ ...(sampling !== undefined ? { sampling } : {}),
146
176
  });
147
177
  }
148
178
  /** "provider/model" string or {provider, model} mapping → CandidateSpec. */
@@ -328,6 +358,21 @@ function loadBundleFile(filePath) {
328
358
  if (typeof capture !== "boolean") {
329
359
  problems.push("capture must be a boolean");
330
360
  }
361
+ // Request-path caching opt-in (cache-aware-routing WS-C). Exactly one value
362
+ // is supported in v1 — `cache: auto`. Anything else is a config error (a typo
363
+ // must not silently ship an un-cached route). Gemini is NOT excluded here: a
364
+ // mixed-provider route may opt in, and the run-loop's policy gate skips
365
+ // gemini's request path (cache-stamp.ts) while its implicit hit rate still
366
+ // arrives passively — the exclusion is code-enforced, not author-policed.
367
+ let cache = false;
368
+ if (doc.cache !== undefined && doc.cache !== null) {
369
+ if (doc.cache === "auto") {
370
+ cache = true;
371
+ }
372
+ else {
373
+ problems.push(`cache "${String(doc.cache)}" is not supported — use "cache: auto" or omit it`);
374
+ }
375
+ }
331
376
  let groundingQuery;
332
377
  if (doc.grounding !== undefined) {
333
378
  if (!isRecord(doc.grounding) || typeof doc.grounding.query !== "string" || doc.grounding.query === "") {
@@ -377,6 +422,28 @@ function loadBundleFile(filePath) {
377
422
  if (problems.length > 0) {
378
423
  throw new RouteConfigError("invalid route bundle", { file: filePath, problems });
379
424
  }
425
+ // Prompt-stability lint (WS-C step 4, spec §5.3): a non-fatal warning — a
426
+ // volatile interpolation before the stable tail defeats prompt caching. Only
427
+ // meaningful once the route opted into `cache: auto`, so scoped to it to keep
428
+ // the signal high (same console-warn posture as manifest dimension warnings).
429
+ if (cache) {
430
+ for (const w of (0, cache_lint_1.lintCacheTemplate)(systemTemplate)) {
431
+ console.warn(`modelrig: route "${String(route)}" (${filePath}): ${w.message}`);
432
+ }
433
+ }
434
+ // CE-6 sampling advisory: a sub-1.0 temperature on a Gemini-3 candidate loads
435
+ // and is sent as declared, but Google recommends 1.0 for the family — warn so
436
+ // the choice is deliberate, never silent (spec §3/§6).
437
+ const geminiTempWarning = (0, sampling_config_1.gemini3LowTemperatureWarning)(route, policy, candidates);
438
+ if (geminiTempWarning !== null) {
439
+ console.warn(`modelrig: ${geminiTempWarning}`);
440
+ }
441
+ // CE-2 A5 co-item: an array-root schema cannot use OpenAI strict enforcement —
442
+ // warn once at load so the incompatibility is deliberate, never silent.
443
+ const arrayRootWarning = arrayRootSchemaWarning(route, outputSchema);
444
+ if (arrayRootWarning !== null) {
445
+ console.warn(`modelrig: ${arrayRootWarning}`);
446
+ }
380
447
  return Object.freeze({
381
448
  route: route,
382
449
  version: version,
@@ -390,12 +457,112 @@ function loadBundleFile(filePath) {
390
457
  ...(repair !== undefined ? { repair } : {}),
391
458
  variants: Object.freeze(variants),
392
459
  capture: capture === true,
460
+ cache,
393
461
  ...(groundingQuery !== undefined ? { groundingQuery } : {}),
394
462
  // Network-effect instrumentation (spec WS6): computed once at load,
395
463
  // stamped on every telemetry row for this route.
396
464
  fingerprintCell: (0, fingerprint_1.fingerprintSchema)(outputSchema)?.cellKey ?? null,
397
465
  });
398
466
  }
467
+ /**
468
+ * Merge two loaded route sets into one store — a UNION, collision is a loud
469
+ * error. Used to fold a deployment's bundled INTERNAL infra routes (e.g. the
470
+ * server's run-judge@v1 / analyst-summarize@v1) into the operator's configured
471
+ * routes without replacing them (index.ts createRig, extraRoutesDir). The
472
+ * reserved `@v<n>` infra namespace never collides with customer/demo routes;
473
+ * if it ever did, failing at boot is correct.
474
+ */
475
+ /**
476
+ * Load a routes directory and OPTIONALLY union a second (deployment-bundled
477
+ * infra) directory into it — the one call createRig makes so the extra-routes
478
+ * merge does not grow index.ts. `extraDir` absent/empty ⇒ exactly loadRoutes.
479
+ */
480
+ function loadRoutesWithExtra(routesDir, extraDir) {
481
+ const base = loadRoutes(routesDir);
482
+ if (extraDir === undefined || extraDir === "")
483
+ return base;
484
+ return mergeLoadedRoutes(base, loadRoutes(extraDir));
485
+ }
486
+ function mergeLoadedRoutes(primary, extra) {
487
+ const byName = new Map();
488
+ for (const b of primary.list())
489
+ byName.set(b.route, b);
490
+ for (const b of extra.list()) {
491
+ if (byName.has(b.route)) {
492
+ throw new RouteConfigError(`duplicate route "${b.route}" — an internal infra route collides with a configured route`);
493
+ }
494
+ byName.set(b.route, b);
495
+ }
496
+ return {
497
+ get(route) {
498
+ const bundle = byName.get(route);
499
+ if (!bundle) {
500
+ throw new RouteConfigError(`unknown route "${route}" — known routes: [${[...byName.keys()].join(", ")}]`);
501
+ }
502
+ return bundle;
503
+ },
504
+ list() {
505
+ return Object.freeze([...byName.values()]);
506
+ },
507
+ };
508
+ }
509
+ /**
510
+ * Structurally validate every route bundle in a directory WITHOUT requiring any
511
+ * provider key (CE-6 `modelrig validate`). Unlike {@link loadRoutes}, one bad
512
+ * bundle does NOT abort the rest — each file is loaded independently and its
513
+ * outcome collected, so a newbie sees every problem in one pass. Duplicate route
514
+ * handles across files are reported per the later file. This deliberately does
515
+ * NOT run the serveable-candidate gate (serveability.ts) — that needs keys and
516
+ * stays fail-closed for SERVING; validation only checks authoring.
517
+ *
518
+ * A missing directory is still a hard error (nothing to validate) — it throws
519
+ * the same teaching RouteConfigError as loadRoutes.
520
+ */
521
+ function validateRoutesDir(routesDir) {
522
+ if (!(0, node_fs_1.existsSync)(routesDir)) {
523
+ throw new RouteConfigError(`routes directory not found: ${routesDir}\n\n` +
524
+ "There are no route bundles to validate here. Create your first with\n" +
525
+ " modelrig init . draft routes from the provider calls in this repo\n" +
526
+ "or point elsewhere with MODELRIG_ROUTES_DIR. Docs: https://modelrig.dev/quickstart.html");
527
+ }
528
+ const files = (0, node_fs_1.readdirSync)(routesDir)
529
+ .filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"))
530
+ .sort()
531
+ .map((f) => (0, node_path_1.join)(routesDir, f));
532
+ const results = [];
533
+ const seen = new Map();
534
+ for (const file of files) {
535
+ try {
536
+ const bundle = loadBundleFile(file);
537
+ const prior = seen.get(bundle.route);
538
+ if (prior !== undefined) {
539
+ results.push({
540
+ file,
541
+ route: bundle.route,
542
+ ok: false,
543
+ problems: [`duplicate route "${bundle.route}" — already declared in ${prior}`],
544
+ });
545
+ continue;
546
+ }
547
+ seen.set(bundle.route, file);
548
+ results.push({ file, route: bundle.route, ok: true, problems: [] });
549
+ }
550
+ catch (err) {
551
+ if (err instanceof RouteConfigError) {
552
+ results.push({
553
+ file,
554
+ route: null,
555
+ ok: false,
556
+ problems: err.problems.length > 0 ? err.problems : [err.message],
557
+ });
558
+ }
559
+ else {
560
+ results.push({ file, route: null, ok: false, problems: [err.message] });
561
+ }
562
+ }
563
+ }
564
+ return results;
565
+ }
399
566
  function loadRoutes(routesDir) {
400
567
  if (!(0, node_fs_1.existsSync)(routesDir)) {
401
568
  // First-run moment (Phase 5 WS4): this is the most likely error a new
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The RESOLVED route bundle — http-gateway spec §13 (Phase D).
3
+ *
4
+ * A `RouteBundle` in memory already has its file references FOLLOWED and
5
+ * INLINED: `loader.ts` reads the schema file into `outputSchema` (a parsed JSON
6
+ * object) and the prompt file into `systemTemplate` (a string), and a MISSING
7
+ * file fails there with a teaching `RouteConfigError` — at deploy, where the
8
+ * developer is, never at request where the customer is (§13.1 rule 6). So the
9
+ * "resolved bundle" the exporter mirrors is a pure, serialisable PROJECTION of
10
+ * an already-loaded bundle; this module is the ONE definition of that shape and
11
+ * of the round-trip both sides use.
12
+ *
13
+ * exporter (SDK) resolveRouteBundle(bundle) → ResolvedRouteBundle + sha
14
+ * → mirrored onto public.routes.bundle / bundle_sha
15
+ * gateway (server) bundleFromResolved(mirrored) → RouteBundle
16
+ * → rig.runBundle(bundle, opts) → the SAME verified pipeline
17
+ *
18
+ * `bundle_sha` is the content hash of the CANONICAL serialisation (keys sorted
19
+ * recursively, array order preserved), so the same bundle always hashes the
20
+ * same and the console can flag a mirror that no longer matches git (§13.1
21
+ * rule 5). The hash is computed once, at sync time, from the resolved bundle.
22
+ *
23
+ * `fingerprintCell` is NOT carried on the wire: it is derived from
24
+ * `outputSchema` and recomputed on rebuild, so it can never drift from the
25
+ * schema it fingerprints.
26
+ */
27
+ import type { LoadedRoutes } from "./loader";
28
+ import type { CandidateSpec, Preference, RepairPolicy, Requirement, RouteBundle, RoutePolicy, RouteVariant } from "../types/route";
29
+ /**
30
+ * The canonical, JSON-serialisable form of a resolved route bundle. Every field
31
+ * here defines how the route BEHAVES — the gateway needs all of them to drive
32
+ * the verified pipeline without the customer's filesystem. `fingerprintCell` is
33
+ * deliberately absent (derived from `outputSchema`, recomputed on rebuild).
34
+ */
35
+ export interface ResolvedRouteBundle {
36
+ readonly route: string;
37
+ readonly version: number;
38
+ /** Resolved, parsed JSON Schema (null = unstructured route). Inlined at sync. */
39
+ readonly outputSchema: object | null;
40
+ readonly candidates: readonly CandidateSpec[];
41
+ readonly require: readonly Requirement[];
42
+ readonly prefer: readonly Preference[];
43
+ /** Raw template text — the prompt FILE, inlined at sync. */
44
+ readonly systemTemplate: string;
45
+ readonly variables: readonly string[];
46
+ readonly policy: RoutePolicy;
47
+ readonly repair?: RepairPolicy;
48
+ readonly variants: readonly RouteVariant[];
49
+ readonly capture: boolean;
50
+ readonly groundingQuery?: string;
51
+ }
52
+ /** Project an already-loaded (resolved) bundle to its serialisable form. Pure —
53
+ * no filesystem; the loader already followed every file reference. */
54
+ export declare function resolveRouteBundle(bundle: RouteBundle): ResolvedRouteBundle;
55
+ /** The canonical serialisation whose hash becomes `bundle_sha`. */
56
+ export declare function canonicalBundleJson(bundle: ResolvedRouteBundle): string;
57
+ /** Content hash of the canonical bundle — `sha256:<hex>`. Self-describing so a
58
+ * future algorithm change is visible in the stored value rather than silent. */
59
+ export declare function hashResolvedBundle(bundle: ResolvedRouteBundle): string;
60
+ /**
61
+ * Rebuild a runnable `RouteBundle` from a mirrored resolved bundle. Recomputes
62
+ * `fingerprintCell` from `outputSchema` (never trusted from the wire) and
63
+ * freezes to match loader output. Throws `RouteConfigError` — the loader's own
64
+ * teaching class — if the mirrored payload is structurally unusable, so a
65
+ * corrupt mirror row surfaces as a teaching error, not a downstream crash.
66
+ */
67
+ export declare function bundleFromResolved(raw: unknown): RouteBundle;
68
+ /**
69
+ * A `LoadedRoutes` holding exactly ONE bundle — the seam that lets the verified
70
+ * pipeline run over a mirrored bundle without a local routes directory.
71
+ *
72
+ * `run()`/`runStream()` resolve a route only through `deps.routes.get(name)`, so
73
+ * swapping in this store (and nothing else) runs the WHOLE existing pipeline —
74
+ * validate, repair, envelope, telemetry, ZDR (G-3), routing controls (G-2) —
75
+ * over the provided bundle. Asking for any other name is a teaching error, so a
76
+ * mismatched slug can never silently resolve to the wrong route.
77
+ */
78
+ export declare function singleRouteStore(bundle: RouteBundle): LoadedRoutes;
79
+ //# sourceMappingURL=resolved-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolved-bundle.d.ts","sourceRoot":"","sources":["../../src/routes/resolved-bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,4DAA4D;IAC5D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;sEACsE;AACtE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,mBAAmB,CAgB3E;AAiBD,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEvE;AAED;gFACgF;AAChF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAsC5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,YAAY,CAclE"}
@@ -0,0 +1,148 @@
1
+ "use strict";
2
+ /**
3
+ * The RESOLVED route bundle — http-gateway spec §13 (Phase D).
4
+ *
5
+ * A `RouteBundle` in memory already has its file references FOLLOWED and
6
+ * INLINED: `loader.ts` reads the schema file into `outputSchema` (a parsed JSON
7
+ * object) and the prompt file into `systemTemplate` (a string), and a MISSING
8
+ * file fails there with a teaching `RouteConfigError` — at deploy, where the
9
+ * developer is, never at request where the customer is (§13.1 rule 6). So the
10
+ * "resolved bundle" the exporter mirrors is a pure, serialisable PROJECTION of
11
+ * an already-loaded bundle; this module is the ONE definition of that shape and
12
+ * of the round-trip both sides use.
13
+ *
14
+ * exporter (SDK) resolveRouteBundle(bundle) → ResolvedRouteBundle + sha
15
+ * → mirrored onto public.routes.bundle / bundle_sha
16
+ * gateway (server) bundleFromResolved(mirrored) → RouteBundle
17
+ * → rig.runBundle(bundle, opts) → the SAME verified pipeline
18
+ *
19
+ * `bundle_sha` is the content hash of the CANONICAL serialisation (keys sorted
20
+ * recursively, array order preserved), so the same bundle always hashes the
21
+ * same and the console can flag a mirror that no longer matches git (§13.1
22
+ * rule 5). The hash is computed once, at sync time, from the resolved bundle.
23
+ *
24
+ * `fingerprintCell` is NOT carried on the wire: it is derived from
25
+ * `outputSchema` and recomputed on rebuild, so it can never drift from the
26
+ * schema it fingerprints.
27
+ */
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.resolveRouteBundle = resolveRouteBundle;
30
+ exports.canonicalBundleJson = canonicalBundleJson;
31
+ exports.hashResolvedBundle = hashResolvedBundle;
32
+ exports.bundleFromResolved = bundleFromResolved;
33
+ exports.singleRouteStore = singleRouteStore;
34
+ const node_crypto_1 = require("node:crypto");
35
+ const fingerprint_1 = require("../registry/fingerprint");
36
+ const loader_1 = require("./loader");
37
+ /** Project an already-loaded (resolved) bundle to its serialisable form. Pure —
38
+ * no filesystem; the loader already followed every file reference. */
39
+ function resolveRouteBundle(bundle) {
40
+ return {
41
+ route: bundle.route,
42
+ version: bundle.version,
43
+ outputSchema: bundle.outputSchema,
44
+ candidates: bundle.candidates,
45
+ require: bundle.require,
46
+ prefer: bundle.prefer,
47
+ systemTemplate: bundle.systemTemplate,
48
+ variables: bundle.variables,
49
+ policy: bundle.policy,
50
+ ...(bundle.repair !== undefined ? { repair: bundle.repair } : {}),
51
+ variants: bundle.variants,
52
+ capture: bundle.capture,
53
+ ...(bundle.groundingQuery !== undefined ? { groundingQuery: bundle.groundingQuery } : {}),
54
+ };
55
+ }
56
+ /** Deterministic JSON: object keys sorted recursively, array order preserved.
57
+ * The whole point is that the SAME bundle always produces the SAME bytes, so
58
+ * the content hash is stable across processes and machines. */
59
+ function canonicalize(value) {
60
+ if (Array.isArray(value))
61
+ return value.map(canonicalize);
62
+ if (value !== null && typeof value === "object") {
63
+ const out = {};
64
+ for (const key of Object.keys(value).sort()) {
65
+ out[key] = canonicalize(value[key]);
66
+ }
67
+ return out;
68
+ }
69
+ return value;
70
+ }
71
+ /** The canonical serialisation whose hash becomes `bundle_sha`. */
72
+ function canonicalBundleJson(bundle) {
73
+ return JSON.stringify(canonicalize(bundle));
74
+ }
75
+ /** Content hash of the canonical bundle — `sha256:<hex>`. Self-describing so a
76
+ * future algorithm change is visible in the stored value rather than silent. */
77
+ function hashResolvedBundle(bundle) {
78
+ return `sha256:${(0, node_crypto_1.createHash)("sha256").update(canonicalBundleJson(bundle)).digest("hex")}`;
79
+ }
80
+ /**
81
+ * Rebuild a runnable `RouteBundle` from a mirrored resolved bundle. Recomputes
82
+ * `fingerprintCell` from `outputSchema` (never trusted from the wire) and
83
+ * freezes to match loader output. Throws `RouteConfigError` — the loader's own
84
+ * teaching class — if the mirrored payload is structurally unusable, so a
85
+ * corrupt mirror row surfaces as a teaching error, not a downstream crash.
86
+ */
87
+ function bundleFromResolved(raw) {
88
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
89
+ throw new loader_1.RouteConfigError("mirrored route bundle is not an object");
90
+ }
91
+ const b = raw;
92
+ if (typeof b.route !== "string" || b.route === "") {
93
+ throw new loader_1.RouteConfigError("mirrored route bundle is missing `route`");
94
+ }
95
+ if (typeof b.version !== "number" || !Number.isInteger(b.version)) {
96
+ throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing a valid \`version\``);
97
+ }
98
+ if (!Array.isArray(b.candidates) || b.candidates.length === 0) {
99
+ throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" has no candidates`);
100
+ }
101
+ if (typeof b.systemTemplate !== "string") {
102
+ throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing \`systemTemplate\``);
103
+ }
104
+ if (b.policy === undefined || b.policy === null) {
105
+ throw new loader_1.RouteConfigError(`mirrored route bundle "${b.route}" is missing \`policy\``);
106
+ }
107
+ const outputSchema = b.outputSchema ?? null;
108
+ return Object.freeze({
109
+ route: b.route,
110
+ version: b.version,
111
+ outputSchema: outputSchema === null ? null : Object.freeze(outputSchema),
112
+ candidates: Object.freeze([...b.candidates]),
113
+ require: Object.freeze([...(b.require ?? [])]),
114
+ prefer: Object.freeze([...(b.prefer ?? [])]),
115
+ systemTemplate: b.systemTemplate,
116
+ variables: Object.freeze([...(b.variables ?? [])]),
117
+ policy: b.policy,
118
+ ...(b.repair !== undefined ? { repair: b.repair } : {}),
119
+ variants: Object.freeze([...(b.variants ?? [])]),
120
+ capture: b.capture === true,
121
+ ...(b.groundingQuery !== undefined ? { groundingQuery: b.groundingQuery } : {}),
122
+ // Recomputed, never trusted from the wire — cannot drift from the schema.
123
+ fingerprintCell: (0, fingerprint_1.fingerprintSchema)(outputSchema)?.cellKey ?? null,
124
+ });
125
+ }
126
+ /**
127
+ * A `LoadedRoutes` holding exactly ONE bundle — the seam that lets the verified
128
+ * pipeline run over a mirrored bundle without a local routes directory.
129
+ *
130
+ * `run()`/`runStream()` resolve a route only through `deps.routes.get(name)`, so
131
+ * swapping in this store (and nothing else) runs the WHOLE existing pipeline —
132
+ * validate, repair, envelope, telemetry, ZDR (G-3), routing controls (G-2) —
133
+ * over the provided bundle. Asking for any other name is a teaching error, so a
134
+ * mismatched slug can never silently resolve to the wrong route.
135
+ */
136
+ function singleRouteStore(bundle) {
137
+ return {
138
+ get(route) {
139
+ if (route !== bundle.route) {
140
+ throw new loader_1.RouteConfigError(`route "${route}" is not the mirrored bundle "${bundle.route}"`);
141
+ }
142
+ return bundle;
143
+ },
144
+ list() {
145
+ return Object.freeze([bundle]);
146
+ },
147
+ };
148
+ }