@linxiraos/pi-catalog 1.2.0 → 1.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 (127) hide show
  1. package/CHANGELOG.md +79 -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/types.d.ts +63 -12
  24. package/dist/types/wire/codex.d.ts +2 -0
  25. package/package.json +6 -6
  26. package/src/build.d.ts +3 -2
  27. package/src/build.ts +159 -80
  28. package/src/compat/auth-ids.ts +2 -0
  29. package/src/compat/axes.d.ts +1 -1
  30. package/src/compat/axes.ts +129 -4
  31. package/src/compat/behavior.d.ts +5 -0
  32. package/src/compat/behavior.ts +9 -1
  33. package/src/compat/cascade.ts +4 -1
  34. package/src/compat/catalog-endpoint.ts +43 -0
  35. package/src/compat/image-tokenization.ts +28 -10
  36. package/src/compat/judgment-images.ts +83 -0
  37. package/src/compat/provider-ids.d.ts +1 -1
  38. package/src/compat/provider-ids.ts +1 -0
  39. package/src/compat/request-compression.ts +43 -0
  40. package/src/compat/request-size.ts +20 -5
  41. package/src/compat/resolve.ts +90 -53
  42. package/src/compat/rules/README.md +32 -3
  43. package/src/compat/rules/auth/_order.kdl +1 -1
  44. package/src/compat/rules/auth/google-antigravity.kdl +1 -1
  45. package/src/compat/rules/auth/sambanova.kdl +9 -0
  46. package/src/compat/rules/auth/stencil.kdl +1 -1
  47. package/src/compat/rules/classes/anthropic.kdl +42 -2
  48. package/src/compat/rules/classes/deepseek.kdl +4 -0
  49. package/src/compat/rules/classes/gemini.kdl +46 -2
  50. package/src/compat/rules/classes/meta.kdl +7 -0
  51. package/src/compat/rules/classes/minimax.kdl +11 -1
  52. package/src/compat/rules/classes/openai.kdl +34 -3
  53. package/src/compat/rules/classes/xai.kdl +26 -0
  54. package/src/compat/rules/providers/amazon-bedrock.kdl +1 -0
  55. package/src/compat/rules/providers/anthropic.kdl +23 -0
  56. package/src/compat/rules/providers/azure.kdl +2 -0
  57. package/src/compat/rules/providers/cerebras.kdl +3 -0
  58. package/src/compat/rules/providers/cline-pass.kdl +16 -0
  59. package/src/compat/rules/providers/commandcode.kdl +10 -0
  60. package/src/compat/rules/providers/cursor.kdl +50 -1
  61. package/src/compat/rules/providers/deepinfra.kdl +3 -1
  62. package/src/compat/rules/providers/devin.kdl +117 -6
  63. package/src/compat/rules/providers/factory-droid.kdl +6 -0
  64. package/src/compat/rules/providers/firepass.kdl +15 -0
  65. package/src/compat/rules/providers/fireworks.kdl +26 -0
  66. package/src/compat/rules/providers/github-copilot.kdl +24 -0
  67. package/src/compat/rules/providers/google-antigravity.kdl +37 -0
  68. package/src/compat/rules/providers/google-gemini-cli.kdl +1 -0
  69. package/src/compat/rules/providers/google-vertex.kdl +5 -0
  70. package/src/compat/rules/providers/google.kdl +1 -0
  71. package/src/compat/rules/providers/image-tokenization.kdl +2 -1
  72. package/src/compat/rules/providers/local.kdl +8 -1
  73. package/src/compat/rules/providers/meta.kdl +16 -0
  74. package/src/compat/rules/providers/moonshot.kdl +2 -0
  75. package/src/compat/rules/providers/muse-code.kdl +49 -0
  76. package/src/compat/rules/providers/native-tools.kdl +3 -0
  77. package/src/compat/rules/providers/ollama-cloud.kdl +10 -0
  78. package/src/compat/rules/providers/openai-codex.kdl +29 -5
  79. package/src/compat/rules/providers/openai.kdl +1 -0
  80. package/src/compat/rules/providers/opencode-go.kdl +35 -0
  81. package/src/compat/rules/providers/opencode-zen.kdl +4 -0
  82. package/src/compat/rules/providers/openrouter.kdl +19 -0
  83. package/src/compat/rules/providers/sambanova.kdl +60 -0
  84. package/src/compat/rules/providers/tool-call-names.kdl +37 -0
  85. package/src/compat/rules/providers/typesafe.kdl +11 -0
  86. package/src/compat/rules/providers/umans.kdl +7 -0
  87. package/src/compat/rules/providers/venice.kdl +7 -0
  88. package/src/compat/rules/providers/xai-oauth.kdl +16 -6
  89. package/src/compat/rules/providers/xai.kdl +3 -0
  90. package/src/compat/rules/providers/zai.kdl +5 -0
  91. package/src/compat/rules/runtime/behavior.kdl +128 -8
  92. package/src/compat/rules/taxonomy/_collapse.kdl +11 -0
  93. package/src/compat/rules/taxonomy/gemma.kdl +1 -0
  94. package/src/compat/rules/taxonomy/meta.kdl +7 -0
  95. package/src/compat/rules/taxonomy/openai.kdl +2 -0
  96. package/src/compat/rules.json +1 -1
  97. package/src/compat/thinking-replay.ts +33 -0
  98. package/src/compat/types.d.ts +4 -0
  99. package/src/compat/types.ts +4 -0
  100. package/src/discovery/antigravity.d.ts +7 -0
  101. package/src/discovery/antigravity.ts +13 -0
  102. package/src/discovery/codex.ts +0 -4
  103. package/src/discovery/cursor.ts +28 -20
  104. package/src/discovery/devin.ts +7 -8
  105. package/src/model-cache.d.ts +7 -0
  106. package/src/model-cache.ts +8 -0
  107. package/src/model-manager.d.ts +6 -0
  108. package/src/model-manager.ts +41 -21
  109. package/src/model-thinking.d.ts +80 -0
  110. package/src/model-thinking.ts +13 -1
  111. package/src/model-tokenizer.ts +7 -1
  112. package/src/models.d.ts +16 -1
  113. package/src/models.json +1 -1
  114. package/src/models.ts +26 -12
  115. package/src/pricing.d.ts +37 -1
  116. package/src/pricing.ts +123 -12
  117. package/src/provider-models/bundled-references.ts +5 -2
  118. package/src/provider-models/cache-provider-id.ts +1 -1
  119. package/src/provider-models/descriptors.ts +2 -0
  120. package/src/provider-models/google.ts +7 -1
  121. package/src/provider-models/openai-compat.d.ts +18 -46
  122. package/src/provider-models/openai-compat.ts +188 -104
  123. package/src/provider-models/special.ts +1 -0
  124. package/src/types.d.ts +63 -12
  125. package/src/types.ts +71 -9
  126. package/src/wire/codex.d.ts +2 -0
  127. package/src/wire/codex.ts +17 -0
@@ -39,8 +39,17 @@ import {
39
39
  type OpenAICompat,
40
40
  type Provider,
41
41
  type ThinkingConfig,
42
+ type TokenCost,
42
43
  } from "../types";
43
- import { discoveryFetch, isAnthropicOAuthToken, isRecord, toBoolean, toNumber, toPositiveNumber } from "../utils";
44
+ import {
45
+ discoveryFetch,
46
+ isAnthropicOAuthToken,
47
+ isRecord,
48
+ toBoolean,
49
+ toNumber,
50
+ toPositiveNumber,
51
+ toPositiveNumberOrNull,
52
+ } from "../utils";
44
53
  import { ALIBABA_TOKEN_PLAN_BASE_URL, parseAlibabaTokenPlanCredential } from "../wire/alibaba-token-plan";
45
54
  import { normalizeCharmHyperBaseUrl } from "../wire/charm-hyper";
46
55
  import { CLINEPASS_API_BASE_URL, clinePassClientHeaders } from "../wire/cline-pass";
@@ -550,6 +559,40 @@ function mapWithBundledReference<TApi extends Api>(
550
559
  };
551
560
  }
552
561
 
562
+ /**
563
+ * Rates from an OpenRouter-shaped `pricing` object (USD per token, as decimal
564
+ * strings). Missing, zero, or negative legs (`-1` marks router-priced rows)
565
+ * map to 0, which the model manager fills from its reference sources.
566
+ */
567
+ function perTokenPricingCost(pricing: unknown): TokenCost {
568
+ const rates = isRecord(pricing) ? pricing : {};
569
+ return {
570
+ input: toPositiveNumber(rates.prompt, 0) * 1_000_000,
571
+ output: toPositiveNumber(rates.completion, 0) * 1_000_000,
572
+ cacheRead: toPositiveNumber(rates.input_cache_read, 0) * 1_000_000,
573
+ cacheWrite: toPositiveNumber(rates.input_cache_write, 0) * 1_000_000,
574
+ };
575
+ }
576
+
577
+ /**
578
+ * Rates from NanoGPT's detailed roster: prompt and completion are USD per
579
+ * million tokens, the cache legs per thousand. Legs the row does not price
580
+ * keep `fallback`.
581
+ */
582
+ function nanoGptPricingCost(pricing: unknown, fallback: TokenCost): TokenCost {
583
+ if (!isRecord(pricing) || pricing.unit !== "per_million_tokens") return fallback;
584
+ const perThousand = (value: unknown, fallbackRate: number): number => {
585
+ const rate = toPositiveNumber(value, 0);
586
+ return rate > 0 ? rate * 1_000 : fallbackRate;
587
+ };
588
+ return {
589
+ input: toPositiveNumber(pricing.prompt, fallback.input),
590
+ output: toPositiveNumber(pricing.completion, fallback.output),
591
+ cacheRead: perThousand(pricing.cacheReadInputPer1kTokens, fallback.cacheRead),
592
+ cacheWrite: perThousand(pricing.cacheWriteInputPer1kTokens, fallback.cacheWrite),
593
+ };
594
+ }
595
+
553
596
  function normalizeAnthropicBaseUrl(baseUrl: string | undefined, fallback: string): string {
554
597
  const value = baseUrl?.trim();
555
598
  if (!value) {
@@ -1265,6 +1308,87 @@ export function cerebrasModelManagerOptions(
1265
1308
  });
1266
1309
  }
1267
1310
 
1311
+ // ---------------------------------------------------------------------------
1312
+ // 3.5 SambaNova
1313
+ // ---------------------------------------------------------------------------
1314
+
1315
+ /** SambaNova discovery inputs: `apiKey` enables the live roster; `baseUrl` defaults to SambaNova Cloud. */
1316
+ export interface SambaNovaModelManagerConfig {
1317
+ apiKey?: string;
1318
+ baseUrl?: string;
1319
+ fetch?: FetchImpl;
1320
+ }
1321
+
1322
+ /**
1323
+ * One SambaNova per-token USD rate (a decimal string) as a per-million rate.
1324
+ * `absent` is the value for a missing field; a present field that is not a
1325
+ * nonnegative number yields `undefined`.
1326
+ */
1327
+ function sambanovaRate(value: unknown, absent: number | undefined): number | undefined {
1328
+ if (value === undefined || value === null) return absent;
1329
+ const parsed = toNumber(value);
1330
+ return parsed !== undefined && parsed >= 0 ? Math.round(parsed * 1e12) / 1e6 : undefined;
1331
+ }
1332
+
1333
+ /**
1334
+ * Map SambaNova's `pricing` block. Prompt and completion rates are required;
1335
+ * cache rates default to 0 when absent. An unreadable card stays unpriced
1336
+ * (`unknown`) instead of reading as free, and an all-zero card is a declared
1337
+ * free tier.
1338
+ */
1339
+ function sambanovaPricing(
1340
+ pricing: unknown,
1341
+ fallback: TokenCost,
1342
+ ): Pick<ModelSpec<"openai-completions">, "cost" | "pricingStatus"> {
1343
+ const rates = isRecord(pricing) ? pricing : {};
1344
+ const input = sambanovaRate(rates.prompt, undefined);
1345
+ const output = sambanovaRate(rates.completion, undefined);
1346
+ const cacheRead = sambanovaRate(rates.input_cache_read, 0);
1347
+ const cacheWrite = sambanovaRate(rates.input_cache_write, 0);
1348
+ if (input === undefined || output === undefined || cacheRead === undefined || cacheWrite === undefined) {
1349
+ return { cost: fallback, pricingStatus: "unknown" };
1350
+ }
1351
+ const cost = { input, output, cacheRead, cacheWrite };
1352
+ return input + output + cacheRead + cacheWrite === 0 ? { cost, pricingStatus: "free" } : { cost };
1353
+ }
1354
+
1355
+ /**
1356
+ * SambaNova Cloud's model manager. `/v1/models` requires a key, so a keyless
1357
+ * manager serves only the bundled seed; with a key the live roster replaces
1358
+ * it. Rows carry pricing and limits but no capability metadata: reasoning,
1359
+ * thinking wire format, and image input come from `providers/sambanova.kdl`.
1360
+ */
1361
+ export function sambanovaModelManagerOptions(
1362
+ config?: SambaNovaModelManagerConfig,
1363
+ ): ModelManagerOptions<"openai-completions"> {
1364
+ const apiKey = config?.apiKey;
1365
+ const baseUrl = config?.baseUrl ?? "https://api.sambanova.ai/v1";
1366
+ return {
1367
+ providerId: "sambanova",
1368
+ dynamicModelsAuthoritative: true,
1369
+ // The live `pricing` block is the tariff: an explicit `0` must not revert
1370
+ // to the bundled seed's rate.
1371
+ dynamicCostAuthoritative: true,
1372
+ ...(apiKey && {
1373
+ fetchDynamicModels: () =>
1374
+ fetchOpenAICompatibleModels({
1375
+ api: "openai-completions",
1376
+ provider: "sambanova",
1377
+ baseUrl,
1378
+ apiKey,
1379
+ filterModel: (_entry, model) => !isExcludedModel("sambanova", model.id),
1380
+ mapModel: (entry, defaults) => ({
1381
+ ...defaults,
1382
+ ...sambanovaPricing(entry.pricing, defaults.cost),
1383
+ contextWindow: toPositiveNumber(entry.context_length, defaults.contextWindow),
1384
+ maxTokens: toPositiveNumber(entry.max_completion_tokens, defaults.maxTokens),
1385
+ }),
1386
+ fetch: config?.fetch,
1387
+ }),
1388
+ }),
1389
+ };
1390
+ }
1391
+
1268
1392
  // ---------------------------------------------------------------------------
1269
1393
  // 4. Hugging Face
1270
1394
  // ---------------------------------------------------------------------------
@@ -2296,74 +2420,6 @@ export function zhipuCodingPlanModelManagerOptions(
2296
2420
  // 7.5 Fireworks
2297
2421
  // ---------------------------------------------------------------------------
2298
2422
 
2299
- /**
2300
- * Fireworks-published cap for the Kimi K2 family. Fireworks' `/v1/models`
2301
- * envelope generically reports `max_completion_tokens: 65536` for every Kimi
2302
- * deployment, but Kimi K2 (instruct / thinking / turbo) on Fireworks is
2303
- * documented to ship long reasoning traces that should be bounded — capping
2304
- * at 32,768 prevents handing callers a budget the router cannot honor.
2305
- * See https://github.com/can1357/oh-my-pi/issues/1849.
2306
- */
2307
- export const FIREWORKS_KIMI_MAX_TOKENS = 32_768;
2308
-
2309
- /**
2310
- * Fireworks' output ceiling for Kimi K2.7-Code specifically. Its `/v1/models`
2311
- * generic `max_completion_tokens` is 65,536 and Fireworks serves it in full —
2312
- * verified with a single completion emitting 58,971 output tokens and
2313
- * `max_tokens: 200000` accepted without error. Unlike the older K2.5/K2.6
2314
- * family (see {@link FIREWORKS_KIMI_MAX_TOKENS}), K2.7-Code is not clamped to
2315
- * 32,768; that ceiling only truncated it.
2316
- */
2317
- export const FIREWORKS_KIMI_K27_CODE_MAX_TOKENS = 65_536;
2318
-
2319
- /**
2320
- * Returns true for the Kimi K2.5 / K2.6 family served by Fireworks-backed
2321
- * providers (`fireworks` direct, `firepass` router) that share the 32,768
2322
- * `maxTokens` ceiling. Matches both the public catalog id (`kimi-k2.5`,
2323
- * `kimi-k2.6`, `kimi-k2.6-turbo`) and the canonical Fireworks wire id
2324
- * (`accounts/fireworks/{models,routers}/kimi-k2…`).
2325
- *
2326
- * K2.7-Code (incl. `-fast` / `-highspeed`) is deliberately excluded: unlike the
2327
- * earlier K2 family it serves its full context on Fireworks — verified with a
2328
- * single completion emitting 58,971 output tokens and `max_tokens: 200000`
2329
- * accepted without error — so the 32,768 cap would only truncate it. It inherits
2330
- * Fireworks' reported `max_completion_tokens` (65,536) instead.
2331
- */
2332
- export function isFireworksKimiK2ModelId(modelId: string): boolean {
2333
- const identity = classifyModel("fireworks", modelId, { lenient: true });
2334
- if (identity.class !== "kimi" || identity.family === "k2.7-code") return false;
2335
- return identity.family?.startsWith("k2") === true;
2336
- }
2337
-
2338
- /**
2339
- * Clamp the Kimi K2 family's `maxTokens` to {@link FIREWORKS_KIMI_MAX_TOKENS}
2340
- * on Fireworks-backed providers, leaving every other model untouched.
2341
- */
2342
- export function clampFireworksKimiMaxTokens(modelId: string, candidate: number): number;
2343
- export function clampFireworksKimiMaxTokens(modelId: string, candidate: number | null): number | null;
2344
- export function clampFireworksKimiMaxTokens(modelId: string, candidate: number | null): number | null {
2345
- if (candidate === null) return null;
2346
- return isFireworksKimiK2ModelId(modelId) ? Math.min(candidate, FIREWORKS_KIMI_MAX_TOKENS) : candidate;
2347
- }
2348
-
2349
- /**
2350
- * Kimi K2.7 Code's documented recommended output budget. Some provider
2351
- * discovery rows report the context-sized `max_completion_tokens` instead.
2352
- */
2353
- export const KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS = 32_768;
2354
-
2355
- export function isKimiK27CodeModelId(modelId: string): boolean {
2356
- const identity = classifyModel("fireworks", modelId, { lenient: true });
2357
- return identity.class === "kimi" && identity.family === "k2.7-code";
2358
- }
2359
-
2360
- export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number): number;
2361
- export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number | null): number | null;
2362
- export function clampKimiK27CodeMaxTokens(modelId: string, candidate: number | null): number | null {
2363
- if (candidate === null) return null;
2364
- return isKimiK27CodeModelId(modelId) ? Math.min(candidate, KIMI_K27_CODE_RECOMMENDED_MAX_TOKENS) : candidate;
2365
- }
2366
-
2367
2423
  /**
2368
2424
  * Fireworks Fast variants we surface: the Fast table on
2369
2425
  * https://docs.fireworks.ai/serverless/serverless-modes, minus the US-only
@@ -2493,15 +2549,9 @@ function mapFireworksControlPlaneModel(
2493
2549
  const supportsImage = toBoolean(record.supportsImageInput) === true;
2494
2550
  const supportsTools = toBoolean(record.supportsTools);
2495
2551
  const contextWindow = toPositiveNumber(record.contextLength, reference?.contextWindow ?? null);
2496
- // The control plane reports no max-output budget. Default K2.7-Code to its
2497
- // verified 65,536 ceiling, the older K2.5/K2.6 family to the clamped 32,768,
2498
- // everyone else to the discovery fallback, then clamp.
2499
- const fallbackMaxTokens = isKimiK27CodeModelId(publicModelId)
2500
- ? FIREWORKS_KIMI_K27_CODE_MAX_TOKENS
2501
- : isFireworksKimiK2ModelId(publicModelId)
2502
- ? FIREWORKS_KIMI_MAX_TOKENS
2503
- : null;
2504
- const maxTokens = clampFireworksKimiMaxTokens(publicModelId, reference?.maxTokens ?? fallbackMaxTokens);
2552
+ // The control plane reports no max-output budget; Fireworks' per-model
2553
+ // output caps and fallbacks are rule-owned (providers/fireworks.kdl).
2554
+ const maxTokens = reference?.maxTokens ?? null;
2505
2555
  const base: ModelSpec<"openai-completions"> = reference ?? {
2506
2556
  id: publicModelId,
2507
2557
  name,
@@ -2941,8 +2991,9 @@ function buildClinePassFreeModel(id: string, liveCatalog?: ClinePassLiveCatalog)
2941
2991
  input: upstream?.input ?? live?.input ?? fallback.input,
2942
2992
  contextWindow: upstream?.contextWindow ?? live?.contextWindow ?? fallback.contextWindow,
2943
2993
  maxTokens: upstream?.maxTokens ?? live?.maxTokens ?? fallback.maxTokens,
2944
- // A non-reasoning reference must not carry the Cline effort ladder.
2945
- ...(reasoning ? {} : { thinking: undefined }),
2994
+ // No reasoning-effort control means no effort ladder: the cline-pass
2995
+ // rules trust only explicit thinking, so a copied ladder would surface.
2996
+ thinking: undefined,
2946
2997
  compat: { supportsReasoningEffort: false, wireModelIdMode: "raw" },
2947
2998
  };
2948
2999
  }
@@ -3215,12 +3266,18 @@ export interface OpenCodeModelManagerConfig {
3215
3266
  fetch?: FetchImpl;
3216
3267
  }
3217
3268
 
3269
+ const OPENCODE_ZEN_BASE_PATH = "https://opencode.ai/zen";
3270
+ const OPENCODE_ZEN_GOOGLE_BASE_URL = "https://opencode.ai/inference/google/v1beta";
3271
+
3218
3272
  function normalizeOpenCodeBasePath(baseUrl: string | undefined, fallbackBasePath: string): string {
3219
3273
  const value = normalizeAnthropicBaseUrl(baseUrl, fallbackBasePath);
3220
3274
  return value.endsWith("/v1") ? value.slice(0, -3) : value;
3221
3275
  }
3222
3276
 
3223
3277
  function openCodeBaseUrlForApi(api: Api, basePath: string): string {
3278
+ if (api === "google-generative-ai" && basePath === OPENCODE_ZEN_BASE_PATH) {
3279
+ return OPENCODE_ZEN_GOOGLE_BASE_URL;
3280
+ }
3224
3281
  return api === "anthropic-messages" ? basePath : `${basePath}/v1`;
3225
3282
  }
3226
3283
 
@@ -3234,7 +3291,12 @@ function openCodeBaseUrlForApi(api: Api, basePath: string): string {
3234
3291
  // rules (`runtime/behavior.kdl`; #887, #1617, #8957).
3235
3292
  // Runtime-discovered rows cached before model-identity corrections retain
3236
3293
  // stale capability metadata until the authoritative catalog TTL expires.
3237
- const OPENCODE_CACHE_MIGRATION_MODEL_IDS = ["glm-5.3-flash", "longcat-2.5-preview-free", "space-bunny-free"] as const;
3294
+ const OPENCODE_CACHE_MIGRATION_MODEL_IDS = [
3295
+ "deepseek-flash",
3296
+ "glm-5.3-flash",
3297
+ "longcat-2.5-preview-free",
3298
+ "space-bunny-free",
3299
+ ] as const;
3238
3300
  const OPENCODE_ZEN_CACHE_MIGRATION_MODEL_IDS = ["gemini-3.7-flash", "gemini-3.8-flash"] as const;
3239
3301
 
3240
3302
  // Billing-variant suffixes the OpenCode gateways append to a base model id
@@ -3269,7 +3331,10 @@ function openCodeModelManagerOptions(
3269
3331
  const apiOverrides = (id: string): Api | undefined => {
3270
3332
  // Route pins live in the `api-routes` behavior rules.
3271
3333
  const route = apiRouteFor(providerId, id)?.api;
3272
- return route === "openai-completions" || route === "openai-responses" || route === "anthropic-messages"
3334
+ return route === "openai-completions" ||
3335
+ route === "openai-responses" ||
3336
+ route === "anthropic-messages" ||
3337
+ route === "google-generative-ai"
3273
3338
  ? route
3274
3339
  : undefined;
3275
3340
  };
@@ -3893,6 +3958,7 @@ export function kiloModelManagerOptions(config?: KiloModelManagerConfig): ModelM
3893
3958
  provider: "kilo",
3894
3959
  baseUrl,
3895
3960
  apiKey,
3961
+ mapModel: (entry, defaults) => ({ ...defaults, cost: perTokenPricingCost(entry.pricing) }),
3896
3962
  fetch: config?.fetch,
3897
3963
  }),
3898
3964
  };
@@ -4570,11 +4636,7 @@ export function veniceModelManagerOptions(
4570
4636
  config,
4571
4637
  mapModel: (entry, defaults, reference) => {
4572
4638
  const model = mapWithBundledReference(entry, defaults, reference);
4573
- return {
4574
- ...model,
4575
- maxTokens: clampKimiK27CodeMaxTokens(defaults.id, model.maxTokens),
4576
- compat: { ...model.compat, supportsUsageInStreaming: false },
4577
- };
4639
+ return { ...model, compat: { ...model.compat, supportsUsageInStreaming: false } };
4578
4640
  },
4579
4641
  });
4580
4642
  }
@@ -4727,7 +4789,9 @@ function museSparkLineageSpec(id: string): ModelSpec<"openai-responses"> | undef
4727
4789
  let templateRevision: readonly [number, number, number] | undefined;
4728
4790
  for (const model of seedModels<"openai-responses">("meta")) {
4729
4791
  if ((billingVariantPlain(model.id) !== undefined) !== contributor) continue;
4730
- const candidate = parseRevision(classifyModel("meta", model.id, { lenient: true }).revision ?? "");
4792
+ const seedIdentity = classifyModel("meta", model.id, { lenient: true });
4793
+ if (seedIdentity.family !== "muse-spark") continue;
4794
+ const candidate = parseRevision(seedIdentity.revision ?? "");
4731
4795
  if (candidate === undefined) continue;
4732
4796
  if (templateRevision === undefined || compareRevision(candidate, templateRevision) > 0) {
4733
4797
  template = model;
@@ -6282,10 +6346,29 @@ export function nanoGptModelManagerOptions(
6282
6346
  provider: "nanogpt",
6283
6347
  baseUrl,
6284
6348
  apiKey,
6349
+ // Only the detailed roster carries per-model pricing, names, and limits.
6350
+ query: { detailed: "true" },
6285
6351
  mapModel: (entry, defaults) => {
6286
6352
  const reference = resolveReference(defaults.id);
6287
- const mapped = mapWithBundledReference(entry, defaults, reference);
6288
- return { ...mapped, api: "openai-completions", provider: "nanogpt" };
6353
+ // The detailed roster names its output cap `max_output_tokens`.
6354
+ const maxOutputTokens = entry.max_output_tokens ?? entry.max_completion_tokens;
6355
+ const mapped = mapWithBundledReference(
6356
+ { ...entry, max_completion_tokens: maxOutputTokens },
6357
+ reference
6358
+ ? defaults
6359
+ : {
6360
+ ...defaults,
6361
+ contextWindow: toPositiveNumberOrNull(entry.context_length),
6362
+ maxTokens: toPositiveNumberOrNull(maxOutputTokens),
6363
+ },
6364
+ reference,
6365
+ );
6366
+ return {
6367
+ ...mapped,
6368
+ api: "openai-completions",
6369
+ provider: "nanogpt",
6370
+ cost: { ...mapped.cost, ...nanoGptPricingCost(entry.pricing, mapped.cost) },
6371
+ };
6289
6372
  },
6290
6373
  filterModel: (_entry, model) => {
6291
6374
  const match = NANO_GPT_THINKING_SUFFIX_RE.exec(model.id);
@@ -6969,13 +7052,10 @@ function createOpenCodeApiResolution(
6969
7052
  rules: ApiResolutionRule[];
6970
7053
  } {
6971
7054
  const completionsBaseUrl = `${basePath}/v1`;
6972
- // Per-API base URLs on the OpenCode-style endpoint:
6973
- // - openai-completions / openai-responses / google-generative-ai → /v1
6974
- // - anthropic-messages → bare basePath (the Anthropic client appends /v1/messages)
6975
- const baseUrlForApi = (api: Api): string => (api === "anthropic-messages" ? basePath : completionsBaseUrl);
7055
+ const baseUrlForApi = (api: Api): string => openCodeBaseUrlForApi(api, basePath);
6976
7056
  // Per-id route pins live in the `api-routes` behavior rules.
6977
7057
  const overrideRules: ApiResolutionRule[] = (
6978
- ["openai-completions", "openai-responses", "anthropic-messages"] as const
7058
+ ["openai-completions", "openai-responses", "anthropic-messages", "google-generative-ai"] as const
6979
7059
  ).map(api => ({
6980
7060
  matches: modelId => apiRouteFor(providerId, modelId)?.api === api,
6981
7061
  resolved: { api, baseUrl: baseUrlForApi(api) },
@@ -7004,8 +7084,17 @@ function createOpenCodeApiResolution(
7004
7084
 
7005
7085
  // Resolver rules for the models.dev descriptor path; the per-id pins live in
7006
7086
  // the `api-routes` behavior rules, which also drive the live-discovery mapper.
7007
- const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen", "opencode-zen");
7008
- const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution("https://opencode.ai/zen/go", "opencode-go");
7087
+ const OPENCODE_ZEN_API_RESOLUTION = createOpenCodeApiResolution(OPENCODE_ZEN_BASE_PATH, "opencode-zen");
7088
+ const OPENCODE_GO_BASE_PATH = "https://opencode.ai/inference/go";
7089
+ const OPENCODE_GO_API_RESOLUTION = createOpenCodeApiResolution(OPENCODE_GO_BASE_PATH, "opencode-go");
7090
+
7091
+ /**
7092
+ * OpenCode Go endpoint for one request API. The model generator re-bases
7093
+ * previous-snapshot rows with it so retained rows follow endpoint moves.
7094
+ */
7095
+ export function openCodeGoBaseUrlForApi(api: Api): string {
7096
+ return openCodeBaseUrlForApi(api, OPENCODE_GO_BASE_PATH);
7097
+ }
7009
7098
 
7010
7099
  const COPILOT_BASE_URL = "https://api.githubcopilot.com";
7011
7100
 
@@ -7383,7 +7472,7 @@ const OPENCODE_MODELS_DEV_DESCRIPTORS: readonly ModelsDevProviderDescriptor[] =
7383
7472
  OPENCODE_ZEN_API_RESOLUTION.defaultResolution,
7384
7473
  ),
7385
7474
  }),
7386
- openAiCompletionsDescriptor("opencode-go", "opencode-go", "https://opencode.ai/zen/go/v1", {
7475
+ openAiCompletionsDescriptor("opencode-go", "opencode-go", `${OPENCODE_GO_BASE_PATH}/v1`, {
7387
7476
  filterModel: filterActiveToolCallModels,
7388
7477
  resolveApi: (modelId, raw) =>
7389
7478
  resolveApiByRules(
@@ -7464,12 +7553,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_SPECIALIZED: readonly ModelsDevProviderDes
7464
7553
  // --- Synthetic ---
7465
7554
  openAiCompletionsDescriptor("synthetic", "synthetic", "https://api.synthetic.new/openai/v1"),
7466
7555
  // --- Venice AI ---
7467
- openAiCompletionsDescriptor("venice", "venice", "https://api.venice.ai/api/v1", {
7468
- transformModel: model => {
7469
- const maxTokens = clampKimiK27CodeMaxTokens(model.id, model.maxTokens);
7470
- return maxTokens === model.maxTokens ? model : { ...model, maxTokens };
7471
- },
7472
- }),
7556
+ openAiCompletionsDescriptor("venice", "venice", "https://api.venice.ai/api/v1"),
7473
7557
  // --- Ollama Cloud ---
7474
7558
  simpleModelsDevDescriptor("ollama-cloud", "ollama-cloud", "ollama-chat", "https://ollama.com"),
7475
7559
  // --- Xiaomi Token Plan ---
@@ -64,6 +64,7 @@ export function openaiCodexModelManagerOptions(
64
64
  providerId: "openai-codex",
65
65
  cacheProviderId: resolveModelCacheProviderId("openai-codex", { baseUrl }),
66
66
  dynamicModelsAuthoritative: providerEntry("openai-codex")?.dynamicModelsAuthoritative === true,
67
+ keepStaticNames: providerEntry("openai-codex")?.keepBundledNames === true,
67
68
  ...(resolveAccounts
68
69
  ? {
69
70
  fetchDynamicModels: async () => {
package/src/types.d.ts CHANGED
@@ -93,6 +93,14 @@ export interface ThinkingConfig {
93
93
  * provides an explicit wire off-path.
94
94
  */
95
95
  requiresEffort?: boolean;
96
+ /**
97
+ * The catalog has no evidence this model reasons (unclassified id, no
98
+ * capability metadata). The ladder is offered for the user to opt into:
99
+ * sessions leave thinking unset until the user picks an effort, and a
100
+ * request without an effort goes out as non-reasoning (see
101
+ * `withoutUnverifiedThinking`).
102
+ */
103
+ unverified?: boolean;
96
104
  }
97
105
  export type Provider = string;
98
106
  /** Token budgets for each thinking level (token-based providers only) */
@@ -443,6 +451,8 @@ export interface OpenAICompat {
443
451
  reasoningDeltasMayBeCumulative?: boolean;
444
452
  /** Strip leaked DeepSeek chat-template special tokens from visible content deltas. Default: auto-detected. */
445
453
  stripDeepseekSpecialTokens?: boolean;
454
+ /** Drop assistant thinking blocks from replayed chat-completions history. Default: false. */
455
+ stripThinkingHistory?: boolean;
446
456
  /** Heal leaked chat-template/tool-call/thinking markup from visible content deltas. Default: auto-detected. */
447
457
  streamMarkupHealingPattern?: OpenAIStreamMarkupHealingPattern;
448
458
  /** Whether this wire may revise already-streamed text (`stream-revision` axis). Unassigned: append-only. */
@@ -492,6 +502,14 @@ export interface AnthropicCompat {
492
502
  * contract instead. Default: false.
493
503
  */
494
504
  firstPartyProvider?: boolean;
505
+ /**
506
+ * Whether the provider honors Claude Code's `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`
507
+ * kill switch, which withholds pre-release fields (`context_management`, beta
508
+ * tool fields, task budgets, structured-output format, `thinking.block_binding`,
509
+ * server compaction) with their betas. Rule-owned on the
510
+ * provider whose `ANTHROPIC_*` env routing Claude Code shares. Default: false.
511
+ */
512
+ honorsClaudeCodeBetaKillSwitch?: boolean;
495
513
  /**
496
514
  * Whether requests may carry `output_config.effort` (and its effort beta
497
515
  * header). Vertex AI rejects the field/header. Default: true.
@@ -858,12 +876,15 @@ export interface ResolvedOpenAISharedCompat {
858
876
  * `buildModel`; request handlers read fields and never detect, resolve, or
859
877
  * allocate.
860
878
  */
861
- export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "trustExplicitThinkingOnly" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsReasoningSummary" | "statefulResponses" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "disableReasoningWithTools" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "mistralReasoningContentParts" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "syntheticReasoningContentFallback" | "replayReasoningContent" | "qwenPreserveThinking" | "qwenTemplateReasoningEffort" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "sessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "clampOutputToModelMax" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "streamRevision" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "supportsConfigurationUpdate" | "supportsSteering" | "stripImageInput" | "thinkingLoopGuard" | "whenThinking">> & {
879
+ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "trustExplicitThinkingOnly" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsReasoningSummary" | "statefulResponses" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "disableReasoningWithTools" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "mistralReasoningContentParts" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "syntheticReasoningContentFallback" | "replayReasoningContent" | "qwenPreserveThinking" | "qwenTemplateReasoningEffort" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "stripThinkingHistory" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "sessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "clampOutputToModelMax" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "streamRevision" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "supportsConfigurationUpdate" | "supportsSteering" | "stripImageInput" | "thinkingLoopGuard" | "whenThinking">> & {
862
880
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
863
881
  extraBody?: OpenAICompat["extraBody"];
864
882
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
865
883
  thinkingKeep?: OpenAICompat["thinkingKeep"];
866
884
  streamIdleTimeoutMs?: number;
885
+ stripThinkingHistory?: boolean;
886
+ /** The configured `baseUrl` is the official OpenAI API (provider `openai`); unset otherwise. */
887
+ officialEndpoint?: boolean;
867
888
  toolStrictMode: ResolvedToolStrictMode;
868
889
  /** The model sits behind Vercel AI Gateway. */
869
890
  isVercelGatewayHost: boolean;
@@ -928,9 +949,11 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
928
949
  isVercelGatewayHost: boolean;
929
950
  /**
930
951
  * The configured endpoint is first-party OpenAI (`provider === "openai"` on
931
- * an `api.openai.com` or unset baseUrl). Gates official-only Responses
932
- * behavior: default-on stateful `previous_response_id` chaining and the
933
- * `text.verbosity` field.
952
+ * an `api.openai.com` or unset baseUrl), or the Codex backend
953
+ * (`openai-codex-responses` on the default Codex baseUrl). Gates platform
954
+ * OpenAI's official-only Responses behavior (default-on stateful
955
+ * `previous_response_id` chaining and the `text.verbosity` field; the Codex
956
+ * adapter is not governed by either gate) and host request-size budgets.
934
957
  */
935
958
  officialEndpoint: boolean;
936
959
  /** Run Harmony-protocol leak detection/mitigation on streamed output. */
@@ -1055,23 +1078,43 @@ export interface GoogleCompat {
1055
1078
  antigravityUsageLabel?: string;
1056
1079
  /** Strip image inputs before encoding (text-only serving of a multimodal id). */
1057
1080
  stripImageInput?: boolean;
1081
+ /**
1082
+ * Largest width or height a single image may carry for a Claude reader on
1083
+ * this host (see {@link AnthropicCompat.maxImageDimension}). Setting this or
1084
+ * {@link maxImagePayloadBytes} turns on Claude's image clamp, many-image
1085
+ * cap included; unset leaves images as sent.
1086
+ */
1087
+ maxImageDimension?: number;
1088
+ /**
1089
+ * Largest base64 payload a single image may carry, in bytes (see
1090
+ * {@link AnthropicCompat.maxImagePayloadBytes}).
1091
+ */
1092
+ maxImagePayloadBytes?: number;
1058
1093
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
1059
1094
  thinkingLoopGuard?: "gemini" | "deepseek" | "xai";
1060
1095
  /** See {@link BedrockCompat.supportsSamplingParams}. */
1061
1096
  supportsSamplingParams?: boolean;
1062
1097
  }
1063
1098
  /** Fully-resolved google-API compat view, materialized once by `buildModel`. */
1064
- export type ResolvedGoogleCompat = Required<Omit<GoogleCompat, "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "thinkingLoopGuard" | "antigravityUsageLabel" | "supportsSamplingParams">> & {
1099
+ export type ResolvedGoogleCompat = Required<Omit<GoogleCompat, "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "thinkingLoopGuard" | "antigravityUsageLabel" | "supportsSamplingParams" | "maxImageDimension" | "maxImagePayloadBytes">> & {
1065
1100
  streamFirstEventTimeoutMs?: number;
1066
1101
  streamIdleTimeoutMs?: number;
1067
1102
  thinkingLoopGuard?: GoogleCompat["thinkingLoopGuard"];
1068
1103
  antigravityUsageLabel?: string;
1069
1104
  supportsSamplingParams?: boolean;
1105
+ maxImageDimension?: number;
1106
+ maxImagePayloadBytes?: number;
1070
1107
  };
1108
+ /** Ollama chat history compatibility resolved by the catalog. */
1109
+ export interface OllamaCompat {
1110
+ /** Omit assistant thinking when the deployment rejects it in history. */
1111
+ stripThinkingHistory?: boolean;
1112
+ }
1113
+ export type ResolvedOllamaCompat = Required<OllamaCompat>;
1071
1114
  /** Sparse, user-authored compat overrides for a given API (models.json / config vocabulary). */
1072
- export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "devin-agent" ? DevinCompat : TApi extends "google-generative-ai" | "google-vertex" | "google-gemini-cli" ? GoogleCompat : undefined;
1115
+ export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "devin-agent" ? DevinCompat : TApi extends "google-generative-ai" | "google-vertex" | "google-gemini-cli" ? GoogleCompat : TApi extends "ollama-chat" ? OllamaCompat : undefined;
1073
1116
  /** Resolved compat for a given API: complete record, materialized once by `buildModel`. */
1074
- export type CompatOf<TApi extends Api> = TApi extends "openrouter" ? ResolvedOpenRouterCompat : TApi extends "openai-completions" ? ResolvedOpenAICompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? ResolvedOpenAIResponsesCompat : TApi extends "anthropic-messages" ? ResolvedAnthropicCompat : TApi extends "bedrock-converse-stream" ? ResolvedBedrockCompat : TApi extends "devin-agent" ? ResolvedDevinCompat : TApi extends "google-generative-ai" | "google-vertex" | "google-gemini-cli" ? ResolvedGoogleCompat : undefined;
1117
+ export type CompatOf<TApi extends Api> = TApi extends "openrouter" ? ResolvedOpenRouterCompat : TApi extends "openai-completions" ? ResolvedOpenAICompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? ResolvedOpenAIResponsesCompat : TApi extends "anthropic-messages" ? ResolvedAnthropicCompat : TApi extends "bedrock-converse-stream" ? ResolvedBedrockCompat : TApi extends "devin-agent" ? ResolvedDevinCompat : TApi extends "google-generative-ai" | "google-vertex" | "google-gemini-cli" ? ResolvedGoogleCompat : TApi extends "ollama-chat" ? ResolvedOllamaCompat : undefined;
1075
1118
  /** Provider-native compaction endpoint configuration for one model. */
1076
1119
  export interface RemoteCompactionConfig<TApi extends Api = Api> {
1077
1120
  /** Enables provider-native compaction for providers not enabled by built-in policy. */
@@ -1145,7 +1188,7 @@ export interface ModelCost extends TokenCost {
1145
1188
  * generations rather than providers: DeepSeek V3 through V4 share
1146
1189
  * `"deepseek-v3"`; Kimi K2 through K3 share `"kimi-k2"`.
1147
1190
  */
1148
- export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5";
1191
+ export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "o200k";
1149
1192
  /** One account's discovered entitlements on a model; see {@link Model.accountAccess}. */
1150
1193
  export interface ModelAccountAccess {
1151
1194
  /**
@@ -1198,6 +1241,8 @@ export interface Model<TApi extends Api = Api> {
1198
1241
  requiresGlyphTokenization?: boolean;
1199
1242
  /** Whether this model requires Cursor's tool-schema combiner projection. */
1200
1243
  requiresCursorToolSchemaProjection?: boolean;
1244
+ /** Whether advertised tool schemas occupy every model request's context (false for deferred catalogs). */
1245
+ toolSchemasInContext?: boolean;
1201
1246
  /** Whether this model requires tool-result images hoisted into sibling user content blocks. */
1202
1247
  requiresToolResultImageHoisting?: boolean;
1203
1248
  /**
@@ -1246,6 +1291,12 @@ export interface Model<TApi extends Api = Api> {
1246
1291
  * than ZETA's general image pipeline. `stb` local backends reject WebP.
1247
1292
  */
1248
1293
  imageInputDecoder?: "stb";
1294
+ /**
1295
+ * Most images one request may carry, from user configuration. Replaces the
1296
+ * catalog's `image-count-budget` for hosts no rule names, such as a custom
1297
+ * gateway in front of a vendor with a higher limit.
1298
+ */
1299
+ imageCountBudget?: number;
1249
1300
  /**
1250
1301
  * Native provider tool-call support. `false` is the only unsupported signal:
1251
1302
  * `true` and `undefined` both mean callers may use native tools. Catalog and
@@ -1328,6 +1379,8 @@ export interface Model<TApi extends Api = Api> {
1328
1379
  contextWindow: number | null;
1329
1380
  /** Preserve the host's supplied window instead of applying inferred expansion or reference-price caps. */
1330
1381
  contextWindowAuthoritative?: boolean;
1382
+ /** The long-context price tier does not cap the runtime context window when extended context is off. */
1383
+ longContextWindowUncapped?: boolean;
1331
1384
  /** Optional larger prompt window available when extended context is enabled. */
1332
1385
  maxContextWindow?: number;
1333
1386
  maxTokens: number | null;
@@ -1354,7 +1407,7 @@ export interface Model<TApi extends Api = Api> {
1354
1407
  * Streaming transport override. When `"pi-native"`, `streamSimple` routes
1355
1408
  * the request to the model's `baseUrl` via the auth-gateway's
1356
1409
  * `POST /v1/pi/stream` endpoint instead of dispatching the per-API
1357
- * provider client. The `baseUrl` must point at an `zeta-c auth-gateway`
1410
+ * provider client. The `baseUrl` must point at an `zetacode auth-gateway`
1358
1411
  * (or compatible) host; `headers.Authorization` (or `apiKey` resolved by
1359
1412
  * the registry) carries the gateway bearer.
1360
1413
  *
@@ -1369,8 +1422,6 @@ export interface Model<TApi extends Api = Api> {
1369
1422
  preferWebsockets?: boolean;
1370
1423
  /** Codex Responses Lite transport: send the lite marker and carry instructions/tools as input items (mirrors codex-rs `use_responses_lite`). */
1371
1424
  useResponsesLite?: boolean;
1372
- /** Codex Code Mode restriction: model expects tools routed through a programmatic exec surface (mirrors codex-rs `tool_mode`). */
1373
- toolMode?: "code_mode_only";
1374
1425
  /**
1375
1426
  * Service-tier ids the provider advertises for this model (Codex discovery
1376
1427
  * `service_tiers[].id`, e.g. `priority`, `ultrafast`). Absent when the
@@ -1463,7 +1514,7 @@ export interface Model<TApi extends Api = Api> {
1463
1514
  * vocabulary of `buildModel`. Identical to `Model` except `compat` carries the
1464
1515
  * sparse override shape and nothing is resolved yet.
1465
1516
  */
1466
- export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "identity" | "compatConfig" | "requiresGlyphTokenization" | "requiresCursorToolSchemaProjection" | "requiresToolResultImageHoisting" | "supportsAssistantPrefill" | "supportsComputerUseConfig"> {
1517
+ export interface ModelSpec<TApi extends Api = Api> extends Omit<Model<TApi>, "compat" | "identity" | "compatConfig" | "requiresGlyphTokenization" | "requiresCursorToolSchemaProjection" | "toolSchemasInContext" | "requiresToolResultImageHoisting" | "supportsAssistantPrefill" | "supportsComputerUseConfig"> {
1467
1518
  /** Sparse compatibility overrides; resolved into `Model.compat` by `buildModel`. */
1468
1519
  compat?: CompatConfigOf<TApi>;
1469
1520
  }