@linxiraos/pi-catalog 1.2.1 → 1.3.1

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 (130) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/README.md +4 -2
  3. package/THIRD-PARTY-NOTICES.txt +55 -5
  4. package/dist/types/build.d.ts +3 -2
  5. package/dist/types/compat/auth-ids.d.ts +2 -2
  6. package/dist/types/compat/axes.d.ts +1 -1
  7. package/dist/types/compat/behavior.d.ts +5 -0
  8. package/dist/types/compat/catalog-endpoint.d.ts +8 -0
  9. package/dist/types/compat/image-tokenization.d.ts +9 -1
  10. package/dist/types/compat/judgment-images.d.ts +25 -0
  11. package/dist/types/compat/provider-ids.d.ts +1 -1
  12. package/dist/types/compat/request-compression.d.ts +23 -0
  13. package/dist/types/compat/request-size.d.ts +12 -3
  14. package/dist/types/compat/thinking-replay.d.ts +9 -0
  15. package/dist/types/compat/types.d.ts +4 -0
  16. package/dist/types/discovery/antigravity.d.ts +7 -0
  17. package/dist/types/model-cache.d.ts +7 -0
  18. package/dist/types/model-manager.d.ts +6 -0
  19. package/dist/types/model-thinking.d.ts +11 -1
  20. package/dist/types/models.d.ts +16 -1
  21. package/dist/types/pricing.d.ts +37 -1
  22. package/dist/types/provider-models/openai-compat.d.ts +18 -46
  23. package/dist/types/provider-models/special.d.ts +6 -1
  24. package/dist/types/types.d.ts +63 -12
  25. package/dist/types/wire/codex.d.ts +2 -0
  26. package/package.json +5 -5
  27. package/src/build.d.ts +3 -2
  28. package/src/build.ts +159 -80
  29. package/src/compat/auth-ids.ts +2 -0
  30. package/src/compat/axes.d.ts +1 -1
  31. package/src/compat/axes.ts +129 -4
  32. package/src/compat/behavior.d.ts +5 -0
  33. package/src/compat/behavior.ts +9 -1
  34. package/src/compat/cascade.ts +4 -1
  35. package/src/compat/catalog-endpoint.ts +43 -0
  36. package/src/compat/image-tokenization.ts +28 -10
  37. package/src/compat/judgment-images.ts +83 -0
  38. package/src/compat/provider-ids.d.ts +1 -1
  39. package/src/compat/provider-ids.ts +1 -0
  40. package/src/compat/request-compression.ts +43 -0
  41. package/src/compat/request-size.ts +20 -5
  42. package/src/compat/resolve.ts +90 -53
  43. package/src/compat/rules/README.md +32 -3
  44. package/src/compat/rules/auth/_order.kdl +1 -1
  45. package/src/compat/rules/auth/google-antigravity.kdl +1 -1
  46. package/src/compat/rules/auth/sambanova.kdl +9 -0
  47. package/src/compat/rules/auth/stencil.kdl +1 -1
  48. package/src/compat/rules/classes/anthropic.kdl +42 -2
  49. package/src/compat/rules/classes/deepseek.kdl +4 -0
  50. package/src/compat/rules/classes/gemini.kdl +46 -2
  51. package/src/compat/rules/classes/meta.kdl +7 -0
  52. package/src/compat/rules/classes/minimax.kdl +11 -1
  53. package/src/compat/rules/classes/openai.kdl +34 -3
  54. package/src/compat/rules/classes/xai.kdl +26 -0
  55. package/src/compat/rules/providers/amazon-bedrock.kdl +1 -0
  56. package/src/compat/rules/providers/anthropic.kdl +23 -0
  57. package/src/compat/rules/providers/azure.kdl +2 -0
  58. package/src/compat/rules/providers/cerebras.kdl +3 -0
  59. package/src/compat/rules/providers/cline-pass.kdl +16 -0
  60. package/src/compat/rules/providers/commandcode.kdl +10 -0
  61. package/src/compat/rules/providers/cursor.kdl +50 -1
  62. package/src/compat/rules/providers/deepinfra.kdl +3 -1
  63. package/src/compat/rules/providers/devin.kdl +117 -6
  64. package/src/compat/rules/providers/factory-droid.kdl +6 -0
  65. package/src/compat/rules/providers/firepass.kdl +15 -0
  66. package/src/compat/rules/providers/fireworks.kdl +26 -0
  67. package/src/compat/rules/providers/github-copilot.kdl +24 -0
  68. package/src/compat/rules/providers/google-antigravity.kdl +37 -0
  69. package/src/compat/rules/providers/google-gemini-cli.kdl +1 -0
  70. package/src/compat/rules/providers/google-vertex.kdl +5 -0
  71. package/src/compat/rules/providers/google.kdl +1 -0
  72. package/src/compat/rules/providers/image-tokenization.kdl +2 -1
  73. package/src/compat/rules/providers/local.kdl +8 -1
  74. package/src/compat/rules/providers/meta.kdl +16 -0
  75. package/src/compat/rules/providers/moonshot.kdl +2 -0
  76. package/src/compat/rules/providers/muse-code.kdl +49 -0
  77. package/src/compat/rules/providers/native-tools.kdl +3 -0
  78. package/src/compat/rules/providers/ollama-cloud.kdl +10 -0
  79. package/src/compat/rules/providers/openai-codex.kdl +29 -5
  80. package/src/compat/rules/providers/openai.kdl +1 -0
  81. package/src/compat/rules/providers/opencode-go.kdl +35 -0
  82. package/src/compat/rules/providers/opencode-zen.kdl +4 -0
  83. package/src/compat/rules/providers/openrouter.kdl +27 -1
  84. package/src/compat/rules/providers/sambanova.kdl +60 -0
  85. package/src/compat/rules/providers/tool-call-names.kdl +37 -0
  86. package/src/compat/rules/providers/typesafe.kdl +22 -1
  87. package/src/compat/rules/providers/umans.kdl +7 -0
  88. package/src/compat/rules/providers/venice.kdl +7 -0
  89. package/src/compat/rules/providers/web.kdl +7 -0
  90. package/src/compat/rules/providers/xai-oauth.kdl +16 -6
  91. package/src/compat/rules/providers/xai.kdl +3 -0
  92. package/src/compat/rules/providers/zai.kdl +5 -0
  93. package/src/compat/rules/runtime/behavior.kdl +129 -8
  94. package/src/compat/rules/taxonomy/_collapse.kdl +11 -0
  95. package/src/compat/rules/taxonomy/gemma.kdl +1 -0
  96. package/src/compat/rules/taxonomy/meta.kdl +7 -0
  97. package/src/compat/rules/taxonomy/openai.kdl +2 -0
  98. package/src/compat/rules.json +1 -1
  99. package/src/compat/thinking-replay.ts +33 -0
  100. package/src/compat/types.d.ts +4 -0
  101. package/src/compat/types.ts +4 -0
  102. package/src/discovery/antigravity.d.ts +7 -0
  103. package/src/discovery/antigravity.ts +13 -0
  104. package/src/discovery/codex.ts +0 -4
  105. package/src/discovery/cursor.ts +28 -20
  106. package/src/discovery/devin.ts +7 -8
  107. package/src/model-cache.d.ts +7 -0
  108. package/src/model-cache.ts +8 -0
  109. package/src/model-manager.d.ts +6 -0
  110. package/src/model-manager.ts +41 -21
  111. package/src/model-thinking.d.ts +80 -0
  112. package/src/model-thinking.ts +13 -1
  113. package/src/model-tokenizer.ts +7 -1
  114. package/src/models.d.ts +16 -1
  115. package/src/models.json +1 -1
  116. package/src/models.ts +26 -12
  117. package/src/pricing.d.ts +37 -1
  118. package/src/pricing.ts +123 -12
  119. package/src/provider-models/bundled-references.ts +5 -2
  120. package/src/provider-models/cache-provider-id.ts +1 -1
  121. package/src/provider-models/descriptors.ts +2 -0
  122. package/src/provider-models/google.ts +7 -1
  123. package/src/provider-models/openai-compat.d.ts +18 -46
  124. package/src/provider-models/openai-compat.ts +188 -104
  125. package/src/provider-models/special.d.ts +6 -1
  126. package/src/provider-models/special.ts +15 -9
  127. package/src/types.d.ts +63 -12
  128. package/src/types.ts +71 -9
  129. package/src/wire/codex.d.ts +2 -0
  130. package/src/wire/codex.ts +17 -0
@@ -196,6 +196,8 @@ An `on-api` nested inside `on` under a class requires both the selected provider
196
196
 
197
197
  A `models` string without `*` is an exact, case-sensitive match against the provider-relative model identifier. A string containing `*` is an anchored, ASCII-case-insensitive wildcard match. Prefer taxonomy ranks; retain exact/glob lists only when they isolate the census member set exactly, and keep a `// residue:` comment explaining why ranks do not.
198
198
 
199
+ A row whose own id classifies as `unknown` but whose `requestModelId` does not resolves under the wire id: classification and `models` matching both use it, so the model the row sends gets its own rules. Devin Fusion pairings (`fusion-<lead>-sidekick-*`) resolve as their lead this way.
200
+
199
201
  `priority=N` is an optional signed integer property on the block that owns axis assignments. Its default is zero. Use it only to resolve an intentional equal-specificity overlap; do not use it to encode declaration order.
200
202
 
201
203
  `buildDiscoveredModel(spec, providerType)` resolves the catalog `discovery-api` axis before materializing compatibility. It preserves the credential-bearing provider ID and records `providerType` as the backend used for provider selectors on subsequent rebuilds. Ordinary `buildModel` preserves its input API. This lets custom-named llama.cpp deployments reuse the same rules without model-specific discovery code.
@@ -213,16 +215,36 @@ The three value shapes are:
213
215
  - **Object**: no arguments and a child block, including an empty block. Child names are kebab-case: an axis-directive spelling compiles to its resolved axis key (`template-reasoning-effort` → `qwenTemplateReasoningEffort`), anything else converts mechanically (`input-threshold` → `inputThreshold`); camelCase names are a compile error. `extra-body` payloads (top-level or nested) are the exception — their child names are literal wire JSON keys copied verbatim (`enable_thinking`). Each child is either one scalar or another object; arrays are not representable inside an object payload.
214
216
 
215
217
  A rule cannot assign the same resolved axis twice in one block.
216
- One object axis carries a computed form: `long-context-cost` accepts either the absolute rates (`input-threshold` + `input`/`output`/`cache-read`/`cache-write`) or `input-threshold` + `multiplier` (with optional `input-threshold-inclusive`), which derives the tier from the row's base price at build time, after `cost-patch` and `cost-fallback`, so the rule tracks upstream list-price updates (xAI's SuperGrok 200K tier). Rows without a token price carry no tier. An empty `long-context-cost {}` adds no tier; a more specific rule uses it to opt a host out of a broader rule's band (GitHub Copilot's Haiku 5.5 rows, whose long tier is the `-1m` sibling).
218
+ One object axis carries a computed form: `long-context-cost` accepts either the absolute rates (`input-threshold` + `input`/`output`/`cache-read`/`cache-write`) or `input-threshold` + `multiplier` (with optional `input-threshold-inclusive`, and `output-multiplier` when the tier raises output by a different factor than the other legs), which derives the tier from the row's base price at build time, after `cost-patch` and `cost-fallback`, so the rule tracks upstream list-price updates (xAI's SuperGrok 200K tier). Rows without a token price carry no tier. An empty `long-context-cost {}` adds no tier; a more specific rule uses it to opt a host out of a broader rule's band (GitHub Copilot's Haiku 5.5 rows, whose long tier is the `-1m` sibling).
219
+
220
+ `cost-patch` and `cost-fallback` accept an optional `effective-rates` block of named dated cards, each with a distinct ISO UTC `effective-from` (`YYYY-MM-DDTHH:mm:ssZ` or with three fractional-second digits) and any of `input`/`output`/`cache-read`/`cache-write` as nonnegative rates; a malformed card fails `buildModel` rather than leaving the old price in force. A card overwrites those legs from its date: for `cost-patch` over whatever upstream reports, for `cost-fallback` only when it seeded the row. A patch may carry the dated cards alone, leaving the upstream price in place until the first one starts; pin the current price beside them when upstream may later report the new one, or a row built after that loses the earlier period (Gemini 4 Argon's 2026-12-01 list-price increase in [`classes/gemini.kdl`](classes/gemini.kdl) pins both). `buildModel` sets the row's legs to the card due when it is built, and compiles every price period into `ModelCost.timeBased.effectiveRates` (complete cards, long-context tier and cache-rate derivations included, with a leading card for the price before the first change) under a schedule with no peak windows and a 1x off-peak multiplier. Requests are therefore priced at the card in force when they were made, even on rows built before a change (bundles, caches, long sessions); price displays read `getEffectiveTokenCost`, which selects the same card, and such a schedule reports no peak/off-peak period. Rebuilding a row that already carries such a schedule (dynamic merges, overrides) starts from the card due at the rebuild, and Cursor lanes priced from a sibling slug's card keep that card's schedule, scaled by the lane's multiplier. Cards that leave the price unchanged (a fallback the upstream price outranks) add no schedule. A rule cannot combine these cards with `time-based-cost` `effective-rates`; it may add them to a recurring `time-based-cost` tariff without its own.
217
221
 
218
222
  `context-window-authoritative #true` preserves a host's supplied context window through runtime model selection instead of applying inferred expansion or reference-price-tier caps. Explicit user context overrides still apply afterward. It applies to rows materialized through `buildModel` (discovery and regenerated bundles). See [`providers/factory-droid.kdl`](providers/factory-droid.kdl).
219
223
 
220
- `inline-image-byte-budget` caps retained base64 image characters for a deployment's request body. It leaves room for tool schemas and text beneath the provider's body-size limit; the coding agent reads it through `resolveInlineImageByteBudget` before sending live history. The budget applies only while `compat.officialEndpoint` holds: a custom `baseUrl` imposes its own body limit.
224
+ `long-context-window-uncapped #true` keeps a model's `long-context-cost` tier from capping its runtime context window while extended context is off: the coding agent normally lowers the window to the tier's input threshold so compaction fires before a request crosses into premium pricing. Use it where the tier is an API-equivalent estimate on a subscription host (`xai-oauth`) or where the model is served at its full window regardless (GPT-5.4, except on GitHub Copilot). `buildModel` materializes it as `Model.longContextWindowUncapped`; explicit user context overrides still apply.
225
+
226
+ `max-tokens-fallback` seeds `maxTokens` only when the row reports none, and `max-tokens-ceiling` lowers a larger reported `maxTokens` to the host's output cap (an unknown budget stays unknown unless a fallback seeded it). Both apply in `buildModel` after `limits-patch`, so discovery mappers pass upstream output budgets through verbatim. See the Kimi rules in [`providers/fireworks.kdl`](providers/fireworks.kdl).
227
+
228
+ `inline-image-byte-budget` caps retained base64 image characters for a deployment's request body. It leaves room for tool schemas and text beneath the provider's body-size limit; the coding agent reads it through `resolveInlineImageByteBudget` before sending live history, and snapcompact gives its archive frames half of it (`frameDataBytesBudget`). The budget applies only while the request reaches the official endpoint (the Anthropic API, first-party OpenAI, or the Codex backend at its default URL), which callers decide with pi-ai's `reachesOfficialEndpoint`: `compat.officialEndpoint` for the configured `baseUrl`, and for first-party Anthropic models also the `ANTHROPIC_BASE_URL` or Foundry `FOUNDRY_BASE_URL` reroute applied at request time. A custom or rerouted endpoint imposes its own body limit.
229
+
230
+ `image-count-budget` is the most images one request may carry, archive frames, screenshots and imaged tool results combined. The coding agent reads it through `resolveImageCountBudget`, drops the oldest images past it, and caps snapcompact frames at it; unassigned hosts get a floor of 5. `official-image-count-budget` replaces it while the request reaches the official endpoint (same gate as `inline-image-byte-budget`), for limits established on the host's own endpoint (Anthropic: 600, 100 on 200k-window models; Codex: 2,000); a custom or rerouted endpoint keeps `image-count-budget` (Anthropic 90, Codex 200).
231
+
232
+ `endpoint-hosts` lists the HTTPS hostnames a provider serves besides its bundled base URL's host, such as Copilot's per-plan `api.<plan>.githubcopilot.com` hosts. That host plus these define the provider's own endpoint (`isCatalogEndpointUrl`), which separates first-party traffic from a custom model or provider override that keeps a catalog provider id but points `baseUrl` at a gateway, proxy, or local server. Connection prewarming and request compression act only on the provider's own endpoint.
233
+
234
+ `request-encodings` lists the `Content-Encoding`s (`zstd`, `gzip`) a provider's own endpoint decodes on request bodies, most preferred first; pi-ai's `wrapFetchForRequestCompression` reads them through `resolveRequestCompression` and compresses bodies of 1 KiB or more. A proxy, relay, or `ANTHROPIC_BASE_URL` reroute never receives bytes it cannot read. `request-encoding-rejection` is a regular expression for the JSON 400/415 error body the host returns when it cannot decode the body; unassigned, every JSON 400/415 is re-checked with an uncompressed resend. A 400/403/415 whose body is not JSON (empty included) is presumed to come from a proxy in front of the host, such as a TLS-inspecting gateway that refuses compressed uploads, and is re-checked either way, until the uncompressed resend draws the same status from that host. A rejected encoding is dropped for that host only after the uncompressed resend succeeds. Assign them only from a live probe of every endpoint host: a compressed body, the same plain JSON mislabeled as compressed (a decoding host must reject it), and a real request.
221
235
 
222
236
  `judgment-questions-max` is the most questions one System One judgment request may carry (razorback16/openjev: 256; Cloudflare Clef on Workers AI and through OpenRouter Decisions: 64; unidentified `*openjev*` deployments: 16). The coding agent reads it through `resolveJudgmentQuestionsMax` and `NativeJudge` splits larger requests into parallel chunks; unassigned means the endpoint takes any batch size. [`providers/judgment-questions.kdl`](providers/judgment-questions.kdl) lists the servers verified uncapped or not isolatable.
223
237
 
238
+ `judgment-image-input` assigns model-specific native judgment image bounds, read through `resolveJudgmentImageInput`. Its object accepts `mime-types` as a comma-separated lowercase MIME list and positive integer `max-count`, `max-bytes`, `max-total-bytes`, `min-pixels`, `max-pixels`, `min-side`, and `max-side`; byte limits count decoded data. `NativeJudge` enforces these alongside its wire's encoding constraints without resizing, and candidate filtering skips models whose bounds reject the images. Unassigned adds no model-specific bounds. The Jev selectors in [`providers/typesafe.kdl`](providers/typesafe.kdl) and [`providers/openrouter.kdl`](providers/openrouter.kdl) own Jev's limits rather than imposing them on every model using those APIs.
239
+
224
240
  `prompt-cache-lookback` is how many block positions back from a cache breakpoint the model's prompt cache looks for an earlier request's entry (Claude: 20, counting the breakpoint; a run of consecutive `tool_use` or `tool_result` blocks is one position). The coding agent reads it through `resolvePromptCacheLookback` and keeps warm-cache tool-result pruning inside the window; unassigned means no known bound.
225
241
 
242
+ `task-sibling-prompt-cache #true` lets Task children use an automatically derived sibling cache key on the matching request model. Missing or `#false` keeps the provider's per-child default; explicit cache keys still win. The session resolves this catalog policy against the actual model for primary turns, handoff, and ephemeral requests that reuse the live context, including model changes, without sharing conversation IDs or response state. The automatic candidate is persisted separately from explicit keys and survives full forks, `/tan`, and session reopening; new and partial sessions discard it. A fork carrying an automatic candidate uses its own conversation ID on unsupported models, and re-enables the candidate when returning to an eligible model. Ordinary forks without an automatic candidate keep their existing inherited cache routing. Conventional compaction keeps its existing cache routing because its serialized summary prompt differs from the primary context. The opt-in covers Muse Code's `muse-spark-1.3`, `muse-spark-1.3-contributor`, `muse-wonder-1.0`, `muse-wonder-1.0-contributor`, `muse-wonder-full-1.0`, and `muse-wonder-full-1.0-contributor` over `openai-responses`. Meta documents shared-prefix `prompt_cache_key` routing for Responses. Warmed sibling cache reuse was measured on Spark 1.3 Contributor; the standard Spark variant shares this scope following maintainer guidance. A bounded synthetic probe observed cache reuse on all four Wonder variants; it does not establish production or latency benefits. Auxiliary-request latency has not been measured. Provider selectors also match custom providers that declare the same `providerType`; no benefit is established for custom endpoints.
243
+
244
+ `image-max-edge` is the longest image edge, in pixels, a host passes to the model; it downscales larger images to fit first (Cursor: 1024 for Claude and GPT). Snapcompact reads it through `resolveImageMaxEdge` and renders frames no wider, so their glyphs reach the model at the size they were drawn; unassigned means the host keeps the image's size.
245
+
246
+ `snapcompact-image-reads #true` opts a reader in to getting a fresh `read` result as snapcompact frames when `snapcompact.toolResults` is on. Frames collapse whitespace, so snapcompact draws each row's leading indentation as a count bracket (`[»2]`, `[·4]`) and keeps the `[path#TAG]` header as text beside them. On 3–4 frame reads Opus 5.5 then made exact first edits from frames within one of text (38 vs 39 of 40), so Opus 5.5+ opts in ([`classes/anthropic.kdl`](classes/anthropic.kdl)); Sonnet 5.5 (37 vs 40) and Haiku 5.5 (35 vs 40) still miscounted deep tab indents. Unassigned: `read` results stay text; other tools' results are imaged either way.
247
+
226
248
  The routed-subscription registry axes (`upstream-rotation`, `region-upstreams-global|us|eu`, `region-limits-eu`, `credit-rates`, `list-price-from`, `routing-family`, `policy-aliases`, `entitlement`, `default-reasoning-off`) describe a gateway whose proxy fans one model out to several upstreams. They are read through `src/compat/factory-droid.ts` by Factory Droid discovery and its request provider, not materialized by `buildModel`. A provider-wide `region-upstreams-*` rule is the upstream serving table and a model rule replaces it for that region. `list-price-from "<provider>" ["<id>"]` shows a bundled row's list price beside the subscription's own billing; unlike seed values it is resolved at runtime and degrades to the seed's zero cost when the row is gone. See [`providers/factory-droid.kdl`](providers/factory-droid.kdl), whose wire and billing pool per model are `api-routes` and `quota-tiers` rules in `runtime/behavior.kdl`.
227
249
 
228
250
  ### Time-based pricing
@@ -280,7 +302,7 @@ The highest-ranked matching assignment wins for that axis. Two distinct rules th
280
302
 
281
303
  ### Capability gating
282
304
 
283
- Wire axes are considered for every matching target. Thinking axes require `reasoning`, except that an exact model selector declaring `thinking-efforts` opens the gate for reviewed corrections. A winning `thinking-upgrade-neutral #true` scoped to a recognized class, family, revision, or model selector also opens it when a matching effort ladder exists; provider-wide opt-in alone does not. Materializing a neutral spec as reasoning-capable requires that opt-in and no explicit thinking vocabulary. Family and revision selectors never match targets missing that rank. An unmatched target resolves to empty maps; the cascade does not infer negative capabilities from absence.
305
+ Wire axes are considered for every matching target. Thinking axes require `reasoning`, except that an exact model selector declaring `thinking-efforts` opens the gate for reviewed corrections. A winning `thinking-upgrade-neutral #true` scoped to a recognized class, family, revision, or model selector also opens it when a matching effort ladder exists; provider-wide opt-in alone does not. Materializing a neutral spec as reasoning-capable requires that opt-in and no explicit thinking vocabulary. A winning `thinking-unverified #true` (scoped `class "unknown"` on hosts whose model list carries no capability metadata) instead offers an unclassified neutral spec its ladder as `ThinkingConfig.unverified`: off until the user picks an effort, and sent as non-reasoning without one; it never changes compat. Family and revision selectors never match targets missing that rank. An unmatched target resolves to empty maps; the cascade does not infer negative capabilities from absence.
284
306
 
285
307
  ## Runtime behavior grammar
286
308
 
@@ -313,11 +335,17 @@ behavior {
313
335
  pricing-peer provider="google-antigravity" peers="google" "google-vertex" {
314
336
  alias "gemini-3-pro" peer-id="gemini-3-pro-preview"
315
337
  }
338
+ role-defaults "smol" {
339
+ model "claude-haiku-5-5"
340
+ model "openai-codex/gpt-5.3-codex-spark"
341
+ }
316
342
  }
317
343
  ```
318
344
 
319
345
  `exclude-discovery-modes` takes one or more exact, case-sensitive upstream mode strings plus `provider=`; discovery mappers preserve missing, malformed, and unknown modes unless the provider policy explicitly lists them. Matcher properties on `route` / `exclude-models` / `tier` nodes are `exact=` / `prefix=` / `substring=` / `glob=`, repeatable. `strip-prefix=#true` on a prefix route strips the matched prefix off the wire id. Values are copied verbatim from the TS constants they replaced; runtime accessors live in `src/compat/behavior.ts`.
320
346
 
347
+ `role-defaults "<role>"` declares one agent role's built-in default model selector chain, one `model "<selector>"` child per entry, most preferred first. Selectors are copied verbatim (`provider/id`, bare ids, fuzzy fragments such as `-mini`, and `@role` references); duplicates within a role and repeated roles are compile errors. `roleDefaultModels()` returns every chain keyed by role in declaration order.
348
+
321
349
  ## Auth grammar
322
350
 
323
351
  Every provider has exactly one `auth "<id>" { … }` node under `auth/`; `auth/_order.kdl` holds a single `login-order "id" …` node pinning the `/login` roster order (every provider with a `login` and `show-in-login-list` unset/true must appear; providers without a login sort alphabetically after it). Runtime accessors live in `src/compat/auth.ts`; the id unions in the generated `src/compat/auth-ids.ts`. Every provider in `provider-models/descriptors.ts` needs an auth node (`@linxiraos/pi-ai` type-checks this).
@@ -446,6 +474,7 @@ provider "sakana" {
446
474
  dynamic-models-authoritative #true // discovery replaces bundled rows
447
475
  allow-unauthenticated #true // runtime manager without a key
448
476
  skip-cross-provider-reference-fills #true // generator never backfills from same-id rows elsewhere
477
+ keep-bundled-names #true // discovery never renames a bundled row
449
478
  discovery label="Sakana AI" oauth-provider="sakana" allow-unauthenticated=#true {
450
479
  env "SAKANA_GEN_KEY" // generation-time keys; defaults to the provider env
451
480
  }
@@ -4,7 +4,7 @@
4
4
  login-order "openai-codex" "anthropic" "zai" "zai-coding-plan" "kimi-code" "openrouter" "github-copilot" \
5
5
  "cursor" "devin" "google-antigravity" "google-gemini-cli" "openai-codex-device" "xai" "xai-oauth" \
6
6
  "gitlab-duo" "gitlab-duo-agent" "alibaba-coding-plan" "alibaba-token-plan" "aiand" "abliteration" "zhipu-coding-plan" \
7
- "umans" "qwen-portal" "sakana" "minimax-code" "minimax-code-cn" "xiaomi" "xiaomi-token-plan-sgp" \
7
+ "umans" "qwen-portal" "sakana" "sambanova" "minimax-code" "minimax-code-cn" "xiaomi" "xiaomi-token-plan-sgp" \
8
8
  "xiaomi-token-plan-ams" "xiaomi-token-plan-cn" "firepass" "cline-pass" "factory-droid" "commandcode" "charm-hyper" "deepseek" "stepfun" "muse-code" "meta" "moonshot" \
9
9
  "cerebras" "baseten" "helmcode" "fireworks" "together" "nvidia" "novita" "deepinfra" "huggingface" "perplexity" \
10
10
  "qianfan" "venice" "siliconflow" "siliconflow-cn" "synthetic" "nanogpt" "wafer-serverless" "coreweave" \
@@ -1,5 +1,5 @@
1
1
  auth "google-antigravity" {
2
- name "Antigravity (Gemini 3, Claude, GPT-OSS)"
2
+ name "Google Antigravity (Gemini, Claude, GPT-OSS)"
3
3
  login "oauth-code" {
4
4
  // Antigravity uses different Google OAuth credentials for its additional models.
5
5
  client-id "MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==" encoding="base64"
@@ -0,0 +1,9 @@
1
+ auth "sambanova" {
2
+ name "SambaNova"
3
+ login "api-key" {
4
+ auth-url "https://cloud.sambanova.ai/apis"
5
+ instructions "Copy your API key from the SambaNova Cloud dashboard"
6
+ prompt "Paste your SambaNova API key" placeholder="..."
7
+ validate "models-endpoint" label="SambaNova" url="https://api.sambanova.ai/v1/models"
8
+ }
9
+ }
@@ -4,7 +4,7 @@
4
4
  // Two hosts: the Stencil API at api.stencil.so (`{base}`, STENCIL_BASE_URL)
5
5
  // and the OIDC issuer at auth.stencil.so (`{auth}`, STENCIL_AUTH_URL) which
6
6
  // serves the PKCE-mandatory authorization codes under /oauth/* and mints
7
- // ES256 access tokens. The public client id `omp` is code-defined in the
7
+ // ES256 access tokens. The public client id `zetacode` is code-defined in the
8
8
  // issuer with a port-flexible 127.0.0.1 loopback callback.
9
9
  auth "stencil" {
10
10
  name "Stencil (invite only)"
@@ -14,10 +14,25 @@
14
14
  // consecutive tool_use or tool_result blocks is one position. Warm-cache
15
15
  // tool-result pruning keeps its rewrites inside this window.
16
16
  // https://platform.claude.com/docs/en/build-with-claude/prompt-caching
17
+ // A replayed signed thinking block bills the full reasoning its signature
18
+ // carries (the server rebuilds it from the signature), at the size its turn
19
+ // billed as output, not the signature's bytes. Thinking a request drops bills
20
+ // nothing (earlier turns on lines that strip it: Opus before 4.5, Sonnet
21
+ // before 4.6, Haiku before 5.5; turns before a compaction or prune on
22
+ // prefix-bound lines; another model's blocks the reader cannot read), so
23
+ // those turns count high.
24
+ // https://platform.claude.com/docs/en/build-with-claude/context-windows#the-context-window-with-thinking
25
+ // https://platform.claude.com/docs/en/build-with-claude/preserved-thinking#switching-models
17
26
  class "anthropic" {
18
27
  prompt-cache-lookback 20
28
+ // Claude's input classifier refuses requests that quote earlier reasoning
29
+ // back as text (`reasoning_extraction`, issue #6093).
30
+ classifier-cot-extraction #true
31
+ thinking-replay-tokens "output"
32
+ // verified: billing matched this formula on Opus 4.7+ and Fable 5.1.
19
33
  image-tokenization {
20
34
  regime "anthropic-patch"
35
+ verified #true
21
36
  max-edge 2576
22
37
  max-tokens 4784
23
38
  }
@@ -37,21 +52,39 @@ class "anthropic" {
37
52
  }
38
53
  }
39
54
  }
55
+ // Standard tier. verified: billing matched it on Sonnet 4.6 only; older
56
+ // Sonnet lines keep the floor until measured.
40
57
  family "sonnet" {
41
- revision "<4.7" {
58
+ revision "<4.6" {
42
59
  image-tokenization {
43
60
  regime "anthropic-patch"
44
61
  max-edge 1568
45
62
  max-tokens 1568
46
63
  }
47
64
  }
48
- revision ">=10 <47" {
65
+ revision ">=4.6 <4.7" {
66
+ image-tokenization {
67
+ regime "anthropic-patch"
68
+ verified #true
69
+ max-edge 1568
70
+ max-tokens 1568
71
+ }
72
+ }
73
+ revision ">=10 <46" {
49
74
  image-tokenization {
50
75
  regime "anthropic-patch"
51
76
  max-edge 1568
52
77
  max-tokens 1568
53
78
  }
54
79
  }
80
+ revision ">=46 <47" {
81
+ image-tokenization {
82
+ regime "anthropic-patch"
83
+ verified #true
84
+ max-edge 1568
85
+ max-tokens 1568
86
+ }
87
+ }
55
88
  }
56
89
  family "haiku" {
57
90
  revision "<4.7" {
@@ -87,6 +120,13 @@ class "anthropic" {
87
120
  supports-sampling-params #false
88
121
  }
89
122
  }
123
+ // Opus 5.5 edits from imaged `read` results within one miss of text; Sonnet
124
+ // and Haiku 5.5 miscount deep tab indents (measurements: rules/README.md).
125
+ family "opus" {
126
+ revision ">=5.5 <10" {
127
+ snapcompact-image-reads #true
128
+ }
129
+ }
90
130
  family "sonnet" {
91
131
  revision ">=5 <10" {
92
132
  supports-sampling-params #false
@@ -59,6 +59,10 @@ class "deepseek" {
59
59
  on "alibaba-token-plan" "deepseek" {
60
60
  supports-developer-role #false
61
61
  }
62
+ // Replaces the provider-keyed leaked chat-template special-token strip.
63
+ on "nvidia" "deepseek" {
64
+ strip-deepseek-special-tokens #true
65
+ }
62
66
  on "deepseek" "opencode-go" {
63
67
  family "pro" {
64
68
  max-tokens-field "max_tokens"
@@ -66,13 +66,15 @@ class "gemini" {
66
66
  }
67
67
  }
68
68
  }
69
- // Gemini 3.7+ Flash native thinkingLevel does not accept `minimal`
70
- // (400 THINKING_LEVEL_MINIMAL; observed on 3.7 and 3.8). Only the direct
69
+ // Gemini 3.7+ Flash and Gemini 4 Argon native thinkingLevel does not accept
70
+ // `minimal` (400 THINKING_LEVEL_MINIMAL; observed on Flash 3.7 and 3.8, and
71
+ // documented for Argon and Argon Cyber Permissive). Only the direct
71
72
  // google-level transports emit `thinkingLevel` on the wire, so the tier is
72
73
  // dropped there; budget (anthropic-messages) and reasoning_effort resellers
73
74
  // never send the rejected value and keep the four-tier ladder. The collapsed
74
75
  // google-antigravity / google-gemini-cli variants remap minimal -> LOW and
75
76
  // carry their own explicit ladder, so they are unaffected.
77
+ // https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/gemini/4-argon
76
78
  // https://ai.google.dev/gemini-api/docs/generate-content/thinking#thinking-levels
77
79
  on "google" "google-vertex" "opencode-zen" {
78
80
  family "flash" {
@@ -80,6 +82,41 @@ class "gemini" {
80
82
  thinking-efforts "low" "medium" "high"
81
83
  }
82
84
  }
85
+ // Exact ids: a revision-wide rule would tie with the Pro low/high ladder.
86
+ // Gemini API discovery has no bundled Argon reference and infers
87
+ // `reasoning: false` from the id; repair it so the ladder is selectable.
88
+ models "gemini-4-argon" "gemini-4-argon-cyber-permissive" {
89
+ thinking-upgrade-neutral #true
90
+ thinking-efforts "low" "medium" "high"
91
+ }
92
+ }
93
+ // Gemini 4 Argon's introductory list price ($2/$10, $0.10 cached) ends
94
+ // 2026-11-30; from 2026-12-01 Google bills $4/$20, $0.20 cached, on the
95
+ // Gemini API and Vertex global endpoint alike. Cloud Billing days start at
96
+ // midnight US Pacific. Both cards are pinned over whatever stencil.so or
97
+ // the bundled pricing peer reports: the switch needs no regeneration, and
98
+ // a row built after upstream catches up still prices earlier requests at
99
+ // the introductory rate.
100
+ // https://ai.google.dev/gemini-api/docs/pricing
101
+ // https://cloud.google.com/vertex-ai/generative-ai/pricing
102
+ on "google" "google-vertex" "google-antigravity" {
103
+ models "gemini-4-argon" {
104
+ cost-patch {
105
+ input 2
106
+ output 10
107
+ cache-read 0.1
108
+ cache-write 0
109
+ effective-rates {
110
+ list-price {
111
+ effective-from "2026-12-01T08:00:00Z"
112
+ input 4
113
+ output 20
114
+ cache-read 0.2
115
+ cache-write 0
116
+ }
117
+ }
118
+ }
119
+ }
83
120
  }
84
121
 
85
122
  revision ">=3" {
@@ -88,6 +125,13 @@ class "gemini" {
88
125
  // Replaces the Gemini 3+ mandatory-reasoning fallback.
89
126
  thinking-requires-effort #true
90
127
  }
128
+ // Gemini 3+ takes `thinkingLevel` on the native Google transports; Gemini 4
129
+ // rejects `thinkingBudget` outright (400 "Use thinking level instead").
130
+ on-api "google-generative-ai" "google-gemini-cli" "google-vertex" {
131
+ revision ">=3" {
132
+ thinking-mode "google-level"
133
+ }
134
+ }
91
135
  // Public Gemini requires the bypass sentinel on every unsigned call and
92
136
  // supports function-part ids. Scope by adapter so custom providers using
93
137
  // the same wire protocol inherit both contracts. The Cloud Code Assist
@@ -9,6 +9,13 @@ class "meta" {
9
9
  thinking-efforts "minimal" "low" "medium" "high" "xhigh" "max"
10
10
  thinking-mode "effort"
11
11
  }
12
+ // Meta documents `max` for Muse Wonder (dev.meta.ai/docs/reasoning); the
13
+ // Muse Code-only Wonder Full and contributor tiers accept it too (verified
14
+ // 2026-10-12).
15
+ models "muse-wonder-1.0" "muse-wonder-1.0-contributor" "muse-wonder-full-1.0" "muse-wonder-full-1.0-contributor" {
16
+ thinking-efforts "minimal" "low" "medium" "high" "xhigh" "max"
17
+ thinking-mode "effort"
18
+ }
12
19
  models "muse-spark-1.1" "muse-spark-1.2" "muse-spark-1.2-contributor" {
13
20
  thinking-efforts "minimal" "low" "medium" "high" "xhigh"
14
21
  thinking-mode "effort"
@@ -7,6 +7,16 @@ class "minimax" {
7
7
  }
8
8
  // Replaces the MiniMax lineage branch in both OpenAI baselines.
9
9
  reasoning-deltas-may-be-cumulative #true
10
+ // Replaces the identity default: M2/M3 thinking rides Anthropic adaptive
11
+ // thinking on every Messages route, custom provider ids included.
12
+ on-api "anthropic-messages" {
13
+ family "m2" {
14
+ thinking-mode "anthropic-adaptive"
15
+ }
16
+ family "m3" {
17
+ thinking-mode "anthropic-adaptive"
18
+ }
19
+ }
10
20
  on "aimlapi" "coreweave" "fireworks" "huggingface" "kilo" "nanogpt" "novita" "nvidia" \
11
21
  "opencode-go" "opencode-zen" "together" "venice" "wafer-serverless" "zenmux" {
12
22
  family "m3" {
@@ -30,7 +40,7 @@ class "minimax" {
30
40
  // upstream metadata, not the model ceiling (documented: 1M in / 128K out).
31
41
  // Exact selectors are case-sensitive, so both served spellings are listed:
32
42
  // MiniMax hosts publish `MiniMax-M3`, Charm Hyper publishes `minimax-m3`.
33
- on "charm-hyper" "minimax" "minimax-cn" "minimax-code" "minimax-code-cn" {
43
+ on "charm-hyper" "minimax" "minimax-cn" "minimax-code" "minimax-code-cn" "sambanova" {
34
44
  models "MiniMax-M3" "minimax-m3" {
35
45
  limits-patch {
36
46
  context-window 1000000
@@ -11,6 +11,14 @@ class "openai" {
11
11
  revision ">=5" {
12
12
  supports-sampling-params #false
13
13
  }
14
+ // GPT-5.4 serves its 1M window, and its long-context price tier does not
15
+ // cap that window. GitHub Copilot keeps its own cap (github-copilot.kdl).
16
+ models "gpt-5.4" {
17
+ limits-patch {
18
+ context-window 1000000
19
+ }
20
+ long-context-window-uncapped #true
21
+ }
14
22
  // GPT wire ladders are model generations, not host contracts: <5.2 keeps
15
23
  // the classic four-tier scale, 5.2+ drops minimal for xhigh, and 5.6+
16
24
  // exposes the wire-exact five-tier low..max scale.
@@ -95,9 +103,28 @@ class "openai" {
95
103
  // Older lines carry no lineage rule and bill at their wire API's fallback
96
104
  // (providers/image-tokenization.kdl).
97
105
  class "openai" {
98
- // gpt-5.6 sizing, shared by later lines. `<10` keeps separator-collapsed
99
- // revisions (`openai-gpt-52-codex` parses as 52) off the 5.6+ sizing.
100
- revision ">=5.6 <10" {
106
+ // gpt-5.6 sizing. verified: billing matched this formula on gpt-5.6 through 6.1.
107
+ revision ">=5.6 <6.2" {
108
+ image-tokenization {
109
+ regime "openai-patch"
110
+ verified #true
111
+ multiplier 1.2
112
+ auto "original"
113
+ low {
114
+ max-edge 512
115
+ }
116
+ high {
117
+ max-edge 2048
118
+ patch-budget 2500
119
+ }
120
+ original {
121
+ max-edge 65535
122
+ }
123
+ }
124
+ }
125
+ // Later lines size like gpt-5.6, unverified until measured. `<10` keeps
126
+ // separator-collapsed revisions (`openai-gpt-52-codex` parses as 52) off it.
127
+ revision ">=6.2 <10" {
101
128
  image-tokenization {
102
129
  regime "openai-patch"
103
130
  multiplier 1.2
@@ -114,9 +141,11 @@ class "openai" {
114
141
  }
115
142
  }
116
143
  }
144
+ // verified: billing matched this formula on gpt-5.5.
117
145
  revision "=5.5" {
118
146
  image-tokenization {
119
147
  regime "openai-patch"
148
+ verified #true
120
149
  multiplier 1.2
121
150
  auto "original"
122
151
  low {
@@ -173,9 +202,11 @@ class "openai" {
173
202
  }
174
203
  }
175
204
  // residue: astra's limits differ from the rest of its revision.
205
+ // verified: billing matched this formula on gpt-6-astra.
176
206
  models "*gpt-6-astra*" {
177
207
  image-tokenization {
178
208
  regime "openai-patch"
209
+ verified #true
179
210
  multiplier 1.2
180
211
  auto "original"
181
212
  low {
@@ -41,6 +41,32 @@ class "xai" {
41
41
  }
42
42
  }
43
43
  }
44
+ // Grok bills 32px patches plus ~30–40 tokens per image, resizing past
45
+ // 2,401 patches at every detail level; the 1.017 multiplier carries the
46
+ // per-image tokens at full size (small images bill slightly more).
47
+ // verified: billing matched this rule on Grok 4.6, 4.7 and 4.20.
48
+ family "grok" {
49
+ revision ">=4.6 <=4.20" {
50
+ image-tokenization {
51
+ regime "openai-patch"
52
+ verified #true
53
+ multiplier 1.017
54
+ auto "original"
55
+ low {
56
+ max-edge 65535
57
+ patch-budget 2401
58
+ }
59
+ high {
60
+ max-edge 65535
61
+ patch-budget 2401
62
+ }
63
+ original {
64
+ max-edge 65535
65
+ patch-budget 2401
66
+ }
67
+ }
68
+ }
69
+ }
44
70
  thinking-loop-guard "xai"
45
71
  }
46
72
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  provider "amazon-bedrock" {
4
4
  default-model "us.anthropic.claude-opus-5-5"
5
+ image-count-budget 90
5
6
 
6
7
  // The Converse assembler positions content blocks by wire index
7
8
  // (`amazon-bedrock.ts`), so a block can land above text already rendered.
@@ -74,6 +74,10 @@ provider "anthropic" {
74
74
  // combines this fact with a per-request effective-URL check, so it never
75
75
  // authorizes a block on its own.
76
76
  first-party-provider #true
77
+ // Shares Claude Code's `ANTHROPIC_*` env routing, so it also honors Claude
78
+ // Code's `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` kill switch for gateways
79
+ // that reject experimental beta fields.
80
+ honors-claude-code-beta-kill-switch #true
77
81
  // Messages requests cap at 32 MB. Reserve 8 MB for text, tools, and JSON
78
82
  // framing when retaining inline base64 images in a long conversation.
79
83
  // Applied only on the official endpoint; a custom baseUrl has its own limit.
@@ -81,7 +85,26 @@ provider "anthropic" {
81
85
  on-api "anthropic-messages" {
82
86
  inline-image-byte-budget 24000000
83
87
  }
88
+ // The official endpoint carries at most 600 images and documents per
89
+ // request, 100 on models with a 200k-token window: every Claude line before
90
+ // 4.6, including separator-collapsed revisions (`claude-opus-45` parses as
91
+ // 45). A custom baseUrl keeps the conservative 90.
92
+ // https://platform.claude.com/docs/en/build-with-claude/vision
93
+ image-count-budget 90
94
+ official-image-count-budget 600
95
+ // The Messages API decodes gzip request bodies and rejects zstd and br
96
+ // with a 400 (`request_body_encoding_invalid`). Gzip bytes that arrive
97
+ // without their `Content-Encoding` (a proxy dropped the header) get a 400
98
+ // "The request body is not valid JSON" (live, 2026-10-17).
99
+ request-encodings "gzip"
100
+ request-encoding-rejection "request_body_encoding_invalid|Unsupported Content-Encoding|request body is not valid JSON"
84
101
  class "anthropic" {
102
+ revision "<4.6" {
103
+ official-image-count-budget 100
104
+ }
105
+ revision ">=10 <46" {
106
+ official-image-count-budget 100
107
+ }
85
108
  revision ">=3.7 <4.6" {
86
109
  thinking-efforts "minimal" "low" "medium" "high" "xhigh"
87
110
  }
@@ -3,6 +3,8 @@
3
3
  provider "azure" {
4
4
  default-model "gpt-5.5"
5
5
  env "AZURE_OPENAI_API_KEY"
6
+ // Replaces the provider-keyed strict Responses tool-call/result pairing.
7
+ strict-responses-pairing #true
6
8
 
7
9
  class "openai" {
8
10
  thinking-mode "effort"
@@ -10,6 +10,9 @@ provider "cerebras" {
10
10
  // Replaces the Cerebras provider branches in the completions baseline.
11
11
  supports-usage-in-streaming #false
12
12
  tool-strict-mode "all_strict"
13
+ // Replaces the provider check in agent-loop history normalization: replayed
14
+ // assistant history drops its thinking blocks.
15
+ strip-thinking-history #true
13
16
  // Prompt caching carries no discount: cached input tokens bill at the
14
17
  // standard input rate (https://inference-docs.cerebras.ai/capabilities/prompt-caching).
15
18
  cache-read-at-input-rate #true
@@ -10,4 +10,20 @@ provider "cline-pass" {
10
10
  wire-model-id-mode "cline-pass"
11
11
  // Replaces the Cline Pass thinking-format branch.
12
12
  thinking-format "openai"
13
+ // Replaces the Cline Pass completions baseline branches: the reasoning
14
+ // field, the disable shape, and reasoning kept on under tool choice.
15
+ reasoning-content-field "reasoning"
16
+ reasoning-disable-mode "cline-enabled-false"
17
+ disable-reasoning-on-forced-tool-choice #false
18
+ disable-reasoning-on-tool-choice #false
19
+ // Discovery reports each row's thinking ladder; a row without one
20
+ // (`supportsReasoningEffort: false`) has no reasoning control at all.
21
+ trust-explicit-thinking-only #true
22
+ class "qwen" {
23
+ supports-forced-tool-choice #false
24
+ cache-control-format "anthropic"
25
+ }
26
+ class "anthropic" {
27
+ cache-control-format "anthropic"
28
+ }
13
29
  }
@@ -337,6 +337,16 @@ provider "commandcode" {
337
337
  cache-write 0.0
338
338
  }
339
339
  }
340
+ // Restate the 64K provider output cap beside the openai class's GPT-5.4
341
+ // window patch, or the class rule wins the whole limits axis.
342
+ class "openai" {
343
+ models "gpt-5.4" {
344
+ limits-patch {
345
+ context-window 1000000
346
+ max-tokens 65536
347
+ }
348
+ }
349
+ }
340
350
  models "gpt-5.3-codex" {
341
351
  cost-patch {
342
352
  input 2.0