@yansigit/opencodex 2.31.3 → 2.33.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 +2 -2
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-DKLr4LTE.js +102 -0
- package/gui/dist/assets/index-DrSQdTRd.css +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +6 -5
- package/src/adapters/anthropic.ts +25 -13
- package/src/adapters/azure.ts +20 -4
- package/src/adapters/base.ts +5 -1
- package/src/adapters/command-code.ts +42 -10
- package/src/adapters/cursor/live-models.ts +8 -0
- package/src/adapters/cursor/live-transport.ts +1 -1
- package/src/adapters/cursor/native-exec-desktop.ts +16 -0
- package/src/adapters/cursor/protobuf-events.ts +158 -7
- package/src/adapters/cursor/protobuf-request.ts +33 -15
- package/src/adapters/cursor/request-builder.ts +4 -3
- package/src/adapters/cursor/tool-definitions.ts +27 -1
- package/src/adapters/cursor/types.ts +4 -3
- package/src/adapters/cursor.ts +9 -0
- package/src/adapters/google-antigravity-replay.ts +2 -2
- package/src/adapters/google-antigravity-wire.ts +7 -0
- package/src/adapters/google-errors.ts +6 -2
- package/src/adapters/google-http.ts +30 -7
- package/src/adapters/google-truncation.ts +5 -0
- package/src/adapters/google-wire-compiler.ts +38 -6
- package/src/adapters/google.ts +154 -33
- package/src/adapters/kiro-tools.ts +20 -9
- package/src/adapters/kiro.ts +0 -3
- package/src/adapters/openai-chat.ts +9 -0
- package/src/adapters/openai-responses.ts +4 -1
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-web-search.ts +7 -2
- package/src/bridge.ts +133 -24
- package/src/claude/context-windows.ts +16 -9
- package/src/cli/dispatch.ts +50 -2
- package/src/cli/doctor.ts +26 -13
- package/src/cli/help.ts +4 -3
- package/src/cli/index.ts +20 -6
- package/src/cli/models.ts +13 -3
- package/src/cli/observe.ts +20 -5
- package/src/cli/provider.ts +8 -1
- package/src/cli/registry.ts +7 -5
- package/src/cli/status.ts +25 -1
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +134 -0
- package/src/codex/app-server-processes.ts +3 -1
- package/src/codex/auth-api.ts +4 -2
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +12 -0
- package/src/codex/catalog/effort.ts +42 -12
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/model-metadata.ts +593 -0
- package/src/codex/catalog/parsing.ts +71 -27
- package/src/codex/catalog/provider-fetch.ts +189 -33
- package/src/codex/catalog/sync.ts +6 -5
- package/src/codex/convergence.ts +5 -0
- package/src/codex/desktop-app-restart.ts +342 -0
- package/src/codex/history-job.ts +32 -3
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +5 -5
- package/src/codex/history-provider.ts +825 -247
- package/src/codex/history-worker.ts +8 -5
- package/src/codex/inject.ts +49 -21
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/shim.ts +56 -3
- package/src/combos/failover.ts +27 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +81 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +308 -0
- package/src/config/provider-validation.ts +214 -0
- package/src/config.ts +153 -814
- package/src/generated/compatibility-version.json +232 -148
- package/src/images/loop.ts +37 -6
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +141 -0
- package/src/integrations/ownership.ts +10 -0
- package/src/integrations/state.ts +44 -5
- package/src/integrations/writer.ts +6 -0
- package/src/lib/azure-identity.ts +154 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/debug.ts +42 -0
- package/src/lib/errors.ts +14 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/provider-outbound.ts +45 -33
- package/src/lib/provider-tls-profile.ts +309 -0
- package/src/lib/proxy-env.ts +49 -0
- package/src/lib/redact.ts +10 -1
- package/src/lib/state-store-registrations.ts +2 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/antigravity-routing.ts +282 -236
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/command-code.ts +5 -16
- package/src/oauth/google-antigravity.ts +42 -5
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +15 -3
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +6 -0
- package/src/providers/antigravity-quota.ts +3 -1
- package/src/providers/api-keys.ts +2 -1
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/derive.ts +4 -0
- package/src/providers/key-failover.ts +5 -1
- package/src/providers/openai-tiers.ts +5 -0
- package/src/providers/provider-id-rewrite.ts +1 -0
- package/src/providers/quota.ts +59 -13
- package/src/providers/registry.ts +4 -2
- package/src/providers/request-pacing.ts +33 -6
- package/src/providers/xai-transport.ts +21 -0
- package/src/reasoning-effort.ts +19 -2
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/custom-tool-compat.ts +132 -38
- package/src/responses/google-provider-options.ts +36 -0
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +14 -2
- package/src/responses/provider-opaque-metadata.ts +3 -3
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +37 -0
- package/src/responses/state.ts +94 -4
- package/src/router.ts +8 -2
- package/src/server/auth-cors.ts +37 -7
- package/src/server/images.ts +19 -35
- package/src/server/index.ts +102 -21
- package/src/server/local-management-read-client.ts +1 -1
- package/src/server/local-provider-reload-client.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +206 -16
- package/src/server/management/combo-routes.ts +6 -0
- package/src/server/management/config-routes.ts +35 -6
- package/src/server/management/context.ts +1 -1
- package/src/server/management/logs-usage-routes.ts +27 -6
- package/src/server/management/model-routes.ts +8 -4
- package/src/server/management/model-rows.ts +4 -0
- package/src/server/management/native-integration-routes.ts +2 -1
- package/src/server/management/oauth-account-routes.ts +25 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +113 -15
- package/src/server/management/routing-profile-routes.ts +3 -0
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +2 -1
- package/src/server/request-log-conversation.ts +30 -0
- package/src/server/request-log.ts +21 -0
- package/src/server/responses/agent-task-recovery.ts +1 -1
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/combo-stream-preflight.ts +171 -0
- package/src/server/responses/compact.ts +36 -22
- package/src/server/responses/core.ts +584 -247
- package/src/server/responses/empty-completion-guard.ts +35 -6
- package/src/server/responses/fetch-helpers.ts +20 -102
- package/src/server/responses/v2-native-parent-override.ts +59 -0
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +41 -5
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/service.ts +9 -5
- package/src/types/config.ts +16 -1
- package/src/types/provider.ts +16 -0
- package/src/types/request.ts +34 -1
- package/src/types/tools.ts +114 -11
- package/src/types.ts +7 -1
- package/src/update/index.ts +5 -4
- package/src/update/job.ts +3 -1
- package/src/update/transactional-install.mjs +8 -1
- package/src/usage/log.ts +16 -8
- package/src/usage/summary.ts +201 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/gemini-executor.ts +6 -4
- package/src/web-search/loop.ts +42 -6
- package/gui/dist/assets/index-CGoDO3uO.css +0 -1
- package/gui/dist/assets/index-Cxt5fZMP.js +0 -102
|
@@ -31,7 +31,8 @@ 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, isNativeOpenAiCapabilityAliasModel, nativeMultiAgentVersion, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "./metadata";
|
|
34
|
+
import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, isNativeOpenAiCapabilityAliasModel, nativeMultiAgentVersion, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "./metadata";
|
|
35
|
+
import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
|
|
35
36
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
36
37
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
37
38
|
|
|
@@ -111,8 +112,25 @@ export interface CatalogModel {
|
|
|
111
112
|
defaultReasoningEffort?: string;
|
|
112
113
|
contextWindow?: number;
|
|
113
114
|
maxInputTokens?: number;
|
|
115
|
+
/** Generated `maxTokens` and live output limits; never treated as context. */
|
|
116
|
+
maxOutputTokens?: number;
|
|
117
|
+
/** Soft client compaction threshold; hard context/input limits remain authoritative. */
|
|
118
|
+
autoCompactTokenLimit?: number;
|
|
114
119
|
contextCap?: number;
|
|
115
120
|
contextCapped?: boolean;
|
|
121
|
+
/** Pre-cap discovered window for Models UI copy when a cap lowered it. */
|
|
122
|
+
detectedContextWindow?: number;
|
|
123
|
+
metadataSource?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
124
|
+
metadataObservedAt?: string;
|
|
125
|
+
metadataStale?: boolean;
|
|
126
|
+
metadataFieldSources?: {
|
|
127
|
+
contextWindow?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
128
|
+
maxInputTokens?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
129
|
+
maxOutputTokens?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
130
|
+
inputModalities?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
131
|
+
reasoningEfforts?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
132
|
+
capabilities?: "live" | "registry" | "snapshot" | "config_fallback" | "unknown" | "derived";
|
|
133
|
+
};
|
|
116
134
|
inputModalities?: string[];
|
|
117
135
|
/** Provider opted into parallel tool calls (OcxProviderConfig.parallelToolCalls). */
|
|
118
136
|
parallelToolCalls?: boolean;
|
|
@@ -149,6 +167,9 @@ export const JAWCODE_CATALOG_AUGMENT_PROVIDERS = new Set(["opencode-go", "deepse
|
|
|
149
167
|
export const ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS = new Set([
|
|
150
168
|
// Issue #82: Zen Go /models advertises HY3, but Console Go rejects it as outside the lite list.
|
|
151
169
|
"opencode-go/hy3-preview",
|
|
170
|
+
// Issue #2330: OpenCode Go models absent from current documentation or returning terminal HTTP 400 errors.
|
|
171
|
+
"opencode-go/mimo-v2-omni",
|
|
172
|
+
"opencode-go/mimo-v2-pro",
|
|
152
173
|
]);
|
|
153
174
|
|
|
154
175
|
export function isRoutedModelCompatibilityExcluded(slug: string): boolean {
|
|
@@ -191,6 +212,8 @@ export function shouldExposeRoutedModel(model: CatalogModel): boolean {
|
|
|
191
212
|
}
|
|
192
213
|
|
|
193
214
|
export function readCodexCatalogPath(): string {
|
|
215
|
+
const home = activeCodexHome();
|
|
216
|
+
if (home) return readCodexCatalogPathForHome(home);
|
|
194
217
|
try {
|
|
195
218
|
const configPath = activeCodexConfigPath();
|
|
196
219
|
if (existsSync(configPath)) {
|
|
@@ -202,6 +225,19 @@ export function readCodexCatalogPath(): string {
|
|
|
202
225
|
return activeDefaultCatalogPath();
|
|
203
226
|
}
|
|
204
227
|
|
|
228
|
+
/** Resolve the configured catalog without consulting ambient CODEX_HOME again. */
|
|
229
|
+
export function readCodexCatalogPathForHome(codexHome: string): string {
|
|
230
|
+
try {
|
|
231
|
+
const configPath = join(codexHome, "config.toml");
|
|
232
|
+
if (existsSync(configPath)) {
|
|
233
|
+
const toml = readFileSync(configPath, "utf-8");
|
|
234
|
+
const path = readRootTomlString(toml, "model_catalog_json");
|
|
235
|
+
if (path) return resolve(codexHome, path);
|
|
236
|
+
}
|
|
237
|
+
} catch { /* ignore */ }
|
|
238
|
+
return join(codexHome, "opencodex-catalog.json");
|
|
239
|
+
}
|
|
240
|
+
|
|
205
241
|
export function parseCatalogJson(raw: string): RawCatalog | null {
|
|
206
242
|
try {
|
|
207
243
|
const cat = JSON.parse(raw);
|
|
@@ -274,22 +310,6 @@ export function isNativeOpenAiEntry(entry: RawEntry): boolean {
|
|
|
274
310
|
return typeof entry.slug === "string" && !entry.slug.includes("/");
|
|
275
311
|
}
|
|
276
312
|
|
|
277
|
-
/**
|
|
278
|
-
* Auto-compaction threshold for a native row.
|
|
279
|
-
*
|
|
280
|
-
* The usual rule is 90% of the window, but a row whose input ceiling sits below that has to
|
|
281
|
-
* clamp to the ceiling instead — otherwise the client keeps filling until upstream answers
|
|
282
|
-
* `context_length_exceeded` and compaction never gets a chance to run. Native GPT-5.6 no
|
|
283
|
-
* longer trips this (922,000 window, 829,800 at 90%), but the routed and API-key rows carry
|
|
284
|
-
* the same family at a 1,050,000 window where 90% would be 945,000 — past the ceiling.
|
|
285
|
-
*/
|
|
286
|
-
function nativeAutoCompactLimit(contextWindow: number, maxInputTokens: number | undefined, contextCap?: number): number {
|
|
287
|
-
const ninety = Math.floor(contextWindow * 0.9);
|
|
288
|
-
if (typeof maxInputTokens !== "number" || maxInputTokens <= 0) return ninety;
|
|
289
|
-
const cappedMaxInput = applyProviderContextCap(maxInputTokens, contextCap) ?? maxInputTokens;
|
|
290
|
-
return Math.min(ninety, cappedMaxInput, contextWindow);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
313
|
/**
|
|
294
314
|
* Narrow any already-resolved native window by the user levers.
|
|
295
315
|
*
|
|
@@ -315,6 +335,9 @@ export function applyNativeOpenAiContextOverride(entry: RawEntry, limits?: Nativ
|
|
|
315
335
|
?? (isNativeOpenAiEntry(entry) ? entry.slug as string : undefined);
|
|
316
336
|
if (!nativeSlug) return;
|
|
317
337
|
const override = NATIVE_OPENAI_CONTEXT_OVERRIDES[nativeSlug];
|
|
338
|
+
// Captured before any override/cap rewrites the row: a retained compaction threshold only
|
|
339
|
+
// describes the window it arrived with.
|
|
340
|
+
const incomingContextWindow = typeof entry.context_window === "number" ? entry.context_window : undefined;
|
|
318
341
|
if (override) {
|
|
319
342
|
// Read the effective values through the accessors rather than re-deriving them from the
|
|
320
343
|
// static table: this function used to apply only the provider cap, so a per-model window
|
|
@@ -322,11 +345,6 @@ export function applyNativeOpenAiContextOverride(entry: RawEntry, limits?: Nativ
|
|
|
322
345
|
if (typeof override.contextWindow === "number") {
|
|
323
346
|
const contextWindow = nativeOpenAiContextWindow(nativeSlug, limits) ?? override.contextWindow;
|
|
324
347
|
entry.context_window = contextWindow;
|
|
325
|
-
entry.auto_compact_token_limit = nativeAutoCompactLimit(
|
|
326
|
-
contextWindow,
|
|
327
|
-
nativeOpenAiMaxInputTokens(nativeSlug, limits) ?? override.maxInputTokens,
|
|
328
|
-
undefined,
|
|
329
|
-
);
|
|
330
348
|
}
|
|
331
349
|
if (typeof override.maxContextWindow === "number") {
|
|
332
350
|
const maxContextWindow = narrowNativeMaxContextWindow(nativeSlug, override.maxContextWindow, limits);
|
|
@@ -341,17 +359,43 @@ export function applyNativeOpenAiContextOverride(entry: RawEntry, limits?: Nativ
|
|
|
341
359
|
const cappedContext = narrowNativeMaxContextWindow(nativeSlug, currentContext, limits);
|
|
342
360
|
if (cappedContext !== currentContext && typeof cappedContext === "number") {
|
|
343
361
|
entry.context_window = cappedContext;
|
|
344
|
-
entry.auto_compact_token_limit = nativeAutoCompactLimit(
|
|
345
|
-
cappedContext,
|
|
346
|
-
nativeOpenAiMaxInputTokens(nativeSlug, limits) ?? override?.maxInputTokens,
|
|
347
|
-
undefined,
|
|
348
|
-
);
|
|
349
362
|
}
|
|
350
363
|
const currentMax = typeof entry.max_context_window === "number" ? entry.max_context_window : undefined;
|
|
351
364
|
const cappedMax = narrowNativeMaxContextWindow(nativeSlug, currentMax, limits);
|
|
352
365
|
if (cappedMax !== currentMax) {
|
|
353
366
|
entry.max_context_window = cappedMax;
|
|
354
367
|
}
|
|
368
|
+
const effectiveContext = typeof entry.context_window === "number" && entry.context_window > 0
|
|
369
|
+
? entry.context_window
|
|
370
|
+
: undefined;
|
|
371
|
+
if (effectiveContext !== undefined) {
|
|
372
|
+
const derivedAutoCompactTokenLimit = nativeOpenAiAutoCompactTokenLimit(nativeSlug, limits);
|
|
373
|
+
// Only trust a retained threshold that still describes THIS window. When sync corrects the
|
|
374
|
+
// window, the old number is an artifact of the old one: a 115_200 limit retained from a
|
|
375
|
+
// 128k row would pin a corrected 272k model to 42% of its real window and compact every
|
|
376
|
+
// long turn early. Lower-is-policy still holds whenever the window is unchanged.
|
|
377
|
+
const retainedDescribesCurrentContext = incomingContextWindow === undefined
|
|
378
|
+
|| incomingContextWindow === effectiveContext;
|
|
379
|
+
const retainedAutoCompactTokenLimit = retainedDescribesCurrentContext
|
|
380
|
+
&& isNativeOpenAiEntry(entry)
|
|
381
|
+
&& typeof entry.auto_compact_token_limit === "number"
|
|
382
|
+
&& Number.isSafeInteger(entry.auto_compact_token_limit)
|
|
383
|
+
&& entry.auto_compact_token_limit > 0
|
|
384
|
+
? entry.auto_compact_token_limit
|
|
385
|
+
: undefined;
|
|
386
|
+
// A smaller threshold retained from Codex is policy evidence too. Configuration may
|
|
387
|
+
// lower it further, but catalog sync must never replace it with a larger default.
|
|
388
|
+
const loweringAutoCompactTokenLimit = retainedAutoCompactTokenLimit === undefined
|
|
389
|
+
? derivedAutoCompactTokenLimit
|
|
390
|
+
: derivedAutoCompactTokenLimit === undefined
|
|
391
|
+
? retainedAutoCompactTokenLimit
|
|
392
|
+
: Math.min(retainedAutoCompactTokenLimit, derivedAutoCompactTokenLimit);
|
|
393
|
+
entry.auto_compact_token_limit = clampAutoCompactTokenLimit(
|
|
394
|
+
effectiveContext,
|
|
395
|
+
nativeOpenAiMaxInputTokens(nativeSlug, limits) ?? override?.maxInputTokens,
|
|
396
|
+
loweringAutoCompactTokenLimit,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
355
399
|
}
|
|
356
400
|
|
|
357
401
|
export function ensureStrictCatalogFields(
|
|
@@ -41,7 +41,8 @@ import type { FastPolicyAuthority } from "../../providers/fastwire";
|
|
|
41
41
|
import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry";
|
|
42
42
|
import { parseAntigravityAvailableModels, registerAntigravityDiscoveredWireModels } from "../../providers/antigravity-models";
|
|
43
43
|
import { applyProviderContextCap, providerContextCap, resolveUnknownRoutedContextWindow } from "../../providers/context-cap";
|
|
44
|
-
import {
|
|
44
|
+
import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
|
|
45
|
+
import { routedSlug, slugEquals, slugEquivalenceKey, slugsEquivalent } from "../../providers/slug-codec";
|
|
45
46
|
import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
|
|
46
47
|
import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
|
|
47
48
|
import { fetchCursorUsableModels } from "../../adapters/cursor/live-models";
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
providerOutboundPost,
|
|
61
62
|
providerRedirectError,
|
|
62
63
|
} from "../../lib/provider-outbound";
|
|
64
|
+
import { isAntigravityOAuthProvider } from "../../lib/provider-tls-profile";
|
|
63
65
|
import { redactSecretString } from "../../lib/redact";
|
|
64
66
|
import {
|
|
65
67
|
extractProviderModelItems,
|
|
@@ -75,7 +77,7 @@ import { createAdmissionGate, ResourceAdmissionError, type AdmissionMetrics } fr
|
|
|
75
77
|
|
|
76
78
|
import { CODEX_CUSTOM_MODEL_CATALOG_KIND, JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
|
|
77
79
|
import type { CatalogModel } from "./parsing";
|
|
78
|
-
import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
80
|
+
import { disabledNativeSlugs, hasComboTargets, isNativeOpenAiCapabilityAliasModel, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
79
81
|
import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
|
|
80
82
|
import type { ComboCatalogOmission } from "./aggregation";
|
|
81
83
|
import type { CatalogGatherProviderAuthEvidence } from "./filesystem-evidence";
|
|
@@ -571,6 +573,7 @@ function providerCatalogFingerprint(name: string, prov: OcxProviderConfig): Reco
|
|
|
571
573
|
ctx: prov.contextWindow ?? null,
|
|
572
574
|
ctxW: prov.modelContextWindows ?? null,
|
|
573
575
|
maxIn: prov.modelMaxInputTokens ?? null,
|
|
576
|
+
autoCompact: prov.modelAutoCompactTokenLimits ?? null,
|
|
574
577
|
inMod: prov.modelInputModalities ?? null,
|
|
575
578
|
re: prov.modelReasoningEfforts ?? null,
|
|
576
579
|
defRe: prov.modelDefaultReasoningEfforts ?? null,
|
|
@@ -625,6 +628,17 @@ export function configuredMaxInputTokens(prov: OcxProviderConfig, id: string): n
|
|
|
625
628
|
return typeof configured === "number" && configured > 0 ? configured : undefined;
|
|
626
629
|
}
|
|
627
630
|
|
|
631
|
+
export function configuredAutoCompactTokenLimit(
|
|
632
|
+
prov: OcxProviderConfig | undefined,
|
|
633
|
+
id: string,
|
|
634
|
+
): number | undefined {
|
|
635
|
+
if (!prov) return undefined;
|
|
636
|
+
const configured = modelRecordValue(prov.modelAutoCompactTokenLimits, id);
|
|
637
|
+
return typeof configured === "number" && Number.isSafeInteger(configured) && configured > 0
|
|
638
|
+
? configured
|
|
639
|
+
: undefined;
|
|
640
|
+
}
|
|
641
|
+
|
|
628
642
|
function configuredReasoningSummarySupport(prov: OcxProviderConfig | undefined, id: string): boolean | undefined {
|
|
629
643
|
if (!prov) return undefined;
|
|
630
644
|
const explicit = modelRecordValue(prov.modelSupportsReasoningSummaries, id);
|
|
@@ -636,6 +650,7 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
636
650
|
void name;
|
|
637
651
|
const configuredCap = configuredContextWindow(prov, model.id);
|
|
638
652
|
const configuredMaxInput = configuredMaxInputTokens(prov, model.id);
|
|
653
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(prov, model.id);
|
|
639
654
|
let inputModalities = configuredInputModalities(prov, model.id);
|
|
640
655
|
// Vision-sidecar coverage: `noVisionModels` marks models whose images the PROXY describes
|
|
641
656
|
// (src/vision/index.ts). The catalog must still advertise image input for them — the Codex app
|
|
@@ -689,10 +704,31 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
689
704
|
...(prov.codexToolMode !== undefined ? { codexToolMode: prov.codexToolMode } : {}),
|
|
690
705
|
};
|
|
691
706
|
const capped = applyProviderContextCap(hinted.contextWindow, providerCap);
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
707
|
+
const withCap = providerCap !== undefined
|
|
708
|
+
? capped !== hinted.contextWindow
|
|
709
|
+
? { ...hinted, contextWindow: capped, contextCap: providerCap, contextCapped: true }
|
|
710
|
+
: { ...hinted, contextCap: providerCap, contextCapped: false }
|
|
711
|
+
: hinted;
|
|
712
|
+
const contextWindow = typeof withCap.contextWindow === "number" && withCap.contextWindow > 0
|
|
713
|
+
? withCap.contextWindow
|
|
714
|
+
: undefined;
|
|
715
|
+
const boundedMaxInput = typeof withCap.maxInputTokens === "number" && withCap.maxInputTokens > 0
|
|
716
|
+
? (contextWindow !== undefined ? Math.min(withCap.maxInputTokens, contextWindow) : withCap.maxInputTokens)
|
|
717
|
+
: undefined;
|
|
718
|
+
const withHardBounds = boundedMaxInput !== undefined && boundedMaxInput !== withCap.maxInputTokens
|
|
719
|
+
? { ...withCap, maxInputTokens: boundedMaxInput }
|
|
720
|
+
: withCap;
|
|
721
|
+
const softCandidates = [model.autoCompactTokenLimit, configuredAutoCompact]
|
|
722
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
723
|
+
if (contextWindow === undefined || softCandidates.length === 0) return withHardBounds;
|
|
724
|
+
return {
|
|
725
|
+
...withHardBounds,
|
|
726
|
+
autoCompactTokenLimit: clampAutoCompactTokenLimit(
|
|
727
|
+
contextWindow,
|
|
728
|
+
boundedMaxInput,
|
|
729
|
+
Math.min(...softCandidates),
|
|
730
|
+
),
|
|
731
|
+
};
|
|
696
732
|
}
|
|
697
733
|
|
|
698
734
|
export function catalogHintsFromProviderConfig(name: string, prov: OcxProviderConfig, id: string, contextCap?: number): Partial<CatalogModel> {
|
|
@@ -719,6 +755,7 @@ interface ComboCatalogMemberFallback {
|
|
|
719
755
|
readonly contextWindow?: number;
|
|
720
756
|
/** Input ceiling when it is lower than the window (native GPT-5.6: 922k under 1.05M). */
|
|
721
757
|
readonly maxInputTokens?: number;
|
|
758
|
+
readonly autoCompactTokenLimit?: number;
|
|
722
759
|
readonly inputModalities?: readonly string[];
|
|
723
760
|
readonly reasoningEfforts?: readonly string[];
|
|
724
761
|
}
|
|
@@ -747,26 +784,33 @@ export function resolveComboCatalogMember(
|
|
|
747
784
|
if (prov?.disabled === true) return undefined;
|
|
748
785
|
|
|
749
786
|
const withFallbackMetadata = (member: CatalogModel): CatalogModel => {
|
|
750
|
-
if (!fallback) return member;
|
|
751
787
|
const contextWindow = typeof member.contextWindow === "number" && member.contextWindow > 0
|
|
752
788
|
? member.contextWindow
|
|
753
789
|
: undefined;
|
|
754
|
-
const addMaxInput = contextWindow !== undefined
|
|
790
|
+
const addMaxInput = fallback !== undefined && contextWindow !== undefined
|
|
755
791
|
&& !(typeof member.maxInputTokens === "number" && member.maxInputTokens > 0);
|
|
792
|
+
const effectiveMaxInput = addMaxInput
|
|
793
|
+
? Math.min(fallback?.maxInputTokens ?? contextWindow!, contextWindow!)
|
|
794
|
+
: member.maxInputTokens;
|
|
795
|
+
const softCandidates = [member.autoCompactTokenLimit, fallback?.autoCompactTokenLimit]
|
|
796
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
797
|
+
const autoCompactTokenLimit = contextWindow !== undefined && softCandidates.length > 0
|
|
798
|
+
? clampAutoCompactTokenLimit(contextWindow, effectiveMaxInput, Math.min(...softCandidates))
|
|
799
|
+
: member.autoCompactTokenLimit;
|
|
800
|
+
const adjustAutoCompact = autoCompactTokenLimit !== member.autoCompactTokenLimit;
|
|
756
801
|
const addModalities = (!Array.isArray(member.inputModalities) || member.inputModalities.length === 0)
|
|
757
|
-
&& fallback
|
|
802
|
+
&& fallback?.inputModalities !== undefined;
|
|
758
803
|
const addReasoning = member.reasoningEfforts === undefined
|
|
759
|
-
&& fallback
|
|
760
|
-
if (!addMaxInput && !addModalities && !addReasoning) return member;
|
|
804
|
+
&& fallback?.reasoningEfforts !== undefined;
|
|
805
|
+
if (!addMaxInput && !adjustAutoCompact && !addModalities && !addReasoning) return member;
|
|
761
806
|
return {
|
|
762
807
|
...member,
|
|
763
808
|
// Never claim a larger input budget than the window, and prefer the model's own
|
|
764
809
|
// measured ceiling when the fallback carries one.
|
|
765
|
-
...(addMaxInput
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
...(
|
|
769
|
-
...(addReasoning ? { reasoningEfforts: [...fallback.reasoningEfforts!] } : {}),
|
|
810
|
+
...(addMaxInput ? { maxInputTokens: effectiveMaxInput } : {}),
|
|
811
|
+
...(adjustAutoCompact && autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
812
|
+
...(addModalities ? { inputModalities: [...fallback!.inputModalities!] } : {}),
|
|
813
|
+
...(addReasoning ? { reasoningEfforts: [...fallback!.reasoningEfforts!] } : {}),
|
|
770
814
|
};
|
|
771
815
|
};
|
|
772
816
|
|
|
@@ -847,6 +891,20 @@ export function resolveComboCatalogMember(
|
|
|
847
891
|
const maxInputTokens = effectiveMaxInput !== undefined
|
|
848
892
|
? Math.min(effectiveMaxInput, contextWindow)
|
|
849
893
|
: contextWindow;
|
|
894
|
+
const softCandidates = [
|
|
895
|
+
hinted.autoCompactTokenLimit,
|
|
896
|
+
base.autoCompactTokenLimit,
|
|
897
|
+
fallback?.autoCompactTokenLimit,
|
|
898
|
+
configuredAutoCompactTokenLimit(prov, target.model),
|
|
899
|
+
].filter((value): value is number => typeof value === "number" && value > 0);
|
|
900
|
+
// A generic 128k synthesis is a catalog compatibility fallback, not evidence
|
|
901
|
+
// that a configured soft policy has an authoritative window to clamp against.
|
|
902
|
+
const hasAuthoritativeAutoCompactBasis = hintedContext !== undefined
|
|
903
|
+
|| fallbackContext !== undefined
|
|
904
|
+
|| contextCap !== undefined;
|
|
905
|
+
const autoCompactTokenLimit = hasAuthoritativeAutoCompactBasis && softCandidates.length > 0
|
|
906
|
+
? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, Math.min(...softCandidates))
|
|
907
|
+
: undefined;
|
|
850
908
|
|
|
851
909
|
return {
|
|
852
910
|
...hinted,
|
|
@@ -854,6 +912,7 @@ export function resolveComboCatalogMember(
|
|
|
854
912
|
...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
|
|
855
913
|
contextWindow,
|
|
856
914
|
maxInputTokens,
|
|
915
|
+
...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
857
916
|
...(fallbackCapped ? { contextCap, contextCapped: true as const } : {}),
|
|
858
917
|
};
|
|
859
918
|
}
|
|
@@ -1043,15 +1102,32 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid
|
|
|
1043
1102
|
item.context_size,
|
|
1044
1103
|
item.max_model_len,
|
|
1045
1104
|
item.max_context_length,
|
|
1105
|
+
item.context_window,
|
|
1106
|
+
item.max_context_window,
|
|
1107
|
+
item.max_context_size,
|
|
1108
|
+
item.n_ctx,
|
|
1109
|
+
plainRecord(item.top_provider)?.max_context_length,
|
|
1110
|
+
plainRecord(metadata?.top_provider)?.max_context_length,
|
|
1046
1111
|
// llama.cpp reports the served context under `meta`: `n_ctx` is what the
|
|
1047
1112
|
// server was actually started with, `n_ctx_train` the model's trained
|
|
1048
1113
|
// maximum. Prefer the served value — routing must not promise a window the
|
|
1049
1114
|
// running server will refuse. Both come LAST so no provider already
|
|
1050
1115
|
// supplying a recognized field changes behavior (#1797).
|
|
1051
1116
|
plainRecord(item.meta)?.n_ctx,
|
|
1117
|
+
item.default_context_size,
|
|
1052
1118
|
plainRecord(item.meta)?.n_ctx_train,
|
|
1053
1119
|
);
|
|
1054
|
-
const maxInputTokens = positiveSafeInteger(
|
|
1120
|
+
const maxInputTokens = positiveSafeInteger(
|
|
1121
|
+
limits?.max_input_tokens,
|
|
1122
|
+
item.max_input_tokens,
|
|
1123
|
+
item.max_input_length,
|
|
1124
|
+
item.max_prompt_tokens,
|
|
1125
|
+
);
|
|
1126
|
+
const maxOutputTokens = positiveSafeInteger(
|
|
1127
|
+
limits?.max_output_tokens,
|
|
1128
|
+
item.max_output_tokens,
|
|
1129
|
+
limits?.max_tokens,
|
|
1130
|
+
);
|
|
1055
1131
|
// Some OpenAI-compatible catalogs expose the selectable ladder under
|
|
1056
1132
|
// `reasoning_parameters.efforts` instead of the older `reasoning_efforts` key.
|
|
1057
1133
|
// Treat both as model metadata: otherwise a valid upstream capability disappears
|
|
@@ -1079,6 +1155,7 @@ export function catalogHintsFromModelsApiItem(providerName: string, item: Provid
|
|
|
1079
1155
|
return {
|
|
1080
1156
|
...(contextWindow && contextWindow > 0 ? { contextWindow } : {}),
|
|
1081
1157
|
...(maxInputTokens && maxInputTokens > 0 ? { maxInputTokens } : {}),
|
|
1158
|
+
...(maxOutputTokens && maxOutputTokens > 0 ? { maxOutputTokens } : {}),
|
|
1082
1159
|
...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
|
|
1083
1160
|
...(inputModalities ? { inputModalities } : {}),
|
|
1084
1161
|
...(capabilities ? { capabilities } : {}),
|
|
@@ -1174,8 +1251,10 @@ async function fetchProviderModelsWithAuth(
|
|
|
1174
1251
|
clearProviderDiscoveryStatus(name);
|
|
1175
1252
|
return observed(configured, "authoritative");
|
|
1176
1253
|
}
|
|
1254
|
+
const cloudCodeAssist = effectiveGoogleMode(name, prov) === "cloud-code-assist";
|
|
1255
|
+
const antigravityOAuth = isAntigravityOAuthProvider(name, prov);
|
|
1177
1256
|
const auth: ModelsAuthResolution = captured.observedAuth ?? (resolveAuth.kind === "refreshing"
|
|
1178
|
-
?
|
|
1257
|
+
? antigravityOAuth && cloudCodeAssist
|
|
1179
1258
|
? await getValidAccessTokenSnapshot(name)
|
|
1180
1259
|
.then(snapshot => ({
|
|
1181
1260
|
apiKey: snapshot.accessToken,
|
|
@@ -1259,14 +1338,14 @@ async function fetchProviderModelsWithAuth(
|
|
|
1259
1338
|
"degraded",
|
|
1260
1339
|
);
|
|
1261
1340
|
}
|
|
1262
|
-
if (prov.authMode === "oauth" && !apiKey) {
|
|
1341
|
+
if ((prov.authMode === "oauth" || antigravityOAuth) && !apiKey) {
|
|
1263
1342
|
// No usable token (logged out, or account marked needsReauth). Still surface the
|
|
1264
1343
|
// configured static catalog so the GUI Models tab / rail counts are not empty —
|
|
1265
1344
|
// matching Cursor's !apiKey → configured degradation and fetch-failure fallback.
|
|
1266
1345
|
return observed(configured, "degraded");
|
|
1267
1346
|
}
|
|
1268
|
-
|
|
1269
|
-
const project = prov.project ?? auth.oauthProjectId;
|
|
1347
|
+
if (antigravityOAuth && !cloudCodeAssist) return observed(configured, "degraded");
|
|
1348
|
+
const project = antigravityOAuth ? auth.oauthProjectId : prov.project ?? auth.oauthProjectId;
|
|
1270
1349
|
if (cloudCodeAssist && !project) return observed(configured, "degraded");
|
|
1271
1350
|
const fresh = getFreshCached(name, ttlMs);
|
|
1272
1351
|
if (fresh) {
|
|
@@ -1560,7 +1639,24 @@ export function filterCatalogVisibleModels(
|
|
|
1560
1639
|
const allowByProvider = new Map<string, Set<string>>();
|
|
1561
1640
|
for (const [name, prov] of Object.entries(config.providers)) {
|
|
1562
1641
|
const sel = prov.selectedModels;
|
|
1563
|
-
|
|
1642
|
+
// Keyed the way `sync.ts` keys the same list, so a slash-bearing native id and
|
|
1643
|
+
// the encoded slug the Codex picker displays are one entry rather than two. A
|
|
1644
|
+
// bare `Set(sel)` matched only the native form, so an allowlist written from the
|
|
1645
|
+
// displayed slug — which `ocx models remove` also accepts — hid every model it
|
|
1646
|
+
// was meant to keep.
|
|
1647
|
+
//
|
|
1648
|
+
// The key is deliberately lossy: `p/a/b` and `p/a-b` collapse to one entry, so a
|
|
1649
|
+
// provider publishing both spellings has them selected together. That is a real
|
|
1650
|
+
// limitation, pinned by the tests below and tracked as a follow-up; it is NOT
|
|
1651
|
+
// fixed here. Resolving selections against the current roster instead was tried
|
|
1652
|
+
// and rejected — the roster is an incomplete dictionary (live discovery can omit
|
|
1653
|
+
// a published id), so it produces the same over-grant while additionally
|
|
1654
|
+
// disagreeing with the `slugEquivalenceKey` contract `sync.ts` uses at merge time.
|
|
1655
|
+
// Two catalog stages with different equivalence relations is the exact bug class
|
|
1656
|
+
// this change exists to remove.
|
|
1657
|
+
if (Array.isArray(sel) && sel.length > 0) {
|
|
1658
|
+
allowByProvider.set(name, new Set(sel.map(model => slugEquivalenceKey(routedSlug(name, model)))));
|
|
1659
|
+
}
|
|
1564
1660
|
}
|
|
1565
1661
|
return models.filter(m => {
|
|
1566
1662
|
const nativeAlias = m.provider === COMBO_NAMESPACE && m.nativeAlias === true;
|
|
@@ -1572,7 +1668,7 @@ export function filterCatalogVisibleModels(
|
|
|
1572
1668
|
if (slugEquals(stored, m.provider, m.id)) return false;
|
|
1573
1669
|
}
|
|
1574
1670
|
const allow = allowByProvider.get(m.provider);
|
|
1575
|
-
return !allow || allow.has(m.id);
|
|
1671
|
+
return !allow || allow.has(slugEquivalenceKey(routedSlug(m.provider, m.id)));
|
|
1576
1672
|
});
|
|
1577
1673
|
}
|
|
1578
1674
|
|
|
@@ -1757,6 +1853,7 @@ async function gatherRoutedModelsUncached(
|
|
|
1757
1853
|
// stay separate fields because routed/API rows of the same family run a wider window.
|
|
1758
1854
|
// Falls back to the window for slugs with no separate ceiling.
|
|
1759
1855
|
maxInputTokens: Math.min(nativeOpenAiMaxInputTokens(slug, openaiContextCap) ?? contextWindow, contextWindow),
|
|
1856
|
+
autoCompactTokenLimit: nativeOpenAiAutoCompactTokenLimit(slug, openaiContextCap),
|
|
1760
1857
|
inputModalities: nativeInputModalities(slug),
|
|
1761
1858
|
reasoningEfforts: nativeReasoningEfforts(slug),
|
|
1762
1859
|
...(nativeParallelToolCalls(slug) ? { parallelToolCalls: true } : {}),
|
|
@@ -1773,18 +1870,23 @@ async function gatherRoutedModelsUncached(
|
|
|
1773
1870
|
for (const id of listComboIds(config)) {
|
|
1774
1871
|
const combo = getCombo(config, id);
|
|
1775
1872
|
if (!combo) continue;
|
|
1873
|
+
const comboNativeLimits = nativeContextLimits(config);
|
|
1776
1874
|
const nativeContextWindow = combo.nativeAlias && combo.alias
|
|
1777
|
-
? nativeOpenAiContextWindow(combo.alias,
|
|
1875
|
+
? nativeOpenAiContextWindow(combo.alias, comboNativeLimits)
|
|
1778
1876
|
: undefined;
|
|
1779
1877
|
const nativeAliasMaxInput = combo.nativeAlias && combo.alias
|
|
1780
1878
|
? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
|
|
1781
1879
|
? NATIVE_GPT56_MAX_INPUT_TOKENS
|
|
1782
1880
|
: nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
|
|
1783
1881
|
: undefined;
|
|
1882
|
+
const nativeAliasAutoCompact = combo.nativeAlias && combo.alias
|
|
1883
|
+
? nativeOpenAiAutoCompactTokenLimit(combo.alias, comboNativeLimits)
|
|
1884
|
+
: undefined;
|
|
1784
1885
|
const nativeAliasFallback = combo.nativeAlias && combo.alias && nativeContextWindow !== undefined
|
|
1785
1886
|
? {
|
|
1786
1887
|
contextWindow: nativeContextWindow,
|
|
1787
1888
|
...(nativeAliasMaxInput !== undefined ? { maxInputTokens: nativeAliasMaxInput } : {}),
|
|
1889
|
+
...(nativeAliasAutoCompact !== undefined ? { autoCompactTokenLimit: nativeAliasAutoCompact } : {}),
|
|
1788
1890
|
inputModalities: nativeInputModalities(combo.alias),
|
|
1789
1891
|
reasoningEfforts: nativeReasoningEfforts(combo.alias),
|
|
1790
1892
|
}
|
|
@@ -1847,9 +1949,23 @@ async function gatherRoutedModelsUncached(
|
|
|
1847
1949
|
const nativeAliasMaxInputTokens = codexForwardNativeCapabilityAlias
|
|
1848
1950
|
? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
|
|
1849
1951
|
: undefined;
|
|
1850
|
-
const
|
|
1851
|
-
?
|
|
1852
|
-
:
|
|
1952
|
+
const configuredMaxInput = rawProvider
|
|
1953
|
+
? configuredMaxInputTokens(rawProvider, cm.modelId)
|
|
1954
|
+
: undefined;
|
|
1955
|
+
const hardMaxCandidates = [nativeAliasMaxInputTokens, configuredMaxInput]
|
|
1956
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
1957
|
+
const customMaxInputTokens = hardMaxCandidates.length > 0
|
|
1958
|
+
? Math.min(
|
|
1959
|
+
...hardMaxCandidates,
|
|
1960
|
+
...(customContextWindow !== undefined ? [customContextWindow] : []),
|
|
1961
|
+
)
|
|
1962
|
+
: undefined;
|
|
1963
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(rawProvider, cm.modelId);
|
|
1964
|
+
const customAutoCompactTokenLimit = codexForwardNativeCapabilityAlias
|
|
1965
|
+
? nativeOpenAiAutoCompactTokenLimit(cm.modelId, customNativeLimits)
|
|
1966
|
+
: customContextWindow !== undefined && configuredAutoCompact !== undefined
|
|
1967
|
+
? clampAutoCompactTokenLimit(customContextWindow, customMaxInputTokens, configuredAutoCompact)
|
|
1968
|
+
: undefined;
|
|
1853
1969
|
const nativeAliasDefaultEffort = codexForwardNativeCapabilityAlias
|
|
1854
1970
|
? nativeDefaultReasoningEffort(cm.modelId)
|
|
1855
1971
|
: undefined;
|
|
@@ -1870,6 +1986,7 @@ async function gatherRoutedModelsUncached(
|
|
|
1870
1986
|
: codexForwardNativeCapabilityAlias ? { displayName: "Daybreak Blue" } : {}),
|
|
1871
1987
|
...(customContextWindow !== undefined ? { contextWindow: customContextWindow } : {}),
|
|
1872
1988
|
...(customMaxInputTokens !== undefined ? { maxInputTokens: customMaxInputTokens } : {}),
|
|
1989
|
+
...(customAutoCompactTokenLimit !== undefined ? { autoCompactTokenLimit: customAutoCompactTokenLimit } : {}),
|
|
1873
1990
|
...(cm.inputModalities
|
|
1874
1991
|
? { inputModalities: cm.inputModalities }
|
|
1875
1992
|
: codexForwardNativeCapabilityAlias ? { inputModalities: nativeInputModalities(cm.modelId) } : {}),
|
|
@@ -1915,10 +2032,18 @@ async function gatherRoutedModelsUncached(
|
|
|
1915
2032
|
// along when it is actually a member — otherwise a provider default like "xhigh" would
|
|
1916
2033
|
// re-apply onto a narrower custom ladder and override the fallback in applyReasoningLevels.
|
|
1917
2034
|
const effectiveLadder = base.reasoningEfforts ?? replaced?.reasoningEfforts;
|
|
2035
|
+
const mergedMaxInputCandidates = [base.maxInputTokens, replaced?.maxInputTokens]
|
|
2036
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
2037
|
+
const mergedMaxInput = mergedMaxInputCandidates.length > 0
|
|
2038
|
+
? Math.min(...mergedMaxInputCandidates)
|
|
2039
|
+
: undefined;
|
|
1918
2040
|
const merged: CatalogModel = replaced ? {
|
|
1919
2041
|
...base,
|
|
1920
2042
|
...(base.contextWindow === undefined && replaced.contextWindow !== undefined ? { contextWindow: replaced.contextWindow } : {}),
|
|
1921
|
-
...(
|
|
2043
|
+
...(mergedMaxInput !== undefined ? { maxInputTokens: mergedMaxInput } : {}),
|
|
2044
|
+
...(base.autoCompactTokenLimit === undefined && replaced.autoCompactTokenLimit !== undefined
|
|
2045
|
+
? { autoCompactTokenLimit: replaced.autoCompactTokenLimit }
|
|
2046
|
+
: {}),
|
|
1922
2047
|
...(base.inputModalities === undefined && replaced.inputModalities !== undefined ? { inputModalities: replaced.inputModalities } : {}),
|
|
1923
2048
|
...(base.reasoningEfforts === undefined && replaced.reasoningEfforts !== undefined ? { reasoningEfforts: replaced.reasoningEfforts } : {}),
|
|
1924
2049
|
...(base.defaultReasoningEffort === undefined && replaced.defaultReasoningEffort !== undefined
|
|
@@ -1935,14 +2060,36 @@ async function gatherRoutedModelsUncached(
|
|
|
1935
2060
|
// (#349/#344). Deliberately NOT the full applyProviderConfigHints pass — custom rows are a
|
|
1936
2061
|
// user override, so their explicit contextWindow / inputModalities / reasoning fields must be
|
|
1937
2062
|
// preserved verbatim (the hint pass would cap context and overwrite modalities from registry).
|
|
2063
|
+
const mergedContext = typeof merged.contextWindow === "number" && merged.contextWindow > 0
|
|
2064
|
+
? merged.contextWindow
|
|
2065
|
+
: undefined;
|
|
2066
|
+
const boundedMergedMaxInput = typeof merged.maxInputTokens === "number" && merged.maxInputTokens > 0
|
|
2067
|
+
? (mergedContext !== undefined ? Math.min(merged.maxInputTokens, mergedContext) : merged.maxInputTokens)
|
|
2068
|
+
: undefined;
|
|
2069
|
+
const mergedWithHardBounds = boundedMergedMaxInput !== undefined
|
|
2070
|
+
&& boundedMergedMaxInput !== merged.maxInputTokens
|
|
2071
|
+
? { ...merged, maxInputTokens: boundedMergedMaxInput }
|
|
2072
|
+
: merged;
|
|
2073
|
+
const mergedSoftCandidates = [mergedWithHardBounds.autoCompactTokenLimit, configuredAutoCompact]
|
|
2074
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
2075
|
+
const mergedWithAutoCompact: CatalogModel = mergedContext !== undefined && mergedSoftCandidates.length > 0
|
|
2076
|
+
? {
|
|
2077
|
+
...mergedWithHardBounds,
|
|
2078
|
+
autoCompactTokenLimit: clampAutoCompactTokenLimit(
|
|
2079
|
+
mergedContext,
|
|
2080
|
+
boundedMergedMaxInput,
|
|
2081
|
+
Math.min(...mergedSoftCandidates),
|
|
2082
|
+
),
|
|
2083
|
+
}
|
|
2084
|
+
: mergedWithHardBounds;
|
|
1938
2085
|
const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
|
|
1939
|
-
if (enrichedProvider && modelInList(enrichedProvider.noVisionModels,
|
|
1940
|
-
const current =
|
|
2086
|
+
if (enrichedProvider && modelInList(enrichedProvider.noVisionModels, mergedWithAutoCompact.id)) {
|
|
2087
|
+
const current = mergedWithAutoCompact.inputModalities ?? ["text"];
|
|
1941
2088
|
if (!current.includes("image")) {
|
|
1942
|
-
return { ...
|
|
2089
|
+
return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
|
|
1943
2090
|
}
|
|
1944
2091
|
}
|
|
1945
|
-
return
|
|
2092
|
+
return mergedWithAutoCompact;
|
|
1946
2093
|
});
|
|
1947
2094
|
// Custom rows override discovered rows that encode to the same Codex-facing slug.
|
|
1948
2095
|
const customKeys = new Set(customModels.map(c => routedSlug(c.provider, c.id)));
|
|
@@ -1998,7 +2145,15 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
|
|
|
1998
2145
|
? Math.min(officialContext, userContext ?? officialContext, providerCap ?? officialContext)
|
|
1999
2146
|
: undefined;
|
|
2000
2147
|
const maxInputTokens = typeof officialMaxInput === "number"
|
|
2001
|
-
? Math.min(
|
|
2148
|
+
? Math.min(
|
|
2149
|
+
officialMaxInput,
|
|
2150
|
+
userMaxInput ?? officialMaxInput,
|
|
2151
|
+
contextWindow ?? officialMaxInput,
|
|
2152
|
+
)
|
|
2153
|
+
: undefined;
|
|
2154
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(configured, id);
|
|
2155
|
+
const autoCompactTokenLimit = contextWindow !== undefined && configuredAutoCompact !== undefined
|
|
2156
|
+
? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, configuredAutoCompact)
|
|
2002
2157
|
: undefined;
|
|
2003
2158
|
return {
|
|
2004
2159
|
provider: OPENAI_API_PROVIDER_ID,
|
|
@@ -2006,6 +2161,7 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
|
|
|
2006
2161
|
owned_by: OPENAI_API_PROVIDER_ID,
|
|
2007
2162
|
...(contextWindow ? { contextWindow } : {}),
|
|
2008
2163
|
...(maxInputTokens ? { maxInputTokens } : {}),
|
|
2164
|
+
...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
2009
2165
|
...(policy.modelInputModalities?.[id] ? { inputModalities: [...policy.modelInputModalities[id]!] } : {}),
|
|
2010
2166
|
...(policy.modelReasoningEfforts?.[id] ? { reasoningEfforts: [...policy.modelReasoningEfforts[id]!] } : {}),
|
|
2011
2167
|
};
|
|
@@ -41,7 +41,7 @@ import {
|
|
|
41
41
|
} from "../model-entitlements";
|
|
42
42
|
|
|
43
43
|
|
|
44
|
-
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";
|
|
44
|
+
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, readCodexCatalogPathForHome, readNativeBaseline } from "./parsing";
|
|
45
45
|
import type { CatalogModel, MultiAgentMode, RawCatalog, RawEntry } from "./parsing";
|
|
46
46
|
import { accountBoundNativeOpenAiSlugs, accountBoundNativeOpenAiSlugsBySelector, applyNativeVisibility, CODEX_NATIVE_ALIAS_CATALOG_KIND, desktopAllowlistSuppressedNativeSlugs, disabledNativeSlugs, isNativeAliasCatalogEntry, isUnsupportedOpenAiNativeSlug, NATIVE_OPENAI_MODELS, nativeContextLimits, observedAccountBoundNativeEntries, shouldIncludeAccountBoundNativeOpenAi, shouldIncludeNativeOpenAi, shouldUpgradeToUpstreamEntry, SUPPORTED_NATIVE_OPENAI_SLUGS, upstreamNativeEntry, type NativeContextLimitsInput } from "./metadata";
|
|
47
47
|
import {
|
|
@@ -1158,7 +1158,7 @@ export function mergeCatalogEntriesForSync(
|
|
|
1158
1158
|
isNativeAliasCatalogEntry(entry) && typeof entry.slug === "string" ? [entry.slug] : []
|
|
1159
1159
|
)),
|
|
1160
1160
|
),
|
|
1161
|
-
openaiContextCap?:
|
|
1161
|
+
openaiContextCap?: NativeContextLimitsInput,
|
|
1162
1162
|
keepNativeChatGptOnV1 = false,
|
|
1163
1163
|
): RawEntry[] {
|
|
1164
1164
|
// Retained for source compatibility with the original helper contract. Raw provider ids must
|
|
@@ -1832,11 +1832,12 @@ export function invalidateCodexModelsCacheWithPermit(
|
|
|
1832
1832
|
// The catalog-only sync override applies here too so an explicit refresh
|
|
1833
1833
|
// keeps the cache consistent with the catalog it just wrote.
|
|
1834
1834
|
if (!shouldSyncCodexOnStart(loadConfig()) && options?.allowWhenDesiredDisabled !== true) return false;
|
|
1835
|
-
const catalogPath =
|
|
1835
|
+
const catalogPath = readCodexCatalogPathForHome(owningCodexHome);
|
|
1836
|
+
const cachePath = join(owningCodexHome, "models_cache.json");
|
|
1836
1837
|
if (!existsSync(catalogPath)) return false;
|
|
1837
1838
|
const catalog = JSON.parse(readFileSync(catalogPath, "utf8"));
|
|
1838
1839
|
const models = catalog.models ?? catalog;
|
|
1839
|
-
const currentCache = readCatalog(
|
|
1840
|
+
const currentCache = readCatalog(cachePath);
|
|
1840
1841
|
const existingSlugs = new Set(models.flatMap((entry: RawEntry) =>
|
|
1841
1842
|
typeof entry.slug === "string" ? [entry.slug] : []));
|
|
1842
1843
|
const currentConfig = loadConfig();
|
|
@@ -1864,7 +1865,7 @@ export function invalidateCodexModelsCacheWithPermit(
|
|
|
1864
1865
|
models: [...models, ...observedAccountModels],
|
|
1865
1866
|
};
|
|
1866
1867
|
replaceCodexModelsCache(permit, owningCodexHome, {
|
|
1867
|
-
path:
|
|
1868
|
+
path: cachePath,
|
|
1868
1869
|
content: `${JSON.stringify(wrapper, null, 2)}\n`,
|
|
1869
1870
|
});
|
|
1870
1871
|
return true;
|