@bitkyc08/opencodex 2.11.0 → 2.11.1
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/gui/dist/assets/{index-Bk-PN-70.css → index-BA1dgo4q.css} +1 -1
- package/gui/dist/assets/index-DDZpgzKk.js +70 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -4
- package/src/adapters/anthropic.ts +10 -2
- package/src/adapters/cursor/native-exec-common.ts +6 -2
- package/src/adapters/google-antigravity-replay.ts +3 -2
- package/src/adapters/google-antigravity-wire.ts +38 -6
- package/src/adapters/google.ts +57 -10
- package/src/adapters/openai-chat.ts +19 -2
- package/src/claude/model-info.ts +1 -1
- package/src/cli/account-auth.ts +3 -1
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +15 -1
- package/src/cli/codex-shim-readiness.ts +69 -0
- package/src/cli/combo.ts +5 -0
- package/src/cli/config-command.ts +33 -4
- package/src/cli/doctor.ts +21 -0
- package/src/cli/export-command.ts +6 -6
- package/src/cli/help.ts +6 -6
- package/src/cli/index.ts +22 -5
- package/src/cli/models-runtime.ts +13 -1
- package/src/cli/provider.ts +7 -0
- package/src/clients/config-export.ts +67 -5
- package/src/codex/account-lifecycle.ts +99 -10
- package/src/codex/auth-api.ts +222 -34
- package/src/codex/catalog/account-models.ts +9 -4
- package/src/codex/catalog/aggregation.ts +41 -8
- package/src/codex/catalog/bundled.ts +54 -22
- package/src/codex/catalog/effort.ts +47 -20
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +70 -18
- package/src/codex/catalog/native-models.ts +7 -0
- package/src/codex/catalog/parsing.ts +29 -11
- package/src/codex/catalog/provider-fetch.ts +335 -50
- package/src/codex/catalog/sync.ts +588 -126
- package/src/codex/catalog-refresh-status.ts +87 -0
- package/src/codex/catalog-write-serialization.ts +2 -1
- package/src/codex/catalog.ts +4 -3
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +190 -52
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/features.ts +79 -1
- package/src/codex/history-job.ts +81 -1
- package/src/codex/history-lock.ts +2 -1
- package/src/codex/history-provider.ts +4 -3
- package/src/codex/inject.ts +56 -12
- package/src/codex/model-cache.ts +50 -10
- package/src/codex/transition-state.ts +10 -2
- package/src/codex/user-identity.ts +110 -2
- package/src/combos/index.ts +3 -0
- package/src/combos/types.ts +75 -9
- package/src/config.ts +26 -12
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +12 -10
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/writer.ts +1 -1
- package/src/lib/pinned-http.ts +40 -9
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +42 -9
- package/src/oauth/cursor.ts +25 -5
- package/src/oauth/index.ts +59 -45
- package/src/providers/antigravity-models.ts +115 -3
- package/src/providers/context-cap.ts +13 -5
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +14 -15
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +93 -19
- package/src/providers/registry.ts +16 -5
- package/src/providers/slug-codec.ts +13 -6
- package/src/reasoning-effort.ts +22 -0
- package/src/router.ts +0 -20
- package/src/routing/history/indexer.ts +54 -39
- package/src/routing/quota.ts +77 -56
- package/src/server/index.ts +32 -7
- package/src/server/management/combo-routes.ts +43 -19
- package/src/server/management/config-routes.ts +115 -20
- package/src/server/management/model-routes.ts +10 -7
- package/src/server/management/model-rows.ts +6 -2
- package/src/server/management/oauth-account-routes.ts +12 -0
- package/src/server/management/provider-routes.ts +114 -40
- package/src/server/management/routing-profile-routes.ts +0 -22
- package/src/server/management-api.ts +7 -25
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/policy-fallback.ts +152 -0
- package/src/server/responses.ts +3 -2
- package/src/types.ts +15 -2
- package/src/usage/cost.ts +0 -0
- package/src/vision/describe.ts +3 -1
- package/src/vision/index.ts +17 -6
- package/src/vision/reasoning.ts +55 -0
- package/src/web-search/parse.ts +15 -3
- package/gui/dist/assets/index-BynIEIV-.js +0 -70
|
@@ -9,7 +9,7 @@ import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
|
|
|
9
9
|
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
10
10
|
import { modelInList } from "../../types";
|
|
11
11
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
12
|
-
import {
|
|
12
|
+
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
|
|
13
13
|
import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
14
14
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
15
15
|
import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap";
|
|
@@ -35,7 +35,7 @@ import { readCatalog, readCodexCatalogPath } from "./parsing";
|
|
|
35
35
|
import type { CatalogModel, RawEntry } from "./parsing";
|
|
36
36
|
import { UPSTREAM_NATIVE_ENTRIES } from "./metadata";
|
|
37
37
|
import { loadBundledCodexCatalog } from "./bundled";
|
|
38
|
-
import type { BundledCatalogDeps } from "./bundled";
|
|
38
|
+
import type { BundledCatalogDeps, ReadonlyRawCatalog } from "./bundled";
|
|
39
39
|
import { deriveEntry } from "./sync";
|
|
40
40
|
import {
|
|
41
41
|
formatClampLogLines,
|
|
@@ -116,10 +116,9 @@ export function applyCatalogModelMetadata(entry: RawEntry, model?: CatalogModel)
|
|
|
116
116
|
if (model.provider === COMBO_NAMESPACE) entry.owned_by = model.owned_by ?? COMBO_NAMESPACE;
|
|
117
117
|
// displayName is DISPLAY-ONLY: it relabels the picker row but never touches the routing
|
|
118
118
|
// slug, alias, or provider. deriveEntry already stamped the slug as display_name; a
|
|
119
|
-
// configured displayName overrides just the label.
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
// upstream marketing names and combo alias labels are preserved untouched.
|
|
119
|
+
// configured displayName overrides just the label. Custom-model inputs reject `/`; combos
|
|
120
|
+
// validate their bounded display label independently. Natives never reach here (no CatalogModel),
|
|
121
|
+
// so genuine upstream marketing names are preserved untouched.
|
|
123
122
|
const displayName = typeof model.displayName === "string" ? model.displayName.trim() : "";
|
|
124
123
|
if (displayName) entry.display_name = displayName;
|
|
125
124
|
if (typeof model.contextWindow === "number" && model.contextWindow > 0) {
|
|
@@ -214,11 +213,13 @@ export function ensureUltraReasoningLevel(entry: RawEntry): void {
|
|
|
214
213
|
entry.supported_reasoning_levels = levels;
|
|
215
214
|
}
|
|
216
215
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
/** Derive the installed Codex effort vocabulary from caller-observed bundled catalog bytes. */
|
|
217
|
+
export function supportedCodexReasoningEffortsFromObservedCatalog(
|
|
218
|
+
catalog: ReadonlyRawCatalog | null,
|
|
219
|
+
): ReadonlySet<string> | null {
|
|
220
|
+
if (!catalog) return null;
|
|
220
221
|
const efforts = new Set<string>();
|
|
221
|
-
for (const model of
|
|
222
|
+
for (const model of catalog.models ?? []) {
|
|
222
223
|
if (typeof model.slug !== "string" || model.slug.includes("/")) continue;
|
|
223
224
|
const levels = Array.isArray(model.supported_reasoning_levels) ? model.supported_reasoning_levels : [];
|
|
224
225
|
for (const level of levels) {
|
|
@@ -230,6 +231,10 @@ export function codexSupportedReasoningEfforts(deps: BundledCatalogDeps = {}): S
|
|
|
230
231
|
return efforts.size > 0 ? efforts : null;
|
|
231
232
|
}
|
|
232
233
|
|
|
234
|
+
export function codexSupportedReasoningEfforts(deps: BundledCatalogDeps = {}): ReadonlySet<string> | null {
|
|
235
|
+
return supportedCodexReasoningEffortsFromObservedCatalog(loadBundledCodexCatalog(deps));
|
|
236
|
+
}
|
|
237
|
+
|
|
233
238
|
export function clampedDefaultEffort(original: string, surviving: readonly string[]): string {
|
|
234
239
|
if (surviving.length === 0) return "medium";
|
|
235
240
|
const ranked = [...surviving]
|
|
@@ -240,7 +245,10 @@ export function clampedDefaultEffort(original: string, surviving: readonly strin
|
|
|
240
245
|
return (atOrBelow.at(-1) ?? ranked[0]!).effort;
|
|
241
246
|
}
|
|
242
247
|
|
|
243
|
-
export function clampEntryToCodexSupportedEfforts(
|
|
248
|
+
export function clampEntryToCodexSupportedEfforts(
|
|
249
|
+
entry: RawEntry,
|
|
250
|
+
supported: ReadonlySet<string> | null,
|
|
251
|
+
): void {
|
|
244
252
|
if (!supported) return;
|
|
245
253
|
const levels = Array.isArray(entry.supported_reasoning_levels)
|
|
246
254
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
@@ -263,12 +271,17 @@ export function clampEntryToCodexSupportedEfforts(entry: RawEntry, supported: Se
|
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
|
266
|
-
export
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
export interface ObservedCatalogEffortClamp {
|
|
275
|
+
readonly removedEfforts: readonly string[];
|
|
276
|
+
readonly affectedModels: readonly string[];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** Apply an already-observed runtime ladder without probing, logging, or writing diagnostics. */
|
|
280
|
+
export function clampCatalogModelsToObservedCodexSupport(
|
|
281
|
+
models: RawEntry[],
|
|
282
|
+
supported: ReadonlySet<string> | null,
|
|
283
|
+
): ObservedCatalogEffortClamp {
|
|
284
|
+
if (!supported) return { removedEfforts: [], affectedModels: [] };
|
|
272
285
|
|
|
273
286
|
const removed = new Set<string>();
|
|
274
287
|
const affected: string[] = [];
|
|
@@ -301,6 +314,20 @@ export function clampCatalogModelsToCodexSupport(models: RawEntry[], deps: Bundl
|
|
|
301
314
|
}
|
|
302
315
|
}
|
|
303
316
|
|
|
317
|
+
return {
|
|
318
|
+
removedEfforts: [...removed].sort(),
|
|
319
|
+
affectedModels: affected,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function clampCatalogModelsToCodexSupport(models: RawEntry[], deps: BundledCatalogDeps = {}): RawEntry[] {
|
|
324
|
+
const supported = codexSupportedReasoningEfforts(deps);
|
|
325
|
+
if (!supported) {
|
|
326
|
+
if (!deps.commandCandidates) persistEffortClamp(null, { configDir: deps.configDir });
|
|
327
|
+
return models;
|
|
328
|
+
}
|
|
329
|
+
const clamp = clampCatalogModelsToObservedCodexSupport(models, supported);
|
|
330
|
+
|
|
304
331
|
let runtimePath = "codex";
|
|
305
332
|
let runtimeVersion: string | null = null;
|
|
306
333
|
if (!deps.commandCandidates) {
|
|
@@ -337,12 +364,12 @@ export function clampCatalogModelsToCodexSupport(models: RawEntry[], deps: Bundl
|
|
|
337
364
|
}
|
|
338
365
|
}
|
|
339
366
|
|
|
340
|
-
if (
|
|
367
|
+
if (clamp.removedEfforts.length > 0) {
|
|
341
368
|
const diagnostic: EffortClampDiagnostic = {
|
|
342
369
|
runtimePath,
|
|
343
370
|
runtimeVersion,
|
|
344
|
-
removedEfforts: [...
|
|
345
|
-
affectedModels:
|
|
371
|
+
removedEfforts: [...clamp.removedEfforts],
|
|
372
|
+
affectedModels: [...clamp.affectedModels],
|
|
346
373
|
};
|
|
347
374
|
for (const line of formatClampLogLines(diagnostic)) console.warn(line);
|
|
348
375
|
if (!deps.commandCandidates) persistEffortClamp(diagnostic, { configDir: deps.configDir });
|
|
@@ -9,7 +9,7 @@ import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
|
|
|
9
9
|
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
10
10
|
import { modelInList } from "../../types";
|
|
11
11
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
12
|
-
import {
|
|
12
|
+
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
|
|
13
13
|
import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
14
14
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
15
15
|
import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap";
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
COMBO_NAMESPACE,
|
|
23
23
|
comboModelId,
|
|
24
24
|
getCombo,
|
|
25
|
+
isNativeAliasCombo,
|
|
25
26
|
listComboIds,
|
|
26
27
|
targetKey,
|
|
27
28
|
} from "../../combos";
|
|
@@ -34,18 +35,48 @@ import upstreamModelsSnapshot from "../data/upstream-models.json";
|
|
|
34
35
|
import type { RawEntry } from "./parsing";
|
|
35
36
|
import { readCurrentCatalogOrCache, unique } from "./bundled";
|
|
36
37
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
];
|
|
38
|
+
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
39
|
+
import { NATIVE_OPENAI_MODELS, SUPPORTED_NATIVE_OPENAI_SLUGS } from "./native-models";
|
|
40
|
+
export { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
41
|
+
export { NATIVE_OPENAI_MODELS, SUPPORTED_NATIVE_OPENAI_SLUGS } from "./native-models";
|
|
42
42
|
|
|
43
43
|
export const DOCUMENTED_NATIVE_OPENAI_ADDITIONS = [
|
|
44
44
|
"gpt-5.3-codex-spark",
|
|
45
45
|
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna",
|
|
46
46
|
];
|
|
47
47
|
|
|
48
|
-
export
|
|
48
|
+
export function configuredNativeAliasSlugs(
|
|
49
|
+
config: Pick<OcxConfig, "combos">,
|
|
50
|
+
): Set<string> {
|
|
51
|
+
const aliases = new Set<string>();
|
|
52
|
+
for (const raw of Object.values(config.combos ?? {})) {
|
|
53
|
+
if (!isNativeAliasCombo(raw)) continue;
|
|
54
|
+
const alias = raw.alias!.trim();
|
|
55
|
+
if (SUPPORTED_NATIVE_OPENAI_SLUGS.has(alias)) aliases.add(alias);
|
|
56
|
+
}
|
|
57
|
+
return aliases;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Bare native rows that must be absent, rather than merely hidden, while Desktop native-alias
|
|
62
|
+
* compatibility is active. Codex Desktop's remote allowlist can ignore `visibility: "hide"`;
|
|
63
|
+
* omitting disabled native rows is therefore part of the explicit native-alias opt-in.
|
|
64
|
+
*/
|
|
65
|
+
export function desktopAllowlistSuppressedNativeSlugs(
|
|
66
|
+
config: Pick<OcxConfig, "combos" | "disabledModels">,
|
|
67
|
+
): Set<string> {
|
|
68
|
+
const suppressed = configuredNativeAliasSlugs(config);
|
|
69
|
+
if (suppressed.size === 0) return suppressed;
|
|
70
|
+
const disabled = disabledNativeSlugs(config);
|
|
71
|
+
for (const slug of NATIVE_OPENAI_MODELS) {
|
|
72
|
+
if (disabled.has(slug)) suppressed.add(slug);
|
|
73
|
+
}
|
|
74
|
+
return suppressed;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isNativeAliasCatalogEntry(entry: RawEntry): boolean {
|
|
78
|
+
return entry.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND;
|
|
79
|
+
}
|
|
49
80
|
|
|
50
81
|
export function isUnsupportedOpenAiNativeSlug(slug: string): boolean {
|
|
51
82
|
if (slug.includes("/")) return false;
|
|
@@ -64,15 +95,27 @@ export const NATIVE_OPENAI_CONTEXT_OVERRIDES: Record<string, { contextWindow?: n
|
|
|
64
95
|
"gpt-5.6-luna": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW },
|
|
65
96
|
};
|
|
66
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Pinned capability metadata is safe to use as a fallback for every supported native model.
|
|
100
|
+
* Keep it separate from UPSTREAM_NATIVE_ENTRIES: that narrower map also authorizes replacing
|
|
101
|
+
* persisted native rows during sync, which is currently intentional only for the GPT-5.6 family.
|
|
102
|
+
*/
|
|
103
|
+
const PINNED_NATIVE_CAPABILITY_ENTRIES: Map<string, RawEntry> = new Map(
|
|
104
|
+
((upstreamModelsSnapshot as unknown as { models?: RawEntry[] }).models ?? [])
|
|
105
|
+
.filter(m => typeof m.slug === "string"
|
|
106
|
+
&& SUPPORTED_NATIVE_OPENAI_SLUGS.has(m.slug as string))
|
|
107
|
+
.map(m => [m.slug as string, m]),
|
|
108
|
+
);
|
|
109
|
+
|
|
67
110
|
export function nativeOpenAiContextWindow(slug: string): number | undefined {
|
|
68
111
|
return NATIVE_OPENAI_CONTEXT_OVERRIDES[slug]?.contextWindow
|
|
69
|
-
?? (typeof
|
|
70
|
-
?
|
|
112
|
+
?? (typeof PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)?.context_window === "number"
|
|
113
|
+
? PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)!.context_window as number
|
|
71
114
|
: undefined);
|
|
72
115
|
}
|
|
73
116
|
|
|
74
117
|
export function nativeInputModalities(slug: string): string[] {
|
|
75
|
-
const upstream =
|
|
118
|
+
const upstream = PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug);
|
|
76
119
|
if (Array.isArray(upstream?.input_modalities) && upstream!.input_modalities!.length > 0) {
|
|
77
120
|
return [...upstream!.input_modalities as string[]];
|
|
78
121
|
}
|
|
@@ -82,7 +125,7 @@ export function nativeInputModalities(slug: string): string[] {
|
|
|
82
125
|
}
|
|
83
126
|
|
|
84
127
|
export function nativeReasoningEfforts(slug: string): string[] {
|
|
85
|
-
const upstream =
|
|
128
|
+
const upstream = PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug);
|
|
86
129
|
const levels = Array.isArray(upstream?.supported_reasoning_levels)
|
|
87
130
|
? upstream!.supported_reasoning_levels as Array<{ effort?: string }>
|
|
88
131
|
: [];
|
|
@@ -97,12 +140,18 @@ export function nativeReasoningEfforts(slug: string): string[] {
|
|
|
97
140
|
|
|
98
141
|
/** Upstream-pinned default for a native slug, when present and non-empty. */
|
|
99
142
|
export function nativeDefaultReasoningEffort(slug: string): string | undefined {
|
|
100
|
-
const level =
|
|
143
|
+
const level = PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)?.default_reasoning_level;
|
|
101
144
|
return typeof level === "string" && level.length > 0 ? level : undefined;
|
|
102
145
|
}
|
|
103
146
|
|
|
147
|
+
/** Upstream-pinned multi-agent surface for a supported native slug, when present. */
|
|
148
|
+
export function nativeMultiAgentVersion(slug: string): string | undefined {
|
|
149
|
+
const version = PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)?.multi_agent_version;
|
|
150
|
+
return typeof version === "string" && version.length > 0 ? version : undefined;
|
|
151
|
+
}
|
|
152
|
+
|
|
104
153
|
export function nativeParallelToolCalls(slug: string): boolean {
|
|
105
|
-
return
|
|
154
|
+
return PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)?.supports_parallel_tool_calls === true
|
|
106
155
|
|| false;
|
|
107
156
|
}
|
|
108
157
|
|
|
@@ -116,9 +165,10 @@ export function disabledNativeSlugs(config: Pick<OcxConfig, "disabledModels">):
|
|
|
116
165
|
return new Set((config.disabledModels ?? []).filter(id => !id.includes("/")));
|
|
117
166
|
}
|
|
118
167
|
|
|
119
|
-
export function visibleNativeSlugs(config: Pick<OcxConfig, "disabledModels">): string[] {
|
|
168
|
+
export function visibleNativeSlugs(config: Pick<OcxConfig, "disabledModels" | "combos">): string[] {
|
|
120
169
|
const disabled = disabledNativeSlugs(config);
|
|
121
|
-
|
|
170
|
+
const shadowed = configuredNativeAliasSlugs(config);
|
|
171
|
+
return nativeOpenAiSlugs().filter(slug => !disabled.has(slug) && !shadowed.has(slug));
|
|
122
172
|
}
|
|
123
173
|
|
|
124
174
|
/** Whether an enabled canonical OpenAI provider can serve exact account-qualified routes. */
|
|
@@ -144,14 +194,15 @@ export function shouldIncludeNativeOpenAi(config: Pick<OcxConfig, "providers">):
|
|
|
144
194
|
}
|
|
145
195
|
|
|
146
196
|
/** Native slugs exposed to Claude Desktop show/export/apply (opt-out via claudeCode.desktopNativeModels). */
|
|
147
|
-
export function desktopVisibleNativeSlugs(config: Pick<OcxConfig, "claudeCode" | "disabledModels">): string[] {
|
|
197
|
+
export function desktopVisibleNativeSlugs(config: Pick<OcxConfig, "claudeCode" | "disabledModels" | "combos">): string[] {
|
|
148
198
|
if (config.claudeCode?.desktopNativeModels === false) return [];
|
|
149
199
|
return visibleNativeSlugs(config);
|
|
150
200
|
}
|
|
151
201
|
|
|
152
|
-
export function nativeModelRows(config: Pick<OcxConfig, "disabledModels">): Array<{ slug: string; disabled: boolean; contextWindow?: number }> {
|
|
202
|
+
export function nativeModelRows(config: Pick<OcxConfig, "disabledModels" | "combos">): Array<{ slug: string; disabled: boolean; contextWindow?: number }> {
|
|
153
203
|
const disabled = disabledNativeSlugs(config);
|
|
154
|
-
|
|
204
|
+
const shadowed = configuredNativeAliasSlugs(config);
|
|
205
|
+
return NATIVE_OPENAI_MODELS.filter(slug => !shadowed.has(slug)).map(slug => {
|
|
155
206
|
const contextWindow = nativeOpenAiContextWindow(slug);
|
|
156
207
|
return { slug, disabled: disabled.has(slug), ...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
157
208
|
});
|
|
@@ -163,6 +214,7 @@ export function applyNativeVisibility(
|
|
|
163
214
|
hideBareNative = false,
|
|
164
215
|
): RawEntry[] {
|
|
165
216
|
for (const entry of entries) {
|
|
217
|
+
if (isNativeAliasCatalogEntry(entry)) continue;
|
|
166
218
|
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
167
219
|
const accountBoundSlug = trustedAccountBoundNativeCatalogSlug(entry);
|
|
168
220
|
const nativeSlug = accountBoundSlug ?? slug;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Native OpenAI model ids that this release can route and restore with authoritative metadata. */
|
|
2
|
+
export const NATIVE_OPENAI_MODELS = [
|
|
3
|
+
"gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark",
|
|
4
|
+
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna",
|
|
5
|
+
];
|
|
6
|
+
|
|
7
|
+
export const SUPPORTED_NATIVE_OPENAI_SLUGS = new Set(NATIVE_OPENAI_MODELS);
|
|
@@ -9,7 +9,7 @@ import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
|
|
|
9
9
|
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
10
10
|
import { modelInList } from "../../types";
|
|
11
11
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
12
|
-
import {
|
|
12
|
+
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
|
|
13
13
|
import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
14
14
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
15
15
|
import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap";
|
|
@@ -31,8 +31,9 @@ import { redactSecretString } from "../../lib/redact";
|
|
|
31
31
|
import upstreamModelsSnapshot from "../data/upstream-models.json";
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES } from "./metadata";
|
|
34
|
+
import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, nativeMultiAgentVersion } from "./metadata";
|
|
35
35
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
36
|
+
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
36
37
|
|
|
37
38
|
export function legacyCatalogBackupPath(): string {
|
|
38
39
|
return join(getConfigDir(), "catalog-backup.json");
|
|
@@ -85,11 +86,18 @@ export function isDefaultCatalogPath(path: string): boolean {
|
|
|
85
86
|
return samePath(path, activeDefaultCatalogPath());
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
/** Stable nonsemantic ownership marker for rows projected from config.customModels. */
|
|
90
|
+
export const CODEX_CUSTOM_MODEL_CATALOG_KIND = "custom-model-v1";
|
|
91
|
+
/** A formerly ambiguous slug was authoritatively observed as an ordinary provider row. */
|
|
92
|
+
export const CODEX_PROVIDER_MODEL_CATALOG_KIND = "provider-model-v1";
|
|
93
|
+
|
|
88
94
|
export interface CatalogModel {
|
|
89
95
|
id: string;
|
|
90
96
|
provider: string;
|
|
91
97
|
/** Public Codex-facing slug override (used by combo aliases). */
|
|
92
98
|
alias?: string;
|
|
99
|
+
/** Explicit combo takeover of a bare OpenAI-native catalog id. */
|
|
100
|
+
nativeAlias?: boolean;
|
|
93
101
|
/**
|
|
94
102
|
* Display-only Codex catalog `display_name` override. Relabels the picker row ONLY — it never
|
|
95
103
|
* affects the routing slug, alias-collision order, native marketing-name precedence, or provider
|
|
@@ -113,13 +121,15 @@ export interface CatalogModel {
|
|
|
113
121
|
supportsReasoningSummaries?: boolean;
|
|
114
122
|
/** Normalized upstream capability names retained for management/API consumers (#485 follow-up). */
|
|
115
123
|
capabilities?: string[];
|
|
124
|
+
/** OpenCodex-only catalog ownership marker; Codex ignores the serialized extension field. */
|
|
125
|
+
catalogKind?: typeof CODEX_CUSTOM_MODEL_CATALOG_KIND | typeof CODEX_PROVIDER_MODEL_CATALOG_KIND;
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
export type RawEntry = Record<string, unknown>;
|
|
119
129
|
|
|
120
130
|
export type RawCatalog = { models?: RawEntry[]; [k: string]: unknown };
|
|
121
131
|
|
|
122
|
-
export const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go"]);
|
|
132
|
+
export const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go", "deepseek"]);
|
|
123
133
|
|
|
124
134
|
export const ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS = new Set([
|
|
125
135
|
// Issue #82: Zen Go /models advertises HY3, but Console Go rejects it as outside the lite list.
|
|
@@ -193,7 +203,12 @@ export function readCatalog(path: string): RawCatalog | null {
|
|
|
193
203
|
|
|
194
204
|
export function findNativeTemplate(catalog: RawCatalog | null): RawEntry | null {
|
|
195
205
|
return catalog?.models?.find(
|
|
196
|
-
m => typeof m.slug === "string"
|
|
206
|
+
m => typeof m.slug === "string"
|
|
207
|
+
&& !m.slug.includes("/")
|
|
208
|
+
&& "base_instructions" in m
|
|
209
|
+
&& m.opencodex_catalog_kind !== CODEX_NATIVE_ALIAS_CATALOG_KIND
|
|
210
|
+
&& m.owned_by !== COMBO_NAMESPACE
|
|
211
|
+
&& !(typeof m.description === "string" && m.description.startsWith("Routed via opencodex → ")),
|
|
197
212
|
) ?? null;
|
|
198
213
|
}
|
|
199
214
|
|
|
@@ -320,8 +335,10 @@ export function applyMultiAgentMode(entries: RawEntry[], mode: MultiAgentMode, v
|
|
|
320
335
|
// re-apply upstream pins from the snapshot for native entries that have one.
|
|
321
336
|
for (const entry of entries) {
|
|
322
337
|
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
323
|
-
const
|
|
324
|
-
const upstreamPin =
|
|
338
|
+
const nativeAlias = entry.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND;
|
|
339
|
+
const upstreamPin = nativeAlias
|
|
340
|
+
? nativeMultiAgentVersion(slug)
|
|
341
|
+
: UPSTREAM_NATIVE_ENTRIES.get(trustedAccountBoundNativeCatalogSlug(entry) ?? slug)?.multi_agent_version;
|
|
325
342
|
if (typeof upstreamPin === "string") {
|
|
326
343
|
entry.multi_agent_version = upstreamPin;
|
|
327
344
|
} else if (v2FeatureEnabled) {
|
|
@@ -392,11 +409,11 @@ export function catalogModelSupportsReasoningSummaries(modelId: string): boolean
|
|
|
392
409
|
return values.size === 1 ? values.values().next().value : undefined;
|
|
393
410
|
}
|
|
394
411
|
|
|
395
|
-
export function
|
|
396
|
-
const jawcodeProvider =
|
|
412
|
+
export function applyCatalogMetadata(entry: RawEntry, provider: string, modelId: string, contextCap?: number): void {
|
|
413
|
+
const jawcodeProvider = resolveMetadataProvider(provider);
|
|
397
414
|
if (!jawcodeProvider) return;
|
|
398
|
-
const meta =
|
|
399
|
-
?? (shouldCaseFoldMetadataModelId(provider) ?
|
|
415
|
+
const meta = getModelMetadata(jawcodeProvider, modelId)
|
|
416
|
+
?? (shouldCaseFoldMetadataModelId(provider) ? getModelMetadataCaseInsensitive(jawcodeProvider, modelId) : undefined);
|
|
400
417
|
if (!meta) return;
|
|
401
418
|
if (typeof meta.contextWindow === "number" && meta.contextWindow > 0) {
|
|
402
419
|
const contextWindow = applyProviderContextCap(meta.contextWindow, contextCap) ?? meta.contextWindow;
|
|
@@ -425,7 +442,8 @@ export function readCatalogBackup(catalogPath: string): RawCatalog | null {
|
|
|
425
442
|
}
|
|
426
443
|
|
|
427
444
|
export function catalogHasRoutedEntries(catalog: RawCatalog | null): boolean {
|
|
428
|
-
return (catalog?.models ?? []).some(m => typeof m.slug === "string"
|
|
445
|
+
return (catalog?.models ?? []).some(m => typeof m.slug === "string"
|
|
446
|
+
&& (m.slug.includes("/") || m.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND));
|
|
429
447
|
}
|
|
430
448
|
|
|
431
449
|
export function writePristineCatalogBackup(backupPath: string, catalogPath: string, catalog: RawCatalog): void {
|