@oh-my-pi/pi-catalog 17.3.3 → 17.3.4

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,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.3.4] - 2026-08-14
6
+
7
+ ### Added
8
+
9
+ - Added wire constants for Codex V2 remote-compaction feature negotiation.
10
+
11
+ ### Fixed
12
+
13
+ - Fixed raw `COPILOT_GITHUB_TOKEN` credentials skipping plan-specific endpoint discovery, which routed GitHub Copilot Business model requests to the personal endpoint and returned HTTP 403. The GitHub Copilot model cache is now scoped per credential, so switching the token no longer serves another account's stale endpoint for the cache TTL ([#8507](https://github.com/can1357/oh-my-pi/issues/8507)).
14
+ - Fixed the OpenRouter `deepseek/deepseek-v4-pro-0813` route silently clamping the reasoning effort to `high`: the dated SKU advertises (and accepts) the wire-exact `low`/`high`/`max` ladder, so its effort override no longer collapses to `high`-only. The undated `deepseek/deepseek-v4-pro` OpenRouter route stays `high`-only. ([#8517](https://github.com/can1357/oh-my-pi/issues/8517))
15
+
5
16
  ## [17.3.2] - 2026-08-13
6
17
 
7
18
  ### Added
@@ -8,6 +8,8 @@ export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
8
8
  export declare const CODEX_CLIENT_VERSION = "0.144.1";
9
9
  export declare const OPENAI_HEADERS: {
10
10
  readonly BETA: "OpenAI-Beta";
11
+ /** Codex feature-negotiation header; values identify opt-in wire protocols. */
12
+ readonly CODEX_BETA_FEATURES: "x-codex-beta-features";
11
13
  readonly ACCOUNT_ID: "chatgpt-account-id";
12
14
  readonly ORIGINATOR: "originator";
13
15
  readonly VERSION: "version";
@@ -28,6 +30,7 @@ export declare const OPENAI_HEADERS: {
28
30
  export declare const OPENAI_HEADER_VALUES: {
29
31
  readonly BETA_RESPONSES: "responses=experimental";
30
32
  readonly BETA_RESPONSES_WEBSOCKETS_V2: "responses_websockets=2026-02-06";
33
+ readonly REMOTE_COMPACTION_V2: "remote_compaction_v2";
31
34
  readonly ORIGINATOR_CODEX: "pi";
32
35
  };
33
36
  export declare const URL_PATHS: {
@@ -1,3 +1,4 @@
1
+ import type { FetchImpl } from "../types.js";
1
2
  /**
2
3
  * GitHub Copilot wire metadata: API-key envelope parsing and endpoint
3
4
  * derivation shared by catalog discovery and the pi-ai OAuth flow. The device
@@ -34,6 +35,13 @@ export declare const PERSONAL_GITHUB_COPILOT_BASE_URL: "https://api.githubcopilo
34
35
  export declare function isPersonalGitHubCopilotBaseUrl(baseUrl: string | undefined): boolean;
35
36
  export declare function normalizeGitHubCopilotEnterpriseDomain(input: string | undefined): string | undefined;
36
37
  export declare function normalizeGitHubCopilotApiEndpoint(input: string | undefined): string | undefined;
38
+ /**
39
+ * Resolve the plan-specific Copilot API endpoint advertised for a GitHub token.
40
+ * Login and raw environment-token discovery share this best-effort probe. Pass
41
+ * a `signal` to bound it against the same discovery deadline as `/models`; a
42
+ * stalled probe otherwise blocks discovery indefinitely.
43
+ */
44
+ export declare function discoverGitHubCopilotApiEndpoint(token: string, fetchImpl: FetchImpl, signal?: AbortSignal): Promise<string | undefined>;
37
45
  export declare function parseGitHubCopilotApiKey(apiKeyRaw: string): ParsedGitHubCopilotApiKey;
38
46
  export declare function normalizeDomain(input: string): string | null;
39
47
  export declare function getGitHubCopilotBaseUrl(enterpriseDomain?: string): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "17.3.3",
4
+ "version": "17.3.4",
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",
@@ -35,11 +35,11 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@bufbuild/protobuf": "^2.12.1",
38
- "@oh-my-pi/omptype": "17.3.3",
39
- "@oh-my-pi/pi-utils": "17.3.3"
38
+ "@oh-my-pi/omptype": "17.3.4",
39
+ "@oh-my-pi/pi-utils": "17.3.4"
40
40
  },
41
41
  "devDependencies": {
42
- "@oh-my-pi/pi-ai": "17.3.3",
42
+ "@oh-my-pi/pi-ai": "17.3.4",
43
43
  "@types/bun": "^1.3.14"
44
44
  },
45
45
  "engines": {
@@ -374,13 +374,20 @@ function getModelDefinedEfforts<TApi extends Api>(
374
374
  // on every first-party/aggregator host — the direct API, aggregators, and
375
375
  // Ollama Cloud alike (medium/xhigh fold into high, max is a real wire
376
376
  // tier). See https://api-docs.deepseek.com/api/create-chat-completion.
377
- // OpenRouter's non-Flash V4 route still exposes only high; the older
378
- // reasoners (V3.x, R1, deepseek-reasoner) top out at high/max.
377
+ // OpenRouter's non-Flash V4 route exposes only high, except the dated
378
+ // `deepseek-v4-pro-0813` SKU: its /models metadata advertises (and the
379
+ // route accepts) the full low/high/max ladder like every other host.
380
+ // The older reasoners (V3.x, R1, deepseek-reasoner) top out at high/max.
379
381
  if (isDeepseekV4FlashModelId(spec.id)) {
380
382
  return LOW_HIGH_MAX_REASONING_EFFORTS;
381
383
  }
382
384
  if (bareModelId(spec.id).toLowerCase().includes("deepseek-v4")) {
383
- return isOpenRouterThinkingFormat(compat) ? HIGH_ONLY_REASONING_EFFORTS : LOW_HIGH_MAX_REASONING_EFFORTS;
385
+ if (!isOpenRouterThinkingFormat(compat)) {
386
+ return LOW_HIGH_MAX_REASONING_EFFORTS;
387
+ }
388
+ return bareModelId(spec.id).toLowerCase() === "deepseek-v4-pro-0813"
389
+ ? LOW_HIGH_MAX_REASONING_EFFORTS
390
+ : HIGH_ONLY_REASONING_EFFORTS;
384
391
  }
385
392
  return isOpenRouterThinkingFormat(compat) ? HIGH_ONLY_REASONING_EFFORTS : HIGH_MAX_REASONING_EFFORTS;
386
393
  }
@@ -1,3 +1,5 @@
1
+ import { PERSONAL_GITHUB_COPILOT_BASE_URL } from "../wire/github-copilot";
2
+
1
3
  export interface ModelCacheProviderIdOptions {
2
4
  apiKey?: string;
3
5
  baseUrl?: string;
@@ -56,6 +58,18 @@ export function resolveModelCacheProviderId(providerId: string, options: ModelCa
56
58
  const scope = `${options.apiKey ?? ""}\u0000${discoveryBaseUrl}`;
57
59
  return `${providerId}:models-v1:${Bun.hash(scope).toString(36)}`;
58
60
  }
61
+ case "github-copilot": {
62
+ // Copilot model specs bake in the plan-specific endpoint (personal vs
63
+ // Business/Enterprise) resolved from the credential. Discovery writes an
64
+ // authoritative cache, so `online-if-uncached` serves it for the full
65
+ // TTL without re-probing. Keying the namespace on the credential means
66
+ // switching `COPILOT_GITHUB_TOKEN` to a different account misses the
67
+ // prior endpoint's cache and re-runs discovery instead of hitting the
68
+ // stale host and 403ing (PR #8510 review).
69
+ const baseUrl = options.baseUrl ?? PERSONAL_GITHUB_COPILOT_BASE_URL;
70
+ const scope = `${options.apiKey ?? ""}\u0000${baseUrl}`;
71
+ return `github-copilot:models-v1:${Bun.hash(scope).toString(36)}`;
72
+ }
59
73
  case "openrouter":
60
74
  return "openrouter:pseudo-api";
61
75
  case "vllm": {
@@ -1,6 +1,7 @@
1
1
  import { USER_AGENT } from "@oh-my-pi/pi-utils";
2
2
  import * as logger from "@oh-my-pi/pi-utils/logger";
3
3
  import {
4
+ DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS,
4
5
  fetchOpenAICompatibleModels,
5
6
  type OpenAICompatibleModelMapperContext,
6
7
  type OpenAICompatibleModelRecord,
@@ -25,6 +26,7 @@ import { ALIBABA_TOKEN_PLAN_BASE_URL, parseAlibabaTokenPlanCredential } from "..
25
26
  import { coreWeaveProjectHeaders } from "../wire/coreweave";
26
27
  import {
27
28
  COPILOT_API_HEADERS,
29
+ discoverGitHubCopilotApiEndpoint,
28
30
  getGitHubCopilotBaseUrl,
29
31
  isPersonalGitHubCopilotBaseUrl,
30
32
  parseGitHubCopilotApiKey,
@@ -5191,6 +5193,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5191
5193
  const resolveReference = createReferenceResolver(getProviderReferences);
5192
5194
  return {
5193
5195
  providerId: "github-copilot",
5196
+ cacheProviderId: resolveModelCacheProviderId("github-copilot", { apiKey: rawApiKey, baseUrl }),
5194
5197
  dropCachedModelIdsOnStaticMismatch: COPILOT_CACHE_INVALIDATED_MODEL_IDS,
5195
5198
  // COPILOT_API_HEADERS are compile-time constants (User-Agent + API
5196
5199
  // version), not credentials. The cache omits all request headers for
@@ -5201,11 +5204,17 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5201
5204
  restorableHeaderFallback: { ...COPILOT_API_HEADERS },
5202
5205
  ...(apiKey && {
5203
5206
  fetchDynamicModels: async () => {
5207
+ const fetchImpl = discoveryFetch(config?.fetch);
5208
+ const requestBaseUrl = isPersonalGitHubCopilotBaseUrl(baseUrl)
5209
+ ? ((await withCatalogDiscoveryTimeout(DEFAULT_OPENAI_COMPATIBLE_DISCOVERY_TIMEOUT_MS, signal =>
5210
+ discoverGitHubCopilotApiEndpoint(apiKey, fetchImpl, signal),
5211
+ )) ?? baseUrl)
5212
+ : baseUrl;
5204
5213
  const longContextVariants: ModelSpec<Api>[] = [];
5205
5214
  const models = await fetchOpenAICompatibleModels<Api>({
5206
5215
  api: "openai-completions",
5207
5216
  provider: "github-copilot",
5208
- baseUrl,
5217
+ baseUrl: requestBaseUrl,
5209
5218
  apiKey,
5210
5219
  headers: COPILOT_API_HEADERS,
5211
5220
  mapModel: (
@@ -5251,7 +5260,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5251
5260
  const input: ModelSpec<Api>["input"] =
5252
5261
  supportsVision === true
5253
5262
  ? ["text", "image"]
5254
- : supportsVision === false || !isPersonalGitHubCopilotBaseUrl(baseUrl)
5263
+ : supportsVision === false || !isPersonalGitHubCopilotBaseUrl(requestBaseUrl)
5255
5264
  ? ["text"]
5256
5265
  : (reference?.input ?? defaults.input);
5257
5266
  // With COPILOT_API_HEADERS the served window is the long-context
@@ -5272,7 +5281,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5272
5281
  ...reference,
5273
5282
  api,
5274
5283
  provider: "github-copilot",
5275
- baseUrl,
5284
+ baseUrl: requestBaseUrl,
5276
5285
  name,
5277
5286
  input,
5278
5287
  contextWindow: defaultTierWindow,
@@ -5294,7 +5303,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5294
5303
  : {
5295
5304
  ...defaults,
5296
5305
  api,
5297
- baseUrl,
5306
+ baseUrl: requestBaseUrl,
5298
5307
  name,
5299
5308
  input,
5300
5309
  contextWindow: defaultTierWindow,
@@ -5340,7 +5349,7 @@ export function githubCopilotModelManagerOptions(config?: GithubCopilotModelMana
5340
5349
  }
5341
5350
  return base;
5342
5351
  },
5343
- fetch: config?.fetch,
5352
+ fetch: fetchImpl,
5344
5353
  });
5345
5354
  if (models === null) {
5346
5355
  return null;
package/src/wire/codex.ts CHANGED
@@ -11,6 +11,8 @@ export const CODEX_CLIENT_VERSION = "0.144.1";
11
11
 
12
12
  export const OPENAI_HEADERS = {
13
13
  BETA: "OpenAI-Beta",
14
+ /** Codex feature-negotiation header; values identify opt-in wire protocols. */
15
+ CODEX_BETA_FEATURES: "x-codex-beta-features",
14
16
  ACCOUNT_ID: "chatgpt-account-id",
15
17
  ORIGINATOR: "originator",
16
18
  VERSION: "version",
@@ -32,6 +34,7 @@ export const OPENAI_HEADERS = {
32
34
  export const OPENAI_HEADER_VALUES = {
33
35
  BETA_RESPONSES: "responses=experimental",
34
36
  BETA_RESPONSES_WEBSOCKETS_V2: "responses_websockets=2026-02-06",
37
+ REMOTE_COMPACTION_V2: "remote_compaction_v2",
35
38
  ORIGINATOR_CODEX: "pi",
36
39
  } as const;
37
40
 
@@ -1,3 +1,6 @@
1
+ import type { FetchImpl } from "../types";
2
+ import { isRecord } from "../utils";
3
+
1
4
  /**
2
5
  * GitHub Copilot wire metadata: API-key envelope parsing and endpoint
3
6
  * derivation shared by catalog discovery and the pi-ai OAuth flow. The device
@@ -72,6 +75,35 @@ export function normalizeGitHubCopilotApiEndpoint(input: string | undefined): st
72
75
  return undefined;
73
76
  }
74
77
  }
78
+ /**
79
+ * Resolve the plan-specific Copilot API endpoint advertised for a GitHub token.
80
+ * Login and raw environment-token discovery share this best-effort probe. Pass
81
+ * a `signal` to bound it against the same discovery deadline as `/models`; a
82
+ * stalled probe otherwise blocks discovery indefinitely.
83
+ */
84
+ export async function discoverGitHubCopilotApiEndpoint(
85
+ token: string,
86
+ fetchImpl: FetchImpl,
87
+ signal?: AbortSignal,
88
+ ): Promise<string | undefined> {
89
+ try {
90
+ const response = await fetchImpl("https://api.github.com/copilot_internal/user", {
91
+ headers: {
92
+ Accept: "application/json",
93
+ Authorization: `token ${token}`,
94
+ ...OPENCODE_HEADERS,
95
+ },
96
+ signal,
97
+ });
98
+ if (!response.ok) return undefined;
99
+ const data: unknown = await response.json();
100
+ if (!isRecord(data) || !isRecord(data.endpoints)) return undefined;
101
+ const endpoint = data.endpoints.api;
102
+ return typeof endpoint === "string" ? normalizeGitHubCopilotApiEndpoint(endpoint) : undefined;
103
+ } catch {
104
+ return undefined;
105
+ }
106
+ }
75
107
 
76
108
  export function parseGitHubCopilotApiKey(apiKeyRaw: string): ParsedGitHubCopilotApiKey {
77
109
  try {