@iislee/opencodex 2.31.0 → 2.35.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 +3 -3
- package/bin/ocx.mjs +99 -70
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/package.json +3 -1
- package/src/adapters/anthropic.ts +104 -15
- package/src/adapters/base.ts +29 -1
- package/src/adapters/command-code.ts +42 -6
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +15 -10
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/discovery.ts +69 -18
- package/src/adapters/cursor/effort-map.ts +79 -1
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/live-models.ts +7 -2
- package/src/adapters/cursor/live-transport.ts +14 -0
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +2 -2
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -2
- package/src/adapters/cursor/native-exec.ts +20 -0
- package/src/adapters/cursor/protobuf-request.ts +68 -14
- package/src/adapters/cursor/request-builder.ts +40 -10
- package/src/adapters/cursor/tool-definitions.ts +14 -4
- package/src/adapters/cursor/tool-result-normalize.ts +28 -5
- package/src/adapters/cursor/types.ts +20 -0
- package/src/adapters/cursor.ts +209 -38
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +173 -25
- package/src/adapters/google-antigravity-wire.ts +5 -0
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google.ts +136 -24
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-tools.ts +55 -16
- package/src/adapters/kiro.ts +182 -23
- package/src/adapters/openai-chat.ts +300 -242
- package/src/adapters/openai-responses.ts +353 -27
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-catalog-nudge.ts +4 -3
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +17 -16
- package/src/bridge.ts +90 -37
- package/src/chat/inbound.ts +14 -1
- package/src/chat/outbound.ts +15 -7
- package/src/claude/agents-inject.ts +8 -1
- package/src/claude/context-windows.ts +16 -9
- package/src/claude/outbound.ts +24 -11
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +116 -19
- package/src/cli/account-extended.ts +262 -29
- package/src/cli/account-main.ts +12 -12
- package/src/cli/account.ts +69 -15
- package/src/cli/agent.ts +8 -1
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-desktop.ts +31 -11
- package/src/cli/claude.ts +26 -1
- package/src/cli/dispatch.ts +256 -28
- package/src/cli/doctor.ts +126 -14
- package/src/cli/help.ts +17 -6
- package/src/cli/index.ts +39 -9
- package/src/cli/init.ts +1 -0
- package/src/cli/inspect.ts +230 -0
- package/src/cli/models-runtime.ts +95 -0
- package/src/cli/models.ts +26 -10
- package/src/cli/observe.ts +31 -8
- package/src/cli/provider-runtime.ts +16 -2
- package/src/cli/provider.ts +2 -1
- package/src/cli/registry.ts +49 -10
- package/src/cli/runtime-api.ts +51 -7
- package/src/cli/status.ts +41 -1
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +1 -1
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +34 -10
- package/src/cli/version-skew.ts +46 -0
- package/src/clients/config-export.ts +85 -3
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +21 -0
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +3 -2
- package/src/codex/app-server-processes.ts +83 -7
- package/src/codex/auth-api.ts +50 -10
- package/src/codex/auth-context.ts +21 -18
- package/src/codex/autostart-health.ts +16 -0
- package/src/codex/catalog/aggregation.ts +18 -0
- package/src/codex/catalog/effort.ts +18 -3
- package/src/codex/catalog/metadata.ts +27 -1
- package/src/codex/catalog/native-models.ts +5 -2
- package/src/codex/catalog/parsing.ts +72 -27
- package/src/codex/catalog/provider-fetch.ts +187 -31
- package/src/codex/catalog/sync.ts +131 -5
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-write-lock.ts +3 -1
- package/src/codex/convergence-types.ts +1 -1
- package/src/codex/convergence.ts +27 -2
- package/src/codex/desired-state.ts +2 -2
- package/src/codex/desktop-app-restart.ts +355 -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-coordination.ts +83 -0
- package/src/codex/inject.ts +63 -22
- package/src/codex/injected-marker.ts +1 -1
- package/src/codex/internal/history-writer.ts +4 -3
- package/src/codex/log-guard/inspect.ts +22 -4
- package/src/codex/model-entitlements.ts +9 -2
- package/src/codex/native-profile-startup.ts +157 -27
- package/src/codex/native-residue.ts +26 -33
- package/src/codex/prompt-layers.ts +371 -25
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota.ts +91 -10
- package/src/codex/routing.ts +9 -0
- package/src/codex/shim.ts +56 -3
- package/src/codex/subagent-model-fallback.ts +104 -25
- package/src/codex/transition-state.ts +116 -8
- package/src/combos/failover.ts +27 -0
- package/src/combos/types.ts +10 -0
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +206 -824
- package/src/generated/compatibility-version.json +373 -213
- package/src/images/loop.ts +5 -4
- package/src/images/plan.ts +5 -4
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +46 -2
- package/src/integrations/registry.ts +7 -0
- package/src/integrations/state.ts +79 -7
- package/src/integrations/writer.ts +26 -2
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +1 -1
- package/src/lab/fabric/producer-child.ts +1 -1
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/bounded-body.ts +14 -2
- package/src/lib/config-ownership.ts +20 -0
- package/src/lib/errors.ts +36 -3
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/process-control.ts +2 -1
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/tool-argument-integers.ts +56 -5
- package/src/oauth/callback-server.ts +22 -2
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/health.ts +1 -1
- package/src/oauth/index.ts +65 -6
- package/src/oauth/key-providers.ts +10 -1
- package/src/oauth/kimi.ts +9 -1
- package/src/oauth/kiro.ts +45 -0
- package/src/oauth/local-token-detect.ts +2 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +14 -5
- package/src/oauth/types.ts +15 -0
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/command-code-efforts.ts +38 -6
- package/src/providers/context-cap.ts +4 -3
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +28 -0
- package/src/providers/fastwire.ts +7 -1
- package/src/providers/label.ts +34 -1
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/provider-id-rewrite.ts +2 -1
- package/src/providers/quota.ts +65 -7
- package/src/providers/registry.ts +184 -69
- package/src/providers/slug-codec.ts +52 -0
- 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/code-mode-helper-compat.ts +50 -0
- package/src/responses/custom-tool-compat.ts +157 -39
- package/src/responses/namespace-tool-compat.ts +84 -4
- package/src/responses/parser.ts +7 -2
- package/src/responses/reasoning-replay-cache.ts +81 -3
- package/src/responses/schema.ts +5 -1
- package/src/responses/state.ts +94 -4
- package/src/responses/thought-signature-replay.ts +17 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +40 -2
- package/src/routing/analytics.ts +1 -0
- package/src/routing/quota.ts +10 -0
- package/src/server/auth-cors.ts +27 -7
- package/src/server/chat-completions.ts +24 -16
- package/src/server/chat-native-sse.ts +3 -3
- package/src/server/chat-native.ts +30 -11
- package/src/server/claude-messages.ts +1 -1
- package/src/server/index.ts +179 -32
- package/src/server/lifecycle.ts +52 -1
- 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 +45 -23
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +2 -1
- package/src/server/management/config-routes.ts +59 -12
- package/src/server/management/context.ts +10 -1
- package/src/server/management/logs-usage-routes.ts +30 -24
- package/src/server/management/model-routes.ts +274 -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 +10 -4
- package/src/server/management/provider-capability-config.ts +1 -1
- package/src/server/management/provider-routes.ts +81 -11
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-profile-routes.ts +2 -2
- package/src/server/management/system-restart.ts +1 -1
- package/src/server/management-api.ts +18 -16
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/proxy-liveness.ts +29 -5
- package/src/server/relay-eager.ts +147 -20
- package/src/server/relay.ts +251 -19
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +70 -16
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +37 -3
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +6 -21
- package/src/server/responses/core.ts +810 -208
- package/src/server/responses/empty-completion-guard.ts +63 -6
- package/src/server/responses/fetch-helpers.ts +17 -102
- package/src/server/responses/input-admission.ts +3 -1
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/responses-field-backfill.ts +105 -13
- package/src/server/responses/ws-upstream.ts +75 -2
- package/src/server/responses-custom-tool-repair.ts +87 -6
- package/src/server/responses-terminal-repair.ts +25 -4
- package/src/server/responses-undeclared-tool-guard.ts +241 -18
- package/src/server/sse-frame-buffer.ts +31 -4
- package/src/server/ws-bridge.ts +14 -2
- package/src/service.ts +42 -4
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/types/config.ts +66 -0
- package/src/types/provider.ts +69 -0
- package/src/types/request.ts +17 -4
- package/src/types/tools.ts +116 -11
- package/src/types.ts +3 -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/expected-prices.ts +43 -24
- package/src/usage/log.ts +63 -12
- package/src/usage/summary.ts +211 -8
- package/src/vision/describe.ts +18 -13
- package/src/web-search/exa-executor.ts +40 -9
- package/src/web-search/executor.ts +10 -3
- package/src/web-search/loop.ts +5 -4
- package/gui/dist/assets/index-BF38heuV.js +0 -104
- package/gui/dist/assets/index-DMiI18Kv.css +0 -1
|
@@ -38,13 +38,15 @@ import {
|
|
|
38
38
|
serviceTierSupportFromPolicy,
|
|
39
39
|
} from "../../providers/service-tier";
|
|
40
40
|
import type { FastPolicyAuthority } from "../../providers/fastwire";
|
|
41
|
-
import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../../providers/registry";
|
|
41
|
+
import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport, registryEntryForProviderDestination } 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";
|
|
49
|
+
import { recordLiveCursorMaxModeModels } from "../../adapters/cursor/catalog";
|
|
48
50
|
import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
49
51
|
import {
|
|
50
52
|
COMBO_NAMESPACE,
|
|
@@ -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";
|
|
@@ -579,10 +581,12 @@ function providerCatalogFingerprint(name: string, prov: OcxProviderConfig): Reco
|
|
|
579
581
|
ctx: prov.contextWindow ?? null,
|
|
580
582
|
ctxW: prov.modelContextWindows ?? null,
|
|
581
583
|
maxIn: prov.modelMaxInputTokens ?? null,
|
|
584
|
+
autoCompact: prov.modelAutoCompactTokenLimits ?? null,
|
|
582
585
|
inMod: prov.modelInputModalities ?? null,
|
|
583
586
|
re: prov.modelReasoningEfforts ?? null,
|
|
584
587
|
defRe: prov.modelDefaultReasoningEfforts ?? null,
|
|
585
588
|
rsSum: prov.modelSupportsReasoningSummaries ?? null,
|
|
589
|
+
verbosity: prov.modelSupportsVerbosity ?? null,
|
|
586
590
|
rsDel: prov.modelReasoningSummaryDelivery ?? null,
|
|
587
591
|
serviceTier: prov.modelSupportsServiceTier ?? null,
|
|
588
592
|
noVis: [...(prov.noVisionModels ?? [])].sort(),
|
|
@@ -633,6 +637,17 @@ export function configuredMaxInputTokens(prov: OcxProviderConfig, id: string): n
|
|
|
633
637
|
return typeof configured === "number" && configured > 0 ? configured : undefined;
|
|
634
638
|
}
|
|
635
639
|
|
|
640
|
+
export function configuredAutoCompactTokenLimit(
|
|
641
|
+
prov: OcxProviderConfig | undefined,
|
|
642
|
+
id: string,
|
|
643
|
+
): number | undefined {
|
|
644
|
+
if (!prov) return undefined;
|
|
645
|
+
const configured = modelRecordValue(prov.modelAutoCompactTokenLimits, id);
|
|
646
|
+
return typeof configured === "number" && Number.isSafeInteger(configured) && configured > 0
|
|
647
|
+
? configured
|
|
648
|
+
: undefined;
|
|
649
|
+
}
|
|
650
|
+
|
|
636
651
|
function configuredReasoningSummarySupport(prov: OcxProviderConfig | undefined, id: string): boolean | undefined {
|
|
637
652
|
if (!prov) return undefined;
|
|
638
653
|
const explicit = modelRecordValue(prov.modelSupportsReasoningSummaries, id);
|
|
@@ -640,10 +655,26 @@ function configuredReasoningSummarySupport(prov: OcxProviderConfig | undefined,
|
|
|
640
655
|
return modelRecordValue(prov.modelReasoningSummaryDelivery, id) !== undefined ? true : undefined;
|
|
641
656
|
}
|
|
642
657
|
|
|
643
|
-
|
|
658
|
+
function configuredVerbositySupport(name: string, prov: OcxProviderConfig | undefined, id: string): boolean | undefined {
|
|
659
|
+
const explicit = prov ? modelRecordValue(prov.modelSupportsVerbosity, id) : undefined;
|
|
660
|
+
if (explicit !== undefined) return explicit;
|
|
661
|
+
if (!prov) return undefined;
|
|
644
662
|
void name;
|
|
663
|
+
// Provider-wide fallback for ids the per-model map does not enumerate — a live-discovered
|
|
664
|
+
// model would otherwise re-advertise a control the upstream accepts and ignores.
|
|
665
|
+
//
|
|
666
|
+
// Read from the PROVIDER CONFIG, never from PROVIDER_REGISTRY. A gather flight captures its
|
|
667
|
+
// registry authority up front and forbids any later registry read, so consulting the registry
|
|
668
|
+
// here made a custom-destination flight fall back to "configured" instead of serving its own
|
|
669
|
+
// discovery result (tests/codex-gather-authority.test.ts). `applyVerbosityDefaults` in
|
|
670
|
+
// providers/derive.ts materializes the registry default into the config at seed/enrich time.
|
|
671
|
+
return prov.supportsVerbosity;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
export function applyProviderConfigHints(name: string, prov: OcxProviderConfig, model: CatalogModel, providerCap?: number): CatalogModel {
|
|
645
675
|
const configuredCap = configuredContextWindow(prov, model.id);
|
|
646
676
|
const configuredMaxInput = configuredMaxInputTokens(prov, model.id);
|
|
677
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(prov, model.id);
|
|
647
678
|
let inputModalities = configuredInputModalities(prov, model.id);
|
|
648
679
|
// Vision-sidecar coverage: `noVisionModels` marks models whose images the PROXY describes
|
|
649
680
|
// (src/vision/index.ts). The catalog must still advertise image input for them — the Codex app
|
|
@@ -656,6 +687,7 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
656
687
|
const reasoningEfforts = configuredReasoningEfforts(prov, model.id);
|
|
657
688
|
const defaultReasoningEffort = modelRecordValue(prov.modelDefaultReasoningEfforts, model.id) ?? model.defaultReasoningEffort;
|
|
658
689
|
const supportsReasoningSummaries = configuredReasoningSummarySupport(prov, model.id);
|
|
690
|
+
const supportsVerbosity = configuredVerbositySupport(name, prov, model.id);
|
|
659
691
|
const fastPolicy = fastPolicyForModel(prov, model.id, name);
|
|
660
692
|
const supportsServiceTier = serviceTierSupportFromPolicy(fastPolicy);
|
|
661
693
|
const {
|
|
@@ -684,11 +716,11 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
684
716
|
: {}),
|
|
685
717
|
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
|
|
686
718
|
...(typeof supportsReasoningSummaries === "boolean" ? { supportsReasoningSummaries } : {}),
|
|
719
|
+
...(typeof supportsVerbosity === "boolean" ? { supportsVerbosity } : {}),
|
|
687
720
|
...(typeof supportsServiceTier === "boolean" ? { supportsServiceTier } : {}),
|
|
688
721
|
...(supportsServiceTier === true && fastPolicy.fastTierDescription !== undefined
|
|
689
722
|
? { fastTierDescription: fastPolicy.fastTierDescription }
|
|
690
723
|
: {}),
|
|
691
|
-
...(prov.adapter === "kiro" ? { supportsVerbosity: false } : {}),
|
|
692
724
|
// Default-on for openai-chat providers (explicit false opts out); other adapters
|
|
693
725
|
// advertise only on explicit opt-in.
|
|
694
726
|
...(prov.parallelToolCalls === true || (prov.adapter === "openai-chat" && prov.parallelToolCalls !== false)
|
|
@@ -697,10 +729,31 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
|
|
|
697
729
|
...(prov.codexToolMode !== undefined ? { codexToolMode: prov.codexToolMode } : {}),
|
|
698
730
|
};
|
|
699
731
|
const capped = applyProviderContextCap(hinted.contextWindow, providerCap);
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
732
|
+
const withCap = providerCap !== undefined
|
|
733
|
+
? capped !== hinted.contextWindow
|
|
734
|
+
? { ...hinted, contextWindow: capped, contextCap: providerCap, contextCapped: true }
|
|
735
|
+
: { ...hinted, contextCap: providerCap, contextCapped: false }
|
|
736
|
+
: hinted;
|
|
737
|
+
const contextWindow = typeof withCap.contextWindow === "number" && withCap.contextWindow > 0
|
|
738
|
+
? withCap.contextWindow
|
|
739
|
+
: undefined;
|
|
740
|
+
const boundedMaxInput = typeof withCap.maxInputTokens === "number" && withCap.maxInputTokens > 0
|
|
741
|
+
? (contextWindow !== undefined ? Math.min(withCap.maxInputTokens, contextWindow) : withCap.maxInputTokens)
|
|
742
|
+
: undefined;
|
|
743
|
+
const withHardBounds = boundedMaxInput !== undefined && boundedMaxInput !== withCap.maxInputTokens
|
|
744
|
+
? { ...withCap, maxInputTokens: boundedMaxInput }
|
|
745
|
+
: withCap;
|
|
746
|
+
const softCandidates = [model.autoCompactTokenLimit, configuredAutoCompact]
|
|
747
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
748
|
+
if (contextWindow === undefined || softCandidates.length === 0) return withHardBounds;
|
|
749
|
+
return {
|
|
750
|
+
...withHardBounds,
|
|
751
|
+
autoCompactTokenLimit: clampAutoCompactTokenLimit(
|
|
752
|
+
contextWindow,
|
|
753
|
+
boundedMaxInput,
|
|
754
|
+
Math.min(...softCandidates),
|
|
755
|
+
),
|
|
756
|
+
};
|
|
704
757
|
}
|
|
705
758
|
|
|
706
759
|
export function catalogHintsFromProviderConfig(name: string, prov: OcxProviderConfig, id: string, contextCap?: number): Partial<CatalogModel> {
|
|
@@ -727,6 +780,7 @@ interface ComboCatalogMemberFallback {
|
|
|
727
780
|
readonly contextWindow?: number;
|
|
728
781
|
/** Input ceiling when it is lower than the window (native GPT-5.6: 922k under 1.05M). */
|
|
729
782
|
readonly maxInputTokens?: number;
|
|
783
|
+
readonly autoCompactTokenLimit?: number;
|
|
730
784
|
readonly inputModalities?: readonly string[];
|
|
731
785
|
readonly reasoningEfforts?: readonly string[];
|
|
732
786
|
}
|
|
@@ -755,26 +809,33 @@ export function resolveComboCatalogMember(
|
|
|
755
809
|
if (prov?.disabled === true) return undefined;
|
|
756
810
|
|
|
757
811
|
const withFallbackMetadata = (member: CatalogModel): CatalogModel => {
|
|
758
|
-
if (!fallback) return member;
|
|
759
812
|
const contextWindow = typeof member.contextWindow === "number" && member.contextWindow > 0
|
|
760
813
|
? member.contextWindow
|
|
761
814
|
: undefined;
|
|
762
|
-
const addMaxInput = contextWindow !== undefined
|
|
815
|
+
const addMaxInput = fallback !== undefined && contextWindow !== undefined
|
|
763
816
|
&& !(typeof member.maxInputTokens === "number" && member.maxInputTokens > 0);
|
|
817
|
+
const effectiveMaxInput = addMaxInput
|
|
818
|
+
? Math.min(fallback?.maxInputTokens ?? contextWindow!, contextWindow!)
|
|
819
|
+
: member.maxInputTokens;
|
|
820
|
+
const softCandidates = [member.autoCompactTokenLimit, fallback?.autoCompactTokenLimit]
|
|
821
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
822
|
+
const autoCompactTokenLimit = contextWindow !== undefined && softCandidates.length > 0
|
|
823
|
+
? clampAutoCompactTokenLimit(contextWindow, effectiveMaxInput, Math.min(...softCandidates))
|
|
824
|
+
: member.autoCompactTokenLimit;
|
|
825
|
+
const adjustAutoCompact = autoCompactTokenLimit !== member.autoCompactTokenLimit;
|
|
764
826
|
const addModalities = (!Array.isArray(member.inputModalities) || member.inputModalities.length === 0)
|
|
765
|
-
&& fallback
|
|
827
|
+
&& fallback?.inputModalities !== undefined;
|
|
766
828
|
const addReasoning = member.reasoningEfforts === undefined
|
|
767
|
-
&& fallback
|
|
768
|
-
if (!addMaxInput && !addModalities && !addReasoning) return member;
|
|
829
|
+
&& fallback?.reasoningEfforts !== undefined;
|
|
830
|
+
if (!addMaxInput && !adjustAutoCompact && !addModalities && !addReasoning) return member;
|
|
769
831
|
return {
|
|
770
832
|
...member,
|
|
771
833
|
// Never claim a larger input budget than the window, and prefer the model's own
|
|
772
834
|
// measured ceiling when the fallback carries one.
|
|
773
|
-
...(addMaxInput
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
...(
|
|
777
|
-
...(addReasoning ? { reasoningEfforts: [...fallback.reasoningEfforts!] } : {}),
|
|
835
|
+
...(addMaxInput ? { maxInputTokens: effectiveMaxInput } : {}),
|
|
836
|
+
...(adjustAutoCompact && autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
837
|
+
...(addModalities ? { inputModalities: [...fallback!.inputModalities!] } : {}),
|
|
838
|
+
...(addReasoning ? { reasoningEfforts: [...fallback!.reasoningEfforts!] } : {}),
|
|
778
839
|
};
|
|
779
840
|
};
|
|
780
841
|
|
|
@@ -855,6 +916,20 @@ export function resolveComboCatalogMember(
|
|
|
855
916
|
const maxInputTokens = effectiveMaxInput !== undefined
|
|
856
917
|
? Math.min(effectiveMaxInput, contextWindow)
|
|
857
918
|
: contextWindow;
|
|
919
|
+
const softCandidates = [
|
|
920
|
+
hinted.autoCompactTokenLimit,
|
|
921
|
+
base.autoCompactTokenLimit,
|
|
922
|
+
fallback?.autoCompactTokenLimit,
|
|
923
|
+
configuredAutoCompactTokenLimit(prov, target.model),
|
|
924
|
+
].filter((value): value is number => typeof value === "number" && value > 0);
|
|
925
|
+
// A generic 128k synthesis is a catalog compatibility fallback, not evidence
|
|
926
|
+
// that a configured soft policy has an authoritative window to clamp against.
|
|
927
|
+
const hasAuthoritativeAutoCompactBasis = hintedContext !== undefined
|
|
928
|
+
|| fallbackContext !== undefined
|
|
929
|
+
|| contextCap !== undefined;
|
|
930
|
+
const autoCompactTokenLimit = hasAuthoritativeAutoCompactBasis && softCandidates.length > 0
|
|
931
|
+
? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, Math.min(...softCandidates))
|
|
932
|
+
: undefined;
|
|
858
933
|
|
|
859
934
|
return {
|
|
860
935
|
...hinted,
|
|
@@ -862,6 +937,7 @@ export function resolveComboCatalogMember(
|
|
|
862
937
|
...(reasoningEfforts !== undefined ? { reasoningEfforts } : {}),
|
|
863
938
|
contextWindow,
|
|
864
939
|
maxInputTokens,
|
|
940
|
+
...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
865
941
|
...(fallbackCapped ? { contextCap, contextCapped: true as const } : {}),
|
|
866
942
|
};
|
|
867
943
|
}
|
|
@@ -1242,6 +1318,9 @@ async function fetchProviderModelsWithAuth(
|
|
|
1242
1318
|
});
|
|
1243
1319
|
if (liveResult.ok) {
|
|
1244
1320
|
const available = filterCursorConfiguredModelsByLiveDiscovery(configured, liveResult.models);
|
|
1321
|
+
// Live Max-Mode evidence feeds the umbrella resolver's ultra gate
|
|
1322
|
+
// (devlog 260828_cursor_umbrella_catalog; union with static evidence).
|
|
1323
|
+
recordLiveCursorMaxModeModels(liveResult.maxModeModels ?? []);
|
|
1245
1324
|
const result = available.length > 0 ? available : configured;
|
|
1246
1325
|
// Cache the discovery-filtered roster without combo retention so a later
|
|
1247
1326
|
// gather can re-apply the current capture's retain set on read.
|
|
@@ -1568,7 +1647,24 @@ export function filterCatalogVisibleModels(
|
|
|
1568
1647
|
const allowByProvider = new Map<string, Set<string>>();
|
|
1569
1648
|
for (const [name, prov] of Object.entries(config.providers)) {
|
|
1570
1649
|
const sel = prov.selectedModels;
|
|
1571
|
-
|
|
1650
|
+
// Keyed the way `sync.ts` keys the same list, so a slash-bearing native id and
|
|
1651
|
+
// the encoded slug the Codex picker displays are one entry rather than two. A
|
|
1652
|
+
// bare `Set(sel)` matched only the native form, so an allowlist written from the
|
|
1653
|
+
// displayed slug — which `ocx models remove` also accepts — hid every model it
|
|
1654
|
+
// was meant to keep.
|
|
1655
|
+
//
|
|
1656
|
+
// The key is deliberately lossy: `p/a/b` and `p/a-b` collapse to one entry, so a
|
|
1657
|
+
// provider publishing both spellings has them selected together. That is a real
|
|
1658
|
+
// limitation, pinned by the tests below and tracked as a follow-up; it is NOT
|
|
1659
|
+
// fixed here. Resolving selections against the current roster instead was tried
|
|
1660
|
+
// and rejected — the roster is an incomplete dictionary (live discovery can omit
|
|
1661
|
+
// a published id), so it produces the same over-grant while additionally
|
|
1662
|
+
// disagreeing with the `slugEquivalenceKey` contract `sync.ts` uses at merge time.
|
|
1663
|
+
// Two catalog stages with different equivalence relations is the exact bug class
|
|
1664
|
+
// this change exists to remove.
|
|
1665
|
+
if (Array.isArray(sel) && sel.length > 0) {
|
|
1666
|
+
allowByProvider.set(name, new Set(sel.map(model => slugEquivalenceKey(routedSlug(name, model)))));
|
|
1667
|
+
}
|
|
1572
1668
|
}
|
|
1573
1669
|
return models.filter(m => {
|
|
1574
1670
|
const nativeAlias = m.provider === COMBO_NAMESPACE && m.nativeAlias === true;
|
|
@@ -1580,7 +1676,7 @@ export function filterCatalogVisibleModels(
|
|
|
1580
1676
|
if (slugEquals(stored, m.provider, m.id)) return false;
|
|
1581
1677
|
}
|
|
1582
1678
|
const allow = allowByProvider.get(m.provider);
|
|
1583
|
-
return !allow || allow.has(m.id);
|
|
1679
|
+
return !allow || allow.has(slugEquivalenceKey(routedSlug(m.provider, m.id)));
|
|
1584
1680
|
});
|
|
1585
1681
|
}
|
|
1586
1682
|
|
|
@@ -1921,6 +2017,7 @@ async function gatherRoutedModelsUncached(
|
|
|
1921
2017
|
// stay separate fields because routed/API rows of the same family run a wider window.
|
|
1922
2018
|
// Falls back to the window for slugs with no separate ceiling.
|
|
1923
2019
|
maxInputTokens: Math.min(nativeOpenAiMaxInputTokens(slug, openaiContextCap) ?? contextWindow, contextWindow),
|
|
2020
|
+
autoCompactTokenLimit: nativeOpenAiAutoCompactTokenLimit(slug, openaiContextCap),
|
|
1924
2021
|
inputModalities: nativeInputModalities(slug),
|
|
1925
2022
|
reasoningEfforts: nativeReasoningEfforts(slug),
|
|
1926
2023
|
...(nativeParallelToolCalls(slug) ? { parallelToolCalls: true } : {}),
|
|
@@ -1937,18 +2034,23 @@ async function gatherRoutedModelsUncached(
|
|
|
1937
2034
|
for (const id of listComboIds(config)) {
|
|
1938
2035
|
const combo = getCombo(config, id);
|
|
1939
2036
|
if (!combo) continue;
|
|
2037
|
+
const comboNativeLimits = nativeContextLimits(config);
|
|
1940
2038
|
const nativeContextWindow = combo.nativeAlias && combo.alias
|
|
1941
|
-
? nativeOpenAiContextWindow(combo.alias,
|
|
2039
|
+
? nativeOpenAiContextWindow(combo.alias, comboNativeLimits)
|
|
1942
2040
|
: undefined;
|
|
1943
2041
|
const nativeAliasMaxInput = combo.nativeAlias && combo.alias
|
|
1944
2042
|
? (combo.alias.startsWith("gpt-5.6-") || combo.alias.includes("daybreak")
|
|
1945
2043
|
? NATIVE_GPT56_MAX_INPUT_TOKENS
|
|
1946
2044
|
: nativeOpenAiMaxInputTokens(combo.alias) ?? nativeOpenAiContextWindow(combo.alias))
|
|
1947
2045
|
: undefined;
|
|
2046
|
+
const nativeAliasAutoCompact = combo.nativeAlias && combo.alias
|
|
2047
|
+
? nativeOpenAiAutoCompactTokenLimit(combo.alias, comboNativeLimits)
|
|
2048
|
+
: undefined;
|
|
1948
2049
|
const nativeAliasFallback = combo.nativeAlias && combo.alias && nativeContextWindow !== undefined
|
|
1949
2050
|
? {
|
|
1950
2051
|
contextWindow: nativeContextWindow,
|
|
1951
2052
|
...(nativeAliasMaxInput !== undefined ? { maxInputTokens: nativeAliasMaxInput } : {}),
|
|
2053
|
+
...(nativeAliasAutoCompact !== undefined ? { autoCompactTokenLimit: nativeAliasAutoCompact } : {}),
|
|
1952
2054
|
inputModalities: nativeInputModalities(combo.alias),
|
|
1953
2055
|
reasoningEfforts: nativeReasoningEfforts(combo.alias),
|
|
1954
2056
|
}
|
|
@@ -2011,9 +2113,23 @@ async function gatherRoutedModelsUncached(
|
|
|
2011
2113
|
const nativeAliasMaxInputTokens = codexForwardNativeCapabilityAlias
|
|
2012
2114
|
? nativeOpenAiMaxInputTokens(cm.modelId, customNativeLimits)
|
|
2013
2115
|
: undefined;
|
|
2014
|
-
const
|
|
2015
|
-
?
|
|
2016
|
-
:
|
|
2116
|
+
const configuredMaxInput = rawProvider
|
|
2117
|
+
? configuredMaxInputTokens(rawProvider, cm.modelId)
|
|
2118
|
+
: undefined;
|
|
2119
|
+
const hardMaxCandidates = [nativeAliasMaxInputTokens, configuredMaxInput]
|
|
2120
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
2121
|
+
const customMaxInputTokens = hardMaxCandidates.length > 0
|
|
2122
|
+
? Math.min(
|
|
2123
|
+
...hardMaxCandidates,
|
|
2124
|
+
...(customContextWindow !== undefined ? [customContextWindow] : []),
|
|
2125
|
+
)
|
|
2126
|
+
: undefined;
|
|
2127
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(rawProvider, cm.modelId);
|
|
2128
|
+
const customAutoCompactTokenLimit = codexForwardNativeCapabilityAlias
|
|
2129
|
+
? nativeOpenAiAutoCompactTokenLimit(cm.modelId, customNativeLimits)
|
|
2130
|
+
: customContextWindow !== undefined && configuredAutoCompact !== undefined
|
|
2131
|
+
? clampAutoCompactTokenLimit(customContextWindow, customMaxInputTokens, configuredAutoCompact)
|
|
2132
|
+
: undefined;
|
|
2017
2133
|
const nativeAliasDefaultEffort = codexForwardNativeCapabilityAlias
|
|
2018
2134
|
? nativeDefaultReasoningEffort(cm.modelId)
|
|
2019
2135
|
: undefined;
|
|
@@ -2034,6 +2150,7 @@ async function gatherRoutedModelsUncached(
|
|
|
2034
2150
|
: codexForwardNativeCapabilityAlias ? { displayName: "Daybreak Blue" } : {}),
|
|
2035
2151
|
...(customContextWindow !== undefined ? { contextWindow: customContextWindow } : {}),
|
|
2036
2152
|
...(customMaxInputTokens !== undefined ? { maxInputTokens: customMaxInputTokens } : {}),
|
|
2153
|
+
...(customAutoCompactTokenLimit !== undefined ? { autoCompactTokenLimit: customAutoCompactTokenLimit } : {}),
|
|
2037
2154
|
...(cm.inputModalities
|
|
2038
2155
|
? { inputModalities: cm.inputModalities }
|
|
2039
2156
|
: codexForwardNativeCapabilityAlias ? { inputModalities: nativeInputModalities(cm.modelId) } : {}),
|
|
@@ -2079,10 +2196,18 @@ async function gatherRoutedModelsUncached(
|
|
|
2079
2196
|
// along when it is actually a member — otherwise a provider default like "xhigh" would
|
|
2080
2197
|
// re-apply onto a narrower custom ladder and override the fallback in applyReasoningLevels.
|
|
2081
2198
|
const effectiveLadder = base.reasoningEfforts ?? replaced?.reasoningEfforts;
|
|
2199
|
+
const mergedMaxInputCandidates = [base.maxInputTokens, replaced?.maxInputTokens]
|
|
2200
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
2201
|
+
const mergedMaxInput = mergedMaxInputCandidates.length > 0
|
|
2202
|
+
? Math.min(...mergedMaxInputCandidates)
|
|
2203
|
+
: undefined;
|
|
2082
2204
|
const merged: CatalogModel = replaced ? {
|
|
2083
2205
|
...base,
|
|
2084
2206
|
...(base.contextWindow === undefined && replaced.contextWindow !== undefined ? { contextWindow: replaced.contextWindow } : {}),
|
|
2085
|
-
...(
|
|
2207
|
+
...(mergedMaxInput !== undefined ? { maxInputTokens: mergedMaxInput } : {}),
|
|
2208
|
+
...(base.autoCompactTokenLimit === undefined && replaced.autoCompactTokenLimit !== undefined
|
|
2209
|
+
? { autoCompactTokenLimit: replaced.autoCompactTokenLimit }
|
|
2210
|
+
: {}),
|
|
2086
2211
|
...(base.inputModalities === undefined && replaced.inputModalities !== undefined ? { inputModalities: replaced.inputModalities } : {}),
|
|
2087
2212
|
...(base.reasoningEfforts === undefined && replaced.reasoningEfforts !== undefined ? { reasoningEfforts: replaced.reasoningEfforts } : {}),
|
|
2088
2213
|
...(base.defaultReasoningEffort === undefined && replaced.defaultReasoningEffort !== undefined
|
|
@@ -2099,14 +2224,36 @@ async function gatherRoutedModelsUncached(
|
|
|
2099
2224
|
// (#349/#344). Deliberately NOT the full applyProviderConfigHints pass — custom rows are a
|
|
2100
2225
|
// user override, so their explicit contextWindow / inputModalities / reasoning fields must be
|
|
2101
2226
|
// preserved verbatim (the hint pass would cap context and overwrite modalities from registry).
|
|
2227
|
+
const mergedContext = typeof merged.contextWindow === "number" && merged.contextWindow > 0
|
|
2228
|
+
? merged.contextWindow
|
|
2229
|
+
: undefined;
|
|
2230
|
+
const boundedMergedMaxInput = typeof merged.maxInputTokens === "number" && merged.maxInputTokens > 0
|
|
2231
|
+
? (mergedContext !== undefined ? Math.min(merged.maxInputTokens, mergedContext) : merged.maxInputTokens)
|
|
2232
|
+
: undefined;
|
|
2233
|
+
const mergedWithHardBounds = boundedMergedMaxInput !== undefined
|
|
2234
|
+
&& boundedMergedMaxInput !== merged.maxInputTokens
|
|
2235
|
+
? { ...merged, maxInputTokens: boundedMergedMaxInput }
|
|
2236
|
+
: merged;
|
|
2237
|
+
const mergedSoftCandidates = [mergedWithHardBounds.autoCompactTokenLimit, configuredAutoCompact]
|
|
2238
|
+
.filter((value): value is number => typeof value === "number" && value > 0);
|
|
2239
|
+
const mergedWithAutoCompact: CatalogModel = mergedContext !== undefined && mergedSoftCandidates.length > 0
|
|
2240
|
+
? {
|
|
2241
|
+
...mergedWithHardBounds,
|
|
2242
|
+
autoCompactTokenLimit: clampAutoCompactTokenLimit(
|
|
2243
|
+
mergedContext,
|
|
2244
|
+
boundedMergedMaxInput,
|
|
2245
|
+
Math.min(...mergedSoftCandidates),
|
|
2246
|
+
),
|
|
2247
|
+
}
|
|
2248
|
+
: mergedWithHardBounds;
|
|
2102
2249
|
const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
|
|
2103
|
-
if (enrichedProvider && modelInList(enrichedProvider.noVisionModels,
|
|
2104
|
-
const current =
|
|
2250
|
+
if (enrichedProvider && modelInList(enrichedProvider.noVisionModels, mergedWithAutoCompact.id)) {
|
|
2251
|
+
const current = mergedWithAutoCompact.inputModalities ?? ["text"];
|
|
2105
2252
|
if (!current.includes("image")) {
|
|
2106
|
-
return { ...
|
|
2253
|
+
return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
|
|
2107
2254
|
}
|
|
2108
2255
|
}
|
|
2109
|
-
return
|
|
2256
|
+
return mergedWithAutoCompact;
|
|
2110
2257
|
});
|
|
2111
2258
|
// Custom rows override discovered rows that encode to the same Codex-facing slug.
|
|
2112
2259
|
const customKeys = new Set(customModels.map(c => routedSlug(c.provider, c.id)));
|
|
@@ -2162,7 +2309,15 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
|
|
|
2162
2309
|
? Math.min(officialContext, userContext ?? officialContext, providerCap ?? officialContext)
|
|
2163
2310
|
: undefined;
|
|
2164
2311
|
const maxInputTokens = typeof officialMaxInput === "number"
|
|
2165
|
-
? Math.min(
|
|
2312
|
+
? Math.min(
|
|
2313
|
+
officialMaxInput,
|
|
2314
|
+
userMaxInput ?? officialMaxInput,
|
|
2315
|
+
contextWindow ?? officialMaxInput,
|
|
2316
|
+
)
|
|
2317
|
+
: undefined;
|
|
2318
|
+
const configuredAutoCompact = configuredAutoCompactTokenLimit(configured, id);
|
|
2319
|
+
const autoCompactTokenLimit = contextWindow !== undefined && configuredAutoCompact !== undefined
|
|
2320
|
+
? clampAutoCompactTokenLimit(contextWindow, maxInputTokens, configuredAutoCompact)
|
|
2166
2321
|
: undefined;
|
|
2167
2322
|
return {
|
|
2168
2323
|
provider: OPENAI_API_PROVIDER_ID,
|
|
@@ -2170,6 +2325,7 @@ function augmentRoutedModelsWithCapturedOpenAiApiRows(
|
|
|
2170
2325
|
owned_by: OPENAI_API_PROVIDER_ID,
|
|
2171
2326
|
...(contextWindow ? { contextWindow } : {}),
|
|
2172
2327
|
...(maxInputTokens ? { maxInputTokens } : {}),
|
|
2328
|
+
...(autoCompactTokenLimit !== undefined ? { autoCompactTokenLimit } : {}),
|
|
2173
2329
|
...(policy.modelInputModalities?.[id] ? { inputModalities: [...policy.modelInputModalities[id]!] } : {}),
|
|
2174
2330
|
...(policy.modelReasoningEfforts?.[id] ? { reasoningEfforts: [...policy.modelReasoningEfforts[id]!] } : {}),
|
|
2175
2331
|
};
|
|
@@ -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, readConfiguredAutoReviewModel, 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
|
|
@@ -1403,6 +1403,130 @@ function catalogModelsForMergeWithNativeRecovery(
|
|
|
1403
1403
|
]);
|
|
1404
1404
|
}
|
|
1405
1405
|
|
|
1406
|
+
const AUTO_REVIEW_MODEL_CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f\u2028\u2029\s]/;
|
|
1407
|
+
|
|
1408
|
+
export function isValidAutoReviewModel(value: unknown): value is string {
|
|
1409
|
+
if (typeof value !== "string") return false;
|
|
1410
|
+
const trimmed = value.trim();
|
|
1411
|
+
return Boolean(trimmed)
|
|
1412
|
+
&& trimmed.length <= 1024
|
|
1413
|
+
&& !AUTO_REVIEW_MODEL_CONTROL_CHARS.test(trimmed);
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
export type AutoReviewModelOverrideResult = "absent" | "applied" | "invalid" | "unresolved";
|
|
1417
|
+
|
|
1418
|
+
function isRoutedCatalogEntry(entry: RawEntry): boolean {
|
|
1419
|
+
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
1420
|
+
return slug.includes("/")
|
|
1421
|
+
|| (typeof entry.description === "string" && entry.description.startsWith("Routed via opencodex → "));
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function clearAutoReviewModelOverride(
|
|
1425
|
+
models: readonly RawEntry[],
|
|
1426
|
+
sourceModels: readonly RawEntry[] = [],
|
|
1427
|
+
): void {
|
|
1428
|
+
const observedModels = [...models, ...sourceModels];
|
|
1429
|
+
const configuredValues = new Set(observedModels.flatMap(entry => {
|
|
1430
|
+
const value = entry?.auto_review_model_override;
|
|
1431
|
+
return typeof value === "string" && value.trim() ? [value] : [];
|
|
1432
|
+
}));
|
|
1433
|
+
const globalStamp = configuredValues.size === 1
|
|
1434
|
+
&& observedModels.some(entry => {
|
|
1435
|
+
const value = entry.auto_review_model_override;
|
|
1436
|
+
return isRoutedCatalogEntry(entry)
|
|
1437
|
+
&& typeof value === "string"
|
|
1438
|
+
&& value.trim().length > 0
|
|
1439
|
+
&& configuredValues.has(value);
|
|
1440
|
+
})
|
|
1441
|
+
&& observedModels.every(entry => {
|
|
1442
|
+
const value = entry?.auto_review_model_override;
|
|
1443
|
+
return value === null
|
|
1444
|
+
|| value === undefined
|
|
1445
|
+
|| (typeof value === "string" && configuredValues.has(value));
|
|
1446
|
+
});
|
|
1447
|
+
for (const entry of models) {
|
|
1448
|
+
if (!entry || typeof entry !== "object") continue;
|
|
1449
|
+
const current = entry.auto_review_model_override;
|
|
1450
|
+
if (isRoutedCatalogEntry(entry)
|
|
1451
|
+
|| (globalStamp && typeof current === "string" && configuredValues.has(current))) {
|
|
1452
|
+
entry.auto_review_model_override = null;
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
function warnAutoReviewModelDiagnostic(
|
|
1458
|
+
reason: "invalid" | "unresolved",
|
|
1459
|
+
configured: string,
|
|
1460
|
+
): void {
|
|
1461
|
+
const safeConfigured = JSON.stringify(redactSecretString(configured));
|
|
1462
|
+
const detail = reason === "unresolved"
|
|
1463
|
+
? "the selector was not found in the final catalog"
|
|
1464
|
+
: "the selector format is invalid";
|
|
1465
|
+
console.warn(
|
|
1466
|
+
`[opencodex] auto_review_model ${detail} (${safeConfigured}); preserving normal upstream auto-review behavior.`,
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
function preserveNativeAutoReviewModelOverrides(
|
|
1471
|
+
models: readonly RawEntry[],
|
|
1472
|
+
sourceModels: readonly RawEntry[],
|
|
1473
|
+
): void {
|
|
1474
|
+
const existing = new Map<string, string | null>();
|
|
1475
|
+
for (const entry of sourceModels) {
|
|
1476
|
+
const slug = typeof entry.slug === "string" ? entry.slug : undefined;
|
|
1477
|
+
const value = entry.auto_review_model_override;
|
|
1478
|
+
if (!slug || isRoutedCatalogEntry(entry)) continue;
|
|
1479
|
+
if (typeof value === "string" || value === null) existing.set(slug, value);
|
|
1480
|
+
}
|
|
1481
|
+
for (const entry of models) {
|
|
1482
|
+
const slug = typeof entry.slug === "string" ? entry.slug : undefined;
|
|
1483
|
+
if (!slug || isRoutedCatalogEntry(entry) || !existing.has(slug)) continue;
|
|
1484
|
+
entry.auto_review_model_override = existing.get(slug) ?? null;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
export function applyAutoReviewModelOverride(
|
|
1489
|
+
models: RawEntry[] | undefined,
|
|
1490
|
+
autoReviewModel: string | null | undefined,
|
|
1491
|
+
sourceModels: readonly RawEntry[] = [],
|
|
1492
|
+
): AutoReviewModelOverrideResult {
|
|
1493
|
+
if (!models || !Array.isArray(models)) return "absent";
|
|
1494
|
+
if (autoReviewModel === null || autoReviewModel === undefined) {
|
|
1495
|
+
clearAutoReviewModelOverride(models, sourceModels);
|
|
1496
|
+
return "absent";
|
|
1497
|
+
}
|
|
1498
|
+
const trimmed = autoReviewModel.trim();
|
|
1499
|
+
if (!trimmed) {
|
|
1500
|
+
clearAutoReviewModelOverride(models, sourceModels);
|
|
1501
|
+
return "absent";
|
|
1502
|
+
}
|
|
1503
|
+
if (!isValidAutoReviewModel(trimmed)) {
|
|
1504
|
+
clearAutoReviewModelOverride(models, sourceModels);
|
|
1505
|
+
warnAutoReviewModelDiagnostic("invalid", trimmed);
|
|
1506
|
+
return "invalid";
|
|
1507
|
+
}
|
|
1508
|
+
if (!configuredCatalogEntry(models, trimmed)) {
|
|
1509
|
+
clearAutoReviewModelOverride(models, sourceModels);
|
|
1510
|
+
warnAutoReviewModelDiagnostic("unresolved", trimmed);
|
|
1511
|
+
return "unresolved";
|
|
1512
|
+
}
|
|
1513
|
+
for (const entry of models) {
|
|
1514
|
+
if (entry && typeof entry === "object") {
|
|
1515
|
+
entry.auto_review_model_override = trimmed;
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return "applied";
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/** Apply the root Codex auto-review selector after the final catalog merge. */
|
|
1522
|
+
export function finalizeAutoReviewModelOverride(
|
|
1523
|
+
models: RawEntry[] | undefined,
|
|
1524
|
+
sourceModels: readonly RawEntry[] = [],
|
|
1525
|
+
): AutoReviewModelOverrideResult {
|
|
1526
|
+
if (models && sourceModels.length > 0) preserveNativeAutoReviewModelOverrides(models, sourceModels);
|
|
1527
|
+
return applyAutoReviewModelOverride(models, readConfiguredAutoReviewModel(), sourceModels);
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1406
1530
|
function writeRetainedCatalogSync({
|
|
1407
1531
|
config,
|
|
1408
1532
|
goModels,
|
|
@@ -1596,6 +1720,7 @@ function writeRetainedCatalogSync({
|
|
|
1596
1720
|
},
|
|
1597
1721
|
});
|
|
1598
1722
|
clampCatalogModelsToCodexSupport(catalog.models);
|
|
1723
|
+
finalizeAutoReviewModelOverride(catalog.models, catalogModelsForMerge);
|
|
1599
1724
|
|
|
1600
1725
|
const added = goEntries.length + accountBoundEntries.length;
|
|
1601
1726
|
const content = `${JSON.stringify(catalog, null, 2)}\n`;
|
|
@@ -1832,11 +1957,12 @@ export function invalidateCodexModelsCacheWithPermit(
|
|
|
1832
1957
|
// The catalog-only sync override applies here too so an explicit refresh
|
|
1833
1958
|
// keeps the cache consistent with the catalog it just wrote.
|
|
1834
1959
|
if (!shouldSyncCodexOnStart(loadConfig()) && options?.allowWhenDesiredDisabled !== true) return false;
|
|
1835
|
-
const catalogPath =
|
|
1960
|
+
const catalogPath = readCodexCatalogPathForHome(owningCodexHome);
|
|
1836
1961
|
if (!existsSync(catalogPath)) return false;
|
|
1837
1962
|
const catalog = JSON.parse(readFileSync(catalogPath, "utf8"));
|
|
1838
1963
|
const models = catalog.models ?? catalog;
|
|
1839
|
-
const
|
|
1964
|
+
const cachePath = join(owningCodexHome, "models_cache.json");
|
|
1965
|
+
const currentCache = readCatalog(cachePath);
|
|
1840
1966
|
const existingSlugs = new Set(models.flatMap((entry: RawEntry) =>
|
|
1841
1967
|
typeof entry.slug === "string" ? [entry.slug] : []));
|
|
1842
1968
|
const currentConfig = loadConfig();
|
|
@@ -1864,7 +1990,7 @@ export function invalidateCodexModelsCacheWithPermit(
|
|
|
1864
1990
|
models: [...models, ...observedAccountModels],
|
|
1865
1991
|
};
|
|
1866
1992
|
replaceCodexModelsCache(permit, owningCodexHome, {
|
|
1867
|
-
path:
|
|
1993
|
+
path: cachePath,
|
|
1868
1994
|
content: `${JSON.stringify(wrapper, null, 2)}\n`,
|
|
1869
1995
|
});
|
|
1870
1996
|
return true;
|
package/src/codex/catalog.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { applyProviderConfigHints, isDatedVariantId, filterCatalogVisibleModels,
|
|
|
9
9
|
export type { GatherRoutedModelsOptions } from "./catalog/provider-fetch";
|
|
10
10
|
export { deriveComboCatalogModel, exactComboCatalogSlugs, getLastComboCatalogOmissions, resetOpenAiApiCatalogWarningStateForTests, uniqueCatalogModelsForPublicList, uniqueCatalogModelsForRawPublicList, buildComboCatalogOmission, comboCatalogOmissionReason, summarizeComboCatalogOmissions } from "./catalog/aggregation";
|
|
11
11
|
export type { ComboCatalogOmission, ComboCatalogOmissionReason } from "./catalog/aggregation";
|
|
12
|
-
export { MAX_SPAWN_AGENT_MODEL_OVERRIDES, CANONICAL_NATIVE_CATALOG_CONTENT_POLICY, effectiveSubagentRoster, buildCatalogEntries, mergeCatalogEntriesFromObservedState, resetCatalogRuntimeStateForTests, orderForSubagents, mergeCatalogEntriesForSync, syncCatalogModels, restoreCodexCatalog, invalidateCodexModelsCache } from "./catalog/sync";
|
|
12
|
+
export { MAX_SPAWN_AGENT_MODEL_OVERRIDES, CANONICAL_NATIVE_CATALOG_CONTENT_POLICY, effectiveSubagentRoster, buildCatalogEntries, mergeCatalogEntriesFromObservedState, resetCatalogRuntimeStateForTests, orderForSubagents, mergeCatalogEntriesForSync, syncCatalogModels, restoreCodexCatalog, invalidateCodexModelsCache, finalizeAutoReviewModelOverride } from "./catalog/sync";
|
|
13
13
|
export type { ObservedCatalogMergeInput } from "./catalog/sync";
|
|
14
14
|
export type { SpawnAgentSurface, SubagentRosterExclusionReason, EffectiveSubagentModel, SubagentRosterExclusion, EffectiveSubagentRoster } from "./catalog/sync";
|
|
15
15
|
export { accountBoundNativeDisplayName, accountBoundNativeModelSlugs, CODEX_ACCOUNT_BOUND_CATALOG_KIND, trustedAccountBoundNativeCatalogSlug, visibleCodexAccountSelectors } from "./catalog/account-models";
|
|
@@ -93,6 +93,8 @@ export interface CodexWriteLockOptions {
|
|
|
93
93
|
admitted: CodexWriteWitness;
|
|
94
94
|
/** Authoritative synchronous re-read while N and C are both held. */
|
|
95
95
|
readAdmissionUnderLock(): CodexWriteWitness;
|
|
96
|
+
/** Positively authorized migration of an already-routed pre-substrate home. */
|
|
97
|
+
adoption?: { readonly direction: "apply" | "remove" };
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
/**
|
|
@@ -304,7 +306,7 @@ export async function withCodexWriteLock<T>(
|
|
|
304
306
|
|
|
305
307
|
let transaction: ReturnType<typeof openCodexCoordinatorTransaction> | undefined;
|
|
306
308
|
try {
|
|
307
|
-
transaction = openCodexCoordinatorTransaction(databasePath);
|
|
309
|
+
transaction = openCodexCoordinatorTransaction(databasePath, options.adoption);
|
|
308
310
|
} catch (error) {
|
|
309
311
|
// Only contention retries. A malformed database, an unsafe path, or an
|
|
310
312
|
// identity failure will fail identically forever; telling a caller to retry
|
|
@@ -34,7 +34,7 @@ export interface CodexIntegrationRecord {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export interface CodexHistoryState {
|
|
37
|
-
status: "converged" | "pending" | "running" | "blocked" | "unknown" | "not-evaluated";
|
|
37
|
+
status: "adoption-pending" | "converged" | "pending" | "running" | "blocked" | "unknown" | "not-evaluated";
|
|
38
38
|
/**
|
|
39
39
|
* Why it is not converged, when it is not. These are terminal observations
|
|
40
40
|
* for one attempt, not reasons to collapse the durable retry schedule.
|