modelrig 0.7.0 → 0.8.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 (128) hide show
  1. package/CHANGELOG.md +107 -0
  2. package/README.md +63 -0
  3. package/dist/bakeoff/disagreement.d.ts +93 -0
  4. package/dist/bakeoff/disagreement.d.ts.map +1 -0
  5. package/dist/bakeoff/disagreement.js +140 -0
  6. package/dist/bakeoff/engine.d.ts.map +1 -1
  7. package/dist/bakeoff/engine.js +58 -2
  8. package/dist/bakeoff/pairwise.d.ts +102 -0
  9. package/dist/bakeoff/pairwise.d.ts.map +1 -0
  10. package/dist/bakeoff/pairwise.js +214 -0
  11. package/dist/bakeoff/replay.d.ts +6 -0
  12. package/dist/bakeoff/replay.d.ts.map +1 -1
  13. package/dist/bakeoff/replay.js +6 -2
  14. package/dist/bakeoff/report.d.ts +7 -0
  15. package/dist/bakeoff/report.d.ts.map +1 -1
  16. package/dist/bakeoff/report.js +46 -0
  17. package/dist/bakeoff/types.d.ts +24 -0
  18. package/dist/bakeoff/types.d.ts.map +1 -1
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +9 -9
  21. package/dist/mask/apply.d.ts +80 -0
  22. package/dist/mask/apply.d.ts.map +1 -0
  23. package/dist/mask/apply.js +228 -0
  24. package/dist/mask/detect.d.ts +38 -0
  25. package/dist/mask/detect.d.ts.map +1 -0
  26. package/dist/mask/detect.js +167 -0
  27. package/dist/mask/index.d.ts +21 -0
  28. package/dist/mask/index.d.ts.map +1 -0
  29. package/dist/mask/index.js +54 -0
  30. package/dist/mask/mask-policy-cli.d.ts +11 -0
  31. package/dist/mask/mask-policy-cli.d.ts.map +1 -0
  32. package/dist/mask/mask-policy-cli.js +33 -0
  33. package/dist/mask/policy.d.ts +38 -0
  34. package/dist/mask/policy.d.ts.map +1 -0
  35. package/dist/mask/policy.js +187 -0
  36. package/dist/mask/proof.d.ts +54 -0
  37. package/dist/mask/proof.d.ts.map +1 -0
  38. package/dist/mask/proof.js +120 -0
  39. package/dist/mask/receipt.d.ts +30 -0
  40. package/dist/mask/receipt.d.ts.map +1 -0
  41. package/dist/mask/receipt.js +49 -0
  42. package/dist/mask/seam.d.ts +53 -0
  43. package/dist/mask/seam.d.ts.map +1 -0
  44. package/dist/mask/seam.js +120 -0
  45. package/dist/mask/types.d.ts +81 -0
  46. package/dist/mask/types.d.ts.map +1 -0
  47. package/dist/mask/types.js +10 -0
  48. package/dist/mask/vault.d.ts +50 -0
  49. package/dist/mask/vault.d.ts.map +1 -0
  50. package/dist/mask/vault.js +81 -0
  51. package/dist/receipts/types.d.ts +65 -0
  52. package/dist/receipts/types.d.ts.map +1 -0
  53. package/dist/receipts/types.js +134 -0
  54. package/dist/routes/loader-warnings.d.ts +8 -0
  55. package/dist/routes/loader-warnings.d.ts.map +1 -1
  56. package/dist/routes/loader-warnings.js +23 -0
  57. package/dist/routes/loader.d.ts +1 -1
  58. package/dist/routes/loader.d.ts.map +1 -1
  59. package/dist/routes/loader.js +24 -25
  60. package/dist/routes/mask-loader.d.ts +18 -0
  61. package/dist/routes/mask-loader.d.ts.map +1 -0
  62. package/dist/routes/mask-loader.js +39 -0
  63. package/dist/routes/objective-loader.d.ts.map +1 -1
  64. package/dist/routes/objective-loader.js +51 -17
  65. package/dist/routes/screen-loader.d.ts +23 -0
  66. package/dist/routes/screen-loader.d.ts.map +1 -0
  67. package/dist/routes/screen-loader.js +79 -0
  68. package/dist/routes/template.d.ts +10 -0
  69. package/dist/routes/template.d.ts.map +1 -1
  70. package/dist/routes/template.js +10 -3
  71. package/dist/run/accept.d.ts +4 -0
  72. package/dist/run/accept.d.ts.map +1 -1
  73. package/dist/run/accept.js +2 -0
  74. package/dist/run/dispatch.d.ts +35 -0
  75. package/dist/run/dispatch.d.ts.map +1 -1
  76. package/dist/run/dispatch.js +33 -0
  77. package/dist/run/grounding.d.ts +47 -2
  78. package/dist/run/grounding.d.ts.map +1 -1
  79. package/dist/run/grounding.js +113 -6
  80. package/dist/run/raw.d.ts.map +1 -1
  81. package/dist/run/raw.js +6 -1
  82. package/dist/run/record-attempt.d.ts.map +1 -1
  83. package/dist/run/record-attempt.js +12 -1
  84. package/dist/run/run.d.ts.map +1 -1
  85. package/dist/run/run.js +19 -16
  86. package/dist/screen/provenance.d.ts +59 -0
  87. package/dist/screen/provenance.d.ts.map +1 -0
  88. package/dist/screen/provenance.js +104 -0
  89. package/dist/screen/types.d.ts +71 -0
  90. package/dist/screen/types.d.ts.map +1 -0
  91. package/dist/screen/types.js +9 -0
  92. package/dist/status-cli.d.ts +11 -0
  93. package/dist/status-cli.d.ts.map +1 -0
  94. package/dist/status-cli.js +21 -0
  95. package/dist/status-runs.d.ts +38 -0
  96. package/dist/status-runs.d.ts.map +1 -0
  97. package/dist/status-runs.js +80 -0
  98. package/dist/status.d.ts +7 -2
  99. package/dist/status.d.ts.map +1 -1
  100. package/dist/status.js +11 -2
  101. package/dist/telemetry/ingest-protocol.d.ts +8 -0
  102. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  103. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  104. package/dist/telemetry/store-migrations.js +11 -0
  105. package/dist/telemetry/store.d.ts.map +1 -1
  106. package/dist/telemetry/store.js +12 -3
  107. package/dist/telemetry/supabase-exporter.d.ts +64 -0
  108. package/dist/telemetry/supabase-exporter.d.ts.map +1 -0
  109. package/dist/telemetry/supabase-exporter.js +211 -0
  110. package/dist/types/provider.d.ts +11 -0
  111. package/dist/types/provider.d.ts.map +1 -1
  112. package/dist/types/provider.js +9 -0
  113. package/dist/types/route.d.ts +55 -0
  114. package/dist/types/route.d.ts.map +1 -1
  115. package/dist/types/telemetry.d.ts +7 -0
  116. package/dist/types/telemetry.d.ts.map +1 -1
  117. package/dist/validate-cli.d.ts.map +1 -1
  118. package/dist/validate-cli.js +13 -0
  119. package/dist/verify/types.d.ts +6 -0
  120. package/dist/verify/types.d.ts.map +1 -1
  121. package/docs/agent-skill/SKILL.md +131 -0
  122. package/docs/how-it-fits.md +34 -0
  123. package/docs/instrumentation.md +221 -0
  124. package/docs/llms.txt +276 -0
  125. package/docs/migration-playbook.md +512 -0
  126. package/docs/quickstart.md +333 -0
  127. package/package.json +18 -2
  128. package/receipts.d.ts +12 -0
@@ -0,0 +1,333 @@
1
+ # ModelRig quickstart — see, prove, and save on every model call
2
+
3
+ ModelRig is the operating layer for your AI workstream. Each LLM task becomes a
4
+ **versioned route file** (prompt template + JSON schema + candidate models +
5
+ policy) that you call with one line — `rig.run("task.name", {input, tags})` —
6
+ and three things happen at once:
7
+
8
+ - **See** — the run lands in an **inspectable artifact store** (runs → steps →
9
+ artifacts, lineaged and integrity-hashed; see the
10
+ [instrumentation guide](instrumentation.md)), with cost/tokens/tier and a
11
+ verdict recorded against your tags.
12
+ - **Prove** — you get schema-validated output, served only by a model you
13
+ authorized, and you can replay that traffic against cheaper candidates in a
14
+ bake-off before you change anything.
15
+ - **Save** — a winning candidate is a change to your YAML you review; nothing
16
+ swaps a model on its own, and your routes are always YAML in your git.
17
+
18
+ The rest of this page is Step 0 (two minutes, once), then the two ways to
19
+ instrument your project — point your coding agent at it, or do it by hand.
20
+
21
+ ## Step 0 — create your org and key (two minutes, human-only)
22
+
23
+ Account setup stays human: your coding agent never creates accounts, signs in,
24
+ or holds credentials. Once, up front:
25
+
26
+ 1. Sign up and create your organization at
27
+ [app.modelrig.ai](https://app.modelrig.ai) — email and an org name is the
28
+ whole form.
29
+ 2. Issue an API key in the console (the `rig_sk_…` value is shown once — copy it).
30
+ 3. Export it beside your provider keys: `MODELRIG_API_KEY=rig_sk_…`.
31
+
32
+ That key is the only ModelRig credential your app ever holds — never a database
33
+ URL, never a service-role key. Provider keys never leave your process.
34
+
35
+ **No account needed for the open lane:** with only your provider keys set,
36
+ routes, probes and telemetry run entirely on your machine — the hosted console
37
+ and synced telemetry are what the key unlocks. You can add it later.
38
+
39
+ There are two ways your calls are keyed and billed, and you pick per route:
40
+
41
+ - **Bring your own keys (BYOK).** Your provider keys stay in your environment;
42
+ ModelRig routes to the provider directly. The first 1,000,000 requests each
43
+ month are free, then a flat 2% of list price. On the raw lane (`rig.runRaw`)
44
+ the `timeoutMs` default is tier-aware — **flex ⇒ set `timeoutMs` in minutes**
45
+ (default 900 s since 0.5.0); see the
46
+ [raw-lane defaults table](route-bundles.md#raw-lane-defaults-rigrunraw).
47
+ - **Managed keys.** ModelRig fronts the provider's inference cost, so managed
48
+ calls are provider cost + 2% from the first request — no key for you to hold.
49
+
50
+ Same flat 2% margin either way; your negotiated provider discounts stay yours.
51
+
52
+ ## Point your coding agent at it
53
+
54
+ The fastest migration is the one your agent performs. From inside your project,
55
+ paste this into Claude Code, Codex, or Cursor:
56
+
57
+ ```
58
+ Migrate this project to ModelRig routes (https://modelrig.dev).
59
+
60
+ 0. If MODELRIG_API_KEY is not set and I want hosted telemetry, STOP and ask me
61
+ to create an org and issue a rig_sk_ key at https://app.modelrig.ai. Never
62
+ create accounts, sign in, or handle credentials yourself. Without the key,
63
+ proceed in local-only mode — that is fully supported.
64
+ 1. Read https://modelrig.dev/quickstart.html and
65
+ https://modelrig.dev/route-bundles.html.
66
+ 2. Find every place this codebase calls an LLM API directly.
67
+ 3. For each call site, define a route bundle YAML (schema, candidate models,
68
+ policy, prompt template) and replace the direct call with
69
+ rig.run("<route.name>"). Keep prompt and schema semantics unchanged.
70
+ 4. Before naming any model capability, check the probed registry — declared
71
+ flags and probed behavior differ; trust the probed layer.
72
+ 5. Run the project's existing tests and report every file you changed.
73
+ ```
74
+
75
+ Your agent reads the docs, finds your LLM call sites, and turns them into
76
+ routes. It **won't** create accounts, sign in, or touch your API keys (those
77
+ stay in the environment you set in Step 0), and it swaps call sites to
78
+ `rig.run()` without changing prompt or schema semantics — same model, human-
79
+ reviewed. The full autonomy ladder and same-model guarantees are in the
80
+ [migration playbook](migration-playbook.md).
81
+
82
+ ## Or set it up by hand
83
+
84
+ ### 1. Install
85
+
86
+ ```bash
87
+ pnpm add modelrig # or npm i modelrig — Node >= 20
88
+ ```
89
+
90
+ It builds a native module (`better-sqlite3`) on install, so the machine needs a
91
+ toolchain.
92
+
93
+ ```ts
94
+ import { createRig, loadConfigFromEnv } from "modelrig";
95
+ ```
96
+
97
+ Environment (read once, in ModelRig's config layer only):
98
+
99
+ ```bash
100
+ GEMINI_API_KEY=… # only providers you configure get adapters
101
+ OPENAI_API_KEY=…
102
+ DEEPSEEK_API_KEY=…
103
+
104
+ # Where your telemetry goes — pick ONE lane:
105
+ MODELRIG_API_KEY=rig_sk_… # hosted: rows go to ModelRig,
106
+ # scoped to the org that key names
107
+ # SET IT ON EVERY service/worker/cron
108
+ # (omit it and everything below still works, locally)
109
+ MODELRIG_ROUTES_DIR=./modelrig/routes # default
110
+ MODELRIG_TELEMETRY_DB=./.modelrig/telemetry.db # default
111
+ MODELRIG_ENVELOPE_BUDGET_USD=25 # default envelope budget
112
+ ```
113
+
114
+ Provider keys never leave your process on either lane. A `rig_sk_` key is issued
115
+ in the console at [app.modelrig.ai](https://app.modelrig.ai) and is the only
116
+ ModelRig credential your app ever holds — never a database URL, never a
117
+ service-role key.
118
+
119
+ ### 2. Define a route bundle
120
+
121
+ One YAML file per route under `modelrig/routes/`:
122
+
123
+ ```yaml
124
+ # modelrig/routes/example.support_summarize.yaml
125
+ route: example.support_summarize # the task handle you pass to rig.run
126
+ version: 1 # bump on ANY change
127
+ schema: ./schemas/support_summarize.schema.json # null for unstructured routes
128
+ candidates: # THE candidate set — the invariant boundary.
129
+ - provider: openai # Nothing outside this list can ever serve
130
+ model: gpt-5.4-mini # the route: enforced by a branded type at
131
+ - provider: gemini # compile time and a runtime guard.
132
+ model: gemini-3.1-flash-lite
133
+ require: [schema_conformant] # hard constraints on candidate eligibility
134
+ prefer: [cost] # advisory ordering (cheapest first)
135
+ prompt:
136
+ system: ./prompts/support_summarize.system.md
137
+ variables: [ticket, product, priorContext]
138
+ policy:
139
+ retries: { content_invalid: 2, network: 4, capacity_shed: 3 }
140
+ timeout_ms: 60000
141
+ tier: flex # requested tier; served tier is recorded
142
+ json: native # native strict schema | json_mode (rung 2)
143
+ sampling: { temperature: 0.1, max_output_tokens: 8192 }
144
+ # ^ preserve the original call's sampling;
145
+ # absent = the adapter's own defaults
146
+ ```
147
+
148
+ Prompt templates support `{{variable}}` substitution and capability-conditional
149
+ blocks resolved against the **serving** candidate's flags:
150
+
151
+ ```
152
+ Summarise this ticket for the next agent: {{ticket}}
153
+
154
+ {{#if capability.grounded_native}}
155
+ Use web search to verify recent events.
156
+ {{/if}}
157
+ {{#unless capability.structured_native}}
158
+ CRITICAL: respond with a single JSON object, no markdown fences.
159
+ {{/unless}}
160
+ ```
161
+
162
+ Variables must be declared in `prompt.variables`; referencing an undeclared
163
+ variable fails at load time, not at run time.
164
+
165
+ The `schema` file is a JSON Schema. ModelRig validates output under **JSON
166
+ Schema 2020-12** as the primary dialect and **also accepts draft-07** (a schema
167
+ declaring either `$schema` compiles). Validation runs non-strict with union
168
+ types allowed, and `format` is annotation-only — it is not asserted (an
169
+ `email`/`date-time` format is documentation, not a constraint). Author 2020-12
170
+ unless you have a draft-07 schema already; both work.
171
+
172
+ ### 3. Call it
173
+
174
+ ```ts
175
+ const rig = createRig(loadConfigFromEnv());
176
+
177
+ const result = await rig.run("example.support_summarize", {
178
+ input: { ticket: rawTicket, product: "Acme Cloud", priorContext: history },
179
+ tags: { run_id: "run-123", step: "summarize", customer: "acme" },
180
+ budget: { envelope: "run-123" }, // hard-stop cost envelope for the run
181
+ });
182
+
183
+ result.output; // JSON, already validated against your schema
184
+ result.meta.provider; // which candidate actually served
185
+ result.meta.costEstimateUsd; // priced from the pinned LiteLLM snapshot
186
+ result.meta.servedTier; // vs meta.requestedTier — downgrades visible
187
+ result.meta.attemptsByClass; // retries consumed, per failure class
188
+
189
+ rig.close();
190
+ ```
191
+
192
+ ### 4. Wrap it in a run — the standard path
193
+
194
+ **Every pipeline execution is a run.** A run is the standard way ModelRig records
195
+ what your workstream did — not an appendix. Wrap the execution in a run context,
196
+ name one **step** per model-call family, and **save the work products** as
197
+ artifacts; then the console's **Runs** tab shows the run → step → artifact chain
198
+ you can click through, and `/projects` groups it by tag.
199
+
200
+ ```ts
201
+ const rig = createRig(loadConfigFromEnv());
202
+
203
+ // One run per pipeline execution. run.scope isolates concurrent runs; use
204
+ // run.start()/run.end() for one run at a time on a request path.
205
+ await rig.artifacts.run.scope(
206
+ { pipeline: "support", episodeKey: ticketId },
207
+ async () => {
208
+ const result = await rig.run("example.support_summarize", {
209
+ input: { ticket, product, priorContext },
210
+ tags: { subject: customer, feature: "summarize" }, // run_id auto-stamped
211
+ stepKey: "summarize", // names this step on /runs
212
+ });
213
+ // Save the work product — prompt/raw/parsed all save the same way.
214
+ rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output" });
215
+ },
216
+ );
217
+ rig.close();
218
+ ```
219
+
220
+ - **Tags:** carry `subject` (what the run is *about*) and `feature`. You no longer
221
+ hand-write `run_id` — **inside a run context the SDK stamps `tags.run_id`** (the
222
+ run's `episodeKey`, else its id) on every attempt row of both lanes; a value you
223
+ pass always wins, and outside a run context nothing is stamped. It also stamps
224
+ `tags.step` (the current step key) the same way; a value you pass always wins,
225
+ so name the step yourself with `stepKey` (or `tags.step`) when you want a
226
+ specific label.
227
+ - **Raw lane:** a `rig.runRaw` call inside a run context records a ground-truth
228
+ step too (since 0.5.0), so grounded/cached pipelines fill the same graph.
229
+ - **See it:** open `/runs` — the run, its steps, its artifacts. `modelrig status`
230
+ prints `runs recorded: N (last: …)` as the local mirror, and `modelrig validate`
231
+ warns if a rig records calls but never starts a run.
232
+
233
+ The full artifact API — lineage (`link`), evaluations (`evaluate`), the reusable
234
+ per-step seam, and `run.scope` vs `run.start` — is the
235
+ [instrumentation guide](instrumentation.md); this section is the standard path
236
+ that links to it.
237
+
238
+ ## When it fails, it fails typed
239
+
240
+ ```ts
241
+ import { RigFailureError } from "modelrig";
242
+
243
+ try {
244
+ await rig.run("example.support_summarize", opts);
245
+ } catch (err) {
246
+ if (err instanceof RigFailureError) {
247
+ err.failure.class; // content_invalid | capacity_shed | network | refusal
248
+ // | cache_invalid | timeout | config_auth
249
+ // | budget_exhausted | invariant_violation
250
+ // | quality_rejected
251
+ err.failure.fixHint; // machine-readable remediation when available
252
+ }
253
+ }
254
+ ```
255
+
256
+ Retry budgets are **per class and non-fungible** — exhausting `network`
257
+ retries never consumes the `content_invalid` budget. Terminal classes
258
+ (`budget_exhausted`, `invariant_violation`) are never retried.
259
+ `config_auth` (bad or missing credentials) is **non-retryable by contract**:
260
+ it cannot appear in a bundle's retry budgets, is never retried on the same
261
+ candidate, and the run loop advances straight to the next candidate — a dead
262
+ key never burns backoff time. Failures that still billed tokens (refusals,
263
+ truncations) carry their token usage, so envelopes and telemetry account the
264
+ real spend.
265
+
266
+ Each class — what it means, whether it retries, and how it backs off — is on [Routing & reliability](routing-reliability.md#5-failure-classes); how the whole candidate ladder runs (ordering, validate → repair → fall-through, and what is not built) is on that page too.
267
+
268
+ ## Where the telemetry goes
269
+
270
+ Every attempt (not just every run) writes one row to package-owned SQLite,
271
+ tagged with your `tags` — always, with no key. Setting `MODELRIG_API_KEY` turns
272
+ on **api export mode**: a background exporter ships batches to the ingest API
273
+ (`api.modelrig.ai`, override with `MODELRIG_INGEST_URL`), which the console
274
+ reads — fire-and-forget, queue-on-failure; the inference path never blocks on it.
275
+ Inspect live rows in your local SQLite file, or in the console once the key is
276
+ set — never a database URL of your own.
277
+
278
+ `MODELRIG_API_KEY` gates the exporter **per process**. Set it on EVERY service,
279
+ worker, and cron that constructs a rig — a process without it buffers to its own
280
+ local SQLite and ships nothing. A multi-service deployment that sets the key on
281
+ only the web tier silently loses every row a worker or cron produced.
282
+
283
+ Telemetry says what happened; **verdicts** say whether it was good. Attach a
284
+ thumbs up/down to a call or a whole run with `rig.feedback(...)` — from the SDK
285
+ or over HTTP for a non-SDK surface (see the
286
+ [feedback protocol](feedback-protocol.md)) — and every run gets an automatic
287
+ `run-outcome@v1` verdict, with an explicit, bounded model judge available when
288
+ you want one (see [run verdicts](run-verdicts.md)).
289
+
290
+ ## Runtime requirements
291
+
292
+ ModelRig assumes a **long-lived Node process with a writable disk**, and a few
293
+ things follow from that. Check them before you deploy:
294
+
295
+ - **Node ≥ 20**, and a native build toolchain at install time — it compiles
296
+ `better-sqlite3` (a C/C++ addon) for the local telemetry buffer. On a slim
297
+ container add the build essentials (`python3`, `make`, a C++ compiler).
298
+ - **A writable disk.** Telemetry and captures go to a local SQLite file
299
+ (`MODELRIG_TELEMETRY_DB`, default `./.modelrig/telemetry.db`); the background
300
+ exporter batches from it. A read-only filesystem breaks this.
301
+ - **Serverless (Vercel functions, AWS Lambda, Google Cloud Functions):**
302
+ supported, with two required adjustments. (1) Point the telemetry DB at the
303
+ only writable location — `MODELRIG_TELEMETRY_DB=/tmp/modelrig/telemetry.db`.
304
+ (2) `await rig.close()` **once per invocation** before the handler returns:
305
+ the function may freeze or be killed the moment you respond, and `close()` is
306
+ what drains the final rows (H1) — skip it and you silently lose the last
307
+ attempts of every invocation. Expect a native-module cold-start cost on the
308
+ first call after a scale-up.
309
+ - **Edge runtimes (Vercel Edge, Cloudflare Workers) are unsupported.** They have
310
+ no native-addon support and no filesystem, so `better-sqlite3` cannot load at
311
+ all. Run ModelRig in a Node runtime; if an edge function must call a route,
312
+ put ModelRig behind a Node service it calls over HTTP.
313
+
314
+ ## When it doesn't work
315
+
316
+ The first three failures below stop the bundle loading; the fourth lets it load
317
+ and then misbehave, so it is the one to watch for. The full list, in the
318
+ loader's own words, is in [route-bundles.md](./route-bundles.md#when-it-doesnt-work).
319
+
320
+ - **The bundle won't load, naming a schema or prompt path.** The file isn't at
321
+ the path the bundle names — paths resolve relative to the bundle. Create it
322
+ there, or set `schema: null` for a free-form task.
323
+ - **The bundle won't load, naming an undeclared variable.** A `{{name}}` in the
324
+ template isn't in `prompt.variables`. Add it (or fix the typo).
325
+ - **The first call succeeds locally but nothing appears in the console.**
326
+ `MODELRIG_API_KEY` isn't set in that environment. Everything runs without it;
327
+ it only decides where telemetry lands.
328
+ - **The call runs but the output isn't what the old code produced.** Usually a
329
+ wrong `json: native` — a claim about the model, valid only for one whose
330
+ registry entry shows probed `structured_native`. Check the probed registry,
331
+ not memory.
332
+
333
+ Still stuck? Open an issue at <https://github.com/modelrig/modelrig/issues>.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "modelrig",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "description": "ModelRig — capability-aware AI gateway: routes-as-code, provider adapters, typed failures, budget envelopes, and local-first telemetry.",
6
6
  "license": "Apache-2.0",
@@ -24,17 +24,33 @@
24
24
  },
25
25
  "main": "dist/index.js",
26
26
  "types": "dist/index.d.ts",
27
+ "//exports": "ADDITIVE sub-path map (RP-1). `.` preserves the historical bare entry; `./receipts` (Receipt@v1 shape + canonicalize/hash/classification) is exposed ONLY here, never re-exported from index.ts (keeps the public surface — and index.ts's LOC — untouched). `./data/*` + `./registry/*` keep the runtime data/registry files reachable now that an exports map turns resolution into an allowlist. The root `receipts.d.ts` re-export shim exists ALONGSIDE this so classic-`Node` moduleResolution consumers (e.g. modelrig-server) type-resolve the sub-path too.",
28
+ "exports": {
29
+ ".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
30
+ "./receipts": { "types": "./dist/receipts/types.d.ts", "default": "./dist/receipts/types.js" },
31
+ "./package.json": "./package.json",
32
+ "./data/*": "./data/*",
33
+ "./registry/*": "./registry/*"
34
+ },
27
35
  "//files": "ALLOWLIST, not a denylist, and the difference is not academic. Without this field npm falls back to .npmignore, then to THIS DIRECTORY's .gitignore — never the repo root's. The root ignores `.env.*`, which is why git has never committed .env.local; the package-level .gitignore says nothing about it, so `npm pack` happily included it. A publish would have shipped 13 live secrets, the Supabase service-role key among them, into an immutable public tarball. Anything added here must be something we would hand a stranger. See __tests__/publish-safety.test.ts.",
36
+ "//files-docs": "GS-3: the customer-facing runs/onboarding docs ship so `npm i modelrig` + read-the-package teaches run→step→artifact OFFLINE (the C1-WALK newbie walkthrough surfaced that the tarball taught nothing about runs). These are the SAME files publish-oss already mirrors PUBLIC via the docs/** allowlist (oss-publish-policy §3) — shipping them in the tarball is consistent, not a new disclosure. Curated named set (NOT docs/**) so internal phase-*/verification reports stay out. docs/llms.txt is GENERATED by scripts/build-docs-site.ts (it also writes the committed copy here) and tripwire-tested against llmsTxt() so the shipped copy can never go stale. publish-safety.test.ts asserts each ships AND carries no leak pattern.",
28
37
  "//files-registry": "registry/{registry,leaderboard}.json are fail-open runtime reads (facts.ts/rows.ts degrade to the static baseline when absent) — shipped by R-1 F2 RULING so a clean install routes on the PROBED capability profile the docs and the DVP report describe, not the static fallback. Enumerated file entries, NOT the registry/ dir (it also holds build scripts + tests). Public CC BY 4.0.",
29
38
  "//files-data": "`data/` is a RUNTIME dependency, not a build input (R-1 clean-room audit, A1): getPricing() reads data/pricing-map.json from the package root at first cost lookup, with NO existsSync guard — so an absent data/ throws, not degrades. 0.1.0 shipped dist-only and broke the flagship 'effective cost' story on a clean `npm i`. It is public CC BY 4.0 pricing data (data/PROVENANCE.md) — safe to hand a stranger. The tarball clean-room test (__tests__/tarball-cleanroom.test.ts) installs the packed artifact and asserts getPricing() returns a real rate, so this can never silently regress again.",
30
39
  "files": [
31
40
  "dist",
32
41
  "data",
42
+ "receipts.d.ts",
33
43
  "registry/registry.json",
34
44
  "registry/leaderboard.json",
35
45
  "README.md",
36
46
  "CHANGELOG.md",
37
- "LICENSE"
47
+ "LICENSE",
48
+ "docs/quickstart.md",
49
+ "docs/instrumentation.md",
50
+ "docs/how-it-fits.md",
51
+ "docs/migration-playbook.md",
52
+ "docs/agent-skill/SKILL.md",
53
+ "docs/llms.txt"
38
54
  ],
39
55
  "scripts": {
40
56
  "build": "tsc",
package/receipts.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Root type shim for the `modelrig/receipts` sub-path export (RP-1).
3
+ *
4
+ * The package.json `exports` map points `modelrig/receipts` at
5
+ * `dist/receipts/types` for runtime (Node honours `exports`) and for modern
6
+ * (`Node16`/`NodeNext`/`Bundler`) TypeScript consumers via its `types`
7
+ * condition. Classic `moduleResolution: "Node"` consumers — including this
8
+ * monorepo's `modelrig-server` — ignore `exports` and resolve a bare subpath
9
+ * against the package root, so this file is what they type-resolve. Both paths
10
+ * land on the same declarations; there is no second source of truth.
11
+ */
12
+ export * from "./dist/receipts/types";