@oh-my-pi/pi-catalog 18.15.0 → 18.17.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 (61) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/types/compat/auth-ids.d.ts +2 -2
  3. package/dist/types/compat/catalog-policy.d.ts +6 -4
  4. package/dist/types/compat/factory-droid.d.ts +7 -1
  5. package/dist/types/compat/provider-ids.d.ts +1 -1
  6. package/dist/types/compat/resolve.d.ts +1 -1
  7. package/dist/types/compat/responses-websocket.d.ts +6 -0
  8. package/dist/types/compat/revision.d.ts +2 -0
  9. package/dist/types/compat/server-side-fallback.d.ts +6 -4
  10. package/dist/types/compat/types.d.ts +9 -1
  11. package/dist/types/discovery/devin-proto.d.ts +15 -0
  12. package/dist/types/discovery/openai-compatible.d.ts +6 -0
  13. package/dist/types/model-manager.d.ts +2 -0
  14. package/dist/types/models.d.ts +16 -0
  15. package/dist/types/pricing.d.ts +66 -6
  16. package/dist/types/provider-models/openai-compat.d.ts +18 -0
  17. package/dist/types/types.d.ts +25 -4
  18. package/dist/types/wire/codex.d.ts +0 -1
  19. package/dist/types/wire/factory-droid.d.ts +1 -1
  20. package/package.json +5 -5
  21. package/src/compat/auth-ids.ts +7 -0
  22. package/src/compat/axes.ts +11 -2
  23. package/src/compat/catalog-policy.ts +14 -10
  24. package/src/compat/collapse.ts +1 -1
  25. package/src/compat/factory-droid.ts +9 -1
  26. package/src/compat/provider-ids.ts +1 -0
  27. package/src/compat/resolve.ts +6 -2
  28. package/src/compat/responses-websocket.ts +12 -0
  29. package/src/compat/revision.ts +10 -0
  30. package/src/compat/rules/README.md +2 -0
  31. package/src/compat/rules/auth/_order.kdl +2 -2
  32. package/src/compat/rules/auth/devin-cli.kdl +6 -0
  33. package/src/compat/rules/auth/devin-windsurf.kdl +6 -0
  34. package/src/compat/rules/auth/mistral.kdl +4 -0
  35. package/src/compat/rules/auth/scaleway.kdl +17 -0
  36. package/src/compat/rules/classes/anthropic.kdl +16 -0
  37. package/src/compat/rules/providers/factory-droid.kdl +275 -17
  38. package/src/compat/rules/providers/muse-code.kdl +16 -0
  39. package/src/compat/rules/providers/openai.kdl +3 -0
  40. package/src/compat/rules/providers/scaleway.kdl +179 -0
  41. package/src/compat/rules/providers/web.kdl +7 -0
  42. package/src/compat/rules/runtime/behavior.kdl +15 -2
  43. package/src/compat/rules.json +1 -1
  44. package/src/compat/server-side-fallback.ts +7 -5
  45. package/src/compat/types.ts +9 -1
  46. package/src/discovery/codex.ts +0 -1
  47. package/src/discovery/devin-proto.ts +28 -0
  48. package/src/discovery/factory-droid.ts +58 -23
  49. package/src/discovery/openai-compatible.ts +1 -1
  50. package/src/model-cache.ts +27 -54
  51. package/src/model-catalog-format.ts +5 -8
  52. package/src/model-manager.ts +40 -131
  53. package/src/model-tokenizer.ts +13 -12
  54. package/src/models.json +1 -1
  55. package/src/models.ts +32 -1
  56. package/src/pricing.ts +59 -94
  57. package/src/provider-models/descriptors.ts +2 -0
  58. package/src/provider-models/openai-compat.ts +54 -39
  59. package/src/types.ts +25 -3
  60. package/src/wire/codex.ts +0 -1
  61. package/src/wire/factory-droid.ts +5 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.17.0] - 2026-11-06
6
+
7
+ ### Added
8
+
9
+ - Added `tool-name-prefix` compat axis for Responses hosts that reject certain tool names; Muse Code sets it to `_` ([#17435](https://github.com/can1357/oh-my-pi/issues/17435), [#17515](https://github.com/can1357/oh-my-pi/pull/17515) by [@harshav167](https://github.com/harshav167), continued in [#18053](https://github.com/can1357/oh-my-pi/pull/18053) by [@abilliontokens](https://github.com/abilliontokens))
10
+ - Added `supports-message-threads` compat axis (`supportsMessageThreads`), enabled for Claude 4.5+ on first-party Anthropic
11
+ - Added opt-in Responses WebSocket capability via OMP settings or SDK preferences, off by default ([#17354](https://github.com/can1357/oh-my-pi/pull/17354) by [@TatsukiMeng](https://github.com/TatsukiMeng))
12
+ - Added Factory Droid Gemini 4 Argon on Global inference for accounts with the feature flag ([#17861](https://github.com/can1357/oh-my-pi/pull/17861) by [@will-bogusz](https://github.com/will-bogusz))
13
+ - Added Factory Droid Muse Spark 1.3 (Global region, `muse_spark_1_3` flag) with low to xhigh effort ([#17862](https://github.com/can1357/oh-my-pi/pull/17862) by [@will-bogusz](https://github.com/will-bogusz))
14
+
15
+ ### Changed
16
+
17
+ - Updated Factory Droid client version reporting; improved tool-input streaming for Claude via Azure Foundry
18
+
19
+ ### Fixed
20
+
21
+ - Fixed newly published Azure models missing after catalog refresh ([#18173](https://github.com/can1357/oh-my-pi/issues/18173), [#18174](https://github.com/can1357/oh-my-pi/pull/18174) by [@dunkpi](https://github.com/dunkpi))
22
+ - Fixed Factory Droid hiding models when the live-routing family default yields no usable upstream; explicit empty per-model routes still hide them ([#17862](https://github.com/can1357/oh-my-pi/pull/17862) by [@will-bogusz](https://github.com/will-bogusz))
23
+
24
+ ## [18.16.0] - 2026-11-02
25
+
26
+ ### Added
27
+
28
+ - `/login mistral` now offers the Mistral Vibe browser sign-in beside `MISTRAL_API_KEY` ([#13875](https://github.com/can1357/oh-my-pi/pull/13875) by [@Gauthier-Huguenin](https://github.com/Gauthier-Huguenin), continued in [#17672](https://github.com/can1357/oh-my-pi/pull/17672) by [@abilliontokens](https://github.com/abilliontokens))
29
+ - Added `revisionAtLeast` to `compat/revision` and exported `withOpenAICompatibleDiscoveryTimeout` from `discovery/openai-compatible`
30
+ - Added the Factory Droid models droid 1.1.0 serves: GPT-6.1 Terra, GPT-6 Sol and Luna Fast Mode, GPT-6 Astra and GPT-6.1 Sol Ultra Fast (after the organization's high-cost opt-in), Nemotron 3.5 Super, Muse Spark Open, Muse Spark 1.2 and Beam 1.0, each listed once the account may use it ([#17766](https://github.com/can1357/oh-my-pi/pull/17766) by [@will-bogusz](https://github.com/will-bogusz))
31
+ - Added Factory Droid's dated credit rates: the credit rate follows Factory's promotions, discounted from a promotion's start and back to the base rate at its end, using the same dated rate cards as dollar prices (currently GPT-5.6 Sol and Gemini 3.7/3.8 Flash) ([#16309](https://github.com/can1357/oh-my-pi/pull/16309) by [@will-bogusz](https://github.com/will-bogusz))
32
+
33
+ ### Changed
34
+
35
+ - Factory Droid lists Opus 5.5 Fast, GPT-6.1 Sol and MiniMax M3 without the feature flags Factory retired, and no longer routes GPT-5.5 to Snowflake ([#17766](https://github.com/can1357/oh-my-pi/pull/17766) by [@will-bogusz](https://github.com/will-bogusz))
36
+ - Factory Droid Opus 5.5 now falls back to Opus 5 when Anthropic refuses a request, as droid does, while your account can use Opus 5 ([#17772](https://github.com/can1357/oh-my-pi/pull/17772) by [@will-bogusz](https://github.com/will-bogusz))
37
+
38
+ ### Fixed
39
+
40
+ - GPT-3.5 and GPT-4 (Turbo) resolve the `cl100k` tokenizer, so their context estimates count in cl100k_base rather than the o200k_base default for models without one ([#17607](https://github.com/can1357/oh-my-pi/pull/17607) by [@will-bogusz](https://github.com/will-bogusz))
41
+ - Added Scaleway Generative APIs as a built-in provider with 12 serverless chat models (glm-5.3 default), API-key login, and live model discovery ([#16687](https://github.com/can1357/oh-my-pi/pull/16687) by [@reda-maizate](https://github.com/reda-maizate))
42
+
5
43
  ## [18.15.0] - 2026-11-01
6
44
 
7
45
  ### Added
@@ -1,4 +1,4 @@
1
1
  /** Every provider with an `auth/<id>.kdl` policy. */
2
- export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
2
+ export type AuthProviderId = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "devin-cli" | "devin-windsurf" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "scaleway" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
3
3
  /** Providers whose policy declares a `login` flow (the `/login` roster). */
4
- export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "helmcode" | "huggingface" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
4
+ export type LoginProviderId = "abliteration" | "aiand" | "alibaba-coding-plan" | "alibaba-token-plan" | "anthropic" | "baseten" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "devin-cli" | "devin-windsurf" | "exa" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google-antigravity" | "google-gemini-cli" | "helmcode" | "huggingface" | "inception" | "kagi" | "kilo" | "kimi-code" | "litellm" | "llama.cpp" | "lm-studio" | "meta" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "openrouter" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "scaleway" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stencil" | "stepfun" | "synthetic" | "tavily" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zai-coding-plan" | "zenmux" | "zhipu-coding-plan";
@@ -1,7 +1,9 @@
1
1
  import type { CatalogPolicyInputs } from "../types.js";
2
+ import { type ResolveRoute } from "./resolve.js";
2
3
  /**
3
- * The `catalog` axis bag authored in the KDL cascade rules for a model,
4
- * memoized per distinct model. Frozen: the bag is shared across callers, so it
5
- * must stay read-only — extract the value you need rather than handing it on.
4
+ * The `catalog` axis bag authored in the KDL cascade rules for a model (on one
5
+ * upstream when `route` names it), memoized per distinct model. Frozen: the bag
6
+ * is shared across callers, so it must stay read-only — extract the value you
7
+ * need rather than handing it on.
6
8
  */
7
- export declare function resolveCatalogPolicy(model: CatalogPolicyInputs): Readonly<Record<string, unknown>>;
9
+ export declare function resolveCatalogPolicy(model: CatalogPolicyInputs, route?: ResolveRoute): Readonly<Record<string, unknown>>;
@@ -1,3 +1,4 @@
1
+ import { type DatedCostCard } from "../pricing.js";
1
2
  import type { Api, ModelSpec } from "../types.js";
2
3
  import { type FactoryDroidRegion, type FactoryDroidWire } from "../wire/factory-droid.js";
3
4
  /** Native token limits for one inference region. */
@@ -5,11 +6,16 @@ export interface FactoryDroidLimits {
5
6
  contextWindow: number | null;
6
7
  maxTokens: number | null;
7
8
  }
8
- /** Standard Credits rates: `input` is the per-token weight; `output` and `cacheRead` multiply it. */
9
+ /**
10
+ * Standard Credits rates: `input` is the per-token weight; `output` and
11
+ * `cacheRead` multiply it. `effectiveRates` are dated cards (ascending) that
12
+ * overwrite those legs from their date, as on `cost-patch`.
13
+ */
9
14
  export interface FactoryDroidCreditRates {
10
15
  input: number;
11
16
  output?: number;
12
17
  cacheRead?: number;
18
+ effectiveRates?: readonly DatedCostCard[];
13
19
  }
14
20
  /** Account gates on one registry model (the `entitlement` axis). */
15
21
  export interface FactoryDroidEntitlement {
@@ -1,2 +1,2 @@
1
1
  /** Every chat-model provider with a catalog entry (`default-model` in its provider rules). */
2
- export type KnownProvider = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "inception" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "opencode-go" | "opencode-zen" | "openrouter" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stepfun" | "synthetic" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zenmux" | "zhipu-coding-plan";
2
+ export type KnownProvider = "abliteration" | "aiand" | "aimlapi" | "alibaba-coding-plan" | "alibaba-token-plan" | "amazon-bedrock" | "anthropic" | "apple" | "azure" | "baseten" | "bedrock-mantle" | "cerebras" | "charm-hyper" | "cline-pass" | "cloudflare-ai-gateway" | "cloudflare-workers-ai" | "commandcode" | "coralbricks" | "coreweave" | "cursor" | "deepinfra" | "deepseek" | "devin" | "factory-droid" | "firepass" | "fireworks" | "github-copilot" | "gitlab-duo" | "gitlab-duo-agent" | "gmi-cloud" | "google" | "google-antigravity" | "google-gemini-cli" | "google-vertex" | "groq" | "helmcode" | "huggingface" | "inception" | "kilo" | "kimi-code" | "litellm" | "lm-studio" | "local" | "meta" | "minimax" | "minimax-code" | "minimax-code-cn" | "mistral" | "modular" | "moonshot" | "muse-code" | "nanogpt" | "novita" | "nvidia" | "ollama" | "ollama-cloud" | "openai" | "openai-codex" | "opencode-go" | "opencode-zen" | "openrouter" | "qianfan" | "qwen-portal" | "sakana" | "sambanova" | "scaleway" | "siliconflow" | "siliconflow-cn" | "singularityapi-dev" | "singularityapi-tech" | "snowflake" | "stepfun" | "synthetic" | "together" | "typesafe" | "umans" | "venice" | "vercel-ai-gateway" | "vllm" | "wafer-serverless" | "web" | "xai" | "xai-oauth" | "xiaomi" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-sgp" | "yolo-auto" | "zai" | "zenmux" | "zhipu-coding-plan";
@@ -21,7 +21,7 @@ export declare function resolveDiscoveryApi(spec: ModelSpec<Api>, providerType:
21
21
  * {@link resolveModelPolicy} without resolving the compat and thinking
22
22
  * policies, which dominate its cost on catalog-wide scans.
23
23
  */
24
- export declare function resolveCatalogAxes(spec: CatalogPolicyInputs): Record<string, unknown>;
24
+ export declare function resolveCatalogAxes(spec: CatalogPolicyInputs, route?: ResolveRoute): Record<string, unknown>;
25
25
  /**
26
26
  * Resolves the full policy surface for one model spec: structured identity,
27
27
  * complete compat record, thinking metadata, and catalog-data corrections.
@@ -0,0 +1,6 @@
1
+ import type { Model } from "../types.js";
2
+ /**
3
+ * Capability is rule-owned; callers must opt in. Either explicit false vetoes
4
+ * the other preference, including a per-request true for a disabled model.
5
+ */
6
+ export declare function resolveResponsesWebSocketPreference(model: Model, requestPreference?: boolean): boolean;
@@ -22,6 +22,8 @@ export declare function parseRevision(value: string): Revision | undefined;
22
22
  export declare function parseRevisionPrefix(value: string): Revision | undefined;
23
23
  /** Lexicographic triple comparison: negative, zero, or positive. */
24
24
  export declare function compareRevision(a: Revision, b: Revision): number;
25
+ /** Whether a revision string parses and is at or above `floor`; `undefined` or unparsable is never. */
26
+ export declare function revisionAtLeast(revision: string | undefined, floor: string): boolean;
25
27
  /** Renders a revision as its canonical `major.minor.patch` string. */
26
28
  export declare function formatRevision(revision: Revision): string;
27
29
  /** Comparison operator inside a `revision ">=2.5 <4"` cascade constraint. */
@@ -1,7 +1,9 @@
1
- import type { Model } from "../types.js";
1
+ import type { CatalogPolicyInputs } from "../types.js";
2
+ import type { ResolveRoute } from "./resolve.js";
2
3
  /**
3
4
  * Ordered model ids for Anthropic's server-side refusal `fallbacks` chain,
4
- * authored on the `server-side-fallback-models` catalog axis. Empty when no
5
- * rule assigns a chain (the model or host does not support the feature).
5
+ * authored on the `server-side-fallback-models` catalog axis, on `route`'s
6
+ * upstream when it names one. Empty when no rule assigns a chain (the model,
7
+ * host or upstream does not support the feature).
6
8
  */
7
- export declare function serverSideFallbackModels(model: Model): readonly string[];
9
+ export declare function serverSideFallbackModels(model: CatalogPolicyInputs, route?: ResolveRoute): readonly string[];
@@ -714,6 +714,8 @@ export interface ResolvedAxes {
714
714
  export interface RequestPolicy {
715
715
  completionsReasoningMode?: "none" | "effort" | "opt-in" | "forced-on";
716
716
  completionsReasoningHistory?: "omit" | "preserved" | "interleaved";
717
+ /** Send `allow_missing_thinking` with the effort so tool-call turns may arrive without reasoning. */
718
+ completionsAllowMissingThinking?: boolean;
717
719
  anthropicThinking?: "adaptive" | "adaptive-summarized" | "budget-interleaved" | "budget-effort";
718
720
  /** Advertise `fine-grained-tool-streaming-2025-05-14` on requests that carry tools. */
719
721
  anthropicToolStreamingBeta?: boolean;
@@ -721,12 +723,18 @@ export interface RequestPolicy {
721
723
  openaiPlatformHeader?: string;
722
724
  responsesCacheRetention?: boolean;
723
725
  responsesVerbosity?: "low";
724
- responsesServiceTier?: "priority";
726
+ responsesServiceTier?: "priority" | "ultrafast";
725
727
  responsesParallelToolCalls?: boolean;
726
728
  responsesSafetyIdentifier?: boolean;
727
729
  /** Default `tool_choice` to `auto` when the request carries tools and the caller picks none. */
728
730
  responsesToolChoiceAuto?: boolean;
729
731
  googleThinking?: "level" | "level-medium";
732
+ /** Send no `temperature`, `topP` or `topK` in `generationConfig`. */
733
+ googleOmitSampling?: boolean;
734
+ /** Send `generationConfig.maxOutputTokens` as the model's output maximum. */
735
+ googlePinMaxOutputTokens?: boolean;
736
+ /** Replay the model's own turns as the native Gemini content it streamed. */
737
+ googleNativeReplay?: boolean;
730
738
  /** The route locks its upstream for the session (`x-provider-routing-source: session_lock`). */
731
739
  routingSessionLock?: boolean;
732
740
  }
@@ -1297,6 +1297,21 @@ export interface DevinPlanInfo extends ProtoMessage {
1297
1297
  accountDisplayName: string;
1298
1298
  }
1299
1299
  export declare const DevinPlanInfoSchema: MessageCodec<DevinPlanInfo>;
1300
+ /** Devin message exa.seat_management_pb.ExchangePKCEAuthorizationCodeRequest. */
1301
+ export interface ExchangePKCEAuthorizationCodeRequest extends ProtoMessage {
1302
+ authorizationCode: string;
1303
+ codeVerifier: string;
1304
+ }
1305
+ export declare const ExchangePKCEAuthorizationCodeRequestSchema: MessageCodec<ExchangePKCEAuthorizationCodeRequest>;
1306
+ /** Devin message exa.seat_management_pb.ExchangePKCEAuthorizationCodeResponse. */
1307
+ export interface ExchangePKCEAuthorizationCodeResponse extends ProtoMessage {
1308
+ apiKey: string;
1309
+ name: string;
1310
+ apiServerUrl: string;
1311
+ devinWebappHost: string;
1312
+ devinApiUrl: string;
1313
+ }
1314
+ export declare const ExchangePKCEAuthorizationCodeResponseSchema: MessageCodec<ExchangePKCEAuthorizationCodeResponse>;
1300
1315
  /** Devin message exa.codeium_common_pb.ExperimentConfig. */
1301
1316
  export interface ExperimentConfig extends ProtoMessage {
1302
1317
  experiments: ExperimentWithVariant[];
@@ -10,6 +10,12 @@ import type { Api, FetchImpl, ModelSpec, Provider } from "../types.js";
10
10
  * (issue #8315). 10s matches the coding-agent's remote-discovery budget.
11
11
  */
12
12
  export declare const DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS = 10000;
13
+ /**
14
+ * Uses a cancellable timer rather than the native abort-timeout helper so
15
+ * successful fast discovery requests do not leave armed timeout signals for
16
+ * concurrent GC to trip over later.
17
+ */
18
+ export declare function withOpenAICompatibleDiscoveryTimeout<T>(timeoutMs: number, run: (signal: AbortSignal) => Promise<T>): Promise<T>;
13
19
  /**
14
20
  * Minimal OpenAI-style model entry shape consumed by discovery.
15
21
  *
@@ -110,6 +110,8 @@ export interface ModelResolutionResult<TApi extends Api = Api> {
110
110
  stale: boolean;
111
111
  source: ModelResolutionSource;
112
112
  updatedAt?: number;
113
+ /** Why the provider's endpoint fetcher threw this call; the models then come from cache, models.dev or bundled data. */
114
+ fetchError?: string;
113
115
  }
114
116
  /**
115
117
  * Stateful facade over provider model resolution.
@@ -11,6 +11,22 @@ export declare function getBundledProviders(): readonly KnownProvider[];
11
11
  export declare function getBundledModels(provider: GeneratedProvider): Model<Api>[];
12
12
  /** Resolve display semantics without mistaking an absent rate card for a free model. */
13
13
  export declare function getModelPricingStatus(model: Pick<Model<Api>, "cost" | "pricingStatus">): ModelPricingStatus;
14
+ /** Factory Droid credit rate in force at a time, and the next dated change to it. */
15
+ export interface FactoryDroidCreditRate {
16
+ rate: number;
17
+ /** The earliest later `credit-rates` card that changes `rate`: the rate it sets and when (Unix ms). */
18
+ next?: {
19
+ rate: number;
20
+ at: number;
21
+ };
22
+ }
23
+ /**
24
+ * Factory Droid Standard Credits rate at `timestamp` (Unix ms, default now):
25
+ * the `input` of the `credit-rates` card due then (the latest at or before
26
+ * it, as for cost cards), else the base rate. Undefined for models without a
27
+ * credit rate.
28
+ */
29
+ export declare function getFactoryDroidCreditRate(model: Pick<Model, "factoryDroidCredits" | "factoryDroidCreditCards">, timestamp?: number): FactoryDroidCreditRate | undefined;
14
30
  /**
15
31
  * The base token card in force at `timestamp` (Unix ms, default now): the
16
32
  * latest due `timeBased` effective rate, else the row's own legs. Price
@@ -1,24 +1,84 @@
1
1
  import type { EffectiveTokenCost, ModelCost, TimeBasedCost, TokenCost } from "./types.js";
2
+ /** Serialized time-based pricing schedule, as cached model rows carry it. */
3
+ export declare const TimeBasedCostShape: import("@oh-my-pi/omptype").FluentType<{
4
+ effectiveRates?: ({
5
+ cacheRead: number;
6
+ cacheWrite: number;
7
+ input: number;
8
+ output: number;
9
+ } & {
10
+ effectiveFrom: number;
11
+ longContext?: ({
12
+ cacheRead: number;
13
+ cacheWrite: number;
14
+ input: number;
15
+ output: number;
16
+ } & {
17
+ inputThreshold: number;
18
+ inputThresholdInclusive?: boolean | undefined;
19
+ }) | undefined;
20
+ })[] | undefined;
21
+ offPeakMultiplier: number;
22
+ peakWindows: {
23
+ endMinute: number;
24
+ startMinute: number;
25
+ weekdays: number[];
26
+ }[];
27
+ }, {
28
+ effectiveRates?: ({
29
+ cacheRead: number;
30
+ cacheWrite: number;
31
+ input: number;
32
+ output: number;
33
+ } & {
34
+ effectiveFrom: number;
35
+ longContext?: ({
36
+ cacheRead: number;
37
+ cacheWrite: number;
38
+ input: number;
39
+ output: number;
40
+ } & {
41
+ inputThreshold: number;
42
+ inputThresholdInclusive?: boolean | undefined;
43
+ }) | undefined;
44
+ })[] | undefined;
45
+ offPeakMultiplier: number;
46
+ peakWindows: {
47
+ endMinute: number;
48
+ startMinute: number;
49
+ weekdays: number[];
50
+ }[];
51
+ }>;
2
52
  /** Validate serialized schedules before admitting cached model rows. */
3
53
  export declare function isTimeBasedCost(value: unknown): value is TimeBasedCost;
4
54
  /** KDL object children are named records; normalize them once while constructing the model. */
5
55
  export declare function materializeTimeBasedCost(value: unknown): TimeBasedCost;
56
+ /**
57
+ * The dated card due at `timestamp`: the one with the latest `effectiveFrom`
58
+ * at or before it, in any order, or undefined before the first card.
59
+ */
60
+ export declare function dueDatedCard<T extends {
61
+ effectiveFrom: number;
62
+ }>(cards: readonly T[], timestamp: number): T | undefined;
6
63
  /**
7
64
  * The latest `effectiveRates` card of a materialized schedule due at
8
65
  * `timestamp`, or undefined before the first one (or without a schedule).
9
66
  */
10
67
  export declare function dueEffectiveRate(schedule: TimeBasedCost | undefined, timestamp: number): EffectiveTokenCost | undefined;
11
- /** A `cost-patch`/`cost-fallback` dated card: the legs it overwrites from `effectiveFrom` (Unix ms). */
68
+ /**
69
+ * A dated `effective-rates` card of a `cost-patch`, `cost-fallback` or
70
+ * `credit-rates` rule: the legs it overwrites from `effectiveFrom` (Unix ms).
71
+ */
12
72
  export interface DatedCostCard extends Partial<TokenCost> {
13
73
  effectiveFrom: number;
14
74
  }
15
75
  /**
16
- * Parse the compiled `effective-rates` block of a `cost-patch` or
17
- * `cost-fallback` payload into dated cards, ascending by date. Throws on a
18
- * malformed card, so a typo in a rule fails the build instead of silently
19
- * leaving the old price in force.
76
+ * Parse the compiled `effective-rates` block of a `cost-patch`,
77
+ * `cost-fallback` or Factory Droid `credit-rates` payload into dated cards,
78
+ * ascending by date. Throws on a malformed card, so a typo in a rule fails the
79
+ * build instead of silently leaving the old price in force.
20
80
  */
21
- export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback"): DatedCostCard[];
81
+ export declare function parseDatedCostCards(costAxis: unknown, axis: "cost-patch" | "cost-fallback" | "credit-rates"): DatedCostCard[];
22
82
  /**
23
83
  * Earliest instant a rate card can name (the `Date` range minimum). A leading
24
84
  * card from here holds the price in force before the first dated change.
@@ -740,3 +740,21 @@ export declare function singularityApiDevModelManagerOptions(config?: Singularit
740
740
  * reviewed KDL rules own the wire shape, limits patch, and effort ladder.
741
741
  */
742
742
  export declare function singularityApiTechModelManagerOptions(config?: SingularityApiModelManagerConfig): ModelManagerOptions<Api>;
743
+ /**
744
+ * Scaleway discovery configuration: the API key plus optional base-URL and
745
+ * fetch overrides. Consumed by {@link scalewayModelManagerOptions}, and
746
+ * exported for extensions and tests that construct the manager directly.
747
+ */
748
+ export interface ScalewayModelManagerConfig {
749
+ apiKey?: string;
750
+ baseUrl?: string;
751
+ fetch?: FetchImpl;
752
+ }
753
+ /**
754
+ * Scaleway model manager: plain OpenAI-compatible chat completions at
755
+ * `api.scaleway.ai/v1` (fr-par serverless Generative APIs). `/v1/models`
756
+ * returns bare ids, so the authored seed in `providers/scaleway.kdl` is the
757
+ * reference each live row merges names, modalities, prices, and limits from;
758
+ * a successful fetch is the complete roster and prunes retired rows.
759
+ */
760
+ export declare function scalewayModelManagerOptions(config?: ScalewayModelManagerConfig): ModelManagerOptions<"openai-completions">;
@@ -1,5 +1,6 @@
1
1
  import type { ModelIdentity } from "./compat/types.js";
2
2
  import type { Effort } from "./effort.js";
3
+ import type { DatedCostCard } from "./pricing.js";
3
4
  export type { FetchImpl } from "@oh-my-pi/pi-utils";
4
5
  export type { KnownProvider } from "./provider-models/descriptors.js";
5
6
  export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "factory-droid-agent" | "gitlab-duo-agent" | "devin-agent" | "apple-foundation-models";
@@ -492,6 +493,14 @@ export interface AnthropicCompat {
492
493
  * Sonnet 4.6+, Fable/Mythos 5+ on supported hosts. Default: false.
493
494
  */
494
495
  supportsServerCompaction?: boolean;
496
+ /**
497
+ * Whether the model supports message threads (`message-threads-2026-08-12`):
498
+ * later turns continue a server-held conversation and send only new
499
+ * messages. Enabled on Claude 4.5+ for the first-party provider; the
500
+ * transport still requires the official endpoint unless the caller opts in.
501
+ * Default: false.
502
+ */
503
+ supportsMessageThreads?: boolean;
495
504
  /**
496
505
  * Whether the model is served by the first-party Anthropic provider (its
497
506
  * default route is the official API). Rule-owned on the provider; the
@@ -954,6 +963,14 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
954
963
  * `PI_MUSE_STORE_RESPONSES`); stored runs retain prompts and outputs on the provider.
955
964
  */
956
965
  storeResponses: boolean;
966
+ /**
967
+ * Prefix the `openai-responses` handler puts on every function-tool name it
968
+ * declares or replays, and strips from returned calls through the request's
969
+ * wire-name table. Rule-owned: hosts that reject requests declaring certain
970
+ * exact tool names (Muse Code). Unset sends names unchanged. Other
971
+ * Responses-family handlers do not read it.
972
+ */
973
+ toolNamePrefix?: string;
957
974
  /**
958
975
  * Whether the host binds native history items (`encrypted_content`, item
959
976
  * ids) to the connection that issued them, so a new process must rebuild
@@ -1198,12 +1215,12 @@ export interface ModelCost extends TokenCost {
1198
1215
  /**
1199
1216
  * Exact local content tokenizer family for a model.
1200
1217
  *
1201
- * Absent means no first-party local tokenizer is known and consumers retain
1202
- * their estimate/default-tokenizer policy. The values name tokenizer
1218
+ * Absent means no first-party local tokenizer is known and consumers count
1219
+ * in o200k_base. The values name tokenizer
1203
1220
  * generations rather than providers: DeepSeek V3 through V4 share
1204
1221
  * `"deepseek-v3"`; Kimi K2 through K3 share `"kimi-k2"`.
1205
1222
  */
1206
- export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "o200k";
1223
+ export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "cl100k" | "o200k";
1207
1224
  /** One account's discovered entitlements on a model; see {@link Model.accountAccess}. */
1208
1225
  export interface ModelAccountAccess {
1209
1226
  /**
@@ -1393,8 +1410,12 @@ export interface Model<TApi extends Api = Api> {
1393
1410
  factoryDroidApiProviders?: string[];
1394
1411
  /** Factory Droid: `configured_order` when live routing chose the rotation; absent means registry order. */
1395
1412
  factoryDroidRoutingSource?: "configured_order";
1413
+ /** Factory Droid: server-side refusal fallbacks the account can run; absent when none are named. */
1414
+ factoryDroidRefusalFallbacks?: string[];
1396
1415
  /** Factory Droid Standard Credits base rate (relative per-token weight, not dollars). */
1397
1416
  factoryDroidCredits?: number;
1417
+ /** Factory Droid dated `credit-rates` cards, ascending; `getFactoryDroidCreditRate` picks the one due now. */
1418
+ factoryDroidCreditCards?: readonly DatedCostCard[];
1398
1419
  /** Canonical organization resolved during Factory discovery. */
1399
1420
  factoryDroidOrgId?: string;
1400
1421
  cost: ModelCost;
@@ -1461,7 +1482,7 @@ export interface Model<TApi extends Api = Api> {
1461
1482
  * dispatch is redirected.
1462
1483
  */
1463
1484
  transport?: "pi-native";
1464
- /** Hint that websocket transport should be preferred when supported by the provider implementation. */
1485
+ /** Standard Responses is opt-in and catalog-gated; either model/request false vetoes true. Codex keeps its independent policy. */
1465
1486
  preferWebsockets?: boolean;
1466
1487
  /** Codex Responses Lite transport: send the lite marker and carry instructions/tools as input items (mirrors codex-rs `use_responses_lite`). */
1467
1488
  useResponsesLite?: boolean;
@@ -46,7 +46,6 @@ export declare const OPENAI_HEADERS: {
46
46
  readonly ROUTING_HINT: "x-codex-routing-hint";
47
47
  };
48
48
  export declare const OPENAI_HEADER_VALUES: {
49
- readonly BETA_RESPONSES: "responses=experimental";
50
49
  readonly BETA_RESPONSES_WEBSOCKETS_V2: "responses_websockets=2026-02-06";
51
50
  readonly REMOTE_COMPACTION_V2: "remote_compaction_v2";
52
51
  readonly ORIGINATOR_CODEX: "omp";
@@ -3,7 +3,7 @@
3
3
  * native request corpus with `packages/ai/scripts/capture-factory-droid-native.ts`
4
4
  * against the matching CLI binary and make `factory-droid-native-parity.test.ts` pass.
5
5
  */
6
- export declare const FACTORY_DROID_CLIENT_VERSION = "0.237.0";
6
+ export declare const FACTORY_DROID_CLIENT_VERSION = "1.1.0";
7
7
  /** Wire protocols the Factory proxy multiplexes; `api-routes provider="factory-droid"` picks one per model. */
8
8
  export declare const FACTORY_DROID_WIRES: readonly ["openai-completions", "openai-responses", "anthropic-messages", "google-generate"];
9
9
  /** Wire protocol used by the Factory proxy for each model. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-catalog",
3
- "version": "18.15.0",
3
+ "version": "18.17.0",
4
4
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
5
5
  "keywords": [
6
6
  "ai",
@@ -78,7 +78,7 @@
78
78
  },
79
79
  "scripts": {
80
80
  "check": "oxlint . && oxfmt --check --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts' && bun run check:types",
81
- "check:types": "tsgo -p tsconfig.json --noEmit",
81
+ "check:types": "bun check --no-script -p tsconfig.json",
82
82
  "lint": "oxlint .",
83
83
  "test": "bun test --parallel",
84
84
  "fix": "oxlint --fix --fix-suggestions . && bun run fmt",
@@ -88,12 +88,12 @@
88
88
  "gen:proto": "bun scripts/generate-protocols.ts"
89
89
  },
90
90
  "dependencies": {
91
- "@oh-my-pi/omptype": "18.15.0",
92
- "@oh-my-pi/pi-utils": "18.15.0"
91
+ "@oh-my-pi/omptype": "18.17.0",
92
+ "@oh-my-pi/pi-utils": "18.17.0"
93
93
  },
94
94
  "devDependencies": {
95
95
  "@bgotink/kdl": "0.4.0",
96
- "@oh-my-pi/pi-ai": "18.15.0",
96
+ "@oh-my-pi/pi-ai": "18.17.0",
97
97
  "@types/bun": "^1.3.14"
98
98
  },
99
99
  "engines": {
@@ -25,6 +25,8 @@ export type AuthProviderId =
25
25
  | "deepinfra"
26
26
  | "deepseek"
27
27
  | "devin"
28
+ | "devin-cli"
29
+ | "devin-windsurf"
28
30
  | "exa"
29
31
  | "factory-droid"
30
32
  | "firepass"
@@ -73,6 +75,7 @@ export type AuthProviderId =
73
75
  | "qwen-portal"
74
76
  | "sakana"
75
77
  | "sambanova"
78
+ | "scaleway"
76
79
  | "siliconflow"
77
80
  | "siliconflow-cn"
78
81
  | "singularityapi-dev"
@@ -122,6 +125,8 @@ export type LoginProviderId =
122
125
  | "deepinfra"
123
126
  | "deepseek"
124
127
  | "devin"
128
+ | "devin-cli"
129
+ | "devin-windsurf"
125
130
  | "exa"
126
131
  | "factory-droid"
127
132
  | "firepass"
@@ -144,6 +149,7 @@ export type LoginProviderId =
144
149
  | "meta"
145
150
  | "minimax-code"
146
151
  | "minimax-code-cn"
152
+ | "mistral"
147
153
  | "modular"
148
154
  | "moonshot"
149
155
  | "muse-code"
@@ -163,6 +169,7 @@ export type LoginProviderId =
163
169
  | "qwen-portal"
164
170
  | "sakana"
165
171
  | "sambanova"
172
+ | "scaleway"
166
173
  | "siliconflow"
167
174
  | "siliconflow-cn"
168
175
  | "singularityapi-dev"
@@ -109,6 +109,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
109
109
  "preserved",
110
110
  "interleaved",
111
111
  ]),
112
+ "completions-allow-missing-thinking": wire("completionsAllowMissingThinking", ["request"]),
112
113
  "anthropic-thinking": wire("anthropicThinking", ["request"], "scalar", [
113
114
  "adaptive",
114
115
  "adaptive-summarized",
@@ -119,12 +120,15 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
119
120
  "openai-platform-header": wire("openaiPlatformHeader", ["request"]),
120
121
  "responses-cache-retention": wire("responsesCacheRetention", ["request"]),
121
122
  "responses-verbosity": wire("responsesVerbosity", ["request"], "scalar", ["low"]),
122
- "responses-service-tier": wire("responsesServiceTier", ["request"], "scalar", ["priority"]),
123
+ "responses-service-tier": wire("responsesServiceTier", ["request"], "scalar", ["priority", "ultrafast"]),
123
124
  "responses-parallel-tool-calls": wire("responsesParallelToolCalls", ["request"]),
124
125
  "responses-safety-identifier": wire("responsesSafetyIdentifier", ["request"]),
125
126
  "responses-tool-choice-auto": wire("responsesToolChoiceAuto", ["request"]),
126
127
  "routing-session-lock": wire("routingSessionLock", ["request"]),
127
128
  "google-thinking": wire("googleThinking", ["request"], "scalar", ["level", "level-medium"]),
129
+ "google-omit-sampling": wire("googleOmitSampling", ["request"]),
130
+ "google-pin-max-output-tokens": wire("googlePinMaxOutputTokens", ["request"]),
131
+ "google-native-replay": wire("googleNativeReplay", ["request"]),
128
132
  // ── wire: OpenAI-compatible surfaces (chat completions + Responses) ──
129
133
  "allows-synthetic-reasoning-content-for-tool-calls": wire("allowsSyntheticReasoningContentForToolCalls", OAI),
130
134
  "always-send-max-tokens": wire("alwaysSendMaxTokens", OAI),
@@ -206,6 +210,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
206
210
  "supports-reasoning-params": wire("supportsReasoningParams", OAI),
207
211
  "supports-reasoning-summary": wire("supportsReasoningSummary", ["openai-responses"]),
208
212
  "store-responses": wire("storeResponses", ["openai-responses"]),
213
+ "tool-name-prefix": wire("toolNamePrefix", ["openai-responses"], "scalar", ["_"]),
209
214
  "supports-store": wire("supportsStore", ["openai"]),
210
215
  "supports-strict-mode": wire("supportsStrictMode", OAI),
211
216
  "supports-tool-choice": wire("supportsToolChoice", OAI),
@@ -270,6 +275,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
270
275
  "supports-mid-conversation-tool-changes": wire("supportsMidConversationToolChanges", ["anthropic"]),
271
276
  "supports-per-message-effort": wire("supportsPerMessageEffort", ["anthropic"]),
272
277
  "supports-server-compaction": wire("supportsServerCompaction", ["anthropic", "bedrock"]),
278
+ "supports-message-threads": wire("supportsMessageThreads", ["anthropic"]),
273
279
  "supports-between-tools-thinking": wire("supportsBetweenToolsThinking", ["anthropic"]),
274
280
  "supports-thinking-binding-controls": wire("supportsThinkingBindingControls", ["anthropic", "bedrock"]),
275
281
  "supports-turn-scoped-system": wire("supportsTurnScopedSystem", ["anthropic"]),
@@ -338,6 +344,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
338
344
  "thinking-unverified": { key: "unverified", set: "thinking", shape: "scalar" },
339
345
 
340
346
  // ── catalog metadata ──
347
+ "supports-websockets": { key: "supportsWebsockets", set: "catalog", shape: "scalar", values: [true, false] },
341
348
  "apply-patch-tool-type": {
342
349
  key: "applyPatchToolType",
343
350
  set: "catalog",
@@ -672,7 +679,9 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
672
679
  "region-limits-eu": { key: "regionLimitsEu", set: "catalog", shape: "object" },
673
680
  /**
674
681
  * Subscription credit rates: `input` is the per-token credit weight shown
675
- * as the model's multiplier; `output` and `cache-read` multiply it.
682
+ * as the model's multiplier; `output` and `cache-read` multiply it. An
683
+ * `effective-rates` block of dated cards overwrites them from each card's
684
+ * `effective-from`, as on `cost-patch`.
676
685
  */
677
686
  "credit-rates": { key: "creditRates", set: "catalog", shape: "object" },
678
687
  /**