@oh-my-pi/pi-catalog 16.4.1 → 16.4.2

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.4.2] - 2026-07-10
6
+
7
+ ### Fixed
8
+
9
+ - Fixed OpenAI Codex model discovery to include the Codex version header alongside the client_version query parameter.
10
+
5
11
  ## [16.4.1] - 2026-07-10
6
12
 
7
13
  ### Added
@@ -19,8 +19,6 @@ export interface CodexModelDiscoveryOptions {
19
19
  signal?: AbortSignal;
20
20
  /** Optional fetch implementation override for tests. */
21
21
  fetchFn?: typeof fetch;
22
- /** Optional registry fetch implementation override for client version lookup. */
23
- registryFetchFn?: typeof fetch;
24
22
  }
25
23
  /**
26
24
  * Normalized Codex discovery response.
@@ -2,10 +2,15 @@
2
2
  * Constants for OpenAI Codex (ChatGPT OAuth) backend
3
3
  */
4
4
  export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
5
+ /**
6
+ * Pinned OpenAI Codex client version (corresponds to @openai/codex package version).
7
+ */
8
+ export declare const CODEX_CLIENT_VERSION = "0.144.1";
5
9
  export declare const OPENAI_HEADERS: {
6
10
  readonly BETA: "OpenAI-Beta";
7
11
  readonly ACCOUNT_ID: "chatgpt-account-id";
8
12
  readonly ORIGINATOR: "originator";
13
+ readonly VERSION: "version";
9
14
  readonly SESSION_ID: "session_id";
10
15
  readonly CONVERSATION_ID: "conversation_id";
11
16
  readonly SCOPED_SESSION_ID: "session-id";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "16.4.1",
4
+ "version": "16.4.2",
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,12 +34,12 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.0",
37
- "@oh-my-pi/pi-utils": "16.4.1",
38
- "arktype": "^2.2.0",
37
+ "@oh-my-pi/pi-utils": "16.4.2",
38
+ "arktype": "2.2.2",
39
39
  "zod": "^4"
40
40
  },
41
41
  "devDependencies": {
42
- "@oh-my-pi/pi-ai": "16.4.1",
42
+ "@oh-my-pi/pi-ai": "16.4.2",
43
43
  "@types/bun": "^1.3.14"
44
44
  },
45
45
  "engines": {
@@ -595,12 +595,13 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
595
595
  // Azure OpenAI and GitHub Copilot Responses paths require tool results
596
596
  // to strictly match prior tool calls when building Responses inputs.
597
597
  strictResponsesPairing: isAzure || spec.provider === "github-copilot",
598
- // GitHub Copilot's Responses endpoint rejects the `detail: "original"`
599
- // image hint with a 400; every other host preserves native-resolution
600
- // frames (snapcompact relies on `original`). Detect Copilot by provider id
601
- // or base-URL host (mirroring the Anthropic compat builder) so a model
602
- // pointed at the Copilot host under a different provider id still clamps.
603
- supportsImageDetailOriginal: !modelMatchesHost({ provider: spec.provider, baseUrl }, "githubCopilot"),
598
+ // GitHub Copilot and xAI OAuth reject `detail: "original"` (400 / 422).
599
+ // Every other host preserves native-resolution frames (snapcompact relies
600
+ // on `original`). Detect Copilot by provider id or base-URL host so a
601
+ // model pointed at the Copilot host under a different provider id still
602
+ // clamps; xai-oauth is provider-id only (same host family as paid `xai`).
603
+ supportsImageDetailOriginal:
604
+ spec.provider !== "xai-oauth" && !modelMatchesHost({ provider: spec.provider, baseUrl }, "githubCopilot"),
604
605
  reasoningEffortMap: {},
605
606
  supportsReasoningParams: true,
606
607
  thinkingFormat,
@@ -1,14 +1,11 @@
1
- import type { FetchImpl } from "@oh-my-pi/pi-utils";
2
1
  import { type } from "arktype";
3
2
  import type { ModelSpec } from "../types";
4
- import { discoveryFetch, isRecord } from "../utils";
5
- import { CODEX_BASE_URL, OPENAI_HEADER_VALUES, OPENAI_HEADERS } from "../wire/codex";
3
+ import { discoveryFetch } from "../utils";
4
+ import { CODEX_BASE_URL, CODEX_CLIENT_VERSION, OPENAI_HEADER_VALUES, OPENAI_HEADERS } from "../wire/codex";
6
5
 
7
6
  const DEFAULT_MODEL_LIST_PATHS = ["/codex/models", "/models"] as const;
8
7
  const DEFAULT_CONTEXT_WINDOW = 272_000;
9
8
  const DEFAULT_MAX_TOKENS = 128_000;
10
- const DEFAULT_CODEX_CLIENT_VERSION = "0.99.0";
11
- const NPM_CODEX_LATEST_URL = "https://registry.npmjs.org/@openai%2Fcodex/latest";
12
9
  const CODEX_REMOTE_COMPACTION = {
13
10
  enabled: true,
14
11
  api: "openai-codex-responses",
@@ -64,8 +61,6 @@ export interface CodexModelDiscoveryOptions {
64
61
  signal?: AbortSignal;
65
62
  /** Optional fetch implementation override for tests. */
66
63
  fetchFn?: typeof fetch;
67
- /** Optional registry fetch implementation override for client version lookup. */
68
- registryFetchFn?: typeof fetch;
69
64
  }
70
65
 
71
66
  /**
@@ -86,12 +81,8 @@ export async function fetchCodexModels(options: CodexModelDiscoveryOptions): Pro
86
81
  const fetchFn = discoveryFetch(options.fetchFn);
87
82
  const baseUrl = normalizeBaseUrl(options.baseUrl);
88
83
  const paths = normalizePaths(options.paths);
89
- const headers = buildCodexHeaders(options);
90
- const clientVersion = await resolveCodexClientVersion(
91
- options.clientVersion,
92
- options.registryFetchFn ?? fetchFn,
93
- options.signal,
94
- );
84
+ const clientVersion = normalizeClientVersion(options.clientVersion) ?? CODEX_CLIENT_VERSION;
85
+ const headers = buildCodexHeaders(options, clientVersion);
95
86
 
96
87
  let sawSuccessfulResponse = false;
97
88
  for (const path of paths) {
@@ -156,7 +147,7 @@ function buildModelsUrl(baseUrl: string, path: string, clientVersion: string | u
156
147
  return url.toString();
157
148
  }
158
149
 
159
- function buildCodexHeaders(options: CodexModelDiscoveryOptions): Headers {
150
+ function buildCodexHeaders(options: CodexModelDiscoveryOptions, clientVersion: string): Headers {
160
151
  const headers = new Headers(options.headers);
161
152
  headers.set("Authorization", `Bearer ${options.accessToken}`);
162
153
  if (options.accountId && options.accountId.trim().length > 0) {
@@ -164,42 +155,11 @@ function buildCodexHeaders(options: CodexModelDiscoveryOptions): Headers {
164
155
  }
165
156
  headers.set(OPENAI_HEADERS.BETA, OPENAI_HEADER_VALUES.BETA_RESPONSES);
166
157
  headers.set(OPENAI_HEADERS.ORIGINATOR, OPENAI_HEADER_VALUES.ORIGINATOR_CODEX);
158
+ headers.set(OPENAI_HEADERS.VERSION, clientVersion);
167
159
  headers.set("accept", "application/json");
168
160
  return headers;
169
161
  }
170
162
 
171
- async function resolveCodexClientVersion(
172
- clientVersion: string | undefined,
173
- fetchFn: FetchImpl,
174
- signal: AbortSignal | undefined,
175
- ): Promise<string> {
176
- const normalizedClientVersion = normalizeClientVersion(clientVersion);
177
- if (normalizedClientVersion) {
178
- return normalizedClientVersion;
179
- }
180
- try {
181
- const response = await fetchFn(NPM_CODEX_LATEST_URL, {
182
- method: "GET",
183
- headers: { Accept: "application/json" },
184
- signal,
185
- });
186
- if (!response.ok) {
187
- return DEFAULT_CODEX_CLIENT_VERSION;
188
- }
189
- const payload: unknown = await response.json();
190
- if (!isRecord(payload)) {
191
- return DEFAULT_CODEX_CLIENT_VERSION;
192
- }
193
- const npmVersion = normalizeClientVersion(payload.version);
194
- return npmVersion ?? DEFAULT_CODEX_CLIENT_VERSION;
195
- } catch (error) {
196
- if (isAbortError(error)) {
197
- throw error;
198
- }
199
- return DEFAULT_CODEX_CLIENT_VERSION;
200
- }
201
- }
202
-
203
163
  function normalizeClientVersion(value: unknown): string | undefined {
204
164
  if (typeof value !== "string") {
205
165
  return undefined;
@@ -211,10 +171,6 @@ function normalizeClientVersion(value: unknown): string | undefined {
211
171
  return trimmed;
212
172
  }
213
173
 
214
- function isAbortError(error: unknown): error is Error {
215
- return error instanceof Error && error.name === "AbortError";
216
- }
217
-
218
174
  function normalizeCodexModels(payload: unknown, baseUrl: string): ModelSpec<"openai-codex-responses">[] | null {
219
175
  const parsedResponse = codexModelsResponseSchema(payload);
220
176
  if (parsedResponse instanceof type.errors) {
package/src/models.json CHANGED
@@ -17387,6 +17387,26 @@
17387
17387
  "contextWindow": 200000,
17388
17388
  "maxTokens": 64000
17389
17389
  },
17390
+ "nemotron-3-ultra-nvfp4": {
17391
+ "id": "nemotron-3-ultra-nvfp4",
17392
+ "name": "Nemotron 3 Ultra",
17393
+ "api": "devin-agent",
17394
+ "provider": "devin",
17395
+ "baseUrl": "https://server.codeium.com",
17396
+ "reasoning": true,
17397
+ "input": [
17398
+ "text"
17399
+ ],
17400
+ "supportsTools": true,
17401
+ "cost": {
17402
+ "input": 0,
17403
+ "output": 0,
17404
+ "cacheRead": 0,
17405
+ "cacheWrite": 0
17406
+ },
17407
+ "contextWindow": 262144,
17408
+ "maxTokens": 64000
17409
+ },
17390
17410
  "swe-1-6": {
17391
17411
  "id": "swe-1-6",
17392
17412
  "name": "SWE-1.6",
@@ -31094,7 +31114,7 @@
31094
31114
  },
31095
31115
  "openai/gpt-5.6-terra": {
31096
31116
  "id": "openai/gpt-5.6-terra",
31097
- "name": "GPT-5.6 Terra",
31117
+ "name": "GPT-5.6 Terra (new)",
31098
31118
  "api": "openai-completions",
31099
31119
  "provider": "kilo",
31100
31120
  "baseUrl": "https://api.kilo.ai/api/gateway",
@@ -62693,6 +62713,36 @@
62693
62713
  ]
62694
62714
  }
62695
62715
  },
62716
+ "gpt-realtime-2.1": {
62717
+ "id": "gpt-realtime-2.1",
62718
+ "name": "GPT-Realtime-2.1",
62719
+ "api": "openai-responses",
62720
+ "provider": "openai",
62721
+ "baseUrl": "https://api.openai.com/v1",
62722
+ "reasoning": true,
62723
+ "input": [
62724
+ "text",
62725
+ "image"
62726
+ ],
62727
+ "cost": {
62728
+ "input": 4,
62729
+ "output": 24,
62730
+ "cacheRead": 0.4,
62731
+ "cacheWrite": 0
62732
+ },
62733
+ "contextWindow": 128000,
62734
+ "maxTokens": 32000,
62735
+ "thinking": {
62736
+ "mode": "effort",
62737
+ "efforts": [
62738
+ "minimal",
62739
+ "low",
62740
+ "medium",
62741
+ "high",
62742
+ "xhigh"
62743
+ ]
62744
+ }
62745
+ },
62696
62746
  "o1": {
62697
62747
  "id": "o1",
62698
62748
  "name": "o1",
@@ -87250,7 +87300,8 @@
87250
87300
  "includeEncryptedReasoning": false,
87251
87301
  "filterReasoningHistory": true,
87252
87302
  "omitReasoningEffort": true,
87253
- "supportsReasoningEffort": false
87303
+ "supportsReasoningEffort": false,
87304
+ "supportsImageDetailOriginal": false
87254
87305
  }
87255
87306
  },
87256
87307
  "grok-4.20-0309-reasoning": {
@@ -87279,7 +87330,8 @@
87279
87330
  "includeEncryptedReasoning": false,
87280
87331
  "filterReasoningHistory": true,
87281
87332
  "omitReasoningEffort": true,
87282
- "supportsReasoningEffort": false
87333
+ "supportsReasoningEffort": false,
87334
+ "supportsImageDetailOriginal": false
87283
87335
  }
87284
87336
  },
87285
87337
  "grok-4.20-multi-agent-0309": {
@@ -87320,7 +87372,8 @@
87320
87372
  "includeEncryptedReasoning": false,
87321
87373
  "filterReasoningHistory": true,
87322
87374
  "omitReasoningEffort": false,
87323
- "supportsReasoningEffort": true
87375
+ "supportsReasoningEffort": true,
87376
+ "supportsImageDetailOriginal": false
87324
87377
  }
87325
87378
  },
87326
87379
  "grok-4.3": {
@@ -87362,7 +87415,8 @@
87362
87415
  "includeEncryptedReasoning": false,
87363
87416
  "filterReasoningHistory": true,
87364
87417
  "omitReasoningEffort": false,
87365
- "supportsReasoningEffort": true
87418
+ "supportsReasoningEffort": true,
87419
+ "supportsImageDetailOriginal": false
87366
87420
  }
87367
87421
  },
87368
87422
  "grok-4.5": {
@@ -87404,7 +87458,8 @@
87404
87458
  "includeEncryptedReasoning": false,
87405
87459
  "filterReasoningHistory": true,
87406
87460
  "omitReasoningEffort": false,
87407
- "supportsReasoningEffort": true
87461
+ "supportsReasoningEffort": true,
87462
+ "supportsImageDetailOriginal": false
87408
87463
  }
87409
87464
  },
87410
87465
  "grok-build": {
@@ -87433,7 +87488,8 @@
87433
87488
  "includeEncryptedReasoning": false,
87434
87489
  "filterReasoningHistory": true,
87435
87490
  "omitReasoningEffort": true,
87436
- "supportsReasoningEffort": false
87491
+ "supportsReasoningEffort": false,
87492
+ "supportsImageDetailOriginal": false
87437
87493
  }
87438
87494
  },
87439
87495
  "grok-build-0.1": {
@@ -87462,7 +87518,8 @@
87462
87518
  "includeEncryptedReasoning": false,
87463
87519
  "filterReasoningHistory": true,
87464
87520
  "omitReasoningEffort": true,
87465
- "supportsReasoningEffort": false
87521
+ "supportsReasoningEffort": false,
87522
+ "supportsImageDetailOriginal": false
87466
87523
  }
87467
87524
  },
87468
87525
  "grok-composer-2.5-fast": {
@@ -87490,7 +87547,8 @@
87490
87547
  "includeEncryptedReasoning": false,
87491
87548
  "filterReasoningHistory": true,
87492
87549
  "omitReasoningEffort": true,
87493
- "supportsReasoningEffort": false
87550
+ "supportsReasoningEffort": false,
87551
+ "supportsImageDetailOriginal": false
87494
87552
  }
87495
87553
  }
87496
87554
  },
@@ -1176,6 +1176,7 @@ function withXaiOAuthCompatDefaults(model: ModelSpec<"openai-responses">): Model
1176
1176
  ...(model.compat ?? {}),
1177
1177
  includeEncryptedReasoning: model.compat?.includeEncryptedReasoning ?? false,
1178
1178
  filterReasoningHistory: model.compat?.filterReasoningHistory ?? true,
1179
+ supportsImageDetailOriginal: model.compat?.supportsImageDetailOriginal ?? false,
1179
1180
  omitReasoningEffort: model.compat?.omitReasoningEffort ?? !isGrokReasoningEffortCapable(model.id),
1180
1181
  };
1181
1182
  return { ...model, compat };
@@ -1218,6 +1219,7 @@ function mergeCuratedIntoModel(
1218
1219
  reasoningEffortMap: { ...XAI_REASONING_EFFORT_MAP, ...(base.compat?.reasoningEffortMap ?? {}) },
1219
1220
  includeEncryptedReasoning: base.compat?.includeEncryptedReasoning ?? false,
1220
1221
  filterReasoningHistory: base.compat?.filterReasoningHistory ?? true,
1222
+ supportsImageDetailOriginal: base.compat?.supportsImageDetailOriginal ?? false,
1221
1223
  omitReasoningEffort: !effortCapable,
1222
1224
  supportsReasoningEffort: effortCapable,
1223
1225
  };
package/src/wire/codex.ts CHANGED
@@ -4,10 +4,16 @@
4
4
 
5
5
  export const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
6
6
 
7
+ /**
8
+ * Pinned OpenAI Codex client version (corresponds to @openai/codex package version).
9
+ */
10
+ export const CODEX_CLIENT_VERSION = "0.144.1";
11
+
7
12
  export const OPENAI_HEADERS = {
8
13
  BETA: "OpenAI-Beta",
9
14
  ACCOUNT_ID: "chatgpt-account-id",
10
15
  ORIGINATOR: "originator",
16
+ VERSION: "version",
11
17
  SESSION_ID: "session_id",
12
18
  CONVERSATION_ID: "conversation_id",
13
19
  SCOPED_SESSION_ID: "session-id",