@bitkyc08/opencodex 2.41.0 → 2.43.0-preview.20260906
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/AGENTS_INSTALL.md +2 -2
- package/README.md +31 -0
- package/bin/ocx.mjs +10 -1
- package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
- package/gui/dist/assets/index-VGeQEZ_v.js +112 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-codec.ts +304 -0
- package/src/adapters/anthropic-image-normalize.ts +8 -298
- package/src/adapters/anthropic.ts +30 -7
- package/src/adapters/command-code.ts +7 -5
- package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
- package/src/adapters/cursor/images.ts +36 -6
- package/src/adapters/cursor/live-transport.ts +7 -2
- package/src/adapters/cursor/native-exec-desktop.ts +2 -15
- package/src/adapters/cursor/protobuf-request.ts +54 -24
- package/src/adapters/cursor/tool-definitions.ts +5 -670
- package/src/adapters/cursor/tool-guidance.ts +236 -0
- package/src/adapters/cursor/tool-naming.ts +252 -0
- package/src/adapters/cursor/tool-schemas.ts +195 -0
- package/src/adapters/cursor/types.ts +6 -3
- package/src/adapters/exec-tool-result-normalize.ts +1 -1
- package/src/adapters/google-errors.ts +9 -1
- package/src/adapters/google.ts +1 -0
- package/src/adapters/identity.ts +8 -2
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +259 -29
- package/src/adapters/responses-code-mode.ts +59 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-schema-analysis.ts +86 -0
- package/src/adapters/xai-tool-schema.ts +2 -87
- package/src/adapters/xai-web-search.ts +1 -1
- package/src/bridge.ts +47 -13
- package/src/chat/inbound.ts +11 -3
- package/src/claude/inbound-content-options.ts +60 -0
- package/src/claude/inbound-model-options.ts +142 -0
- package/src/claude/inbound-records.ts +7 -0
- package/src/claude/inbound.ts +10 -202
- package/src/claude/model-info.ts +45 -0
- package/src/cli/account-auth.ts +49 -9
- package/src/cli/account-extended.ts +7 -1
- package/src/cli/capabilities.ts +15 -4
- package/src/cli/claude.ts +232 -39
- package/src/cli/config-command.ts +9 -1
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/effort.ts +372 -0
- package/src/cli/export-command.ts +3 -9
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/model-selection-guidance.ts +30 -0
- package/src/cli/models-runtime.ts +3 -2
- package/src/cli/models.ts +8 -3
- package/src/cli/observe.ts +3 -1
- package/src/cli/opencode.ts +4 -1
- package/src/cli/provider-runtime.ts +65 -0
- package/src/cli/provider.ts +8 -0
- package/src/cli/registry.ts +16 -2
- package/src/cli/runtime-api.ts +3 -1
- package/src/cli/star-prompt.ts +22 -6
- package/src/cli/status-probes.ts +168 -0
- package/src/cli/status.ts +5 -168
- package/src/clients/config-export/constants.ts +69 -0
- package/src/clients/config-export/contracts.ts +154 -0
- package/src/clients/config-export/dsh.ts +132 -0
- package/src/clients/config-export/fast-models.ts +29 -0
- package/src/clients/config-export/mcode.ts +83 -0
- package/src/clients/config-export/model-metadata.ts +108 -0
- package/src/clients/config-export/omp.ts +104 -0
- package/src/clients/config-export/zcode.ts +92 -0
- package/src/clients/config-export.ts +18 -710
- package/src/codex/account-lifecycle.ts +20 -3
- package/src/codex/account-usability.ts +2 -0
- package/src/codex/auth-api.ts +272 -32
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +44 -5
- package/src/codex/catalog/metadata.ts +149 -14
- package/src/codex/catalog/native-models.ts +116 -4
- package/src/codex/catalog/parsing.ts +122 -8
- package/src/codex/catalog/provider-fetch.ts +154 -23
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/catalog.ts +1 -1
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +169 -0
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +99 -34
- package/src/codex/injected-marker.ts +30 -4
- package/src/codex/journal.ts +14 -0
- package/src/codex/legacy-config-keys.ts +68 -0
- package/src/codex/log-guard/inspect-schema.ts +137 -0
- package/src/codex/log-guard/inspect.ts +2 -134
- package/src/codex/loopback-target.ts +54 -0
- package/src/codex/main-account-cache.ts +63 -1
- package/src/codex/main-account-hard-lock.ts +52 -0
- package/src/codex/main-account.ts +3 -1
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/model-entitlements.ts +54 -4
- package/src/codex/project-config-warnings.ts +92 -2
- package/src/codex/prompt-layers/encoding.ts +80 -0
- package/src/codex/prompt-layers/paths.ts +54 -0
- package/src/codex/prompt-layers/revision.ts +55 -0
- package/src/codex/prompt-layers/toml-edit.ts +163 -0
- package/src/codex/prompt-layers/toml-read.ts +181 -0
- package/src/codex/prompt-layers.ts +14 -520
- package/src/codex/quota-auto-refresh-state.ts +16 -0
- package/src/codex/quota-auto-refresh.ts +219 -0
- package/src/codex/quota-types.ts +51 -0
- package/src/codex/quota.ts +252 -93
- package/src/codex/reserve-availability.ts +177 -0
- package/src/codex/routing.ts +28 -9
- package/src/codex/shim.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +23 -3
- package/src/combos/failover.ts +125 -7
- package/src/combos/identifiers.ts +89 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/resolve.ts +80 -9
- package/src/combos/types.ts +20 -93
- package/src/config/subagent-models.ts +24 -0
- package/src/config.ts +156 -13
- package/src/generated/compatibility-version.json +474 -178
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/journal.ts +65 -4
- package/src/integrations/store.ts +5 -0
- package/src/lab/events/limits.ts +4 -0
- package/src/lib/destination-policy.ts +31 -2
- package/src/lib/errors.ts +39 -0
- package/src/lib/provider-outbound.ts +69 -3
- package/src/lib/proxy-env.ts +22 -0
- package/src/lib/redact-folding.ts +176 -0
- package/src/lib/redact.ts +2 -175
- package/src/lib/state-store-sweeper.ts +20 -6
- package/src/lib/token-estimate.ts +94 -27
- package/src/lib/windows-user-principal.ts +53 -5
- package/src/oauth/account-quota-rank.ts +40 -1
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/chatgpt-device.ts +187 -0
- package/src/oauth/chatgpt.ts +31 -4
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +140 -29
- package/src/oauth/log.ts +3 -0
- package/src/oauth/login-cli.ts +5 -0
- package/src/oauth/meta-muse.ts +117 -15
- package/src/oauth/pool-settings-capability.ts +15 -4
- package/src/providers/api-keys.ts +8 -10
- package/src/providers/default-aliases.ts +39 -0
- package/src/providers/derive.ts +10 -2
- package/src/providers/fastwire.ts +36 -7
- package/src/providers/initial-model-selection-runtime.ts +90 -0
- package/src/providers/initial-model-selection.ts +120 -0
- package/src/providers/key-failover.ts +134 -54
- package/src/providers/key-store.ts +11 -1
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery.ts +76 -0
- package/src/providers/model-rename-startup.ts +72 -8
- package/src/providers/muse-subscription-usage.ts +95 -0
- package/src/providers/openai-sidecar.ts +17 -5
- package/src/providers/openai-tiers-destination.ts +102 -0
- package/src/providers/openai-tiers.ts +2 -99
- package/src/providers/opencode-go-transport.ts +41 -0
- package/src/providers/quota-key-accounts.ts +141 -0
- package/src/providers/quota-types.ts +9 -0
- package/src/providers/quota.ts +625 -98
- package/src/providers/registry.ts +60 -17
- package/src/providers/xai-responses-opt-in.ts +31 -5
- package/src/quota/reset-activation.ts +81 -0
- package/src/quota/reset-detector.ts +305 -0
- package/src/quota/reset-notify-config.ts +162 -0
- package/src/quota/reset-observer.ts +125 -0
- package/src/quota/reset-poller.ts +160 -0
- package/src/quota/reset-seen-store.ts +385 -0
- package/src/quota/reset-sinks.ts +199 -0
- package/src/quota/window-mapping.ts +106 -0
- package/src/responses/apply-patch-envelope.ts +46 -0
- package/src/responses/code-mode-helper-compat.ts +39 -1
- package/src/responses/custom-tool-compat.ts +10 -4
- package/src/responses/hosted-tool-policy.ts +12 -4
- package/src/responses/parser-content.ts +133 -0
- package/src/responses/parser-text-format.ts +24 -0
- package/src/responses/parser-tools.ts +188 -0
- package/src/responses/parser.ts +3 -326
- package/src/responses/state.ts +124 -28
- package/src/router.ts +48 -13
- package/src/routing/analytics.ts +1 -0
- package/src/routing/capability.ts +17 -4
- package/src/server/auth-cors.ts +7 -1
- package/src/server/background-lifecycle.ts +23 -1
- package/src/server/chat-completions.ts +25 -3
- package/src/server/claude-messages.ts +62 -5
- package/src/server/effort-row.ts +1 -1
- package/src/server/fast-row.ts +295 -0
- package/src/server/gui-static.ts +30 -4
- package/src/server/index.ts +122 -28
- package/src/server/live.ts +18 -4
- package/src/server/management/agent-settings-routes.ts +2 -2
- package/src/server/management/combo-routes.ts +37 -9
- package/src/server/management/config-routes.ts +93 -2
- package/src/server/management/integration-routes.ts +108 -0
- package/src/server/management/model-routes.ts +13 -3
- package/src/server/management/model-rows.ts +20 -1
- package/src/server/management/native-integration-routes.ts +4 -1
- package/src/server/management/oauth-account-routes.ts +45 -10
- package/src/server/management/provider-routes.ts +34 -3
- package/src/server/management/quota-reset-routes.ts +57 -0
- package/src/server/management/route-registry.ts +7 -4
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/system-routes.ts +3 -2
- package/src/server/management-api.ts +14 -2
- package/src/server/ports.ts +12 -2
- package/src/server/relay-eager.ts +38 -23
- package/src/server/relay.ts +4 -0
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/codex-ws-correlation.ts +65 -0
- package/src/server/responses/codex-ws-exchange.ts +261 -0
- package/src/server/responses/codex-ws-metadata.ts +134 -0
- package/src/server/responses/codex-ws-pool.ts +162 -0
- package/src/server/responses/codex-ws-request.ts +87 -0
- package/src/server/responses/codex-ws-session.ts +93 -0
- package/src/server/responses/codex-ws-wire.ts +144 -0
- package/src/server/responses/collaboration.ts +41 -1
- package/src/server/responses/compact.ts +105 -12
- package/src/server/responses/core.ts +510 -57
- package/src/server/responses/empty-completion-guard.ts +4 -0
- package/src/server/responses/fetch-helpers.ts +10 -3
- package/src/server/responses/input-admission.ts +16 -9
- package/src/server/responses/responses-field-backfill.ts +1 -1
- package/src/server/responses/ws-upstream.ts +34 -318
- package/src/server/responses-custom-tool-repair.ts +20 -4
- package/src/server/responses-undeclared-tool-guard.ts +100 -8
- package/src/server/safe-response-headers.ts +23 -0
- package/src/server/search.ts +9 -0
- package/src/server/subagent-models-startup.ts +27 -0
- package/src/server/system-env-shell.ts +238 -0
- package/src/server/system-env.ts +7 -234
- package/src/server/ws-bridge.ts +3 -25
- package/src/server/xai-responses-startup.ts +21 -0
- package/src/service-manager-probe.ts +1 -1
- package/src/service.ts +55 -16
- package/src/types/config.ts +108 -12
- package/src/types/provider.ts +36 -7
- package/src/types/request.ts +8 -0
- package/src/types/tools.ts +26 -1
- package/src/types.ts +2 -0
- package/src/update/notify.ts +8 -2
- package/src/usage/cost.ts +38 -28
- package/src/usage/expected-prices.ts +34 -15
- package/src/usage/log.ts +2 -0
- package/src/usage/model-identity.ts +26 -0
- package/src/usage/summary.ts +15 -1
- package/src/vision/describe.ts +6 -0
- package/src/vision/image-rewrite.ts +108 -0
- package/src/vision/index.ts +19 -306
- package/src/vision/plan.ts +205 -0
- package/src/web-search/executor.ts +6 -0
- package/src/web-search/index.ts +8 -1
- package/gui/dist/assets/index-B2YjLA-i.css +0 -1
- package/gui/dist/assets/index-aPup8CKb.js +0 -112
|
@@ -2,7 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
5
|
-
import { atomicWriteFile, expandUserPath, getConfigDir, websocketsEnabled } from "../../config";
|
|
5
|
+
import { atomicWriteFile, expandUserPath, getConfigDir, loadConfig, ultraFastTierEnabled, websocketsEnabled } from "../../config";
|
|
6
6
|
import { CODEX_CONFIG_PATH, CODEX_MODELS_CACHE_PATH, DEFAULT_CATALOG_PATH, readRootTomlString, resolveCodexConfigPath } from "../paths";
|
|
7
7
|
import { clearModelCache, DEFAULT_MODEL_CACHE_TTL_MS, getFreshCached, getStaleCached, isModelsFetchCoolingDown, markModelsFetchFailure, setCached } from "../model-cache";
|
|
8
8
|
import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
|
|
@@ -31,10 +31,11 @@ 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,
|
|
34
|
+
import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREAM_NATIVE_ENTRIES, hasNativeOpenAiCapabilityMetadata, nativeMultiAgentVersion, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "./metadata";
|
|
35
35
|
import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
|
|
36
36
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
37
37
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
38
|
+
import { NATIVE_GPT6_ASTRA_MODEL } from "./native-models";
|
|
38
39
|
|
|
39
40
|
export function legacyCatalogBackupPath(): string {
|
|
40
41
|
return join(getConfigDir(), "catalog-backup.json");
|
|
@@ -95,6 +96,8 @@ export const CODEX_PROVIDER_MODEL_CATALOG_KIND = "provider-model-v1";
|
|
|
95
96
|
export interface CatalogModel {
|
|
96
97
|
id: string;
|
|
97
98
|
provider: string;
|
|
99
|
+
/** Canonical or configured short alias for the provider segment. */
|
|
100
|
+
providerAlias?: string | null;
|
|
98
101
|
/** Public Codex-facing slug override (used by combo aliases). */
|
|
99
102
|
alias?: string;
|
|
100
103
|
/** Explicit combo takeover of a bare OpenAI-native catalog id. */
|
|
@@ -303,7 +306,98 @@ const NO_FAST_TIER_NATIVE_SLUGS = new Set([
|
|
|
303
306
|
"gpt-5.3-codex-spark",
|
|
304
307
|
]);
|
|
305
308
|
|
|
309
|
+
/** Does this row already carry an `ultrafast` tier the operator put there themselves? */
|
|
310
|
+
/**
|
|
311
|
+
* Read the opt-in from the live config, ONCE per catalog build.
|
|
312
|
+
*
|
|
313
|
+
* `deriveEntry` and its call sites are pure `RawEntry -> RawEntry` transforms with no
|
|
314
|
+
* config parameter, so the flag is resolved here rather than threaded through all of them.
|
|
315
|
+
* It is memoized because `normalizeRoutedCatalogEntry` runs per entry in a sync loop, and
|
|
316
|
+
* `loadConfig()` chmods the config dir, hardens three secrets, reads the file and runs a
|
|
317
|
+
* full Zod parse — doing that once per catalog row would be a real cost for one boolean.
|
|
318
|
+
* Callers holding a config still pass `opts.ultraFastTier` explicitly, which bypasses this
|
|
319
|
+
* entirely and is what the tests do. A read failure means OFF, matching `.catch(false)`.
|
|
320
|
+
*/
|
|
321
|
+
let ultraFastOptInCache: { value: boolean; at: number } | null = null;
|
|
322
|
+
const ULTRA_FAST_OPT_IN_TTL_MS = 5_000;
|
|
323
|
+
|
|
324
|
+
function ultraFastTierOptIn(): boolean {
|
|
325
|
+
const now = Date.now();
|
|
326
|
+
if (ultraFastOptInCache && now - ultraFastOptInCache.at < ULTRA_FAST_OPT_IN_TTL_MS) {
|
|
327
|
+
return ultraFastOptInCache.value;
|
|
328
|
+
}
|
|
329
|
+
let value = false;
|
|
330
|
+
try {
|
|
331
|
+
value = ultraFastTierEnabled(loadConfig());
|
|
332
|
+
} catch {
|
|
333
|
+
value = false;
|
|
334
|
+
}
|
|
335
|
+
ultraFastOptInCache = { value, at: now };
|
|
336
|
+
return value;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Test seam: drop the memoized opt-in so a config change is observed immediately. */
|
|
340
|
+
export function resetUltraFastTierOptInCache(): void {
|
|
341
|
+
ultraFastOptInCache = null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function entryDeclaresUltraFast(entry: RawEntry): boolean {
|
|
345
|
+
const tiers = entry.service_tiers;
|
|
346
|
+
const declaredInTiers = Array.isArray(tiers) && tiers.some(tier => (
|
|
347
|
+
!!tier && typeof tier === "object" && "id" in tier
|
|
348
|
+
&& String((tier as { id?: unknown }).id).trim().toLowerCase() === "ultrafast"
|
|
349
|
+
));
|
|
350
|
+
const speeds = entry.additional_speed_tiers;
|
|
351
|
+
const declaredInSpeeds = Array.isArray(speeds) && speeds.some(speed => (
|
|
352
|
+
typeof speed === "string" && speed.trim().toLowerCase() === "ultrafast"
|
|
353
|
+
));
|
|
354
|
+
return declaredInTiers || declaredInSpeeds;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Keep the operator's `ultrafast` and drop everything else.
|
|
359
|
+
*
|
|
360
|
+
* A routed row must not inherit the native template's `priority` tier, which is the whole
|
|
361
|
+
* reason this strip exists. Preserving the supplied tier without this narrowing would
|
|
362
|
+
* smuggle Fast onto third-party providers under an unrelated flag.
|
|
363
|
+
*/
|
|
364
|
+
function retainOnlyUltraFastTier(entry: RawEntry): void {
|
|
365
|
+
const tiers = entry.service_tiers;
|
|
366
|
+
const keptTiers = Array.isArray(tiers)
|
|
367
|
+
? tiers.filter(tier => (
|
|
368
|
+
!!tier && typeof tier === "object" && "id" in tier
|
|
369
|
+
&& String((tier as { id?: unknown }).id).trim().toLowerCase() === "ultrafast"
|
|
370
|
+
))
|
|
371
|
+
: [];
|
|
372
|
+
if (keptTiers.length > 0) entry.service_tiers = keptTiers;
|
|
373
|
+
else delete entry.service_tiers;
|
|
374
|
+
|
|
375
|
+
const speeds = entry.additional_speed_tiers;
|
|
376
|
+
const keptSpeeds = Array.isArray(speeds)
|
|
377
|
+
? speeds.filter(speed => typeof speed === "string" && speed.trim().toLowerCase() === "ultrafast")
|
|
378
|
+
: [];
|
|
379
|
+
if (keptSpeeds.length > 0) entry.additional_speed_tiers = keptSpeeds;
|
|
380
|
+
else delete entry.additional_speed_tiers;
|
|
381
|
+
|
|
382
|
+
// A default of `priority` on a row that now only offers ultrafast would name a tier the
|
|
383
|
+
// row no longer carries.
|
|
384
|
+
if (String(entry.service_tier ?? "").trim().toLowerCase() !== "ultrafast") delete entry.service_tier;
|
|
385
|
+
if (String(entry.default_service_tier ?? "").trim().toLowerCase() !== "ultrafast") {
|
|
386
|
+
delete entry.default_service_tier;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
306
390
|
export function normalizeServiceTiers(entry: RawEntry): RawEntry {
|
|
391
|
+
// Repair only the old built-in Astra speed copy on persisted native/account rows.
|
|
392
|
+
// A custom description (and every other field) remains user-owned.
|
|
393
|
+
const nativeSlug = trustedAccountBoundNativeCatalogSlug(entry) ?? entry.slug;
|
|
394
|
+
if (nativeSlug === NATIVE_GPT6_ASTRA_MODEL && Array.isArray(entry.service_tiers)) {
|
|
395
|
+
entry.service_tiers = entry.service_tiers.map(tier =>
|
|
396
|
+
tier && typeof tier === "object" && tier.id === "priority"
|
|
397
|
+
&& tier.description === "1.5x speed, increased usage"
|
|
398
|
+
? { ...tier, description: "2x speed, increased usage" } : tier,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
307
401
|
// Strip service tiers for models that do not actually support the Fast tier.
|
|
308
402
|
if (typeof entry.slug === "string" && NO_FAST_TIER_NATIVE_SLUGS.has(entry.slug)) {
|
|
309
403
|
delete entry.service_tier;
|
|
@@ -502,6 +596,8 @@ export function ensureStrictCatalogFields(
|
|
|
502
596
|
export type MultiAgentMode = "v1" | "default" | "v2";
|
|
503
597
|
|
|
504
598
|
export interface MultiAgentModeOptions {
|
|
599
|
+
/** Caller-owned source metadata already defines the default for these projected rows. */
|
|
600
|
+
preserveDefaultMultiAgentVersion?: (entry: RawEntry) => boolean;
|
|
505
601
|
/**
|
|
506
602
|
* When the catalog is in v2 mode, stamp ChatGPT-native rows as v1 instead.
|
|
507
603
|
* Routed parents get v2 (plaintext child tasks). Native Sol/Terra stay on v1
|
|
@@ -526,7 +622,7 @@ export function catalogEntryIsNativeChatGpt(entry: RawEntry): boolean {
|
|
|
526
622
|
if (
|
|
527
623
|
entry.opencodex_catalog_kind === CODEX_CUSTOM_MODEL_CATALOG_KIND
|
|
528
624
|
&& entry.use_responses_lite === true
|
|
529
|
-
&&
|
|
625
|
+
&& hasNativeOpenAiCapabilityMetadata(routedNativeSlug)
|
|
530
626
|
) return true;
|
|
531
627
|
if (UPSTREAM_NATIVE_ENTRIES.has(slug) || SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug)) return true;
|
|
532
628
|
return false;
|
|
@@ -577,6 +673,7 @@ export function applyMultiAgentMode(
|
|
|
577
673
|
// Restore upstream defaults: clear any stale forced multi_agent_version and
|
|
578
674
|
// re-apply upstream pins from the snapshot for native entries that have one.
|
|
579
675
|
for (const entry of entries) {
|
|
676
|
+
if (options.preserveDefaultMultiAgentVersion?.(entry)) continue;
|
|
580
677
|
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
581
678
|
const nativeAlias = entry.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND;
|
|
582
679
|
const routedNativeSlug = slug.startsWith(`${OPENAI_CODEX_PROVIDER_ID}/`)
|
|
@@ -584,7 +681,7 @@ export function applyMultiAgentMode(
|
|
|
584
681
|
: "";
|
|
585
682
|
const codexForwardCapabilityAlias = entry.opencodex_catalog_kind === CODEX_CUSTOM_MODEL_CATALOG_KIND
|
|
586
683
|
&& entry.use_responses_lite === true
|
|
587
|
-
&&
|
|
684
|
+
&& hasNativeOpenAiCapabilityMetadata(routedNativeSlug)
|
|
588
685
|
? routedNativeSlug
|
|
589
686
|
: undefined;
|
|
590
687
|
const upstreamPin = nativeAlias
|
|
@@ -612,17 +709,34 @@ export function normalizeRoutedCatalogEntry(
|
|
|
612
709
|
entry: RawEntry,
|
|
613
710
|
parallelToolCalls = false,
|
|
614
711
|
toolMode?: "code_mode_only" | "shell" | string,
|
|
712
|
+
opts?: { ultraFastTier?: boolean },
|
|
615
713
|
): RawEntry {
|
|
616
714
|
delete entry.model_messages;
|
|
617
715
|
delete entry.tool_mode;
|
|
618
716
|
applyRoutedCodexToolMode(entry, toolMode);
|
|
619
717
|
delete entry.multi_agent_version;
|
|
718
|
+
delete entry.multi_agent_reasoning_effort;
|
|
620
719
|
delete entry.use_responses_lite;
|
|
621
720
|
delete entry.supports_websockets;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
721
|
+
/*
|
|
722
|
+
* Tier metadata is stripped from routed rows because a row cloned from a native template
|
|
723
|
+
* would otherwise hand a third-party provider OpenAI's tiers.
|
|
724
|
+
*
|
|
725
|
+
* The opt-in carves out exactly one case: an `ultrafast` the OPERATOR put in their own
|
|
726
|
+
* catalog. #3429's reporter added it by hand and watched a regeneration delete it every
|
|
727
|
+
* time. Preserving what they wrote is not the same as advertising a tier — upstream
|
|
728
|
+
* publishes only `priority`, and synthesizing an ultrafast row is what PR #2994 was
|
|
729
|
+
* closed for. So this keeps a supplied tier and still never invents one.
|
|
730
|
+
*/
|
|
731
|
+
const keepUltraFast = (opts?.ultraFastTier ?? ultraFastTierOptIn()) && entryDeclaresUltraFast(entry);
|
|
732
|
+
if (!keepUltraFast) {
|
|
733
|
+
delete entry.additional_speed_tiers;
|
|
734
|
+
delete entry.service_tier;
|
|
735
|
+
delete entry.service_tiers;
|
|
736
|
+
delete entry.default_service_tier;
|
|
737
|
+
} else {
|
|
738
|
+
retainOnlyUltraFastTier(entry);
|
|
739
|
+
}
|
|
626
740
|
// Routed rows cloned from native templates must not inherit OpenAI-only summary delivery.
|
|
627
741
|
// Explicit provider/model metadata is re-applied after this normalization step.
|
|
628
742
|
delete entry.supports_reasoning_summaries;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { effectiveProviderAlias, effectiveProviderAliasDecision } from "../../providers/default-aliases";
|
|
2
|
+
import { initialModelSelectionPending } from "../../providers/initial-model-selection";
|
|
1
3
|
import { execFileSync } from "node:child_process";
|
|
2
4
|
import { createHash, createHmac, randomBytes } from "node:crypto";
|
|
3
5
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
@@ -32,7 +34,7 @@ import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
|
32
34
|
import { modelInList } from "../../types";
|
|
33
35
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
34
36
|
import { isModelVisionSidecarConsumer } from "../../vision/eligibility";
|
|
35
|
-
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
|
|
37
|
+
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider, type ModelMetadata } from "../../generated/model-metadata";
|
|
36
38
|
import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
37
39
|
import {
|
|
38
40
|
captureFastPolicyAuthority,
|
|
@@ -68,6 +70,7 @@ import {
|
|
|
68
70
|
import { redactSecretString } from "../../lib/redact";
|
|
69
71
|
import {
|
|
70
72
|
extractProviderModelItems,
|
|
73
|
+
isRegistryModelDiscoveryUrl,
|
|
71
74
|
readBoundedDiscoveryJson,
|
|
72
75
|
resolveProviderModelDiscovery,
|
|
73
76
|
type ModelDiscoveryResponseFailure,
|
|
@@ -81,7 +84,7 @@ import { createAdmissionGate, ResourceAdmissionError, type AdmissionMetrics } fr
|
|
|
81
84
|
|
|
82
85
|
import { CODEX_CUSTOM_MODEL_CATALOG_KIND, JAWCODE_CATALOG_AUGMENT_PROVIDERS, catalogModelSlug, shouldExposeRoutedModel } from "./parsing";
|
|
83
86
|
import type { CatalogModel } from "./parsing";
|
|
84
|
-
import { disabledNativeSlugs, hasComboTargets,
|
|
87
|
+
import { disabledNativeSlugs, hasComboTargets, hasNativeOpenAiCapabilityMetadata, NATIVE_GPT56_MAX_INPUT_TOKENS, nativeContextLimits, nativeOpenAiCapabilityDisplayName, nativeDefaultReasoningEffort, nativeInputModalities, nativeOpenAiAutoCompactTokenLimit, nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, nativeOpenAiMaxOutputTokens, nativeOpenAiSlugs, nativeParallelToolCalls, nativeReasoningEfforts } from "./metadata";
|
|
85
88
|
import { deriveComboCatalogModel, normalizedOpenAiApiSignature, openAiApiCollisionWarnings, replaceLastComboCatalogOmissions, warnUncataloguedComboOnce } from "./aggregation";
|
|
86
89
|
import type { ComboCatalogOmission } from "./aggregation";
|
|
87
90
|
import type { CatalogGatherProviderAuthEvidence } from "./filesystem-evidence";
|
|
@@ -164,6 +167,7 @@ interface CapturedProviderGather {
|
|
|
164
167
|
readonly request: CapturedModelsRequest;
|
|
165
168
|
readonly fastPolicyAuthority: FastPolicyAuthority;
|
|
166
169
|
readonly metadataModelIdCaseFold: boolean;
|
|
170
|
+
readonly effectiveAlias?: string | null;
|
|
167
171
|
readonly observedAuth?: ModelsAuthResolution;
|
|
168
172
|
/**
|
|
169
173
|
* Configured model ids this provider must keep even when live discovery omits
|
|
@@ -380,6 +384,7 @@ function captureTrustedOpenAiApiPolicy(
|
|
|
380
384
|
models: entry.models,
|
|
381
385
|
...(entry.modelContextWindows ? { modelContextWindows: entry.modelContextWindows } : {}),
|
|
382
386
|
...(entry.modelMaxInputTokens ? { modelMaxInputTokens: entry.modelMaxInputTokens } : {}),
|
|
387
|
+
...(entry.modelMaxOutputTokens ? { modelMaxOutputTokens: entry.modelMaxOutputTokens } : {}),
|
|
383
388
|
...(entry.virtualModels ? { virtualModels: entry.virtualModels } : {}),
|
|
384
389
|
...(entry.modelInputModalities ? { modelInputModalities: entry.modelInputModalities } : {}),
|
|
385
390
|
...(entry.modelReasoningEfforts ? { modelReasoningEfforts: entry.modelReasoningEfforts } : {}),
|
|
@@ -413,6 +418,7 @@ function captureProviderGather(
|
|
|
413
418
|
configured: OcxProviderConfig,
|
|
414
419
|
authResolver: ModelsAuthResolver,
|
|
415
420
|
retainConfiguredModelIds?: ReadonlySet<string>,
|
|
421
|
+
config?: Pick<OcxConfig, "providers">,
|
|
416
422
|
): CapturedProviderGather {
|
|
417
423
|
const enriched = detachedClone(withCanonicalOpenAiForwardAuthDefault(name, configured));
|
|
418
424
|
enrichProviderFromRegistry(name, enriched);
|
|
@@ -454,6 +460,7 @@ function captureProviderGather(
|
|
|
454
460
|
maxModels: discovery.maxModels,
|
|
455
461
|
trustedOpenAiApi,
|
|
456
462
|
});
|
|
463
|
+
const effectiveAlias = effectiveProviderAliasDecision(name, configured, config);
|
|
457
464
|
return Object.freeze({
|
|
458
465
|
name,
|
|
459
466
|
provider,
|
|
@@ -462,6 +469,7 @@ function captureProviderGather(
|
|
|
462
469
|
request,
|
|
463
470
|
fastPolicyAuthority,
|
|
464
471
|
metadataModelIdCaseFold,
|
|
472
|
+
effectiveAlias,
|
|
465
473
|
...(observedAuth ? { observedAuth: Object.freeze({ ...observedAuth }) } : {}),
|
|
466
474
|
...(retainConfiguredModelIds && retainConfiguredModelIds.size > 0
|
|
467
475
|
? { retainConfiguredModelIds }
|
|
@@ -503,6 +511,7 @@ function captureGatherFlight(
|
|
|
503
511
|
provider,
|
|
504
512
|
authResolver,
|
|
505
513
|
comboTargetsByProvider.get(name),
|
|
514
|
+
config,
|
|
506
515
|
));
|
|
507
516
|
const discoveryPolicySnapshots = Object.freeze(providers.map(provider => provider.policy));
|
|
508
517
|
return Object.freeze({
|
|
@@ -625,8 +634,36 @@ export function clearGatherRoutedModelsInflight(): void {
|
|
|
625
634
|
gatherInflight.clear();
|
|
626
635
|
}
|
|
627
636
|
|
|
637
|
+
const NUMERIC_MODEL_ID_SEGMENT = /^\d+$/;
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Resolve an unknown Claude point release or date pin from the nearest configured
|
|
641
|
+
* family row. Only numeric tail segments are removed so unrelated model families
|
|
642
|
+
* cannot inherit one another's limits.
|
|
643
|
+
*/
|
|
644
|
+
function anthropicFamilyContextWindow(
|
|
645
|
+
record: Record<string, number> | undefined,
|
|
646
|
+
id: string,
|
|
647
|
+
): number | undefined {
|
|
648
|
+
if (!record || !id.toLowerCase().startsWith("claude-")) return undefined;
|
|
649
|
+
let candidate = id;
|
|
650
|
+
while (true) {
|
|
651
|
+
const cut = candidate.lastIndexOf("-");
|
|
652
|
+
if (cut <= 0 || !NUMERIC_MODEL_ID_SEGMENT.test(candidate.slice(cut + 1))) return undefined;
|
|
653
|
+
candidate = candidate.slice(0, cut);
|
|
654
|
+
const value = modelRecordValue(record, candidate);
|
|
655
|
+
if (typeof value === "number" && value > 0) return value;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/**
|
|
660
|
+
* Resolve the configured context window in exact-model, Anthropic numeric-family,
|
|
661
|
+
* then provider-wide order. Return undefined when the selected value is not positive.
|
|
662
|
+
*/
|
|
628
663
|
export function configuredContextWindow(prov: OcxProviderConfig, id: string): number | undefined {
|
|
629
|
-
const configured = modelRecordValue(prov.modelContextWindows, id)
|
|
664
|
+
const configured = modelRecordValue(prov.modelContextWindows, id)
|
|
665
|
+
?? (prov.adapter === "anthropic" ? anthropicFamilyContextWindow(prov.modelContextWindows, id) : undefined)
|
|
666
|
+
?? prov.contextWindow;
|
|
630
667
|
return typeof configured === "number" && configured > 0 ? configured : undefined;
|
|
631
668
|
}
|
|
632
669
|
|
|
@@ -717,7 +754,7 @@ function configuredVerbositySupport(name: string, prov: OcxProviderConfig | unde
|
|
|
717
754
|
// Read from the PROVIDER CONFIG, never from PROVIDER_REGISTRY. A gather flight captures its
|
|
718
755
|
// registry authority up front and forbids any later registry read, so consulting the registry
|
|
719
756
|
// here made a custom-destination flight fall back to "configured" instead of serving its own
|
|
720
|
-
// discovery result (tests/codex-gather-authority.test.ts). `applyVerbosityDefaults` in
|
|
757
|
+
// discovery result (tests/codex-integration/codex-gather-authority.test.ts). `applyVerbosityDefaults` in
|
|
721
758
|
// providers/derive.ts materializes the registry default into the config at seed/enrich time.
|
|
722
759
|
return prov.supportsVerbosity;
|
|
723
760
|
}
|
|
@@ -728,8 +765,17 @@ export function applyProviderConfigHints(
|
|
|
728
765
|
model: CatalogModel,
|
|
729
766
|
providerCap?: number,
|
|
730
767
|
metadataModelIdCaseFold?: boolean,
|
|
768
|
+
effectiveAlias?: string | null,
|
|
731
769
|
): CatalogModel {
|
|
732
770
|
const displayName = configuredModelDisplayName(prov, model.id);
|
|
771
|
+
// The alias decision is resolved once at flight admission (captureProviderGather) and threaded
|
|
772
|
+
// through as `effectiveAlias`. Re-deriving it here would read PROVIDER_REGISTRY after admission,
|
|
773
|
+
// which is exactly the authority leak tests/codex-integration/codex-gather-authority.test.ts
|
|
774
|
+
// forbids: a flight must not consult the live registry once its transport has been captured.
|
|
775
|
+
// When no decision was threaded in, carry whatever the row already resolved to instead.
|
|
776
|
+
const providerAlias = typeof effectiveAlias === "string" || effectiveAlias === null
|
|
777
|
+
? effectiveAlias
|
|
778
|
+
: model.providerAlias;
|
|
733
779
|
const configuredCap = configuredContextWindow(prov, model.id);
|
|
734
780
|
const configuredMaxInput = configuredMaxInputTokens(prov, model.id);
|
|
735
781
|
const maxOutputTokens = routedMaxOutputTokens(name, prov, model, model.id, metadataModelIdCaseFold);
|
|
@@ -755,6 +801,7 @@ export function applyProviderConfigHints(
|
|
|
755
801
|
const {
|
|
756
802
|
supportsServiceTier: _staleServiceTier,
|
|
757
803
|
fastTierDescription: _staleFastTierDescription,
|
|
804
|
+
providerAlias: _staleProviderAlias,
|
|
758
805
|
...modelWithoutServiceTier
|
|
759
806
|
} = model;
|
|
760
807
|
// 已发现窗口只允许被配置值压低;缺窗口时,已开的 Context cap 就是实际窗口。
|
|
@@ -767,6 +814,7 @@ export function applyProviderConfigHints(
|
|
|
767
814
|
const hinted = {
|
|
768
815
|
...modelWithoutServiceTier,
|
|
769
816
|
...(displayName !== undefined ? { displayName } : {}),
|
|
817
|
+
...(providerAlias !== undefined ? { providerAlias } : {}),
|
|
770
818
|
...(hintedWindow !== undefined ? { contextWindow: hintedWindow } : {}),
|
|
771
819
|
...(inputModalities ? { inputModalities } : {}),
|
|
772
820
|
...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
|
|
@@ -826,8 +874,9 @@ export function catalogHintsFromProviderConfig(
|
|
|
826
874
|
id: string,
|
|
827
875
|
contextCap?: number,
|
|
828
876
|
metadataModelIdCaseFold?: boolean,
|
|
877
|
+
effectiveAlias?: string | null,
|
|
829
878
|
): Partial<CatalogModel> {
|
|
830
|
-
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold);
|
|
879
|
+
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold, effectiveAlias);
|
|
831
880
|
const { provider: _provider, id: _id, ...hints } = hinted;
|
|
832
881
|
return hints;
|
|
833
882
|
}
|
|
@@ -838,8 +887,9 @@ export function applyConfigHintsToCachedModels(
|
|
|
838
887
|
models: CatalogModel[],
|
|
839
888
|
contextCap?: number,
|
|
840
889
|
metadataModelIdCaseFold?: boolean,
|
|
890
|
+
effectiveAlias?: string | null,
|
|
841
891
|
): CatalogModel[] {
|
|
842
|
-
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold));
|
|
892
|
+
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold, effectiveAlias));
|
|
843
893
|
}
|
|
844
894
|
|
|
845
895
|
|
|
@@ -862,6 +912,62 @@ interface ComboCatalogMemberFallback {
|
|
|
862
912
|
readonly reasoningEfforts?: readonly string[];
|
|
863
913
|
}
|
|
864
914
|
|
|
915
|
+
/**
|
|
916
|
+
* Ladder advertised for a combo member whose vendor metadata says it reasons but
|
|
917
|
+
* carries no explicit ladder (Claude, Grok). Codex needs a non-empty ladder to show
|
|
918
|
+
* the effort control; the routed adapters clamp to the real upstream top rung.
|
|
919
|
+
*/
|
|
920
|
+
const ROUTED_COMBO_MEMBER_REASONING_EFFORTS: readonly string[] = ["low", "medium", "high", "xhigh", "max"];
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Vendor-table lookup tolerant of point releases and date pins. Configured combo
|
|
924
|
+
* targets often name a variant the table does not carry (`claude-fable-5-1`,
|
|
925
|
+
* `claude-opus-4-5-20251101`); the base family row still describes its modality
|
|
926
|
+
* and reasoning capability, so fall back to it before giving up.
|
|
927
|
+
*/
|
|
928
|
+
function comboMemberVendorMetadata(provider: string, modelId: string): ModelMetadata | undefined {
|
|
929
|
+
const exact = getModelMetadataCaseInsensitive(provider, modelId);
|
|
930
|
+
if (exact) return exact;
|
|
931
|
+
let candidate = modelId.replace(/\[[^\]]*\]$/, "");
|
|
932
|
+
while (true) {
|
|
933
|
+
const trimmed = candidate.replace(/-\d+$/, "");
|
|
934
|
+
if (trimmed === candidate || !trimmed.includes("-")) return undefined;
|
|
935
|
+
const hit = getModelMetadataCaseInsensitive(provider, trimmed);
|
|
936
|
+
if (hit) return hit;
|
|
937
|
+
candidate = trimmed;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Combo members are usually thin discovery rows (id + context window). Without a
|
|
943
|
+
* capability source the combo intersection collapses to text-only / no effort ladder,
|
|
944
|
+
* and the Codex app then refuses image attachments and hides the effort picker for
|
|
945
|
+
* every Claude combo. The generated vendor table knows both, so use it as the
|
|
946
|
+
* last-resort fallback when the caller supplied none.
|
|
947
|
+
*
|
|
948
|
+
* `ModelMetadata.maxTokens` is the OUTPUT ceiling, so it fills `maxOutputTokens`.
|
|
949
|
+
* Mapping it onto `maxInputTokens` would be read by the combo intersection
|
|
950
|
+
* (`aggregation.ts` `Math.min` over member input ceilings) as a 128k input limit and
|
|
951
|
+
* shrink a 1M Claude combo window to 128k, taking autoCompactTokenLimit down with it.
|
|
952
|
+
*/
|
|
953
|
+
function vendorMetadataComboFallback(target: { provider: string; model: string }): ComboCatalogMemberFallback | undefined {
|
|
954
|
+
const metadataProvider = resolveMetadataProvider(target.provider);
|
|
955
|
+
const metadata = metadataProvider ? comboMemberVendorMetadata(metadataProvider, target.model) : undefined;
|
|
956
|
+
if (!metadata) return undefined;
|
|
957
|
+
return {
|
|
958
|
+
...(typeof metadata.contextWindow === "number" && metadata.contextWindow > 0
|
|
959
|
+
? { contextWindow: metadata.contextWindow }
|
|
960
|
+
: {}),
|
|
961
|
+
...(typeof metadata.maxTokens === "number" && metadata.maxTokens > 0
|
|
962
|
+
? { maxOutputTokens: metadata.maxTokens }
|
|
963
|
+
: {}),
|
|
964
|
+
...(Array.isArray(metadata.input) && metadata.input.length > 0
|
|
965
|
+
? { inputModalities: [...metadata.input] }
|
|
966
|
+
: {}),
|
|
967
|
+
...(metadata.reasoning === true ? { reasoningEfforts: [...ROUTED_COMBO_MEMBER_REASONING_EFFORTS] } : {}),
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
|
|
865
971
|
/**
|
|
866
972
|
* Resolve a combo target to a catalog member for derivation.
|
|
867
973
|
* Prefer discovery metadata; when the target is missing from the gather map or
|
|
@@ -877,11 +983,12 @@ export function resolveComboCatalogMember(
|
|
|
877
983
|
memberByKey: ReadonlyMap<string, CatalogModel>,
|
|
878
984
|
providers: ReadonlyMap<string, OcxProviderConfig>,
|
|
879
985
|
contextCap?: number,
|
|
880
|
-
|
|
986
|
+
callerFallback?: ComboCatalogMemberFallback,
|
|
881
987
|
metadataModelIdCaseFold?: boolean,
|
|
882
988
|
): CatalogModel | undefined {
|
|
883
989
|
const existing = memberByKey.get(targetKey(target));
|
|
884
990
|
const prov = providers.get(target.provider);
|
|
991
|
+
const fallback = callerFallback ?? vendorMetadataComboFallback(target);
|
|
885
992
|
// Disabled providers never contribute members — even a complete discovery row
|
|
886
993
|
// is unusable for catalog derivation while the provider is off.
|
|
887
994
|
if (prov?.disabled === true) return undefined;
|
|
@@ -1410,7 +1517,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1410
1517
|
const configured: CatalogModel[] = configuredIds.map(id => ({
|
|
1411
1518
|
id,
|
|
1412
1519
|
provider: name,
|
|
1413
|
-
...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold),
|
|
1520
|
+
...catalogHintsFromProviderConfig(name, prov, id, contextCap, metadataModelIdCaseFold, captured.effectiveAlias),
|
|
1414
1521
|
}));
|
|
1415
1522
|
const withConfiguredRetention = (
|
|
1416
1523
|
models: CatalogModel[],
|
|
@@ -1464,7 +1571,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1464
1571
|
: [{
|
|
1465
1572
|
id: prov.defaultModel,
|
|
1466
1573
|
provider: name,
|
|
1467
|
-
...catalogHintsFromProviderConfig(name, prov, prov.defaultModel, contextCap, metadataModelIdCaseFold),
|
|
1574
|
+
...catalogHintsFromProviderConfig(name, prov, prov.defaultModel, contextCap, metadataModelIdCaseFold, captured.effectiveAlias),
|
|
1468
1575
|
}];
|
|
1469
1576
|
const vertexDefaultSeed = seedVertexDefault ? configured[0] : undefined;
|
|
1470
1577
|
const withVertexDefaultSeed = (models: CatalogModel[]): CatalogModel[] => (
|
|
@@ -1481,7 +1588,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1481
1588
|
const cachedCursor = getFreshCached(name, ttlMs);
|
|
1482
1589
|
if (cachedCursor) {
|
|
1483
1590
|
return observed(
|
|
1484
|
-
withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedCursor, undefined, metadataModelIdCaseFold)),
|
|
1591
|
+
withConfiguredRetention(applyConfigHintsToCachedModels(name, prov, cachedCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias)),
|
|
1485
1592
|
"authoritative",
|
|
1486
1593
|
);
|
|
1487
1594
|
}
|
|
@@ -1489,7 +1596,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1489
1596
|
const cooling = getStaleCached(name);
|
|
1490
1597
|
return observed(
|
|
1491
1598
|
withConfiguredRetention(
|
|
1492
|
-
cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, metadataModelIdCaseFold) : configured,
|
|
1599
|
+
cooling ? applyConfigHintsToCachedModels(name, prov, cooling, undefined, metadataModelIdCaseFold, captured.effectiveAlias) : configured,
|
|
1493
1600
|
),
|
|
1494
1601
|
"degraded",
|
|
1495
1602
|
);
|
|
@@ -1530,7 +1637,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1530
1637
|
const staleCursor = getStaleCached(name);
|
|
1531
1638
|
return observed(
|
|
1532
1639
|
withConfiguredRetention(
|
|
1533
|
-
staleCursor ? applyConfigHintsToCachedModels(name, prov, staleCursor, undefined, metadataModelIdCaseFold) : configured,
|
|
1640
|
+
staleCursor ? applyConfigHintsToCachedModels(name, prov, staleCursor, undefined, metadataModelIdCaseFold, captured.effectiveAlias) : configured,
|
|
1534
1641
|
),
|
|
1535
1642
|
"degraded",
|
|
1536
1643
|
);
|
|
@@ -1548,7 +1655,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1548
1655
|
if (fresh) {
|
|
1549
1656
|
return observed(
|
|
1550
1657
|
withConfiguredRetention(
|
|
1551
|
-
withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold)),
|
|
1658
|
+
withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, fresh, contextCap, metadataModelIdCaseFold, captured.effectiveAlias)),
|
|
1552
1659
|
),
|
|
1553
1660
|
"authoritative",
|
|
1554
1661
|
); // dedups Codex's frequent /v1/models polling within the TTL
|
|
@@ -1560,7 +1667,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1560
1667
|
return observed(
|
|
1561
1668
|
withConfiguredRetention(
|
|
1562
1669
|
stale
|
|
1563
|
-
? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold))
|
|
1670
|
+
? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias))
|
|
1564
1671
|
: failedDiscoveryConfigured,
|
|
1565
1672
|
),
|
|
1566
1673
|
"degraded",
|
|
@@ -1600,7 +1707,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1600
1707
|
return {
|
|
1601
1708
|
models: withConfiguredRetention(
|
|
1602
1709
|
stale
|
|
1603
|
-
? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold))
|
|
1710
|
+
? withVertexDefaultSeed(applyConfigHintsToCachedModels(name, prov, stale, contextCap, metadataModelIdCaseFold, captured.effectiveAlias))
|
|
1604
1711
|
: failedDiscoveryConfigured,
|
|
1605
1712
|
),
|
|
1606
1713
|
fallback: stale ? "stale" : "configured",
|
|
@@ -1608,16 +1715,24 @@ async function fetchProviderModelsWithAuth(
|
|
|
1608
1715
|
};
|
|
1609
1716
|
};
|
|
1610
1717
|
try {
|
|
1718
|
+
// Canonical-URL TUN transparency for Clash/Surge/Mihomo fake-IP DNS:
|
|
1719
|
+
// `isRegistryModelDiscoveryUrl` proves the FINAL request URL is the
|
|
1720
|
+
// registry's own fixed discovery URL, so a purely-benchmark DNS answer may
|
|
1721
|
+
// be pin-connected through the intercepting TUN without proxy env. The
|
|
1722
|
+
// proof is on the URL — not the provider name — because an OAuth/forward
|
|
1723
|
+
// name matches any baseUrl by design. Retargeted or renamed custom rows
|
|
1724
|
+
// fetch a different URL and keep the rejection.
|
|
1725
|
+
const outboundDependencies = { isCanonicalUrl: isRegistryModelDiscoveryUrl };
|
|
1611
1726
|
const res = request.method === "POST"
|
|
1612
1727
|
? await providerOutboundPost(name, prov, url, {
|
|
1613
1728
|
headers,
|
|
1614
1729
|
body: JSON.stringify({ project }),
|
|
1615
1730
|
signal: AbortSignal.timeout(8000),
|
|
1616
|
-
})
|
|
1731
|
+
}, outboundDependencies)
|
|
1617
1732
|
: await providerOutboundGet(name, prov, url, {
|
|
1618
1733
|
headers,
|
|
1619
1734
|
signal: AbortSignal.timeout(8000),
|
|
1620
|
-
});
|
|
1735
|
+
}, outboundDependencies);
|
|
1621
1736
|
const redirectError = await providerRedirectError(res, url);
|
|
1622
1737
|
if (redirectError) {
|
|
1623
1738
|
const { models, fallback, shouldLog } = failedDiscoveryFallback({ reason: "http", httpStatus: res.status });
|
|
@@ -1677,7 +1792,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1677
1792
|
reasoningEfforts: [],
|
|
1678
1793
|
...(model.contextWindow ? { contextWindow: model.contextWindow } : {}),
|
|
1679
1794
|
...(model.inputModalities ? { inputModalities: model.inputModalities } : {}),
|
|
1680
|
-
}, contextCap, metadataModelIdCaseFold));
|
|
1795
|
+
}, contextCap, metadataModelIdCaseFold, captured.effectiveAlias));
|
|
1681
1796
|
const forCache = withConfiguredRetention(live, { retainComboTargets: false });
|
|
1682
1797
|
if (!setCached(name, forCache, Date.now(), cacheGeneration)) {
|
|
1683
1798
|
return observed(withConfiguredRetention(configured), "degraded");
|
|
@@ -1740,7 +1855,7 @@ async function fetchProviderModelsWithAuth(
|
|
|
1740
1855
|
provider: name,
|
|
1741
1856
|
...(ownedBy ? { owned_by: ownedBy } : {}),
|
|
1742
1857
|
...discoveredHints,
|
|
1743
|
-
}, contextCap, metadataModelIdCaseFold);
|
|
1858
|
+
}, contextCap, metadataModelIdCaseFold, captured.effectiveAlias);
|
|
1744
1859
|
})
|
|
1745
1860
|
.filter(m => shouldExposeProviderModel(name, m.id));
|
|
1746
1861
|
// Capture the count BEFORE the alias/configured augmentation below pushes extra rows into
|
|
@@ -1902,6 +2017,7 @@ export function filterCatalogVisibleModels(
|
|
|
1902
2017
|
}
|
|
1903
2018
|
}
|
|
1904
2019
|
return models.filter(m => {
|
|
2020
|
+
if (initialModelSelectionPending(config.providers[m.provider])) return false;
|
|
1905
2021
|
const nativeAlias = m.provider === COMBO_NAMESPACE && m.nativeAlias === true;
|
|
1906
2022
|
// disabledModels may be stored raw (canonical) or encoded (legacy UI writes).
|
|
1907
2023
|
for (const stored of disabled) {
|
|
@@ -2042,6 +2158,18 @@ async function gatherRoutedModelsUncached(
|
|
|
2042
2158
|
config,
|
|
2043
2159
|
capture.openAiApiPolicy,
|
|
2044
2160
|
);
|
|
2161
|
+
const apiProvider = activeProviders.find(provider => provider.name === OPENAI_API_PROVIDER_ID);
|
|
2162
|
+
// Trusted reconstruction replaces whole rows, including the earlier Fast hints.
|
|
2163
|
+
// Restore only that capability from the same captured authority used by discovery.
|
|
2164
|
+
if (apiProvider) {
|
|
2165
|
+
for (const model of apiAugmented) {
|
|
2166
|
+
if (model.provider !== OPENAI_API_PROVIDER_ID) continue;
|
|
2167
|
+
const policy = fastPolicyForModel(apiProvider.provider, model.id, apiProvider.name);
|
|
2168
|
+
const supported = serviceTierSupportFromPolicy(policy);
|
|
2169
|
+
if (supported !== undefined) model.supportsServiceTier = supported;
|
|
2170
|
+
if (supported === true && policy.fastTierDescription !== undefined) model.fastTierDescription = policy.fastTierDescription;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2045
2173
|
const metadataModelIdCaseFoldByProvider = new Map(
|
|
2046
2174
|
activeProviders.map(provider => [provider.name, provider.metadataModelIdCaseFold]),
|
|
2047
2175
|
);
|
|
@@ -2132,7 +2260,7 @@ async function gatherRoutedModelsUncached(
|
|
|
2132
2260
|
const nativeAliasMaxInput = combo.nativeAlias && combo.alias
|
|
2133
2261
|
? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
|
|
2134
2262
|
? NATIVE_GPT56_MAX_INPUT_TOKENS
|
|
2135
|
-
: nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
|
|
2263
|
+
: nativeOpenAiMaxInputTokens(combo.alias, comboNativeLimits) ?? nativeOpenAiContextWindow(combo.alias, comboNativeLimits))
|
|
2136
2264
|
: undefined;
|
|
2137
2265
|
const nativeAliasAutoCompact = combo.nativeAlias && combo.alias
|
|
2138
2266
|
? nativeOpenAiAutoCompactTokenLimit(combo.alias, comboNativeLimits)
|
|
@@ -2190,7 +2318,7 @@ async function gatherRoutedModelsUncached(
|
|
|
2190
2318
|
const codexForwardNativeCapabilityAlias = cm.provider === OPENAI_CODEX_PROVIDER_ID
|
|
2191
2319
|
&& providerForCanonicalCheck !== undefined
|
|
2192
2320
|
&& isCanonicalOpenAiForwardProvider(providerForCanonicalCheck)
|
|
2193
|
-
&&
|
|
2321
|
+
&& hasNativeOpenAiCapabilityMetadata(cm.modelId);
|
|
2194
2322
|
const customNativeLimits = {
|
|
2195
2323
|
...nativeContextLimits(config),
|
|
2196
2324
|
...(typeof cm.contextWindow === "number" && cm.contextWindow > 0
|
|
@@ -2252,7 +2380,8 @@ async function gatherRoutedModelsUncached(
|
|
|
2252
2380
|
// Display-only label: never feeds routing (customModels are keyed by routedSlug below).
|
|
2253
2381
|
...(cm.displayName
|
|
2254
2382
|
? { displayName: cm.displayName }
|
|
2255
|
-
: codexForwardNativeCapabilityAlias
|
|
2383
|
+
: codexForwardNativeCapabilityAlias
|
|
2384
|
+
? { displayName: nativeOpenAiCapabilityDisplayName(cm.modelId) ?? cm.modelId } : {}),
|
|
2256
2385
|
...(customContextWindow !== undefined ? { contextWindow: customContextWindow } : {}),
|
|
2257
2386
|
...(customMaxInputTokens !== undefined ? { maxInputTokens: customMaxInputTokens } : {}),
|
|
2258
2387
|
...(customMaxOutputTokens !== undefined ? { maxOutputTokens: customMaxOutputTokens } : {}),
|
|
@@ -2454,7 +2583,9 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
|
|
|
2454
2583
|
const maxOutputTokens = routedMaxOutputTokens(
|
|
2455
2584
|
OPENAI_API_PROVIDER_ID,
|
|
2456
2585
|
configured,
|
|
2457
|
-
|
|
2586
|
+
policy.modelMaxOutputTokens?.[id] !== undefined
|
|
2587
|
+
? { provider: OPENAI_API_PROVIDER_ID, id, maxOutputTokens: policy.modelMaxOutputTokens[id] }
|
|
2588
|
+
: existingById.get(id) ?? { provider: OPENAI_API_PROVIDER_ID, id },
|
|
2458
2589
|
policy.virtualModels?.[id]?.wireModelId ?? id,
|
|
2459
2590
|
);
|
|
2460
2591
|
return {
|