@oh-my-pi/pi-catalog 18.8.8 → 18.9.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.9.0] - 2026-10-11
6
+
7
+ ### Fixed
8
+
9
+ - Fixed GLM-5.3-FlashX on Zhipu Coding Plan so bundled context, image, and reasoning-level capabilities are reported correctly.
10
+ - Fixed OpenRouter Decisions endpoint derivation when the chat base URL has a trailing slash.
11
+ - Fixed GPT-6.1 Sol Ultrafast usage pricing to apply OpenAI's published premium rates for the OpenAI API and Codex included usage.
12
+ - Fixed GPT-6 Sol, GPT-6.1 Sol, and GPT-6 Luna so changing reasoning effort preserves prompt caching.
13
+
5
14
  ## [18.8.8] - 2026-10-10
6
15
 
7
16
  ### Added
@@ -0,0 +1,21 @@
1
+ import type { ModelIdentity } from "./types.js";
2
+ /**
3
+ * The identity a deployment's reasoning is carried under, or `undefined` when
4
+ * the deployment's model family is not declared portable there. Class-only
5
+ * identities never qualify: they name a lineage, not a set of weights.
6
+ */
7
+ export declare function portableReasoningIdentity(provider: string, api: string, modelId: string): ModelIdentity | undefined;
8
+ /**
9
+ * Whether reasoning recorded on `source` may replay natively on `target`:
10
+ * both deployments declare the family portable and classify to the same
11
+ * class, family, and revision.
12
+ */
13
+ export declare function carriesReasoning(source: {
14
+ provider: string;
15
+ api: string;
16
+ model: string;
17
+ }, target: {
18
+ provider: string;
19
+ api: string;
20
+ id: string;
21
+ }): boolean;
@@ -352,6 +352,11 @@ export interface OpenRouterModelManagerConfig {
352
352
  baseUrl?: string;
353
353
  fetch?: FetchImpl;
354
354
  }
355
+ /**
356
+ * OpenRouter's Decisions API lives at `/api/alpha`, a sibling of the `/api/v1`
357
+ * chat root; derive it so a custom gateway base URL keeps both aligned.
358
+ */
359
+ export declare function openrouterDecisionsBaseUrl(chatBaseUrl: string): string;
355
360
  export declare function openrouterModelManagerOptions(config?: OpenRouterModelManagerConfig): ModelManagerOptions<Api>;
356
361
  export interface ZenMuxModelManagerConfig {
357
362
  apiKey?: string;
@@ -340,6 +340,8 @@ export interface OpenAICompat {
340
340
  extraBody?: Record<string, unknown>;
341
341
  /** Request-session header that should mirror the normalized prompt-cache key. Default: unset. */
342
342
  promptCacheSessionHeader?: "x-grok-conv-id";
343
+ /** Request header carrying the conversation session id, independent of prompt caching. Default: unset. */
344
+ sessionHeader?: "x-litellm-session-id";
343
345
  /** Whether chat-completions payloads should include provider-specific prompt-cache markers. */
344
346
  cacheControlFormat?: "anthropic" | undefined;
345
347
  /**
@@ -423,8 +425,8 @@ export interface OpenAICompat {
423
425
  /**
424
426
  * Whether the Responses endpoint accepts `configuration_update` input items
425
427
  * that change `reasoning.effort` mid-conversation while the request-level
426
- * effort stays pinned for prompt caching (GPT-6 Astra). Default:
427
- * rule-detected (`true` for `gpt-6-astra` on any host, `false` otherwise).
428
+ * effort stays pinned for prompt caching (GPT-6 family). Default:
429
+ * rule-detected (`true` for GPT-6 models on any host, `false` otherwise).
428
430
  * Set `false` for custom `openai-responses` / `openai-codex-responses`
429
431
  * endpoints that reject the item type with HTTP 400; effort changes are then
430
432
  * sent as the top-level `reasoning.effort`.
@@ -621,12 +623,31 @@ export interface AnthropicCompat {
621
623
  injectClaudeCodeInstruction?: boolean;
622
624
  /** Strip image inputs before encoding (text-only serving of a multimodal id). */
623
625
  stripImageInput?: boolean;
626
+ /**
627
+ * Largest width or height a single image block may carry. The canonical
628
+ * Anthropic API rejects anything larger with `At least one of the image
629
+ * dimensions exceed max allowed size: 8000 pixels`, and the rejection
630
+ * poisons every retry and model fallback because the block stays in
631
+ * context, so the request builder downscales to this value first. Hosts
632
+ * with a different image contract override it. Default: 8000.
633
+ */
634
+ maxImageDimension?: number;
635
+ /**
636
+ * Largest base64 payload a single image block may carry, in bytes. The
637
+ * canonical Anthropic API measures the encoded string, not the decoded
638
+ * bytes (`image exceeds 10 MB maximum: 14012300 bytes > 10485760 bytes`),
639
+ * and an image inside {@link maxImageDimension} can still cross it, so
640
+ * payload size is clamped on its own. Hosts with a different image
641
+ * contract override it. Default: 10485760.
642
+ */
643
+ maxImagePayloadBytes?: number;
624
644
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
625
645
  thinkingLoopGuard?: "gemini" | "deepseek" | "xai";
626
646
  /**
627
647
  * Drop the enabled `thinking` config and replayed thinking blocks when the
628
648
  * conversation is not thinking-led (a proxy contract for non-interleaved
629
- * budget models). Default: false.
649
+ * budget models), and replay history without thinking blocks on every turn
650
+ * that sends `thinking: { type: "disabled" }`. Default: false.
630
651
  */
631
652
  stripThinkingHistory?: boolean;
632
653
  /**
@@ -636,10 +657,15 @@ export interface AnthropicCompat {
636
657
  effortBeta?: boolean;
637
658
  /**
638
659
  * Wire form of a disabled-thinking turn: omit the field, send
639
- * `{ type: "disabled" }`, or keep adaptive thinking. Unset keeps the
640
- * direct-provider behavior.
660
+ * `{ type: "disabled" }`, keep adaptive thinking, or send
661
+ * `{ type: "between_tools" }`. Unset keeps the direct-provider behavior.
662
+ */
663
+ disabledThinking?: "omit" | "disabled" | "adaptive" | "between-tools";
664
+ /**
665
+ * `output_config.effort` pinned on a `between_tools` turn. Unset leaves
666
+ * the turn's effort untouched.
641
667
  */
642
- disabledThinking?: "omit" | "disabled" | "adaptive";
668
+ betweenToolsEffort?: "low" | "medium" | "high";
643
669
  /** The model is a fast-mode SKU: send `speed: "fast"` with the fast-mode beta. Default: false. */
644
670
  fastMode?: boolean;
645
671
  }
@@ -787,6 +813,7 @@ export interface ResolvedOpenAISharedCompat {
787
813
  emptyLengthFinishIsContextError: boolean;
788
814
  usesOpenAIToolCallIdLimit: boolean;
789
815
  promptCacheSessionHeader?: OpenAICompat["promptCacheSessionHeader"];
816
+ sessionHeader?: OpenAICompat["sessionHeader"];
790
817
  /**
791
818
  * Whether this model accepts explicit OpenAI prompt-cache breakpoints.
792
819
  * Built catalog models always materialize this false-by-default value;
@@ -821,7 +848,7 @@ export interface ResolvedOpenAISharedCompat {
821
848
  * `buildModel`; request handlers read fields and never detect, resolve, or
822
849
  * allocate.
823
850
  */
824
- 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" | "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">> & {
851
+ 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">> & {
825
852
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
826
853
  extraBody?: OpenAICompat["extraBody"];
827
854
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -851,8 +878,8 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
851
878
  /**
852
879
  * Whether a `configuration_update` input item may change `reasoning.effort`
853
880
  * mid-conversation while the request-level effort stays byte-stable for
854
- * prompt caching. Rule-owned: GPT-6 Astra only; every other model rejects
855
- * the item type with 400.
881
+ * prompt caching. Rule-owned: the GPT-6 family; earlier models do not
882
+ * support the item type.
856
883
  */
857
884
  supportsConfigurationUpdate: boolean;
858
885
  /**
@@ -908,15 +935,28 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
908
935
  */
909
936
  export type ResolvedOpenRouterCompat = ResolvedOpenAICompat & ResolvedOpenAIResponsesCompat;
910
937
  /** Fully-resolved anthropic-messages compat view (same contract as `ResolvedOpenAICompat`). */
911
- export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard" | "bedrockMessagesApi" | "effortBeta" | "disabledThinking" | "stripThinkingHistory" | "fastMode">> & {
938
+ export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard" | "bedrockMessagesApi" | "effortBeta" | "disabledThinking" | "betweenToolsEffort" | "stripThinkingHistory" | "fastMode" | "maxImageDimension" | "maxImagePayloadBytes">> & {
912
939
  /** Effort-beta override; undefined keeps the transport's legacy heuristic. */
913
940
  effortBeta?: AnthropicCompat["effortBeta"];
914
941
  /** Disabled-thinking wire form; undefined keeps the direct-provider behavior. */
915
942
  disabledThinking?: AnthropicCompat["disabledThinking"];
916
- /** Strip thinking history on non-thinking-led turns; undefined behaves as false. */
943
+ /** Effort pinned on `between_tools` turns; undefined leaves effort untouched. */
944
+ betweenToolsEffort?: AnthropicCompat["betweenToolsEffort"];
945
+ /** Strip thinking history on non-thinking-led and disabled-thinking turns; undefined behaves as false. */
917
946
  stripThinkingHistory?: AnthropicCompat["stripThinkingHistory"];
918
947
  /** Fast-mode SKU; undefined behaves as false. */
919
948
  fastMode?: AnthropicCompat["fastMode"];
949
+ /**
950
+ * Largest width or height a single image block may carry. Undefined defers
951
+ * to the canonical Anthropic API limit applied by the request builder.
952
+ */
953
+ maxImageDimension?: number;
954
+ /**
955
+ * Largest base64 image payload a single block may carry, in bytes.
956
+ * Undefined defers to the canonical Anthropic API limit applied by the
957
+ * request builder.
958
+ */
959
+ maxImagePayloadBytes?: number;
920
960
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
921
961
  thinkingLoopGuard?: AnthropicCompat["thinkingLoopGuard"];
922
962
  /**
@@ -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.230.0";
6
+ export declare const FACTORY_DROID_CLIENT_VERSION = "0.237.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.8.8",
3
+ "version": "18.9.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",
@@ -93,12 +93,12 @@
93
93
  "gen:proto": "bun scripts/generate-protocols.ts"
94
94
  },
95
95
  "dependencies": {
96
- "@oh-my-pi/omptype": "18.8.8",
97
- "@oh-my-pi/pi-utils": "18.8.8"
96
+ "@oh-my-pi/omptype": "18.9.0",
97
+ "@oh-my-pi/pi-utils": "18.9.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@bgotink/kdl": "0.4.0",
101
- "@oh-my-pi/pi-ai": "18.8.8",
101
+ "@oh-my-pi/pi-ai": "18.9.0",
102
102
  "@types/bun": "^1.3.14"
103
103
  },
104
104
  "engines": {
@@ -140,6 +140,7 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
140
140
  "qwen-preserve-thinking": wire("qwenPreserveThinking", ["openai"]),
141
141
  "reject-root-object-union": wire("rejectRootObjectUnion", OAI),
142
142
  "retry-without-strict-on-grammar-error": wire("retryWithoutStrictOnGrammarError", OAI),
143
+ "session-header": wire("sessionHeader", OAI, "scalar", ["x-litellm-session-id"]),
143
144
  "reasoning-content-field": wire("reasoningContentField", OAI, "scalar", [
144
145
  "reasoning_content",
145
146
  "reasoning",
@@ -230,12 +231,20 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
230
231
  "allow-anthropic-header-overrides": wire("allowAnthropicHeaderOverrides", ["anthropic"]),
231
232
  "disable-adaptive-thinking": wire("disableAdaptiveThinking", ["anthropic"]),
232
233
  "disable-strict-tools": wire("disableStrictTools", ["anthropic"]),
233
- "disabled-thinking": wire("disabledThinking", ["anthropic", "bedrock"], "scalar", ["omit", "disabled", "adaptive"]),
234
+ "between-tools-effort": wire("betweenToolsEffort", ["anthropic"], "scalar", ["low", "medium", "high"]),
235
+ "disabled-thinking": wire("disabledThinking", ["anthropic", "bedrock"], "scalar", [
236
+ "omit",
237
+ "disabled",
238
+ "adaptive",
239
+ "between-tools",
240
+ ]),
234
241
  "effort-beta": wire("effortBeta", ["anthropic"]),
235
242
  "escape-builtin-tool-names": wire("escapeBuiltinToolNames", ["anthropic"]),
236
243
  "fast-mode": wire("fastMode", ["anthropic"]),
237
244
  "first-party-provider": wire("firstPartyProvider", ["anthropic"]),
238
245
  "inject-claude-code-instruction": wire("injectClaudeCodeInstruction", ["anthropic"]),
246
+ "max-image-dimension": wire("maxImageDimension", ["anthropic"]),
247
+ "max-image-payload-bytes": wire("maxImagePayloadBytes", ["anthropic"]),
239
248
  "official-endpoint": wire("officialEndpoint", ["anthropic", "openai-responses"]),
240
249
  "replay-unsigned-thinking": wire("replayUnsignedThinking", ["anthropic"]),
241
250
  "requires-thinking-enabled": wire("requiresThinkingEnabled", ["anthropic"]),
@@ -408,6 +417,18 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
408
417
  "prompt-cache-lookback": { key: "promptCacheLookback", set: "catalog", shape: "scalar" },
409
418
  "long-usage-limit-fallback": { key: "longUsageLimitFallback", set: "catalog", shape: "scalar" },
410
419
  "max-context-window": { key: "maxContextWindow", set: "catalog", shape: "scalar" },
420
+ /**
421
+ * Every host of the model returns its full plaintext trace wherever the
422
+ * wire does not mark reasoning as a summary (`ThinkingContent.summary ===
423
+ * false` vouches for nothing more), and its identity (class + family +
424
+ * revision) pins one set of weights, so a turn's reasoning may replay
425
+ * natively on another host serving the same model. Marked summaries never
426
+ * carry; do not declare it where a host serves summaries unmarked.
427
+ * Declared per family in `classes/*.kdl`; a host whose reasoning must not be
428
+ * carried in or out sets `#false` in its `providers/*.kdl`. Read through
429
+ * `compat/reasoning-carry.ts`.
430
+ */
431
+ "portable-reasoning": { key: "portableReasoning", set: "catalog", shape: "scalar", values: [true, false] },
411
432
  "pricing-status": {
412
433
  key: "pricingStatus",
413
434
  set: "catalog",
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Reasoning carry: whether reasoning a model produced on one host may replay
3
+ * natively on another host serving the same model.
4
+ *
5
+ * The model-side half is declared with the `portable-reasoning` catalog axis
6
+ * per family in `rules/classes/*.kdl`: the family's stored reasoning is its own
7
+ * full plaintext trace, and its identity pins one set of weights. A host whose
8
+ * reasoning must neither be carried in nor out overrides it to `#false` in its
9
+ * `rules/providers/*.kdl`. Whether the target host has a native slot for
10
+ * earlier reasoning is derived by the request encoders in `@oh-my-pi/pi-ai`,
11
+ * not declared here.
12
+ *
13
+ * Resolved through the cascade on demand (like delegation bias) because
14
+ * bundled rows are frozen by the generator; results are memoized per
15
+ * deployment.
16
+ */
17
+ import { resolveCascade } from "./cascade";
18
+ import { classifyModel } from "./taxonomy";
19
+ import type { ModelIdentity } from "./types";
20
+
21
+ /** `null` records a deployment that does not carry reasoning. */
22
+ const portableIdentityMemo = new Map<string, ModelIdentity | null>();
23
+
24
+ /**
25
+ * The identity a deployment's reasoning is carried under, or `undefined` when
26
+ * the deployment's model family is not declared portable there. Class-only
27
+ * identities never qualify: they name a lineage, not a set of weights.
28
+ */
29
+ export function portableReasoningIdentity(provider: string, api: string, modelId: string): ModelIdentity | undefined {
30
+ const key = `${provider}\0${api}\0${modelId}`;
31
+ const memo = portableIdentityMemo.get(key);
32
+ if (memo !== undefined) return memo ?? undefined;
33
+ const identity = classifyModel(provider, modelId, { lenient: true });
34
+ let portable: ModelIdentity | null = null;
35
+ if (identity.family !== undefined || identity.revision !== undefined) {
36
+ const declared = resolveCascade({
37
+ provider,
38
+ api,
39
+ class: identity.class,
40
+ model: modelId,
41
+ reasoning: true,
42
+ ...(identity.family !== undefined && { family: identity.family }),
43
+ ...(identity.revision !== undefined && { revision: identity.revision }),
44
+ }).catalog.portableReasoning;
45
+ if (declared === true) portable = identity;
46
+ }
47
+ portableIdentityMemo.set(key, portable);
48
+ return portable ?? undefined;
49
+ }
50
+
51
+ /**
52
+ * Whether reasoning recorded on `source` may replay natively on `target`:
53
+ * both deployments declare the family portable and classify to the same
54
+ * class, family, and revision.
55
+ */
56
+ export function carriesReasoning(
57
+ source: { provider: string; api: string; model: string },
58
+ target: { provider: string; api: string; id: string },
59
+ ): boolean {
60
+ const to = portableReasoningIdentity(target.provider, target.api, target.id);
61
+ if (!to) return false;
62
+ const from = portableReasoningIdentity(source.provider, source.api, source.model);
63
+ return from !== undefined && from.class === to.class && from.family === to.family && from.revision === to.revision;
64
+ }
@@ -915,6 +915,11 @@ function resolveAnthropicPolicy(
915
915
  escapeBuiltinToolNames: false,
916
916
  injectClaudeCodeInstruction: true,
917
917
  stripImageInput: false,
918
+ // Unset means "canonical Anthropic image contract"; the request builder
919
+ // owns those numbers. The keys must exist for a KDL rule or a
920
+ // user-authored `compat` block to override them.
921
+ maxImageDimension: undefined,
922
+ maxImagePayloadBytes: undefined,
918
923
  thinkingLoopGuard: undefined,
919
924
  // Present as keys so models.yml `compat` can set them; unset unless a rule assigns them.
920
925
  stripThinkingHistory: undefined,
@@ -98,4 +98,10 @@ class "deepseek" {
98
98
  // providers alike. On hosts that parse it the healer sees no markup.
99
99
  stream-markup-healing-pattern "dsml"
100
100
  thinking-loop-guard "deepseek"
101
+ // V4 reasoning reaches omp as its plaintext trace on every host, and the
102
+ // revision pins the weights (flash 4.0 / 4.1, pro 4.0). Earlier lineages
103
+ // classify too coarsely to carry: `r1` also matches the R1 distills.
104
+ revision ">=4" {
105
+ portable-reasoning #true
106
+ }
101
107
  }
@@ -65,7 +65,7 @@ class "glm" {
65
65
  // GLM Coding Plan: the selectable 1M-context served ids; pin them so
66
66
  // endpoint discovery or older bundled fallbacks cannot regress to 200k.
67
67
  on "zai" "zhipu-coding-plan" {
68
- models "glm-5.2" "glm-5.3" "glm-5.3-flash" {
68
+ models "glm-5.2" "glm-5.3" "glm-5.3-flash" "glm-5.3-flashx" {
69
69
  limits-patch {
70
70
  context-window 1000000
71
71
  max-tokens 131072
@@ -84,10 +84,9 @@ class "glm" {
84
84
  }
85
85
  }
86
86
  }
87
- // glm-5.3-flash ships native image input that upstream metadata still
88
- // reports as text-only.
87
+ // GLM-5.3 Flash and FlashX accept native image input on their coding routes.
89
88
  on "zai" "zhipu-coding-plan" {
90
- models "glm-5.3-flash" {
89
+ models "glm-5.3-flash" "glm-5.3-flashx" {
91
90
  input-modalities "text" "image"
92
91
  }
93
92
  }
@@ -99,17 +98,27 @@ class "glm" {
99
98
  thinking-efforts "minimal" "low" "medium" "high"
100
99
  }
101
100
  }
102
- // GLM-5.3+ coding SKUs (base/air/turbo/flash, non-vision) expose a uniform
101
+ // GLM-5.3+ coding SKUs (base/air/turbo/flash/flashx) expose a uniform
103
102
  // wire-exact low/high/max reasoning-effort ladder on every host, thinking
104
103
  // can no longer be disabled, and the default effort is max.
105
104
  // residue: taxonomy ranks cannot separate these ids from the vision (`…v`)
106
- // and flashx/preview shapes that keep host dialects; the explicit priority resolves
105
+ // and preview shapes that keep host dialects; the explicit priority resolves
107
106
  // the intentional overlap with census host-dialect residues such as
108
107
  // baseten's `zai-org/GLM-5*`.
109
- models "*glm-5.3" "*glm-5.3-air" "*glm-5.3-turbo" "*glm-5.3-flash" "*glm-5.3:*" \
108
+ models "*glm-5.3" "*glm-5.3-air" "*glm-5.3-turbo" "*glm-5.3-flash" "*glm-5.3-flashx" "*glm-5.3:*" \
110
109
  "*glm-5.3-flash-uncensored" "*glm-5.3-free" "*glm-5.3-highspeed" "*glm-5.3-fast" "*glm-5.3-flash-lab" priority=10 {
111
110
  thinking-default-level "max"
112
111
  thinking-efforts "low" "high" "max"
113
112
  thinking-requires-effort #true
114
113
  }
114
+ // GLM 5+ reasoning reaches omp as its plaintext trace on every host, and
115
+ // family plus revision pin the weights (glm-5.2, glm-5.3-flash, ...). GLM 4
116
+ // classifies too coarsely to carry: 4.0 also matches glm-4.5v and glm-4.6v.
117
+ revision ">=5" {
118
+ portable-reasoning #true
119
+ // residue: fine-tuned weights that classify as the base GLM revision.
120
+ models "*-uncensored" "*-cybersecurity" {
121
+ portable-reasoning #false
122
+ }
123
+ }
115
124
  }
@@ -123,4 +123,12 @@ class "kimi" {
123
123
  }
124
124
  }
125
125
  }
126
+ // K3 reasoning reaches omp as its plaintext trace on every host, and K3 was
127
+ // trained to read all of its earlier reasoning back (preserved thinking).
128
+ // A recall probe showed the reasoning slot of moonshot, factory-droid,
129
+ // openrouter and devin reaches the model, so K3 reasoning moves between
130
+ // those hosts natively instead of as visible `<think>` text.
131
+ family "k3" {
132
+ portable-reasoning #true
133
+ }
126
134
  }
@@ -98,4 +98,14 @@ class "minimax" {
98
98
  on "minimax-code" "minimax-code-cn" "wafer-serverless" {
99
99
  reasoning-content-field "reasoning_content"
100
100
  }
101
+ // M3 reasoning reaches omp as its plaintext trace on every host, and
102
+ // MiniMax asks agents to pass all earlier reasoning back.
103
+ family "m3" {
104
+ portable-reasoning #true
105
+ // residue: M3.1 Flash Preview classifies as family m3 (no revision is
106
+ // extracted), but it is a different model.
107
+ models "*m3.1*" {
108
+ portable-reasoning #false
109
+ }
110
+ }
101
111
  }
@@ -49,10 +49,12 @@ class "openai" {
49
49
  delegation-bias "restrained"
50
50
  supports-steering #true
51
51
  }
52
- // GPT-6 Astra changes reasoning effort mid-conversation through a
52
+ // The GPT-6 family changes reasoning effort mid-conversation through a
53
53
  // `configuration_update` input item so the request-level effort (and the
54
- // cached prompt prefix) stays put; every other SKU 400s on the item type.
55
- models "gpt-6-astra" {
54
+ // cached prompt prefix) stays put. OpenAI's reasoning guide ("Change
55
+ // reasoning mid-conversation") documents it for the family, and Codex's
56
+ // `/models` reports `supports_reasoning_effort_updates` on every GPT-6 SKU.
57
+ revision ">=6 <7" {
56
58
  supports-configuration-update #true
57
59
  }
58
60
  // Codex SKUs: the advertised 400K figure includes the output budget; the
@@ -37,4 +37,11 @@ provider "abliteration" {
37
37
 
38
38
  include-encrypted-reasoning #false
39
39
  stream-idle-timeout-ms 0
40
+ // Abliterated weights classify as the GLM revision they derive from, but
41
+ // they are modified weights: their reasoning is not carried to or from GLM.
42
+ class "glm" {
43
+ revision ">=5" {
44
+ portable-reasoning #false
45
+ }
46
+ }
40
47
  }
@@ -36,6 +36,10 @@ provider "cursor" {
36
36
  input-modalities "text" "image"
37
37
  context-window-floor 1000000
38
38
  }
39
+ // Cursor's agent protocol relays thinking opaquely, so it is not
40
+ // verified as K3's full trace: Cursor K3 turns are not carried to
41
+ // other K3 hosts (Cursor itself refuses foreign history).
42
+ portable-reasoning #false
39
43
  }
40
44
  family "k2.7-code" {
41
45
  // Cursor advertised Kimi K2.7 Code Context window 262k.