@bitkyc08/opencodex 2.14.1 → 2.15.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/gui/dist/assets/index-B5T5ADgY.js +76 -0
- package/gui/dist/assets/{index-DWhX3yMp.css → index-DUCH59lJ.css} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/command-code.ts +15 -4
- package/src/adapters/cursor/effort-map.ts +4 -5
- package/src/adapters/cursor/request-builder.ts +55 -11
- package/src/adapters/cursor/tool-definitions.ts +24 -0
- package/src/adapters/kiro.ts +10 -1
- package/src/adapters/openai-chat.ts +5 -3
- package/src/adapters/openai-responses.ts +109 -0
- package/src/adapters/tool-catalog-nudge.ts +26 -4
- package/src/bridge.ts +50 -3
- package/src/cli/init.ts +4 -17
- package/src/codex/catalog/effort.ts +2 -1
- package/src/codex/catalog/metadata.ts +62 -12
- package/src/codex/catalog/native-models.ts +27 -0
- package/src/codex/catalog/parsing.ts +17 -2
- package/src/codex/catalog/provider-fetch.ts +47 -5
- package/src/codex/catalog/sync.ts +21 -7
- package/src/codex/catalog.ts +1 -1
- package/src/config.ts +79 -4
- package/src/generated/compatibility-version.json +54 -42
- package/src/generated/model-metadata.ts +1 -1
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/tool-argument-integers.ts +158 -0
- package/src/oauth/index.ts +3 -0
- package/src/oauth/nous.ts +58 -9
- package/src/providers/antigravity-models.ts +93 -28
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/command-code-efforts.ts +18 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/quota.ts +9 -2
- package/src/providers/registry.ts +13 -6
- package/src/responses/spill-store.ts +5 -1
- package/src/responses/state.ts +50 -2
- package/src/server/index.ts +2 -1
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/logs-usage-routes.ts +48 -10
- package/src/server/management/provider-routes.ts +2 -1
- package/src/server/management/usage-summary-cache.ts +7 -1
- package/src/server/responses/collaboration.ts +12 -2
- package/src/server/responses/core.ts +33 -16
- package/src/server/startup-health-cache.ts +12 -0
- package/src/usage/expected-prices.ts +13 -0
- package/src/usage/log.ts +430 -12
- package/gui/dist/assets/index-DuaUVm_d.js +0 -76
|
@@ -15,7 +15,7 @@ import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../..
|
|
|
15
15
|
import { getProviderRegistryEntry } from "../../providers/registry";
|
|
16
16
|
import { applyProviderContextCap, providerContextCap } from "../../providers/context-cap";
|
|
17
17
|
import { routedSlug, slugEquals, slugsEquivalent } from "../../providers/slug-codec";
|
|
18
|
-
import {
|
|
18
|
+
import { identifyRoutedModel } from "../../adapters/identity";
|
|
19
19
|
import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
|
|
20
20
|
import { fetchCursorUsableModels } from "../../adapters/cursor/live-models";
|
|
21
21
|
import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
@@ -37,9 +37,23 @@ import type { RawEntry } from "./parsing";
|
|
|
37
37
|
import { readCurrentCatalogOrCache, readCurrentCodexCatalog, readCurrentCodexModelsCache, unique } from "./bundled";
|
|
38
38
|
import { trustedAccountBoundNativeCatalogSlug, visibleCodexAccountSelectors } from "./account-models";
|
|
39
39
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
40
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
42
|
+
NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS,
|
|
43
|
+
NATIVE_OPENAI_MODELS,
|
|
44
|
+
SUPPORTED_NATIVE_OPENAI_SLUGS,
|
|
45
|
+
isNativeOpenAiCapabilityAliasModel,
|
|
46
|
+
nativeOpenAiCapabilitySourceSlug,
|
|
47
|
+
} from "./native-models";
|
|
41
48
|
export { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
42
|
-
export {
|
|
49
|
+
export {
|
|
50
|
+
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
51
|
+
NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS,
|
|
52
|
+
NATIVE_OPENAI_MODELS,
|
|
53
|
+
SUPPORTED_NATIVE_OPENAI_SLUGS,
|
|
54
|
+
isNativeOpenAiCapabilityAliasModel,
|
|
55
|
+
nativeOpenAiCapabilitySourceSlug,
|
|
56
|
+
} from "./native-models";
|
|
43
57
|
|
|
44
58
|
export const DOCUMENTED_NATIVE_OPENAI_ADDITIONS = [
|
|
45
59
|
"gpt-5.3-codex-spark",
|
|
@@ -94,18 +108,28 @@ export const NATIVE_OPENAI_CONTEXT_OVERRIDES: Record<string, { contextWindow?: n
|
|
|
94
108
|
"gpt-5.6-sol": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW },
|
|
95
109
|
"gpt-5.6-terra": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW },
|
|
96
110
|
"gpt-5.6-luna": { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW },
|
|
111
|
+
[NATIVE_DAYBREAK_BLUE_MODEL]: { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_CONTEXT_WINDOW },
|
|
97
112
|
};
|
|
98
113
|
|
|
114
|
+
const PINNED_UPSTREAM_MODELS: Map<string, RawEntry> = new Map(
|
|
115
|
+
((upstreamModelsSnapshot as unknown as { models?: RawEntry[] }).models ?? [])
|
|
116
|
+
.flatMap(model => typeof model.slug === "string" ? [[model.slug, model] as const] : []),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
function pinnedNativeCapabilityEntry(slug: string): RawEntry | undefined {
|
|
120
|
+
return PINNED_UPSTREAM_MODELS.get(nativeOpenAiCapabilitySourceSlug(slug));
|
|
121
|
+
}
|
|
122
|
+
|
|
99
123
|
/**
|
|
100
124
|
* Pinned capability metadata is safe to use as a fallback for every supported native model.
|
|
101
125
|
* Keep it separate from UPSTREAM_NATIVE_ENTRIES: that narrower map also authorizes replacing
|
|
102
126
|
* persisted native rows during sync, which is currently intentional only for the GPT-5.6 family.
|
|
103
127
|
*/
|
|
104
128
|
const PINNED_NATIVE_CAPABILITY_ENTRIES: Map<string, RawEntry> = new Map(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
[...NATIVE_OPENAI_MODELS, ...NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS].flatMap(slug => {
|
|
130
|
+
const entry = pinnedNativeCapabilityEntry(slug);
|
|
131
|
+
return entry ? [[slug, entry] as const] : [];
|
|
132
|
+
}),
|
|
109
133
|
);
|
|
110
134
|
|
|
111
135
|
export function nativeOpenAiContextWindow(slug: string, contextCap?: number): number | undefined {
|
|
@@ -259,12 +283,38 @@ export function applyNativeVisibility(
|
|
|
259
283
|
return entries;
|
|
260
284
|
}
|
|
261
285
|
|
|
286
|
+
function upstreamNativeEntryForSlug(slug: string): RawEntry | undefined {
|
|
287
|
+
const sourceSlug = nativeOpenAiCapabilitySourceSlug(slug);
|
|
288
|
+
if (!sourceSlug.startsWith("gpt-5.6-")) return undefined;
|
|
289
|
+
const source = PINNED_UPSTREAM_MODELS.get(sourceSlug);
|
|
290
|
+
if (!source) return undefined;
|
|
291
|
+
if (slug === sourceSlug) return source;
|
|
292
|
+
|
|
293
|
+
const alias = structuredClone(source) as RawEntry;
|
|
294
|
+
alias.slug = slug;
|
|
295
|
+
alias.display_name = "Daybreak Blue";
|
|
296
|
+
alias.description = "Frontier general-purpose model with safeguards for defensive cybersecurity work.";
|
|
297
|
+
if (typeof alias.base_instructions === "string") {
|
|
298
|
+
alias.base_instructions = identifyRoutedModel(alias.base_instructions, slug);
|
|
299
|
+
}
|
|
300
|
+
if (alias.model_messages && typeof alias.model_messages === "object" && !Array.isArray(alias.model_messages)) {
|
|
301
|
+
const modelMessages = alias.model_messages as Record<string, unknown>;
|
|
302
|
+
if (typeof modelMessages.instructions_template === "string") {
|
|
303
|
+
alias.model_messages = {
|
|
304
|
+
...modelMessages,
|
|
305
|
+
instructions_template: identifyRoutedModel(modelMessages.instructions_template, slug),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
delete alias.availability_nux;
|
|
310
|
+
return alias;
|
|
311
|
+
}
|
|
312
|
+
|
|
262
313
|
export const UPSTREAM_NATIVE_ENTRIES: Map<string, RawEntry> = new Map(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
.map(m => [m.slug as string, m]),
|
|
314
|
+
[...NATIVE_OPENAI_MODELS, ...NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS].flatMap(slug => {
|
|
315
|
+
const entry = upstreamNativeEntryForSlug(slug);
|
|
316
|
+
return entry ? [[slug, entry] as const] : [];
|
|
317
|
+
}),
|
|
268
318
|
);
|
|
269
319
|
|
|
270
320
|
export function upstreamNativeEntry(slug: string): RawEntry | null {
|
|
@@ -1,3 +1,30 @@
|
|
|
1
|
+
/** ChatGPT/Codex wire id observed for the account-native Daybreak Blue surface. */
|
|
2
|
+
export const NATIVE_DAYBREAK_BLUE_MODEL = "gpt-daybreak-blue-latest";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Account-native aliases whose Codex capabilities track another pinned native row.
|
|
6
|
+
*
|
|
7
|
+
* This is catalog metadata inheritance only. Routing always preserves the requested
|
|
8
|
+
* wire id, so the ChatGPT/Codex `gpt-daybreak-*` surface never collapses into the
|
|
9
|
+
* separately billed API-key `daybreak-*-latest` surface or into `gpt-5.6-sol`.
|
|
10
|
+
*/
|
|
11
|
+
const NATIVE_OPENAI_CAPABILITY_SOURCES: Readonly<Record<string, string>> = Object.freeze({
|
|
12
|
+
[NATIVE_DAYBREAK_BLUE_MODEL]: "gpt-5.6-sol",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
/** Account-scoped native ids that may inherit metadata but never enter the bare allowlist. */
|
|
16
|
+
export const NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS = Object.freeze(
|
|
17
|
+
Object.keys(NATIVE_OPENAI_CAPABILITY_SOURCES),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
export function isNativeOpenAiCapabilityAliasModel(slug: string): boolean {
|
|
21
|
+
return Object.hasOwn(NATIVE_OPENAI_CAPABILITY_SOURCES, slug);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function nativeOpenAiCapabilitySourceSlug(slug: string): string {
|
|
25
|
+
return NATIVE_OPENAI_CAPABILITY_SOURCES[slug] ?? slug;
|
|
26
|
+
}
|
|
27
|
+
|
|
1
28
|
/** Native OpenAI model ids that this release can route and restore with authoritative metadata. */
|
|
2
29
|
export const NATIVE_OPENAI_MODELS = [
|
|
3
30
|
"gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark",
|
|
@@ -31,7 +31,7 @@ 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, nativeMultiAgentVersion } from "./metadata";
|
|
34
|
+
import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, isNativeOpenAiCapabilityAliasModel, nativeMultiAgentVersion } from "./metadata";
|
|
35
35
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
36
36
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
37
37
|
|
|
@@ -116,6 +116,11 @@ export interface CatalogModel {
|
|
|
116
116
|
inputModalities?: string[];
|
|
117
117
|
/** Provider opted into parallel tool calls (OcxProviderConfig.parallelToolCalls). */
|
|
118
118
|
parallelToolCalls?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* This routed row is an explicitly configured account-native alias on the canonical ChatGPT
|
|
121
|
+
* forward provider. It may inherit pinned native Codex metadata without changing its wire id.
|
|
122
|
+
*/
|
|
123
|
+
codexForwardNativeCapabilityAlias?: boolean;
|
|
119
124
|
/** Whether Codex may send Responses text.verbosity for this routed model. */
|
|
120
125
|
supportsVerbosity?: boolean;
|
|
121
126
|
supportsReasoningSummaries?: boolean;
|
|
@@ -359,9 +364,19 @@ export function applyMultiAgentMode(entries: RawEntry[], mode: MultiAgentMode, v
|
|
|
359
364
|
for (const entry of entries) {
|
|
360
365
|
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
361
366
|
const nativeAlias = entry.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND;
|
|
367
|
+
const routedNativeSlug = slug.startsWith(`${OPENAI_CODEX_PROVIDER_ID}/`)
|
|
368
|
+
? slug.slice(OPENAI_CODEX_PROVIDER_ID.length + 1)
|
|
369
|
+
: "";
|
|
370
|
+
const codexForwardCapabilityAlias = entry.opencodex_catalog_kind === CODEX_CUSTOM_MODEL_CATALOG_KIND
|
|
371
|
+
&& entry.use_responses_lite === true
|
|
372
|
+
&& isNativeOpenAiCapabilityAliasModel(routedNativeSlug)
|
|
373
|
+
? routedNativeSlug
|
|
374
|
+
: undefined;
|
|
362
375
|
const upstreamPin = nativeAlias
|
|
363
376
|
? nativeMultiAgentVersion(slug)
|
|
364
|
-
:
|
|
377
|
+
: codexForwardCapabilityAlias
|
|
378
|
+
? nativeMultiAgentVersion(codexForwardCapabilityAlias)
|
|
379
|
+
: UPSTREAM_NATIVE_ENTRIES.get(trustedAccountBoundNativeCatalogSlug(entry) ?? slug)?.multi_agent_version;
|
|
365
380
|
if (typeof upstreamPin === "string") {
|
|
366
381
|
entry.multi_agent_version = upstreamPin;
|
|
367
382
|
} else if (v2FeatureEnabled) {
|
|
@@ -69,7 +69,7 @@ import { createAdmissionGate, ResourceAdmissionError, type AdmissionMetrics } fr
|
|
|
69
69
|
|
|
70
70
|
import { CODEX_CUSTOM_MODEL_CATALOG_KIND, JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
|
|
71
71
|
import type { CatalogModel } from "./parsing";
|
|
72
|
-
import { disabledNativeSlugs, hasComboTargets, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
72
|
+
import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
73
73
|
import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
|
|
74
74
|
import type { ComboCatalogOmission } from "./aggregation";
|
|
75
75
|
import type { CatalogGatherProviderAuthEvidence } from "./filesystem-evidence";
|
|
@@ -209,6 +209,15 @@ interface GatherInflightEntry {
|
|
|
209
209
|
readonly promise: Promise<GatherFlightResult>;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
function withCanonicalOpenAiForwardAuthDefault(
|
|
213
|
+
name: string,
|
|
214
|
+
provider: OcxProviderConfig,
|
|
215
|
+
): OcxProviderConfig {
|
|
216
|
+
if (name !== OPENAI_CODEX_PROVIDER_ID || provider.authMode !== undefined) return provider;
|
|
217
|
+
const candidate = { ...provider, authMode: "forward" as const };
|
|
218
|
+
return isCanonicalOpenAiForwardProvider(candidate) ? candidate : provider;
|
|
219
|
+
}
|
|
220
|
+
|
|
212
221
|
const gatherInflight = new Map<string, GatherInflightEntry[]>();
|
|
213
222
|
const CATALOG_GATHER_AUTHORITY_KEY = randomBytes(32);
|
|
214
223
|
const REQUEST_CREDENTIAL_SENTINEL = `ocx-catalog-credential-${randomBytes(16).toString("hex")}`;
|
|
@@ -390,7 +399,7 @@ function captureProviderGather(
|
|
|
390
399
|
authResolver: ModelsAuthResolver,
|
|
391
400
|
retainConfiguredModelIds?: ReadonlySet<string>,
|
|
392
401
|
): CapturedProviderGather {
|
|
393
|
-
const enriched = detachedClone(configured);
|
|
402
|
+
const enriched = detachedClone(withCanonicalOpenAiForwardAuthDefault(name, configured));
|
|
394
403
|
enrichProviderFromRegistry(name, enriched);
|
|
395
404
|
const provider = recursivelyFreeze(enriched);
|
|
396
405
|
const observedAuth = authResolver.kind === "observed"
|
|
@@ -1714,16 +1723,49 @@ async function gatherRoutedModelsUncached(
|
|
|
1714
1723
|
const replacedByRoutedSlug = new Map(all.map(model => [routedSlug(model.provider, model.id), model]));
|
|
1715
1724
|
const customModels = (config.customModels ?? []).map(cm => {
|
|
1716
1725
|
const rawProvider = config.providers[cm.provider];
|
|
1726
|
+
// Registry routing backfills an omitted authMode on the built-in OpenAI provider to
|
|
1727
|
+
// forward. Keep the catalog projection on the same contract while still failing closed
|
|
1728
|
+
// for every explicit non-forward mode and every non-canonical endpoint.
|
|
1729
|
+
const providerForCanonicalCheck = rawProvider
|
|
1730
|
+
? withCanonicalOpenAiForwardAuthDefault(cm.provider, rawProvider)
|
|
1731
|
+
: undefined;
|
|
1732
|
+
const codexForwardNativeCapabilityAlias = cm.provider === OPENAI_CODEX_PROVIDER_ID
|
|
1733
|
+
&& providerForCanonicalCheck !== undefined
|
|
1734
|
+
&& isCanonicalOpenAiForwardProvider(providerForCanonicalCheck)
|
|
1735
|
+
&& isNativeOpenAiCapabilityAliasModel(cm.modelId);
|
|
1736
|
+
const nativeAliasContextWindow = codexForwardNativeCapabilityAlias
|
|
1737
|
+
? nativeOpenAiContextWindow(cm.modelId, providerContextCap(config, OPENAI_CODEX_PROVIDER_ID))
|
|
1738
|
+
: undefined;
|
|
1739
|
+
const customContextWindow = cm.contextWindow
|
|
1740
|
+
? nativeAliasContextWindow !== undefined
|
|
1741
|
+
? Math.min(cm.contextWindow, nativeAliasContextWindow)
|
|
1742
|
+
: cm.contextWindow
|
|
1743
|
+
: nativeAliasContextWindow;
|
|
1744
|
+
const nativeAliasDefaultEffort = codexForwardNativeCapabilityAlias
|
|
1745
|
+
? nativeDefaultReasoningEffort(cm.modelId)
|
|
1746
|
+
: undefined;
|
|
1717
1747
|
const supportsReasoningSummaries = configuredReasoningSummarySupport(rawProvider, cm.modelId);
|
|
1718
1748
|
const base: CatalogModel = {
|
|
1719
1749
|
id: cm.modelId,
|
|
1720
1750
|
provider: cm.provider,
|
|
1721
1751
|
catalogKind: CODEX_CUSTOM_MODEL_CATALOG_KIND,
|
|
1722
1752
|
// Display-only label: never feeds routing (customModels are keyed by routedSlug below).
|
|
1723
|
-
...(cm.displayName
|
|
1724
|
-
|
|
1725
|
-
|
|
1753
|
+
...(cm.displayName
|
|
1754
|
+
? { displayName: cm.displayName }
|
|
1755
|
+
: codexForwardNativeCapabilityAlias ? { displayName: "Daybreak Blue" } : {}),
|
|
1756
|
+
...(customContextWindow !== undefined ? { contextWindow: customContextWindow } : {}),
|
|
1757
|
+
...(cm.inputModalities
|
|
1758
|
+
? { inputModalities: cm.inputModalities }
|
|
1759
|
+
: codexForwardNativeCapabilityAlias ? { inputModalities: nativeInputModalities(cm.modelId) } : {}),
|
|
1726
1760
|
...(typeof supportsReasoningSummaries === "boolean" ? { supportsReasoningSummaries } : {}),
|
|
1761
|
+
...(codexForwardNativeCapabilityAlias
|
|
1762
|
+
? {
|
|
1763
|
+
codexForwardNativeCapabilityAlias: true,
|
|
1764
|
+
reasoningEfforts: nativeReasoningEfforts(cm.modelId),
|
|
1765
|
+
parallelToolCalls: nativeParallelToolCalls(cm.modelId),
|
|
1766
|
+
...(nativeAliasDefaultEffort ? { defaultReasoningEffort: nativeAliasDefaultEffort } : {}),
|
|
1767
|
+
}
|
|
1768
|
+
: {}),
|
|
1727
1769
|
};
|
|
1728
1770
|
// #962: the dedupe below drops the provider-derived row this custom row replaces. Inherit that
|
|
1729
1771
|
// row's provider capability metadata (reasoning ladder, default effort, parallel tool calls,
|
|
@@ -248,6 +248,9 @@ export function deriveEntry(
|
|
|
248
248
|
contextCap?: number,
|
|
249
249
|
): RawEntry {
|
|
250
250
|
const preserveExact = isExactComboCatalogModel(model, exactComboSlugs);
|
|
251
|
+
const codexForwardNativeCapabilityAlias = model?.codexForwardNativeCapabilityAlias === true
|
|
252
|
+
? upstreamNativeEntry(model.id)
|
|
253
|
+
: null;
|
|
251
254
|
const isRouted = model !== undefined;
|
|
252
255
|
if (!isRouted && !slug.includes("/")) {
|
|
253
256
|
// Supported native slug covered by the upstream snapshot: use the REAL entry (exact
|
|
@@ -256,8 +259,8 @@ export function deriveEntry(
|
|
|
256
259
|
const upstream = upstreamNativeEntry(slug);
|
|
257
260
|
if (upstream) return finishUpstreamNativeEntry(upstream, priority, contextCap);
|
|
258
261
|
}
|
|
259
|
-
if (template) {
|
|
260
|
-
const e = JSON.parse(JSON.stringify(template)) as RawEntry;
|
|
262
|
+
if (template || codexForwardNativeCapabilityAlias) {
|
|
263
|
+
const e = JSON.parse(JSON.stringify(codexForwardNativeCapabilityAlias ?? template)) as RawEntry;
|
|
261
264
|
e.slug = slug;
|
|
262
265
|
e.display_name = routedDisplayName(slug);
|
|
263
266
|
e.description = desc;
|
|
@@ -272,9 +275,11 @@ export function deriveEntry(
|
|
|
272
275
|
// window when /models omits context metadata (#992). Known metadata
|
|
273
276
|
// restores exact values below; otherwise the strict-fields fallback
|
|
274
277
|
// supplies the conservative 128k triple.
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
if (!codexForwardNativeCapabilityAlias) {
|
|
279
|
+
delete e.context_window;
|
|
280
|
+
delete e.max_context_window;
|
|
281
|
+
delete e.auto_compact_token_limit;
|
|
282
|
+
}
|
|
278
283
|
// Native id for identity text + metadata lookups — the slug may be an encoded
|
|
279
284
|
// alias (`provider/vendor-model`); the model object carries the native id.
|
|
280
285
|
const modelName = model?.id ?? slug.slice(slug.indexOf("/") + 1);
|
|
@@ -283,8 +288,17 @@ export function deriveEntry(
|
|
|
283
288
|
// (leaking that into base_instructions is a non-first-party signature → ToS risk).
|
|
284
289
|
e.base_instructions = identifyRoutedModel(e.base_instructions, modelName);
|
|
285
290
|
}
|
|
286
|
-
applyReasoningLevels(
|
|
287
|
-
|
|
291
|
+
applyReasoningLevels(
|
|
292
|
+
e,
|
|
293
|
+
model?.reasoningEfforts,
|
|
294
|
+
model?.defaultReasoningEffort,
|
|
295
|
+
preserveExact || codexForwardNativeCapabilityAlias !== null,
|
|
296
|
+
);
|
|
297
|
+
// This exact provider/model pair is the ChatGPT/Codex forward surface. Keep the pinned
|
|
298
|
+
// native tool/search/responses-lite contract while preserving the routed slug and wire id.
|
|
299
|
+
if (!codexForwardNativeCapabilityAlias) {
|
|
300
|
+
normalizeRoutedCatalogEntry(e, model?.parallelToolCalls === true);
|
|
301
|
+
}
|
|
288
302
|
if (model) applyCatalogMetadata(e, model.provider, model.id, model.contextCap);
|
|
289
303
|
applyCatalogModelMetadata(e, model);
|
|
290
304
|
if (model?.catalogKind) e.opencodex_catalog_kind = model.catalogKind;
|
package/src/codex/catalog.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Public surface preserved exactly; importers keep using "src/codex/catalog".
|
|
3
3
|
export { isMediaGenerationModelId, shouldExposeRoutedModel, readCodexCatalogPath, readCatalog, normalizeRoutedCatalogEntry, catalogModelSlug, filterSupportedNativeSlugs, catalogModelSupportsReasoningSummaries } from "./catalog/parsing";
|
|
4
4
|
export type { CatalogModel, MultiAgentMode } from "./catalog/parsing";
|
|
5
|
-
export { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, observedAccountBoundNativeEntries, observedAccountBoundNativeOpenAiSlugs, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi } from "./catalog/metadata";
|
|
5
|
+
export { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, CODEX_NATIVE_ALIAS_CATALOG_KIND, NATIVE_DAYBREAK_BLUE_MODEL, NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS, NATIVE_OPENAI_MODELS, configuredNativeAliasSlugs, desktopAllowlistSuppressedNativeSlugs, isNativeAliasCatalogEntry, isNativeOpenAiCapabilityAliasModel, nativeOpenAiCapabilitySourceSlug, nativeOpenAiContextWindow, disabledNativeSlugs, visibleNativeSlugs, desktopVisibleNativeSlugs, nativeModelRows, applyNativeVisibility, observedAccountBoundNativeEntries, observedAccountBoundNativeOpenAiSlugs, upstreamNativeEntry, nativeOpenAiSlugs, listCatalogNativeSlugs, nativeInputModalities, nativeReasoningEfforts, nativeDefaultReasoningEffort, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi } from "./catalog/metadata";
|
|
6
6
|
export { isSpawnableCodexCandidate, codexExecInvocation, loadBundledCodexCatalog, materializeBundledCodexCatalog, loadCatalogTemplate } from "./catalog/bundled";
|
|
7
7
|
export { nativeEffortClamp, shouldApplyNativeEffortClamp, catalogModelEfforts, codexSupportedReasoningEfforts, clampedDefaultEffort, clampEntryToCodexSupportedEfforts, clampCatalogModelsToCodexSupport } from "./catalog/effort";
|
|
8
8
|
export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels, gatherRoutedModels, clearGatherRoutedModelsInflight, augmentRoutedModelsWithRegistryOpenAiApiRows, augmentRoutedModelsWithMetadata, resolveComboCatalogMember, configuredComboTargetModelsByProvider } from "./catalog/provider-fetch";
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
|
-
import { chmodSync, copyFileSync, existsSync, linkSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, truncateSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { chmodSync, constants as fsConstants, copyFileSync, existsSync, linkSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, truncateSync, unlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import { dirname, join, resolve } from "node:path";
|
|
6
6
|
import { Database } from "bun:sqlite";
|
|
@@ -373,7 +373,21 @@ export class OpenAiTierBackupRollbackError extends Error {
|
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
export class OpenAiTierBackupCollisionError extends Error {
|
|
376
|
-
|
|
376
|
+
readonly configPath?: string;
|
|
377
|
+
constructor(configPath?: string) {
|
|
378
|
+
super("Existing OpenAI tier backup differs from the current config");
|
|
379
|
+
this.name = "OpenAiTierBackupCollisionError";
|
|
380
|
+
this.configPath = configPath;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export class OpenAiTierRollbackPreserveError extends Error {
|
|
385
|
+
readonly code?: "missing" | "not-rollback" | "mismatch" | "exhausted";
|
|
386
|
+
constructor(message: string, options?: ErrorOptions & { code?: OpenAiTierRollbackPreserveError["code"] }) {
|
|
387
|
+
super(message, options);
|
|
388
|
+
this.name = "OpenAiTierRollbackPreserveError";
|
|
389
|
+
this.code = options?.code;
|
|
390
|
+
}
|
|
377
391
|
}
|
|
378
392
|
|
|
379
393
|
export class OpenAiTierBackupSecretResidualError extends Error {
|
|
@@ -461,7 +475,7 @@ export function backupConfigBeforeOpenAiTierMigration(
|
|
|
461
475
|
// point would be surprising and potentially destructive.
|
|
462
476
|
const backupBytes = io.read(backup);
|
|
463
477
|
if (classifyOpenAiTierBackup(backupBytes) === "rollback") {
|
|
464
|
-
throw new OpenAiTierBackupCollisionError();
|
|
478
|
+
throw new OpenAiTierBackupCollisionError(source);
|
|
465
479
|
}
|
|
466
480
|
console.warn("[openai-provider-migration] Replacing stale pre-migration backup (post-migration config was rewritten since last migration).");
|
|
467
481
|
io.unlink(backup);
|
|
@@ -516,7 +530,7 @@ export function backupConfigBeforeOpenAiTierMigration(
|
|
|
516
530
|
} catch (cause) {
|
|
517
531
|
if (!isAlreadyExistsError(cause)) throw cause;
|
|
518
532
|
const winner = io.read(backup);
|
|
519
|
-
if (!sameBytes(original, winner)) throw new OpenAiTierBackupCollisionError();
|
|
533
|
+
if (!sameBytes(original, winner)) throw new OpenAiTierBackupCollisionError(source);
|
|
520
534
|
scrubUnpublishedTemp();
|
|
521
535
|
return "reused";
|
|
522
536
|
}
|
|
@@ -552,6 +566,67 @@ export function backupConfigBeforeOpenAiTierMigration(
|
|
|
552
566
|
}
|
|
553
567
|
}
|
|
554
568
|
|
|
569
|
+
export interface OpenAiTierRollbackPreserveIO {
|
|
570
|
+
exists(path: string): boolean;
|
|
571
|
+
read(path: string): Uint8Array;
|
|
572
|
+
copyExclusive(source: string, destination: string): void;
|
|
573
|
+
unlink(path: string): void;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const DEFAULT_ROLLBACK_PRESERVE_IO: OpenAiTierRollbackPreserveIO = {
|
|
577
|
+
exists: existsSync,
|
|
578
|
+
read: target => readFileSync(target),
|
|
579
|
+
copyExclusive: (source, destination) => {
|
|
580
|
+
copyFileSync(source, destination, fsConstants.COPYFILE_EXCL);
|
|
581
|
+
},
|
|
582
|
+
unlink: unlinkSync,
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
const OPENAI_TIER_ROLLBACK_PRESERVE_ATTEMPTS = 16;
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Copy a rollback-classified `.pre-openai-tiers-v2.bak` to a unique
|
|
589
|
+
* `.pre-openai-tiers-v1-rollback.<timestamp>[suffix].bak` path, then unlink the
|
|
590
|
+
* blocking v2 name. The original bytes are copied with no-replace publication;
|
|
591
|
+
* the v2 path is removed only after the copy is verified. Shared by startup
|
|
592
|
+
* migration recovery and `ocx init` cleanup so the two paths cannot drift.
|
|
593
|
+
*/
|
|
594
|
+
export function preserveOpenAiTierRollbackSnapshot(
|
|
595
|
+
configPath = getConfigPath(),
|
|
596
|
+
io: OpenAiTierRollbackPreserveIO = DEFAULT_ROLLBACK_PRESERVE_IO,
|
|
597
|
+
): string {
|
|
598
|
+
const backup = `${configPath}.pre-openai-tiers-v2.bak`;
|
|
599
|
+
if (!io.exists(backup)) {
|
|
600
|
+
throw new OpenAiTierRollbackPreserveError("OpenAI tier rollback backup is missing", { code: "missing" });
|
|
601
|
+
}
|
|
602
|
+
const original = io.read(backup);
|
|
603
|
+
if (classifyOpenAiTierBackup(original) !== "rollback") {
|
|
604
|
+
throw new OpenAiTierRollbackPreserveError("OpenAI tier backup is not a rollback snapshot", { code: "not-rollback" });
|
|
605
|
+
}
|
|
606
|
+
for (let attempt = 0; attempt < OPENAI_TIER_ROLLBACK_PRESERVE_ATTEMPTS; attempt++) {
|
|
607
|
+
const preserved = `${configPath}.pre-openai-tiers-v1-rollback.${Date.now()}${attempt ? `-${attempt}` : ""}.bak`;
|
|
608
|
+
try {
|
|
609
|
+
io.copyExclusive(backup, preserved);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
if (isAlreadyExistsError(error)) continue;
|
|
612
|
+
throw error;
|
|
613
|
+
}
|
|
614
|
+
let copied: Uint8Array;
|
|
615
|
+
try {
|
|
616
|
+
copied = io.read(preserved);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
throw new OpenAiTierRollbackPreserveError("Failed to read preserved rollback snapshot", { cause: error, code: "mismatch" });
|
|
619
|
+
}
|
|
620
|
+
if (!sameBytes(original, copied)) {
|
|
621
|
+
try { io.unlink(preserved); } catch { /* keep the original backup; incomplete copy is best-effort */ }
|
|
622
|
+
throw new OpenAiTierRollbackPreserveError("Preserved rollback snapshot does not match source bytes", { code: "mismatch" });
|
|
623
|
+
}
|
|
624
|
+
io.unlink(backup);
|
|
625
|
+
return preserved;
|
|
626
|
+
}
|
|
627
|
+
throw new OpenAiTierRollbackPreserveError("Unable to find a unique rollback snapshot path", { code: "exhausted" });
|
|
628
|
+
}
|
|
629
|
+
|
|
555
630
|
/**
|
|
556
631
|
* Expand a leading `~` to the home directory in user-supplied paths
|
|
557
632
|
* (OPENCODEX_HOME/CODEX_HOME set from GUIs/service files where no shell expanded it).
|