@bitkyc08/opencodex 2.12.0 → 2.13.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/README.md +1 -1
- package/gui/dist/assets/index-BHldBl6_.js +76 -0
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +15 -0
- package/src/adapters/google-antigravity-replay.ts +269 -5
- package/src/adapters/openai-chat.ts +25 -18
- package/src/adapters/openai-responses.ts +63 -17
- package/src/bridge.ts +11 -5
- package/src/claude/inbound.ts +24 -3
- package/src/cli/account-api.ts +2 -0
- package/src/cli/account-extended.ts +262 -1
- package/src/cli/account.ts +3 -1
- package/src/cli/dispatch.ts +532 -0
- package/src/cli/doctor.ts +26 -21
- package/src/cli/help.ts +9 -278
- package/src/cli/index.ts +45 -506
- package/src/cli/lab.ts +171 -65
- package/src/cli/registry.ts +384 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/status.ts +9 -2
- package/src/cli/v2.ts +37 -2
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-store.ts +12 -1
- package/src/codex/admission.ts +2 -1
- package/src/codex/auth-api.ts +3 -2
- package/src/codex/catalog/metadata.ts +6 -4
- package/src/codex/catalog/parsing.ts +31 -7
- package/src/codex/catalog/provider-fetch.ts +2 -1
- package/src/codex/catalog/sync.ts +65 -11
- package/src/codex/features.ts +437 -31
- package/src/codex/history-job.ts +6 -11
- package/src/codex/history-provider.ts +10 -9
- package/src/codex/inject.ts +14 -1
- package/src/codex/native-profile-startup.ts +49 -3
- package/src/codex/native-residue.ts +14 -2
- package/src/codex/paths.ts +75 -1
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/shim.ts +876 -16
- package/src/codex/sync.ts +27 -2
- package/src/config.ts +148 -10
- package/src/generated/compatibility-version.json +2572 -0
- package/src/images/loop.ts +1 -1
- package/src/lab/artifacts/sanitize.ts +3 -3
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +480 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/types.ts +23 -0
- package/src/lab/events/types.ts +2 -1
- package/src/lab/events/validate.ts +2 -1
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +454 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +2 -0
- package/src/lab/ledger/store.ts +173 -0
- package/src/lab/live/executor.ts +29 -4
- package/src/lab/live/transport.ts +3 -1
- package/src/lab/observe/from-conformance.ts +12 -57
- package/src/lab/observe/from-live.ts +9 -1
- package/src/lab/paths.ts +74 -9
- package/src/lab/projection/schema.ts +14 -1
- package/src/lab/projection/verdicts.ts +89 -53
- package/src/lab/projection/verification.ts +83 -3
- package/src/lab/query/catalog.ts +34 -5
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +11 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/subject/installation-salt.ts +24 -4
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +2 -1
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/windows-elevation.ts +8 -1
- package/src/lib/windows-user-principal.ts +62 -2
- package/src/lib/winsw.ts +8 -2
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/google-antigravity.ts +27 -0
- package/src/oauth/health.ts +12 -48
- package/src/oauth/index.ts +122 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/nous.ts +749 -0
- package/src/oauth/store.ts +56 -3
- package/src/providers/codex-capacity.ts +1 -0
- package/src/providers/registry.ts +161 -16
- package/src/responses/custom-tool-compat.ts +182 -0
- package/src/responses/parser.ts +22 -0
- package/src/responses/reasoning-replay-cache.ts +168 -11
- package/src/responses/schema.ts +1 -1
- package/src/router.ts +5 -20
- package/src/routing/capability.ts +9 -3
- package/src/routing/compatibility/assemble.ts +159 -0
- package/src/routing/compatibility/behavior.ts +224 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/evaluator.ts +27 -32
- package/src/routing/profile.ts +118 -4
- package/src/routing/trace.ts +82 -28
- package/src/server/auth-cors.ts +7 -0
- package/src/server/background-lifecycle.ts +20 -7
- package/src/server/chat-completions.ts +2 -2
- package/src/server/claude-messages.ts +6 -3
- package/src/server/direct-local-http.ts +343 -0
- package/src/server/index.ts +80 -18
- package/src/server/lifecycle.ts +12 -4
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/management/agent-settings-routes.ts +27 -3
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +13 -0
- package/src/server/management/logs-usage-routes.ts +1 -0
- package/src/server/management/oauth-account-routes.ts +57 -1
- package/src/server/management/provider-routes.ts +16 -0
- package/src/server/management/routing-profile-routes.ts +12 -24
- package/src/server/management-api.ts +2 -0
- package/src/server/management-auth.ts +68 -3
- package/src/server/proxy-liveness.ts +3 -2
- package/src/server/request-decompress.ts +9 -3
- package/src/server/request-log.ts +53 -4
- package/src/server/responses/collaboration.ts +93 -17
- package/src/server/responses/compact.ts +3 -0
- package/src/server/responses/core.ts +202 -15
- package/src/server/responses/policy-fallback.ts +1 -0
- package/src/server/responses-custom-tool-repair.ts +280 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/startup-action-control.ts +8 -1
- package/src/service.ts +278 -12
- package/src/types.ts +49 -3
- package/src/usage/expected-prices.ts +30 -0
- package/src/usage/log.ts +30 -4
- package/src/usage/summary.ts +153 -2
- package/src/web-search/loop.ts +1 -1
- package/gui/dist/assets/index-Cw1_mxO8.js +0 -70
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -107,11 +107,12 @@ const PINNED_NATIVE_CAPABILITY_ENTRIES: Map<string, RawEntry> = new Map(
|
|
|
107
107
|
.map(m => [m.slug as string, m]),
|
|
108
108
|
);
|
|
109
109
|
|
|
110
|
-
export function nativeOpenAiContextWindow(slug: string): number | undefined {
|
|
111
|
-
|
|
110
|
+
export function nativeOpenAiContextWindow(slug: string, contextCap?: number): number | undefined {
|
|
111
|
+
const raw = NATIVE_OPENAI_CONTEXT_OVERRIDES[slug]?.contextWindow
|
|
112
112
|
?? (typeof PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)?.context_window === "number"
|
|
113
113
|
? PINNED_NATIVE_CAPABILITY_ENTRIES.get(slug)!.context_window as number
|
|
114
114
|
: undefined);
|
|
115
|
+
return applyProviderContextCap(raw, contextCap) ?? raw;
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
export function nativeInputModalities(slug: string): string[] {
|
|
@@ -199,11 +200,12 @@ export function desktopVisibleNativeSlugs(config: Pick<OcxConfig, "claudeCode" |
|
|
|
199
200
|
return visibleNativeSlugs(config);
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
export function nativeModelRows(config: Pick<OcxConfig, "disabledModels" | "combos">): Array<{ slug: string; disabled: boolean; contextWindow?: number }> {
|
|
203
|
+
export function nativeModelRows(config: Pick<OcxConfig, "disabledModels" | "combos" | "providerContextCaps">): Array<{ slug: string; disabled: boolean; contextWindow?: number }> {
|
|
203
204
|
const disabled = disabledNativeSlugs(config);
|
|
204
205
|
const shadowed = configuredNativeAliasSlugs(config);
|
|
206
|
+
const openaiContextCap = providerContextCap(config, OPENAI_CODEX_PROVIDER_ID);
|
|
205
207
|
return NATIVE_OPENAI_MODELS.filter(slug => !shadowed.has(slug)).map(slug => {
|
|
206
|
-
const contextWindow = nativeOpenAiContextWindow(slug);
|
|
208
|
+
const contextWindow = nativeOpenAiContextWindow(slug, openaiContextCap);
|
|
207
209
|
return { slug, disabled: disabled.has(slug), ...(contextWindow !== undefined ? { contextWindow } : {}) };
|
|
208
210
|
});
|
|
209
211
|
}
|
|
@@ -259,18 +259,34 @@ export function isNativeOpenAiEntry(entry: RawEntry): boolean {
|
|
|
259
259
|
return typeof entry.slug === "string" && !entry.slug.includes("/");
|
|
260
260
|
}
|
|
261
261
|
|
|
262
|
-
export function applyNativeOpenAiContextOverride(entry: RawEntry): void {
|
|
262
|
+
export function applyNativeOpenAiContextOverride(entry: RawEntry, contextCap?: number): void {
|
|
263
263
|
const nativeSlug = trustedAccountBoundNativeCatalogSlug(entry)
|
|
264
264
|
?? (isNativeOpenAiEntry(entry) ? entry.slug as string : undefined);
|
|
265
265
|
if (!nativeSlug) return;
|
|
266
266
|
const override = NATIVE_OPENAI_CONTEXT_OVERRIDES[nativeSlug];
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
267
|
+
if (override) {
|
|
268
|
+
if (typeof override.contextWindow === "number") {
|
|
269
|
+
const contextWindow = applyProviderContextCap(override.contextWindow, contextCap) ?? override.contextWindow;
|
|
270
|
+
entry.context_window = contextWindow;
|
|
271
|
+
entry.auto_compact_token_limit = Math.floor(contextWindow * 0.9);
|
|
272
|
+
}
|
|
273
|
+
if (typeof override.maxContextWindow === "number") {
|
|
274
|
+
entry.max_context_window = applyProviderContextCap(override.maxContextWindow, contextCap) ?? override.maxContextWindow;
|
|
275
|
+
}
|
|
271
276
|
}
|
|
272
|
-
|
|
273
|
-
|
|
277
|
+
// providerContextCaps.openai is a ceiling for native OpenAI rows regardless of where the
|
|
278
|
+
// advertised window came from (#1430): preserved rows without a hardcoded override (e.g.
|
|
279
|
+
// gpt-5.4-mini) must stay under the cap too, and auto-compaction follows the capped window.
|
|
280
|
+
const currentContext = typeof entry.context_window === "number" ? entry.context_window : undefined;
|
|
281
|
+
const cappedContext = applyProviderContextCap(currentContext, contextCap);
|
|
282
|
+
if (cappedContext !== currentContext && typeof cappedContext === "number") {
|
|
283
|
+
entry.context_window = cappedContext;
|
|
284
|
+
entry.auto_compact_token_limit = Math.floor(cappedContext * 0.9);
|
|
285
|
+
}
|
|
286
|
+
const currentMax = typeof entry.max_context_window === "number" ? entry.max_context_window : undefined;
|
|
287
|
+
const cappedMax = applyProviderContextCap(currentMax, contextCap);
|
|
288
|
+
if (cappedMax !== currentMax) {
|
|
289
|
+
entry.max_context_window = cappedMax;
|
|
274
290
|
}
|
|
275
291
|
}
|
|
276
292
|
|
|
@@ -318,6 +334,13 @@ export function ensureStrictCatalogFields(
|
|
|
318
334
|
|
|
319
335
|
export type MultiAgentMode = "v1" | "default" | "v2";
|
|
320
336
|
|
|
337
|
+
export const ROUTED_CODEX_TOOL_MODE = "code_mode_only";
|
|
338
|
+
|
|
339
|
+
export function applyRoutedCodexToolMode(entry: RawEntry): RawEntry {
|
|
340
|
+
entry.tool_mode = ROUTED_CODEX_TOOL_MODE;
|
|
341
|
+
return entry;
|
|
342
|
+
}
|
|
343
|
+
|
|
321
344
|
/**
|
|
322
345
|
* @param v2FeatureEnabled When the native multi_agent_v2 feature is on, "default"
|
|
323
346
|
* mode stamps unpinned entries as "v2" instead of deleting the key. The native
|
|
@@ -358,6 +381,7 @@ export function applyMultiAgentMode(entries: RawEntry[], mode: MultiAgentMode, v
|
|
|
358
381
|
export function normalizeRoutedCatalogEntry(entry: RawEntry, parallelToolCalls = false): RawEntry {
|
|
359
382
|
delete entry.model_messages;
|
|
360
383
|
delete entry.tool_mode;
|
|
384
|
+
applyRoutedCodexToolMode(entry);
|
|
361
385
|
delete entry.multi_agent_version;
|
|
362
386
|
delete entry.use_responses_lite;
|
|
363
387
|
delete entry.supports_websockets;
|
|
@@ -1639,6 +1639,7 @@ async function gatherRoutedModelsUncached(
|
|
|
1639
1639
|
// configs that will never need it.
|
|
1640
1640
|
} else {
|
|
1641
1641
|
const disabled = disabledNativeSlugs(config);
|
|
1642
|
+
const openaiContextCap = providerContextCap(config, OPENAI_CODEX_PROVIDER_ID);
|
|
1642
1643
|
const requiredNativeComboTargets = new Set(listComboIds(config).flatMap(id => {
|
|
1643
1644
|
const combo = getCombo(config, id);
|
|
1644
1645
|
return combo?.targets.flatMap(target => (
|
|
@@ -1649,7 +1650,7 @@ async function gatherRoutedModelsUncached(
|
|
|
1649
1650
|
// A bare native disable key hides the native row, not a combo that targets it.
|
|
1650
1651
|
// Keep synthetic native metadata available to those combos.
|
|
1651
1652
|
if (disabled.has(slug) && !requiredNativeComboTargets.has(slug)) continue;
|
|
1652
|
-
const contextWindow = nativeOpenAiContextWindow(slug);
|
|
1653
|
+
const contextWindow = nativeOpenAiContextWindow(slug, openaiContextCap);
|
|
1653
1654
|
if (contextWindow === undefined) continue;
|
|
1654
1655
|
const synthetic: CatalogModel = {
|
|
1655
1656
|
provider: "openai",
|
|
@@ -29,9 +29,10 @@ import type { NormalizedComboConfig } from "../../combos/types";
|
|
|
29
29
|
import { providerDestinationResolvedError } from "../../lib/destination-policy";
|
|
30
30
|
import { redactSecretString } from "../../lib/redact";
|
|
31
31
|
import upstreamModelsSnapshot from "../data/upstream-models.json";
|
|
32
|
+
import { OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
import { CODEX_CUSTOM_MODEL_CATALOG_KIND, CODEX_PROVIDER_MODEL_CATALOG_KIND, activeCodexModelsCachePath, applyCatalogMetadata, applyMultiAgentMode, applyNativeOpenAiContextOverride, catalogBackupPathFor, catalogHasRoutedEntries, catalogModelSlug, ensureStrictCatalogFields, findNativeTemplate, isDefaultCatalogPath, isRoutedModelCompatibilityExcluded, legacyCatalogBackupPath, normalizeRoutedCatalogEntry, normalizeServiceTiers, readCatalog, readCatalogBackup, readCodexCatalogPath, readNativeBaseline } from "./parsing";
|
|
35
|
+
import { CODEX_CUSTOM_MODEL_CATALOG_KIND, CODEX_PROVIDER_MODEL_CATALOG_KIND, activeCodexModelsCachePath, applyCatalogMetadata, applyMultiAgentMode, applyNativeOpenAiContextOverride, applyRoutedCodexToolMode, catalogBackupPathFor, catalogHasRoutedEntries, catalogModelSlug, ensureStrictCatalogFields, findNativeTemplate, isDefaultCatalogPath, isRoutedModelCompatibilityExcluded, legacyCatalogBackupPath, normalizeRoutedCatalogEntry, normalizeServiceTiers, readCatalog, readCatalogBackup, readCodexCatalogPath, readNativeBaseline } from "./parsing";
|
|
35
36
|
import type { CatalogModel, MultiAgentMode, RawCatalog, RawEntry } from "./parsing";
|
|
36
37
|
import { applyNativeVisibility, CODEX_NATIVE_ALIAS_CATALOG_KIND, desktopAllowlistSuppressedNativeSlugs, disabledNativeSlugs, isNativeAliasCatalogEntry, isUnsupportedOpenAiNativeSlug, NATIVE_OPENAI_MODELS, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, shouldUpgradeToUpstreamEntry, SUPPORTED_NATIVE_OPENAI_SLUGS, upstreamNativeEntry } from "./metadata";
|
|
37
38
|
import {
|
|
@@ -190,9 +191,9 @@ export function effectiveSubagentRoster(
|
|
|
190
191
|
return { candidates, advertised, excluded };
|
|
191
192
|
}
|
|
192
193
|
|
|
193
|
-
export function finishUpstreamNativeEntry(clone: RawEntry, priority: number): RawEntry {
|
|
194
|
+
export function finishUpstreamNativeEntry(clone: RawEntry, priority: number, contextCap?: number): RawEntry {
|
|
194
195
|
if (priority !== 9) clone.priority = priority;
|
|
195
|
-
applyNativeOpenAiContextOverride(clone);
|
|
196
|
+
applyNativeOpenAiContextOverride(clone, contextCap);
|
|
196
197
|
// GPT-5.6 natives keep their exact upstream ladders (e.g. luna has max but no ultra).
|
|
197
198
|
// Older natives (gpt-5.5 / 5.4 / 5.4-mini / 5.3-codex-spark) get mock max + ultra
|
|
198
199
|
// (wire-clamped to xhigh). Ultra is always advertised regardless of v2 toggle.
|
|
@@ -243,6 +244,7 @@ export function deriveEntry(
|
|
|
243
244
|
priority: number,
|
|
244
245
|
model?: CatalogModel,
|
|
245
246
|
exactComboSlugs: ReadonlySet<string> = new Set(),
|
|
247
|
+
contextCap?: number,
|
|
246
248
|
): RawEntry {
|
|
247
249
|
const preserveExact = isExactComboCatalogModel(model, exactComboSlugs);
|
|
248
250
|
const isRouted = model !== undefined;
|
|
@@ -251,7 +253,7 @@ export function deriveEntry(
|
|
|
251
253
|
// reasoning ladder — e.g. luna has no ultra — default effort, identity, model_messages)
|
|
252
254
|
// instead of cloning an older template.
|
|
253
255
|
const upstream = upstreamNativeEntry(slug);
|
|
254
|
-
if (upstream) return finishUpstreamNativeEntry(upstream, priority);
|
|
256
|
+
if (upstream) return finishUpstreamNativeEntry(upstream, priority, contextCap);
|
|
255
257
|
}
|
|
256
258
|
if (template) {
|
|
257
259
|
const e = JSON.parse(JSON.stringify(template)) as RawEntry;
|
|
@@ -286,7 +288,7 @@ export function deriveEntry(
|
|
|
286
288
|
applyCatalogModelMetadata(e, model);
|
|
287
289
|
if (model?.catalogKind) e.opencodex_catalog_kind = model.catalogKind;
|
|
288
290
|
} else {
|
|
289
|
-
applyNativeOpenAiContextOverride(e);
|
|
291
|
+
applyNativeOpenAiContextOverride(e, contextCap);
|
|
290
292
|
if (isGpt56NativeSlug(slug)) ensureGpt56ReasoningLevels(e);
|
|
291
293
|
else ensureUltraReasoningLevel(e);
|
|
292
294
|
// Non-5.6 natives (5.5, 5.4, 5.4-mini, spark) do not support responses-lite;
|
|
@@ -314,6 +316,7 @@ export function deriveEntry(
|
|
|
314
316
|
...(isRouted ? { web_search_tool_type: "text_and_image", supports_search_tool: true } : {}),
|
|
315
317
|
};
|
|
316
318
|
if (isRouted) {
|
|
319
|
+
applyRoutedCodexToolMode(entry);
|
|
317
320
|
applyReasoningLevels(entry, model?.reasoningEfforts, model?.defaultReasoningEffort, preserveExact);
|
|
318
321
|
}
|
|
319
322
|
else {
|
|
@@ -323,7 +326,7 @@ export function deriveEntry(
|
|
|
323
326
|
if (model && isRouted) applyCatalogMetadata(entry, model.provider, model.id, model.contextCap);
|
|
324
327
|
applyCatalogModelMetadata(entry, model);
|
|
325
328
|
if (model?.catalogKind) entry.opencodex_catalog_kind = model.catalogKind;
|
|
326
|
-
if (!isRouted) applyNativeOpenAiContextOverride(entry);
|
|
329
|
+
if (!isRouted) applyNativeOpenAiContextOverride(entry, contextCap);
|
|
327
330
|
return ensureStrictCatalogFields(normalizeServiceTiers(entry), {
|
|
328
331
|
preserveExactInputModalities: preserveExact,
|
|
329
332
|
isRouted,
|
|
@@ -342,6 +345,7 @@ export interface ObservedCatalogEntryBuildInput {
|
|
|
342
345
|
readonly suppressedBareNativeSlugs: ReadonlySet<string>;
|
|
343
346
|
readonly disabledNativeAccountSlugs: ReadonlySet<string>;
|
|
344
347
|
readonly multiAgentV2Enabled: boolean;
|
|
348
|
+
readonly openaiContextCap?: number;
|
|
345
349
|
}
|
|
346
350
|
|
|
347
351
|
/** Build entries with the process-observed Codex feature state. */
|
|
@@ -356,6 +360,7 @@ export function buildCatalogEntries(
|
|
|
356
360
|
accountSelectors: readonly string[] = [],
|
|
357
361
|
suppressedBareNativeSlugs: ReadonlySet<string> = new Set(),
|
|
358
362
|
disabledNativeAccountSlugs: ReadonlySet<string> = new Set(),
|
|
363
|
+
contextCap?: number,
|
|
359
364
|
): RawEntry[] {
|
|
360
365
|
return buildCatalogEntriesFromObservedState({
|
|
361
366
|
template,
|
|
@@ -369,6 +374,7 @@ export function buildCatalogEntries(
|
|
|
369
374
|
suppressedBareNativeSlugs,
|
|
370
375
|
disabledNativeAccountSlugs,
|
|
371
376
|
multiAgentV2Enabled: isMultiAgentV2Enabled(),
|
|
377
|
+
openaiContextCap: contextCap,
|
|
372
378
|
});
|
|
373
379
|
}
|
|
374
380
|
|
|
@@ -385,6 +391,7 @@ export function buildCatalogEntriesFromObservedState({
|
|
|
385
391
|
suppressedBareNativeSlugs,
|
|
386
392
|
disabledNativeAccountSlugs,
|
|
387
393
|
multiAgentV2Enabled,
|
|
394
|
+
openaiContextCap,
|
|
388
395
|
}: ObservedCatalogEntryBuildInput): RawEntry[] {
|
|
389
396
|
// Codex's models-manager sorts by `priority` ASC and advertises the first 5 picker-visible
|
|
390
397
|
// models to spawn_agent (sort_by_key(priority) + MAX_MODEL_OVERRIDES_IN_SPAWN_AGENT=5). Catalog
|
|
@@ -419,7 +426,7 @@ export function buildCatalogEntriesFromObservedState({
|
|
|
419
426
|
.filter(model => model.provider === COMBO_NAMESPACE)
|
|
420
427
|
.map(catalogModelSlug));
|
|
421
428
|
for (const slug of gptSlugs) {
|
|
422
|
-
const native = deriveEntry(template, slug, "OpenAI native model (Codex OAuth passthrough).", 9);
|
|
429
|
+
const native = deriveEntry(template, slug, "OpenAI native model (Codex OAuth passthrough).", 9, undefined, new Set(), openaiContextCap);
|
|
423
430
|
if (rank.has(slug)) native.priority = rank.get(slug)!;
|
|
424
431
|
nativeEntries.push(native);
|
|
425
432
|
const nativeAlias = nativeAliasesBySlug.get(slug);
|
|
@@ -622,6 +629,7 @@ export interface ObservedCatalogMergeInput {
|
|
|
622
629
|
readonly accountBoundEntries: readonly RawEntry[];
|
|
623
630
|
readonly suppressedBareNativeSlugs?: ReadonlySet<string>;
|
|
624
631
|
readonly policy: ObservedCatalogMergePolicy;
|
|
632
|
+
readonly openaiContextCap?: number;
|
|
625
633
|
}
|
|
626
634
|
|
|
627
635
|
/**
|
|
@@ -651,6 +659,7 @@ export function mergeCatalogEntriesFromObservedState({
|
|
|
651
659
|
accountBoundEntries,
|
|
652
660
|
suppressedBareNativeSlugs = new Set(),
|
|
653
661
|
policy,
|
|
662
|
+
openaiContextCap,
|
|
654
663
|
}: ObservedCatalogMergeInput): RawEntry[] {
|
|
655
664
|
// Raw catalog rows contain nested arrays/objects that normalization mutates. Detach every row at
|
|
656
665
|
// the observed-core boundary so callers can safely retain evidence objects or repeat the merge.
|
|
@@ -785,7 +794,7 @@ export function mergeCatalogEntriesFromObservedState({
|
|
|
785
794
|
// genuine catalog entry (real display name) is preserved untouched.
|
|
786
795
|
if (shouldUpgradeToUpstreamEntry(m)) {
|
|
787
796
|
const upstream = upstreamNativeEntry(slug)!;
|
|
788
|
-
const finished = finishUpstreamNativeEntry(upstream, 9);
|
|
797
|
+
const finished = finishUpstreamNativeEntry(upstream, 9, openaiContextCap);
|
|
789
798
|
finished.priority = nativePriority(slug, upstream.priority);
|
|
790
799
|
return finished;
|
|
791
800
|
}
|
|
@@ -814,6 +823,9 @@ export function mergeCatalogEntriesFromObservedState({
|
|
|
814
823
|
slug,
|
|
815
824
|
"OpenAI native model (Codex OAuth passthrough).",
|
|
816
825
|
nativePriority(slug, upstreamNativeEntry(slug)?.priority),
|
|
826
|
+
undefined,
|
|
827
|
+
new Set(),
|
|
828
|
+
openaiContextCap,
|
|
817
829
|
);
|
|
818
830
|
entry.priority = nativePriority(slug, upstreamNativeEntry(slug)?.priority);
|
|
819
831
|
native.push(entry);
|
|
@@ -926,7 +938,7 @@ export function mergeCatalogEntriesFromObservedState({
|
|
|
926
938
|
const managedEntries = [...finalRoutedEntries, ...alignedAccountBoundEntries];
|
|
927
939
|
const mergedEntries = [...native, ...managedEntries].map(m => {
|
|
928
940
|
const normalized = normalizeServiceTiers(m);
|
|
929
|
-
if (!isNativeAliasCatalogEntry(normalized)) applyNativeOpenAiContextOverride(normalized);
|
|
941
|
+
if (!isNativeAliasCatalogEntry(normalized)) applyNativeOpenAiContextOverride(normalized, openaiContextCap);
|
|
930
942
|
const exactCombo = isExactComboCatalogEntry(m, exactComboSlugs);
|
|
931
943
|
const e = ensureStrictCatalogFields(normalized, {
|
|
932
944
|
preserveExactInputModalities: exactCombo,
|
|
@@ -996,6 +1008,7 @@ export function mergeCatalogEntriesForSync(
|
|
|
996
1008
|
isNativeAliasCatalogEntry(entry) && typeof entry.slug === "string" ? [entry.slug] : []
|
|
997
1009
|
)),
|
|
998
1010
|
),
|
|
1011
|
+
openaiContextCap?: number,
|
|
999
1012
|
): RawEntry[] {
|
|
1000
1013
|
// Retained for source compatibility with the original helper contract. Raw provider ids must
|
|
1001
1014
|
// not suppress same-named native rows; actual admitted combo entries own that decision now.
|
|
@@ -1030,6 +1043,7 @@ export function mergeCatalogEntriesForSync(
|
|
|
1030
1043
|
includeNativeOpenAi,
|
|
1031
1044
|
accountBoundEntries,
|
|
1032
1045
|
suppressedBareNativeSlugs,
|
|
1046
|
+
openaiContextCap,
|
|
1033
1047
|
policy: {
|
|
1034
1048
|
...CANONICAL_NATIVE_CATALOG_CONTENT_POLICY,
|
|
1035
1049
|
warningPolicy: "emit",
|
|
@@ -1172,6 +1186,24 @@ function revalidateRetainedCatalogSync(
|
|
|
1172
1186
|
};
|
|
1173
1187
|
}
|
|
1174
1188
|
|
|
1189
|
+
/**
|
|
1190
|
+
* Exact bytes currently on disk at `path`, or null when unreadable/absent.
|
|
1191
|
+
*
|
|
1192
|
+
* Deliberately a Buffer rather than a decoded string: `readFileSync(path, "utf8")`
|
|
1193
|
+
* substitutes U+FFFD for every invalid byte, so a file holding a raw 0x80 decodes
|
|
1194
|
+
* equal to prepared content holding a legitimately encoded U+FFFD. Comparing the
|
|
1195
|
+
* decoded strings would then classify a malformed catalog as identical, skip the
|
|
1196
|
+
* atomic repair write, and leave the corruption on disk while reporting
|
|
1197
|
+
* `catalogWritten: false`.
|
|
1198
|
+
*/
|
|
1199
|
+
function currentCatalogFileContent(path: string): Buffer | null {
|
|
1200
|
+
try {
|
|
1201
|
+
return readFileSync(path);
|
|
1202
|
+
} catch {
|
|
1203
|
+
return null;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1175
1207
|
function pristineCatalogBytes(read: RetainedCatalogSyncRead): string | null {
|
|
1176
1208
|
if (read.onDiskCatalog && !catalogHasRoutedEntries(read.onDiskCatalog)) {
|
|
1177
1209
|
try {
|
|
@@ -1247,6 +1279,7 @@ function writeRetainedCatalogSync({
|
|
|
1247
1279
|
const hasPhysicalComboProvider = Object.hasOwn(config.providers, COMBO_NAMESPACE);
|
|
1248
1280
|
const includeNativeOpenAi = shouldIncludeNativeOpenAi(config);
|
|
1249
1281
|
const includeAccountBoundNativeOpenAi = shouldIncludeAccountBoundNativeOpenAi(config);
|
|
1282
|
+
const openaiContextCap = providerContextCap(config, OPENAI_CODEX_PROVIDER_ID);
|
|
1250
1283
|
const accountSelectors = includeAccountBoundNativeOpenAi
|
|
1251
1284
|
? visibleCodexAccountSelectors(config)
|
|
1252
1285
|
: [];
|
|
@@ -1264,6 +1297,7 @@ function writeRetainedCatalogSync({
|
|
|
1264
1297
|
suppressedBareNativeSlugs,
|
|
1265
1298
|
disabledNativeAccountSlugs: new Set(),
|
|
1266
1299
|
multiAgentV2Enabled,
|
|
1300
|
+
openaiContextCap,
|
|
1267
1301
|
});
|
|
1268
1302
|
// Keep genuine native entries (gpt-*, codex-*) with their real per-model fields and append
|
|
1269
1303
|
// routed providers as namespaced slugs. Cursor and other adopted providers can expose model ids
|
|
@@ -1308,6 +1342,7 @@ function writeRetainedCatalogSync({
|
|
|
1308
1342
|
suppressedBareNativeSlugs,
|
|
1309
1343
|
disabledNativeAccountSlugs: new Set([...disabledNativeSlugs(config)].filter(slug => suppressedBareNativeSlugs.has(slug))),
|
|
1310
1344
|
multiAgentV2Enabled,
|
|
1345
|
+
openaiContextCap,
|
|
1311
1346
|
}).filter(entry => trustedAccountBoundNativeCatalogSlug(entry) !== undefined)
|
|
1312
1347
|
: [];
|
|
1313
1348
|
catalog.models = mergeCatalogEntriesFromObservedState({
|
|
@@ -1330,6 +1365,7 @@ function writeRetainedCatalogSync({
|
|
|
1330
1365
|
includeNativeOpenAi,
|
|
1331
1366
|
accountBoundEntries,
|
|
1332
1367
|
suppressedBareNativeSlugs,
|
|
1368
|
+
openaiContextCap,
|
|
1333
1369
|
policy: {
|
|
1334
1370
|
...CANONICAL_NATIVE_CATALOG_CONTENT_POLICY,
|
|
1335
1371
|
warningPolicy: "emit",
|
|
@@ -1337,12 +1373,30 @@ function writeRetainedCatalogSync({
|
|
|
1337
1373
|
});
|
|
1338
1374
|
clampCatalogModelsToCodexSupport(catalog.models);
|
|
1339
1375
|
|
|
1376
|
+
const added = goEntries.length + accountBoundEntries.length;
|
|
1377
|
+
const content = `${JSON.stringify(catalog, null, 2)}\n`;
|
|
1378
|
+
// A byte-identical rewrite is not a catalog change, but every mtime-keyed reader
|
|
1379
|
+
// has to treat it as one. The app-server staleness classifier (#857) is the one
|
|
1380
|
+
// that matters: it compares this file's mtime against each running Codex's start
|
|
1381
|
+
// time, so an ordinary `ocx start` — or any dashboard action that re-syncs an
|
|
1382
|
+
// unchanged model set — marked every already-running Codex as holding an outdated
|
|
1383
|
+
// in-memory catalog. Since #1407 that verdict silences opencodex's own model
|
|
1384
|
+
// guidance entirely (no preferred model, no roster) for the rest of that Codex's
|
|
1385
|
+
// lifetime, so a configured injectionModel stops reaching the session even though
|
|
1386
|
+
// nothing about the catalog changed. Skipping the no-op write keeps both the mtime
|
|
1387
|
+
// and `catalogWritten` honest; `added` still reports the routed rows the catalog
|
|
1388
|
+
// carries, because they are on disk either way.
|
|
1389
|
+
const onDiskBytes = currentCatalogFileContent(catalogPath);
|
|
1390
|
+
if (onDiskBytes !== null && onDiskBytes.equals(Buffer.from(content, "utf8"))) {
|
|
1391
|
+
return { added, path: catalogPath, catalogWritten: false, comboOmissions };
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1340
1394
|
replaceActiveCodexCatalog(permit, owningCodexHome, {
|
|
1341
1395
|
path: catalogPath,
|
|
1342
|
-
content
|
|
1396
|
+
content,
|
|
1343
1397
|
});
|
|
1344
1398
|
return {
|
|
1345
|
-
added
|
|
1399
|
+
added,
|
|
1346
1400
|
path: catalogPath,
|
|
1347
1401
|
catalogWritten: true,
|
|
1348
1402
|
comboOmissions,
|