@oh-my-pi/pi-catalog 15.10.11 → 15.11.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,26 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [15.11.0] - 2026-06-10
6
+
7
+ ### Fixed
8
+
9
+ - Fixed `buildModel` so malformed explicit thinking metadata without `efforts` is treated as sparse input and inferred instead of crashing during model resolution ([#2251](https://github.com/can1357/oh-my-pi/issues/2251)).
10
+
11
+ ## [15.10.12] - 2026-06-10
12
+
13
+ ### Added
14
+
15
+ - Added `grok-composer-2.5-fast` (Cursor "Composer 2.5 Fast") to the xAI Grok OAuth (SuperGrok) catalog: non-reasoning, text-only, 200K context.
16
+
17
+ ### Changed
18
+
19
+ - Set every xAI Grok OAuth (SuperGrok) curated model's max output tokens to mirror its context window (`grok-build`, `grok-4.3`, `grok-4.20-0309-{reasoning,non-reasoning}`, `grok-4.20-multi-agent-0309`, `grok-composer-2.5-fast`), replacing the `8888` `UNK_MAX_TOKENS` placeholder (and a stale `30000` on three grok-4.x entries). xAI's OAuth `/v1/models` reports no per-request output limit, so the curated catalog now owns `maxTokens` like `contextWindow`, deterministic on both the static-seed and online-overlay paths; the `openai-responses` wire still clamps the actual request to `OPENAI_MAX_OUTPUT_TOKENS` (64k).
20
+
21
+ ### Fixed
22
+
23
+ - Excluded zero-cost `xai-oauth` subscription entries from the model reference indexes (`buildModelReferenceIndex`, `createReferenceResolver`), so their zero pricing and context-window-sized `maxTokens` cannot outrank paid/public Grok references when resolving custom-provider model identities.
24
+
5
25
  ## [15.10.11] - 2026-06-10
6
26
 
7
27
  ### Added
@@ -13,6 +13,7 @@ export interface ModelReferenceIndex {
13
13
  exact: Map<string, Model<Api>>;
14
14
  suffixAlias: Map<string, Model<Api>>;
15
15
  }
16
+ export declare function isZeroCostXaiOAuthReference(candidate: Model<Api>): boolean;
16
17
  /**
17
18
  * Build a reference index from a model catalog (typically the bundled models).
18
19
  * Pure: callers are responsible for memoizing the result.
@@ -151,15 +151,15 @@ export declare const CATALOG_PROVIDERS: readonly [{
151
151
  readonly allowUnauthenticated: true;
152
152
  }, {
153
153
  readonly id: "minimax";
154
- readonly defaultModel: "MiniMax-M2.5";
154
+ readonly defaultModel: "MiniMax-M3";
155
155
  readonly envVars: readonly ["MINIMAX_API_KEY"];
156
156
  }, {
157
157
  readonly id: "minimax-code";
158
- readonly defaultModel: "MiniMax-M2.5";
158
+ readonly defaultModel: "MiniMax-M3";
159
159
  readonly envVars: readonly ["MINIMAX_CODE_API_KEY"];
160
160
  }, {
161
161
  readonly id: "minimax-code-cn";
162
- readonly defaultModel: "MiniMax-M2.5";
162
+ readonly defaultModel: "MiniMax-M3";
163
163
  readonly envVars: readonly ["MINIMAX_CODE_CN_API_KEY"];
164
164
  }, {
165
165
  readonly id: "mistral";
@@ -118,8 +118,9 @@ export declare const XAI_OAUTH_CURATED_MODELS: readonly XAICuratedModel[];
118
118
  *
119
119
  * `reasoning` defaults to `true` for the Grok-4.x family; the explicit
120
120
  * `grok-4.20-0309-non-reasoning` entry opts out via `XAICuratedModel.reasoning`.
121
- * `maxTokens` uses `UNK_MAX_TOKENS` so id-keyed overlays from a successful
122
- * dynamic fetch merge cleanly. Mirrors
121
+ * `maxTokens` mirrors each model's `contextWindow` (the OAuth surface reports
122
+ * no per-request output limit); the openai-responses wire still clamps the
123
+ * actual request to OPENAI_MAX_OUTPUT_TOKENS. Mirrors
123
124
  * `hermes-agent/hermes_cli/models.py:_XAI_STATIC_FALLBACK`.
124
125
  */
125
126
  export declare function buildXaiOAuthStaticSeed(baseUrl?: string): ModelSpec<"openai-responses">[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "15.10.11",
4
+ "version": "15.11.0",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -34,11 +34,11 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.0",
37
- "@oh-my-pi/pi-utils": "15.10.11",
37
+ "@oh-my-pi/pi-utils": "15.11.0",
38
38
  "zod": "4.4.3"
39
39
  },
40
40
  "devDependencies": {
41
- "@oh-my-pi/pi-ai": "15.10.11",
41
+ "@oh-my-pi/pi-ai": "15.11.0",
42
42
  "@types/bun": "^1.3.14"
43
43
  },
44
44
  "engines": {
@@ -17,7 +17,18 @@ export interface ModelReferenceIndex {
17
17
  suffixAlias: Map<string, Model<Api>>;
18
18
  }
19
19
 
20
- // Custom provider entries often front a known upstream model through a local proxy.
20
+ // xai-oauth subscription entries carry zero public pricing and inflated maxTokens;
21
+ // keep them provider-local so they cannot outrank paid/public Grok references.
22
+ export function isZeroCostXaiOAuthReference(candidate: Model<Api>): boolean {
23
+ return (
24
+ candidate.provider === "xai-oauth" &&
25
+ candidate.cost.input === 0 &&
26
+ candidate.cost.output === 0 &&
27
+ candidate.cost.cacheRead === 0 &&
28
+ candidate.cost.cacheWrite === 0
29
+ );
30
+ }
31
+
21
32
  // Prefer the reference with the largest limits and complete cache pricing, then
22
33
  // first-party OpenAI entries.
23
34
  function shouldReplaceReference(existing: Model<Api> | undefined, candidate: Model<Api>): boolean {
@@ -47,6 +58,9 @@ function normalizeReferenceKey(value: string): string {
47
58
  export function buildModelReferenceIndex(models: Iterable<Model<Api>>): ModelReferenceIndex {
48
59
  const exact = new Map<string, Model<Api>>();
49
60
  for (const candidate of models) {
61
+ if (isZeroCostXaiOAuthReference(candidate)) {
62
+ continue;
63
+ }
50
64
  const key = normalizeReferenceKey(candidate.id);
51
65
  if (shouldReplaceReference(exact.get(key), candidate)) {
52
66
  exact.set(key, candidate);
@@ -97,7 +97,7 @@ export function resolveModelThinking<TApi extends Api>(
97
97
  ): ThinkingConfig | undefined {
98
98
  if (!spec.reasoning) return undefined;
99
99
  if (omitsWireReasoningEffort(spec.api, compat)) return undefined;
100
- if (spec.thinking && spec.thinking.efforts.length > 0) {
100
+ if (spec.thinking && Array.isArray(spec.thinking.efforts) && spec.thinking.efforts.length > 0) {
101
101
  return fillThinkingWireDefaults(spec, spec.thinking);
102
102
  }
103
103
  // Empty/malformed explicit metadata is treated as absent — infer instead.
package/src/models.json CHANGED
@@ -69968,7 +69968,7 @@
69968
69968
  "cacheWrite": 0
69969
69969
  },
69970
69970
  "contextWindow": 2000000,
69971
- "maxTokens": 30000,
69971
+ "maxTokens": 2000000,
69972
69972
  "compat": {
69973
69973
  "reasoningEffortMap": {
69974
69974
  "minimal": "low"
@@ -69993,7 +69993,7 @@
69993
69993
  "cacheWrite": 0
69994
69994
  },
69995
69995
  "contextWindow": 2000000,
69996
- "maxTokens": 30000,
69996
+ "maxTokens": 2000000,
69997
69997
  "compat": {
69998
69998
  "reasoningEffortMap": {
69999
69999
  "minimal": "low"
@@ -70018,7 +70018,7 @@
70018
70018
  "cacheWrite": 0
70019
70019
  },
70020
70020
  "contextWindow": 2000000,
70021
- "maxTokens": 8888,
70021
+ "maxTokens": 2000000,
70022
70022
  "compat": {
70023
70023
  "reasoningEffortMap": {
70024
70024
  "minimal": "low"
@@ -70053,7 +70053,7 @@
70053
70053
  "cacheWrite": 0
70054
70054
  },
70055
70055
  "contextWindow": 1000000,
70056
- "maxTokens": 30000,
70056
+ "maxTokens": 1000000,
70057
70057
  "compat": {
70058
70058
  "reasoningEffortMap": {
70059
70059
  "minimal": "low"
@@ -70087,13 +70087,37 @@
70087
70087
  "cacheWrite": 0
70088
70088
  },
70089
70089
  "contextWindow": 512000,
70090
- "maxTokens": 8888,
70090
+ "maxTokens": 512000,
70091
70091
  "compat": {
70092
70092
  "reasoningEffortMap": {
70093
70093
  "minimal": "low"
70094
70094
  },
70095
70095
  "supportsReasoningEffort": false
70096
70096
  }
70097
+ },
70098
+ "grok-composer-2.5-fast": {
70099
+ "id": "grok-composer-2.5-fast",
70100
+ "name": "Grok Composer 2.5 Fast",
70101
+ "api": "openai-responses",
70102
+ "provider": "xai-oauth",
70103
+ "baseUrl": "https://api.x.ai/v1",
70104
+ "reasoning": false,
70105
+ "input": [
70106
+ "text"
70107
+ ],
70108
+ "cost": {
70109
+ "input": 0,
70110
+ "output": 0,
70111
+ "cacheRead": 0,
70112
+ "cacheWrite": 0
70113
+ },
70114
+ "contextWindow": 200000,
70115
+ "maxTokens": 200000,
70116
+ "compat": {
70117
+ "reasoningEffortMap": {
70118
+ "minimal": "low"
70119
+ }
70120
+ }
70097
70121
  }
70098
70122
  },
70099
70123
  "xiaomi": {
@@ -1,3 +1,4 @@
1
+ import { isZeroCostXaiOAuthReference } from "../identity/reference";
1
2
  import { getBundledModels, getBundledProviders } from "../models";
2
3
  import type { Api, Model, ModelSpec } from "../types";
3
4
 
@@ -29,6 +30,9 @@ export function createReferenceResolver<TApi extends Api>(
29
30
  for (const provider of getBundledProviders()) {
30
31
  for (const model of getBundledModels(provider as Parameters<typeof getBundledModels>[0])) {
31
32
  const candidate = model as Model<Api>;
33
+ if (isZeroCostXaiOAuthReference(candidate)) {
34
+ continue;
35
+ }
32
36
  const existing = globalRefs.get(candidate.id);
33
37
  if (!existing) {
34
38
  globalRefs.set(candidate.id, candidate);
@@ -191,17 +191,17 @@ export const CATALOG_PROVIDERS = [
191
191
  },
192
192
  {
193
193
  id: "minimax",
194
- defaultModel: "MiniMax-M2.5",
194
+ defaultModel: "MiniMax-M3",
195
195
  envVars: ["MINIMAX_API_KEY"],
196
196
  },
197
197
  {
198
198
  id: "minimax-code",
199
- defaultModel: "MiniMax-M2.5",
199
+ defaultModel: "MiniMax-M3",
200
200
  envVars: ["MINIMAX_CODE_API_KEY"],
201
201
  },
202
202
  {
203
203
  id: "minimax-code-cn",
204
- defaultModel: "MiniMax-M2.5",
204
+ defaultModel: "MiniMax-M3",
205
205
  envVars: ["MINIMAX_CODE_CN_API_KEY"],
206
206
  },
207
207
  {
@@ -739,6 +739,17 @@ export const XAI_OAUTH_CURATED_MODELS: readonly XAICuratedModel[] = [
739
739
  reasoning: false,
740
740
  input: ["text", "image"],
741
741
  },
742
+ // Cursor's "Composer 2.5 Fast" exposed via SuperGrok: non-reasoning,
743
+ // text-only, 200K context (mirrors Cursor's composer-* catalog entries).
744
+ // Off the GROK_EFFORT_CAPABLE_PREFIXES allowlist, so the wire side already
745
+ // sets omitReasoningEffort=true; reasoning:false also hides the effort dial.
746
+ {
747
+ id: "grok-composer-2.5-fast",
748
+ contextWindow: 200_000,
749
+ name: "Grok Composer 2.5 Fast",
750
+ reasoning: false,
751
+ input: ["text"],
752
+ },
742
753
  ] as const;
743
754
 
744
755
  // xAI /v1/models returns chat, image, voice, and STT entries. Tool surfaces
@@ -754,6 +765,13 @@ const XAI_NON_CHAT_PREFIXES = ["grok-imagine-", "grok-stt-", "grok-voice-"] as c
754
765
  // at request time, downstream of the omitReasoningEffort gate in xai-responses.ts.
755
766
  const XAI_REASONING_EFFORT_MAP = { minimal: "low" } as const;
756
767
 
768
+ // xai-oauth's /v1/models exposes no per-request output limit on the OAuth
769
+ // (Grok Build / SuperGrok) surface, so the curated catalog owns `maxTokens`
770
+ // like it owns `contextWindow`: each entry mirrors its context window. The
771
+ // openai-responses wire clamps the actual request to
772
+ // min(requested, model.maxTokens, OPENAI_MAX_OUTPUT_TOKENS=64000), so this is
773
+ // just "no model-specific sub-cap below 64k", not an unbounded output budget.
774
+
757
775
  // Single source of truth for curated → Model fan-in. Used by the static-seed
758
776
  // and the dynamic overlay/inject paths (applyXAIOAuthCuration) so curated
759
777
  // reasoning/effort flags survive an online refresh (xAI's /v1/models lacks
@@ -776,6 +794,7 @@ function mergeCuratedIntoModel(
776
794
  return {
777
795
  ...base,
778
796
  contextWindow: curated.contextWindow,
797
+ maxTokens: curated.contextWindow,
779
798
  name: curated.name ?? base.name,
780
799
  reasoning: curated.reasoning ?? true,
781
800
  input: curated.input ?? base.input,
@@ -850,8 +869,9 @@ function applyXAIOAuthCuration(dynamic: readonly ModelSpec<"openai-responses">[]
850
869
  *
851
870
  * `reasoning` defaults to `true` for the Grok-4.x family; the explicit
852
871
  * `grok-4.20-0309-non-reasoning` entry opts out via `XAICuratedModel.reasoning`.
853
- * `maxTokens` uses `UNK_MAX_TOKENS` so id-keyed overlays from a successful
854
- * dynamic fetch merge cleanly. Mirrors
872
+ * `maxTokens` mirrors each model's `contextWindow` (the OAuth surface reports
873
+ * no per-request output limit); the openai-responses wire still clamps the
874
+ * actual request to OPENAI_MAX_OUTPUT_TOKENS. Mirrors
855
875
  * `hermes-agent/hermes_cli/models.py:_XAI_STATIC_FALLBACK`.
856
876
  */
857
877
  export function buildXaiOAuthStaticSeed(baseUrl?: string): ModelSpec<"openai-responses">[] {
@@ -871,7 +891,7 @@ export function buildXaiOAuthStaticSeed(baseUrl?: string): ModelSpec<"openai-res
871
891
  input: ["text"],
872
892
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
873
893
  contextWindow: curated.contextWindow,
874
- maxTokens: UNK_MAX_TOKENS,
894
+ maxTokens: curated.contextWindow,
875
895
  compat: { reasoningEffortMap: XAI_REASONING_EFFORT_MAP },
876
896
  };
877
897
  return mergeCuratedIntoModel(base, curated);