@bitkyc08/opencodex 2.81.0 → 2.83.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/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ba8uG696.js +54 -0
- package/gui/dist/assets/{Tray-EwAOsP0S.js → Tray-IImbNPRy.js} +1 -1
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/{index-RsoaAxXY.js → index-BHRZdJuv.js} +13 -13
- package/gui/dist/assets/{quota-summary-D2lN8tDm.js → quota-summary-DiJO6sYY.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +1 -1
- package/src/adapters/cursor/catalog.ts +2 -2
- package/src/adapters/cursor/effort-map.ts +1 -1
- package/src/adapters/devin/live-models.ts +2 -2
- package/src/adapters/google-truncation.ts +22 -0
- package/src/adapters/google.ts +7 -3
- package/src/adapters/ollama-native.ts +18 -14
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +11 -4
- package/src/adapters/openai-responses/plaintext-reasoning-notice.ts +85 -0
- package/src/adapters/openai-responses/reasoning.ts +11 -1
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/bridge/errors.ts +7 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +22 -20
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-picker.ts +1 -1
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound.ts +77 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/account-auth.ts +20 -12
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +2 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +5 -0
- package/src/cli/doctor.ts +16 -3
- package/src/cli/index.ts +14 -9
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integrations.ts +16 -3
- package/src/cli/resolve.ts +26 -3
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/config-export/model-metadata.ts +16 -0
- package/src/clients/config-export/zcode-store.ts +46 -11
- package/src/clients/config-export/zcode.ts +3 -5
- package/src/clients/config-export.ts +1 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/pool-mode-gate.ts +4 -5
- package/src/codex/auth-api/routes.ts +10 -49
- package/src/codex/auth-context.ts +28 -4
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/build-entries.ts +18 -0
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/effort.ts +26 -5
- package/src/codex/catalog/gather-capture.ts +5 -0
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/native-models.ts +1 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/provider-models.ts +68 -35
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/desktop-app/darwin.ts +1 -1
- package/src/codex/desktop-app/handoff.ts +1 -1
- package/src/codex/desktop-app/linux.ts +1 -1
- package/src/codex/desktop-app/lock.ts +1 -1
- package/src/codex/desktop-app/types.ts +1 -1
- package/src/codex/desktop-app/windows.ts +1 -1
- package/src/codex/desktop-app-restart.ts +1 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/main-account.ts +6 -7
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -220
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +8 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/config-schema.ts +4 -0
- package/src/generated/compatibility-version.json +375 -227
- package/src/generated/model-metadata.ts +2 -2
- package/src/grok/inject.ts +276 -206
- package/src/grok/reset-coupon-ledger.ts +223 -57
- package/src/grok/reset-coupons.ts +13 -6
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +29 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +18 -2
- package/src/integrations/cursor-detect.ts +1 -1
- package/src/integrations/writer.ts +9 -3
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/errors.ts +25 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/request-execution-budget.ts +8 -0
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/oauth/anthropic-account-refusal.ts +39 -1
- package/src/oauth/anthropic-routing.ts +1 -1
- package/src/oauth/anthropic-send-ownership.ts +3 -3
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/index.ts +7 -3
- package/src/oauth/meta-muse-device.ts +1 -1
- package/src/oauth/store.ts +1 -1
- package/src/oauth/types.ts +1 -1
- package/src/providers/anthropic-fast.ts +1 -1
- package/src/providers/anthropic-reset-grant-ledger.ts +1 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +21 -0
- package/src/providers/devin-cli-authmode-migration.ts +1 -1
- package/src/providers/devin-provider-merge-migration.ts +1 -1
- package/src/providers/github-copilot-auto.ts +169 -0
- package/src/providers/model-presets.ts +1 -1
- package/src/providers/muse-subscription-usage.ts +1 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota.ts +1 -1
- package/src/providers/registry/entries-core.ts +6 -6
- package/src/providers/registry/entries-extended.ts +4 -0
- package/src/providers/registry/model-seeds.ts +3 -3
- package/src/providers/xai-fast-model.ts +1 -1
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +15 -2
- package/src/responses/state/metrics.ts +8 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +12 -5
- package/src/responses/state.ts +18 -10
- package/src/responses/task-input.ts +17 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +1 -0
- package/src/server/chat-native.ts +28 -12
- package/src/server/claude-messages.ts +43 -6
- package/src/server/inbound-body-admission.ts +25 -7
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/link-listener.ts +30 -17
- package/src/server/index/serve-options.ts +18 -4
- package/src/server/index/spend-ledger-lifecycle.ts +2 -1
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +2 -1
- package/src/server/lifecycle.ts +31 -4
- package/src/server/management/agent-settings-routes.ts +93 -32
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +59 -8
- package/src/server/management/context.ts +2 -2
- package/src/server/management/grok-coupon-routes.ts +277 -52
- package/src/server/management/link-routes.ts +42 -20
- package/src/server/management/route-registry.ts +2 -2
- package/src/server/management/shared.ts +3 -3
- package/src/server/messages-native-oauth.ts +9 -8
- package/src/server/messages-native.ts +49 -13
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/relay-eager.ts +2 -0
- package/src/server/relay.ts +18 -7
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log-failure-attribution.ts +3 -0
- package/src/server/request-log-terminal-status.ts +186 -0
- package/src/server/request-log.ts +15 -89
- package/src/server/responses/adapter-continuation.ts +11 -3
- package/src/server/responses/adapter-dispatch.ts +99 -19
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +3 -2
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +20 -3
- package/src/server/responses/core-combo.ts +22 -14
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-opaque-recovery.ts +5 -5
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-replay.ts +73 -0
- package/src/server/responses/core.ts +9 -9
- package/src/server/responses/empty-completion-guard.ts +18 -2
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/jev-model-invoke.ts +1 -1
- package/src/server/responses/passthrough-delivery.ts +24 -2
- package/src/server/responses/passthrough-dispatch.ts +134 -62
- package/src/server/responses/passthrough-execution.ts +3 -3
- package/src/server/responses/request-prepare.ts +4 -0
- package/src/server/responses/request-send-budget.ts +112 -49
- package/src/server/responses/request-transport.ts +125 -5
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +15 -5
- package/src/server/responses/sidecar-execution.ts +30 -16
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/spend-storage-error.ts +33 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +3 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/config.ts +21 -0
- package/src/types/request.ts +10 -0
- package/src/types/wire.ts +1 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/usage/expected-prices.ts +1 -1
- package/src/web-search/loop.ts +5 -2
- package/gui/dist/assets/App-7yhoys8a.js +0 -54
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
parsePersistedCodexRuntime,
|
|
40
40
|
peekCodexRuntimeProcessCache,
|
|
41
41
|
resolveAndPersistCodexRuntime,
|
|
42
|
+
resolveAndPersistCodexRuntimeAsync,
|
|
43
|
+
codexRuntimeSelectionIdentity,
|
|
44
|
+
codexRuntimeStateEpoch,
|
|
45
|
+
execCodexFileAsync,
|
|
42
46
|
} from "../runtime";
|
|
43
47
|
import type {
|
|
44
48
|
DeepReadonly,
|
|
@@ -63,7 +67,9 @@ export type ReadonlyRawCatalog = DeepReadonly<RawCatalog>;
|
|
|
63
67
|
interface BundledCatalogMemo {
|
|
64
68
|
/** Selected runtime identity; must change when doctor/sync picks a different binary. */
|
|
65
69
|
readonly key: string;
|
|
70
|
+
readonly inputs: string;
|
|
66
71
|
readonly expiresAt: number;
|
|
72
|
+
readonly refreshAt: number;
|
|
67
73
|
readonly epoch: number;
|
|
68
74
|
readonly valueIdentity: string;
|
|
69
75
|
readonly value: ReadonlyRawCatalog | null;
|
|
@@ -76,6 +82,9 @@ export interface BundledCatalogCacheState {
|
|
|
76
82
|
|
|
77
83
|
let bundledCatalogEpoch = 0;
|
|
78
84
|
let bundledCatalogCache: BundledCatalogMemo | null = null;
|
|
85
|
+
let bundledCatalogRetry: { inputs: string; epoch: number; after: number } | null = null;
|
|
86
|
+
let bundledCatalogFlight: { inputs: string; epoch: number; controller: AbortController; promise: Promise<ReadonlyRawCatalog | null> } | null = null;
|
|
87
|
+
const catalogAlwaysCurrent = () => true;
|
|
79
88
|
|
|
80
89
|
function cloneAndDeepFreeze<T>(value: T): DeepReadonly<T> {
|
|
81
90
|
const clone = (current: unknown): unknown => {
|
|
@@ -110,14 +119,18 @@ function publishBundledCatalogCache(
|
|
|
110
119
|
const epoch = ++bundledCatalogEpoch;
|
|
111
120
|
bundledCatalogCache = {
|
|
112
121
|
key,
|
|
122
|
+
inputs: codexRuntimeSelectionIdentity({ discoverAlternatives: false }),
|
|
113
123
|
expiresAt,
|
|
124
|
+
refreshAt: Math.min(expiresAt, Date.now() + 15_000),
|
|
114
125
|
epoch,
|
|
115
126
|
valueIdentity: `bundled:${epoch}`,
|
|
116
127
|
value: value === null ? null : cloneAndDeepFreeze(value),
|
|
117
128
|
};
|
|
129
|
+
bundledCatalogRetry = null;
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
function clearBundledCatalogCache(): void {
|
|
133
|
+
bundledCatalogFlight?.controller.abort();
|
|
121
134
|
bundledCatalogEpoch += 1;
|
|
122
135
|
bundledCatalogCache = null;
|
|
123
136
|
}
|
|
@@ -162,12 +175,17 @@ export type ExecFile = (
|
|
|
162
175
|
windowsHide: boolean;
|
|
163
176
|
shell?: boolean;
|
|
164
177
|
windowsVerbatimArguments?: boolean;
|
|
178
|
+
signal?: AbortSignal;
|
|
179
|
+
maxBuffer?: number;
|
|
165
180
|
},
|
|
166
181
|
) => string;
|
|
167
182
|
|
|
168
183
|
export interface BundledCatalogDeps {
|
|
184
|
+
/** Already-observed source for server clamping; null never triggers a probe. */
|
|
185
|
+
observedCatalog?: ReadonlyRawCatalog | null;
|
|
169
186
|
commandCandidates?: () => string[];
|
|
170
187
|
execFileSync?: ExecFile;
|
|
188
|
+
execFile?: (...args: Parameters<ExecFile>) => Promise<string>;
|
|
171
189
|
onEffortClamp?: (diagnostic: EffortClampDiagnostic) => void;
|
|
172
190
|
configDir?: string;
|
|
173
191
|
env?: NodeJS.ProcessEnv;
|
|
@@ -224,11 +242,11 @@ export function codexShimCommandCandidates(): string[] {
|
|
|
224
242
|
}
|
|
225
243
|
}
|
|
226
244
|
|
|
227
|
-
export function runCodexDebugModels(
|
|
245
|
+
export function runCodexDebugModels<T>(
|
|
228
246
|
command: string,
|
|
229
|
-
execFile: ExecFile,
|
|
247
|
+
execFile: (...args: Parameters<ExecFile>) => T,
|
|
230
248
|
deps: Pick<BundledCatalogDeps, "env" | "platform" | "existsSync"> = {},
|
|
231
|
-
):
|
|
249
|
+
): T {
|
|
232
250
|
const args = ["debug", "models", "--bundled"];
|
|
233
251
|
const invocation = codexExecInvocation(command, args, deps.platform ?? process.platform, {
|
|
234
252
|
env: deps.env,
|
|
@@ -238,23 +256,19 @@ export function runCodexDebugModels(
|
|
|
238
256
|
encoding: "utf8" as const,
|
|
239
257
|
stdio: ["ignore", "pipe", "ignore"] as ["ignore", "pipe", "ignore"],
|
|
240
258
|
timeout: 10_000,
|
|
259
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
241
260
|
windowsHide: true,
|
|
242
261
|
...invocation.options,
|
|
243
262
|
});
|
|
244
263
|
}
|
|
245
264
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const execFile = deps.execFileSync ?? (execFileSync as unknown as ExecFile);
|
|
249
|
-
// Prefer the single resolved runtime so sync/clamp never probe a different binary
|
|
250
|
-
// than OpenCodex will launch. Tests may inject commandCandidates to stub probing.
|
|
251
|
-
let cacheKey: string | null = null;
|
|
252
|
-
const candidates = deps.commandCandidates?.() ?? (() => {
|
|
253
|
-
const resolved = resolveAndPersistCodexRuntime({
|
|
265
|
+
function bundledRuntimeDeps(deps: BundledCatalogDeps) {
|
|
266
|
+
return {
|
|
254
267
|
// Forward an INJECTED execFileSync only. Passing the real one unconditionally made
|
|
255
268
|
// resolveCacheKey() bail out (it refuses to memoize injected-dep resolves), so every
|
|
256
269
|
// catalog read re-ran the ~1s `codex --version` probe even on a warm cache hit.
|
|
257
270
|
...(deps.execFileSync ? { execFileSync: deps.execFileSync } : {}),
|
|
271
|
+
...(deps.execFile ? { execFile: deps.execFile } : {}),
|
|
258
272
|
configDir: deps.configDir,
|
|
259
273
|
env: deps.env,
|
|
260
274
|
platform: deps.platform,
|
|
@@ -266,28 +280,41 @@ export function loadBundledCodexCatalog(deps: BundledCatalogDeps = {}): Readonly
|
|
|
266
280
|
// which alone can exceed the 3s budget `ocx claude` allows /api/claude-code. Priority
|
|
267
281
|
// selection is identical either way; callers wanting discovery diagnostics opt in.
|
|
268
282
|
discoverAlternatives: deps.discoverAlternatives ?? false,
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function* bundledCatalogSteps(
|
|
287
|
+
candidates: string[],
|
|
288
|
+
cacheKey: string | null,
|
|
289
|
+
current: () => boolean = () => true,
|
|
290
|
+
publishFailure = true,
|
|
291
|
+
): Generator<string, ReadonlyRawCatalog | null, string | null> {
|
|
292
|
+
if (cacheKey && bundledCatalogCache?.key === cacheKey && current()) {
|
|
293
|
+
// The driver validated this same command/version under the new pin/env inputs.
|
|
294
|
+
// Re-stamp the memo even if its TTL is due, so failed refresh keeps confirmed rows.
|
|
295
|
+
bundledCatalogCache = { ...bundledCatalogCache,
|
|
296
|
+
inputs: codexRuntimeSelectionIdentity({ discoverAlternatives: false }) };
|
|
297
|
+
}
|
|
275
298
|
if (
|
|
276
|
-
|
|
277
|
-
&& cacheKey
|
|
299
|
+
cacheKey
|
|
278
300
|
&& bundledCatalogCache
|
|
279
301
|
&& bundledCatalogCache.key === cacheKey
|
|
302
|
+
&& bundledCatalogCache.inputs === codexRuntimeSelectionIdentity({ discoverAlternatives: false })
|
|
280
303
|
&& bundledCatalogCache.expiresAt > Date.now()
|
|
281
304
|
) {
|
|
305
|
+
if (!current()) return null;
|
|
306
|
+
bundledCatalogCache = { ...bundledCatalogCache, refreshAt: Date.now() + 15_000 };
|
|
282
307
|
return bundledCatalogCache.value === null
|
|
283
308
|
? null
|
|
284
309
|
: cloneAndDeepFreeze(bundledCatalogCache.value);
|
|
285
310
|
}
|
|
286
311
|
for (const command of unique(candidates)) {
|
|
287
|
-
|
|
288
|
-
const
|
|
312
|
+
if (!current()) return null;
|
|
313
|
+
const output = yield command;
|
|
314
|
+
if (!current()) return null;
|
|
315
|
+
const catalog = output === null ? null : parseCatalogJson(output);
|
|
289
316
|
if (catalog && findNativeTemplate(catalog)) {
|
|
290
|
-
if (
|
|
317
|
+
if (cacheKey) {
|
|
291
318
|
publishBundledCatalogCache(
|
|
292
319
|
cacheKey,
|
|
293
320
|
Date.now() + BUNDLED_CATALOG_CACHE_MS,
|
|
@@ -297,9 +324,8 @@ export function loadBundledCodexCatalog(deps: BundledCatalogDeps = {}): Readonly
|
|
|
297
324
|
}
|
|
298
325
|
return cloneAndDeepFreeze(catalog);
|
|
299
326
|
}
|
|
300
|
-
} catch { /* try next candidate */ }
|
|
301
327
|
}
|
|
302
|
-
if (
|
|
328
|
+
if (cacheKey && (publishFailure || bundledCatalogCache?.key !== cacheKey || !bundledCatalogCache?.value) && current()) {
|
|
303
329
|
publishBundledCatalogCache(
|
|
304
330
|
cacheKey,
|
|
305
331
|
Date.now() + BUNDLED_CATALOG_CACHE_MS,
|
|
@@ -309,6 +335,109 @@ export function loadBundledCodexCatalog(deps: BundledCatalogDeps = {}): Readonly
|
|
|
309
335
|
return null;
|
|
310
336
|
}
|
|
311
337
|
|
|
338
|
+
/** Explicit synchronous CLI driver; request readers use the snapshot below. */
|
|
339
|
+
export function loadBundledCodexCatalog(deps: BundledCatalogDeps = {}): ReadonlyRawCatalog | null {
|
|
340
|
+
const useCache = !deps.commandCandidates && !deps.execFileSync && !deps.execFile && !deps.configDir && !deps.env;
|
|
341
|
+
const resolved = deps.commandCandidates ? null : resolveAndPersistCodexRuntime(bundledRuntimeDeps(deps));
|
|
342
|
+
const inputs = codexRuntimeSelectionIdentity(bundledRuntimeDeps(deps));
|
|
343
|
+
const steps = bundledCatalogSteps(deps.commandCandidates?.() ?? [resolved!.runtime.command],
|
|
344
|
+
useCache ? bundledRuntimeKey(resolved!.runtime) : null,
|
|
345
|
+
() => inputs === codexRuntimeSelectionIdentity(bundledRuntimeDeps(deps)));
|
|
346
|
+
let step = steps.next();
|
|
347
|
+
while (!step.done) {
|
|
348
|
+
let output: string | null = null;
|
|
349
|
+
try { output = runCodexDebugModels(step.value, deps.execFileSync ?? (execFileSync as unknown as ExecFile), deps); }
|
|
350
|
+
catch { /* try next candidate */ }
|
|
351
|
+
step = steps.next(output);
|
|
352
|
+
}
|
|
353
|
+
return step.value;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Same candidate/parser authority, with version and bundled-model execs off the event loop. */
|
|
357
|
+
export function loadBundledCodexCatalogAsync(
|
|
358
|
+
deps: BundledCatalogDeps = {},
|
|
359
|
+
isCurrent: () => boolean = catalogAlwaysCurrent,
|
|
360
|
+
signal?: AbortSignal,
|
|
361
|
+
): Promise<ReadonlyRawCatalog | null> {
|
|
362
|
+
if (signal?.aborted || !isCurrent()) return Promise.resolve(null);
|
|
363
|
+
const view = (promise: Promise<ReadonlyRawCatalog | null>) => {
|
|
364
|
+
if (!signal) return promise.then(value => isCurrent() ? value : null);
|
|
365
|
+
let abort: (() => void) | undefined;
|
|
366
|
+
return Promise.race([promise, new Promise<null>(resolve => {
|
|
367
|
+
abort = () => resolve(null);
|
|
368
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
369
|
+
if (signal.aborted) abort();
|
|
370
|
+
})]).then(value => isCurrent() && !signal.aborted ? value : null)
|
|
371
|
+
.finally(() => { if (abort) signal.removeEventListener("abort", abort); });
|
|
372
|
+
};
|
|
373
|
+
const runtimeDeps = bundledRuntimeDeps(deps);
|
|
374
|
+
const inputs = codexRuntimeSelectionIdentity(runtimeDeps);
|
|
375
|
+
const epoch = bundledCatalogEpoch;
|
|
376
|
+
const useCache = !deps.commandCandidates && !deps.execFileSync && !deps.execFile && !deps.configDir && !deps.env;
|
|
377
|
+
if (useCache && bundledCatalogFlight?.inputs === inputs && bundledCatalogFlight.epoch === epoch) {
|
|
378
|
+
return view(bundledCatalogFlight.promise);
|
|
379
|
+
}
|
|
380
|
+
const controller = new AbortController();
|
|
381
|
+
const flight = { inputs, epoch, controller, promise: Promise.resolve<ReadonlyRawCatalog | null>(null) };
|
|
382
|
+
if (useCache && !signal && isCurrent === catalogAlwaysCurrent) {
|
|
383
|
+
bundledCatalogFlight?.controller.abort();
|
|
384
|
+
bundledCatalogFlight = flight;
|
|
385
|
+
}
|
|
386
|
+
const abort = () => controller.abort();
|
|
387
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
388
|
+
if (signal?.aborted) abort();
|
|
389
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
390
|
+
let deadlineReached = false;
|
|
391
|
+
const work = (async () => {
|
|
392
|
+
const active = () => !controller.signal.aborted && isCurrent() && bundledCatalogEpoch === epoch;
|
|
393
|
+
const resolved = deps.commandCandidates ? null : await resolveAndPersistCodexRuntimeAsync({ ...runtimeDeps, signal: controller.signal },
|
|
394
|
+
() => active() && codexRuntimeSelectionIdentity(runtimeDeps) === inputs);
|
|
395
|
+
if (!active() || (!deps.commandCandidates && !resolved)) return null;
|
|
396
|
+
flight.inputs = codexRuntimeSelectionIdentity(runtimeDeps);
|
|
397
|
+
const runtimeEpoch = codexRuntimeStateEpoch();
|
|
398
|
+
const current = () => active() && runtimeEpoch === codexRuntimeStateEpoch()
|
|
399
|
+
&& flight.inputs === codexRuntimeSelectionIdentity(runtimeDeps);
|
|
400
|
+
const steps = bundledCatalogSteps(deps.commandCandidates?.() ?? [resolved!.runtime.command],
|
|
401
|
+
useCache ? bundledRuntimeKey(resolved!.runtime) : null, current, false);
|
|
402
|
+
let step = steps.next();
|
|
403
|
+
while (!step.done) {
|
|
404
|
+
let output: string | null = null;
|
|
405
|
+
try { output = await runCodexDebugModels(step.value,
|
|
406
|
+
(file, args, options) => (deps.execFile ?? execCodexFileAsync)(file, args, { ...options, signal: controller.signal }), deps); }
|
|
407
|
+
catch { /* preserve last confirmed evidence on failure */ }
|
|
408
|
+
step = steps.next(output);
|
|
409
|
+
}
|
|
410
|
+
return step.value;
|
|
411
|
+
})().catch(() => null);
|
|
412
|
+
flight.promise = Promise.race([work, new Promise<null>(resolve => {
|
|
413
|
+
timer = setTimeout(() => { deadlineReached = true; controller.abort(); resolve(null); }, 45_000);
|
|
414
|
+
timer.unref?.();
|
|
415
|
+
})]).then(value => {
|
|
416
|
+
if (useCache && value === null && (!controller.signal.aborted || deadlineReached)
|
|
417
|
+
&& isCurrent() && bundledCatalogEpoch === epoch
|
|
418
|
+
&& flight.inputs === codexRuntimeSelectionIdentity(runtimeDeps)) {
|
|
419
|
+
bundledCatalogRetry = { inputs: flight.inputs, epoch, after: Date.now() + BUNDLED_CATALOG_CACHE_MS };
|
|
420
|
+
}
|
|
421
|
+
return value;
|
|
422
|
+
}).finally(() => {
|
|
423
|
+
clearTimeout(timer);
|
|
424
|
+
signal?.removeEventListener("abort", abort);
|
|
425
|
+
controller.abort();
|
|
426
|
+
if (bundledCatalogFlight === flight) bundledCatalogFlight = null;
|
|
427
|
+
});
|
|
428
|
+
return view(flight.promise);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** Serve only the same selection's last confirmed catalog; refresh never holds a request. */
|
|
432
|
+
export function bundledCodexCatalogSnapshot(): ReadonlyRawCatalog | null {
|
|
433
|
+
const inputs = codexRuntimeSelectionIdentity({ discoverAlternatives: false });
|
|
434
|
+
const memo = bundledCatalogCache?.inputs === inputs ? bundledCatalogCache : null;
|
|
435
|
+
const cooling = bundledCatalogRetry?.inputs === inputs && bundledCatalogRetry.epoch === bundledCatalogEpoch
|
|
436
|
+
&& bundledCatalogRetry.after > Date.now();
|
|
437
|
+
if ((!memo || memo.refreshAt <= Date.now()) && !cooling) void loadBundledCodexCatalogAsync().catch(() => { /* bounded retry */ });
|
|
438
|
+
return memo?.value ? cloneAndDeepFreeze(memo.value) : null;
|
|
439
|
+
}
|
|
440
|
+
|
|
312
441
|
export type CatalogGatherProcessLocalObservation =
|
|
313
442
|
| Readonly<{ state: "unused" }>
|
|
314
443
|
| Readonly<{ state: "used"; epoch: number; valueIdentity: string }>;
|
|
@@ -524,7 +653,7 @@ export function loadCatalogForSync(path: string): RawCatalog | null {
|
|
|
524
653
|
|
|
525
654
|
export function readCurrentCatalogOrCache(): RawCatalog | null {
|
|
526
655
|
const path = readCodexCatalogPath();
|
|
527
|
-
const bundled = isDefaultCatalogPath(path) ?
|
|
656
|
+
const bundled = isDefaultCatalogPath(path) ? bundledCodexCatalogSnapshot() : null;
|
|
528
657
|
if (bundled) return JSON.parse(JSON.stringify(bundled)) as RawCatalog;
|
|
529
658
|
return readCatalog(path) ?? readCatalog(activeCodexModelsCachePath());
|
|
530
659
|
}
|
|
@@ -547,7 +676,7 @@ export function readCurrentCodexModelsCache(): RawCatalog | null {
|
|
|
547
676
|
|
|
548
677
|
export function loadCatalogTemplate(): RawEntry | null {
|
|
549
678
|
const catalogPath = readCodexCatalogPath();
|
|
550
|
-
const bundled =
|
|
679
|
+
const bundled = bundledCodexCatalogSnapshot();
|
|
551
680
|
// Template inheritance only. The validity gates in this file keep `findNativeTemplate`
|
|
552
681
|
// so a catalog carrying only a newly launched native row stays valid (#2813).
|
|
553
682
|
const native = findSupportedNativeTemplate(readCatalog(catalogPath))
|
|
@@ -218,6 +218,7 @@ export function resolveComboCatalogMember(
|
|
|
218
218
|
contextCap?: number,
|
|
219
219
|
callerFallback?: ComboCatalogMemberFallback,
|
|
220
220
|
metadataModelIdCaseFold?: boolean,
|
|
221
|
+
metadataConfigDir?: string,
|
|
221
222
|
): CatalogModel | undefined {
|
|
222
223
|
const existing = memberByKey.get(targetKey(target));
|
|
223
224
|
const prov = providers.get(target.provider);
|
|
@@ -275,7 +276,7 @@ export function resolveComboCatalogMember(
|
|
|
275
276
|
// supplies a vision sidecar. Apply the same provider hints used for thin
|
|
276
277
|
// rows before deriving a combo from this complete row.
|
|
277
278
|
const hinted = prov && isModelVisionSidecarConsumer(prov, existing.id)
|
|
278
|
-
? applyProviderConfigHints(target.provider, prov, existing, contextCap, metadataModelIdCaseFold)
|
|
279
|
+
? applyProviderConfigHints(target.provider, prov, existing, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir)
|
|
279
280
|
: existing;
|
|
280
281
|
const capped = applyProviderContextCap(hinted.contextWindow, contextCap);
|
|
281
282
|
if (capped === undefined || capped === existing.contextWindow) {
|
|
@@ -298,7 +299,7 @@ export function resolveComboCatalogMember(
|
|
|
298
299
|
provider: target.provider,
|
|
299
300
|
};
|
|
300
301
|
const hinted = prov
|
|
301
|
-
? applyProviderConfigHints(target.provider, prov, base, contextCap, metadataModelIdCaseFold)
|
|
302
|
+
? applyProviderConfigHints(target.provider, prov, base, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir)
|
|
302
303
|
: base;
|
|
303
304
|
const hintedContext = typeof hinted.contextWindow === "number" && hinted.contextWindow > 0
|
|
304
305
|
? hinted.contextWindow
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
displayCodexRuntimePath,
|
|
48
48
|
persistEffortClamp,
|
|
49
49
|
resolveAndPersistCodexRuntime,
|
|
50
|
+
getCodexRuntimeSnapshot,
|
|
50
51
|
UNCLAMPABLE_REASONING_EFFORTS,
|
|
51
52
|
type EffortClampDiagnostic,
|
|
52
53
|
} from "../runtime";
|
|
@@ -86,8 +87,14 @@ export function shouldApplyNativeEffortClamp(
|
|
|
86
87
|
&& isCanonicalOpenAiForwardProvider(provider);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
|
-
export
|
|
90
|
-
|
|
90
|
+
export interface CatalogModelLadder {
|
|
91
|
+
readonly efforts: string[];
|
|
92
|
+
readonly defaultEffort?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Each slug's reasoning ladder and default as the written Codex catalog offers them. */
|
|
96
|
+
export function catalogModelLadders(slugs: readonly string[]): Map<string, CatalogModelLadder> {
|
|
97
|
+
const out = new Map<string, CatalogModelLadder>();
|
|
91
98
|
if (slugs.length === 0) return out;
|
|
92
99
|
const catalog = readCatalog(readCodexCatalogPath());
|
|
93
100
|
if (!catalog) return out;
|
|
@@ -101,11 +108,19 @@ export function catalogModelEfforts(slugs: readonly string[]): Map<string, strin
|
|
|
101
108
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
102
109
|
: [];
|
|
103
110
|
const efforts = levels.flatMap(l => typeof l.effort === "string" ? [l.effort] : []);
|
|
104
|
-
if (efforts.length
|
|
111
|
+
if (efforts.length === 0) continue;
|
|
112
|
+
const defaultEffort = typeof entry.default_reasoning_level === "string" && efforts.includes(entry.default_reasoning_level)
|
|
113
|
+
? entry.default_reasoning_level
|
|
114
|
+
: undefined;
|
|
115
|
+
out.set(callerSlug, defaultEffort === undefined ? { efforts } : { efforts, defaultEffort });
|
|
105
116
|
}
|
|
106
117
|
return out;
|
|
107
118
|
}
|
|
108
119
|
|
|
120
|
+
export function catalogModelEfforts(slugs: readonly string[]): Map<string, string[]> {
|
|
121
|
+
return new Map([...catalogModelLadders(slugs)].map(([slug, ladder]) => [slug, ladder.efforts]));
|
|
122
|
+
}
|
|
123
|
+
|
|
109
124
|
export function catalogEntryEfforts(entry: RawEntry): string[] {
|
|
110
125
|
const levels = Array.isArray(entry.supported_reasoning_levels)
|
|
111
126
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
@@ -366,7 +381,9 @@ export function supportedCodexReasoningEffortsFromObservedCatalog(
|
|
|
366
381
|
}
|
|
367
382
|
|
|
368
383
|
export function codexSupportedReasoningEfforts(deps: BundledCatalogDeps = {}): ReadonlySet<string> | null {
|
|
369
|
-
return supportedCodexReasoningEffortsFromObservedCatalog(
|
|
384
|
+
return supportedCodexReasoningEffortsFromObservedCatalog(
|
|
385
|
+
deps.observedCatalog !== undefined ? deps.observedCatalog : loadBundledCodexCatalog(deps),
|
|
386
|
+
);
|
|
370
387
|
}
|
|
371
388
|
|
|
372
389
|
export function clampedDefaultEffort(original: string, surviving: readonly string[]): string {
|
|
@@ -558,7 +575,11 @@ export function clampCatalogModelsToCodexSupport(models: RawEntry[], deps: Bundl
|
|
|
558
575
|
|
|
559
576
|
let runtimePath = "codex";
|
|
560
577
|
let runtimeVersion: string | null = null;
|
|
561
|
-
if (!deps.commandCandidates) {
|
|
578
|
+
if (deps.observedCatalog !== undefined && !deps.commandCandidates) {
|
|
579
|
+
const snapshot = getCodexRuntimeSnapshot();
|
|
580
|
+
runtimePath = snapshot.runtime.command;
|
|
581
|
+
runtimeVersion = snapshot.runtime.version;
|
|
582
|
+
} else if (!deps.commandCandidates) {
|
|
562
583
|
try {
|
|
563
584
|
const resolved = resolveAndPersistCodexRuntime({
|
|
564
585
|
execFileSync: deps.execFileSync,
|
|
@@ -148,6 +148,8 @@ export interface CapturedModelsRequest {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
export interface CapturedProviderGather {
|
|
151
|
+
/** Public metadata reads and refreshes stay bound to this admission root. */
|
|
152
|
+
readonly metadataConfigDir: string;
|
|
151
153
|
readonly name: string;
|
|
152
154
|
readonly provider: OcxProviderConfig;
|
|
153
155
|
readonly discovery: ResolvedProviderModelDiscovery;
|
|
@@ -354,6 +356,7 @@ export function captureProviderGather(
|
|
|
354
356
|
retainConfiguredModelIds?: ReadonlySet<string>,
|
|
355
357
|
config?: Pick<OcxConfig, "providers">,
|
|
356
358
|
): CapturedProviderGather {
|
|
359
|
+
const metadataConfigDir = getConfigDir();
|
|
357
360
|
const enriched = detachedClone(withCanonicalOpenAiForwardAuthDefault(name, configured));
|
|
358
361
|
enrichProviderFromRegistry(name, enriched);
|
|
359
362
|
const registryTransportMatch = providerMatchesRegistryTransport(name, enriched);
|
|
@@ -428,6 +431,7 @@ export function captureProviderGather(
|
|
|
428
431
|
const effectiveAlias = effectiveProviderAliasDecision(name, configured, config);
|
|
429
432
|
return Object.freeze({
|
|
430
433
|
name,
|
|
434
|
+
metadataConfigDir,
|
|
431
435
|
provider,
|
|
432
436
|
discovery,
|
|
433
437
|
policy,
|
|
@@ -482,6 +486,7 @@ export function captureGatherFlight(
|
|
|
482
486
|
providerGraphIdentity: keyedGatherIdentity("catalog-gather-provider-graph-v1",
|
|
483
487
|
providers.map(provider => ({
|
|
484
488
|
name: provider.name,
|
|
489
|
+
metadataConfigDir: provider.metadataConfigDir,
|
|
485
490
|
// `fetch` is a caller-owned transport executor, not admitted state: the
|
|
486
491
|
// outbound transport honors it so a caller can supply its own HTTP path.
|
|
487
492
|
// It is the one member of a provider row that is legitimately a function,
|
|
@@ -35,7 +35,7 @@ import { modelInList } from "../../types";
|
|
|
35
35
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
36
36
|
import { isModelVisionSidecarConsumer } from "../../vision/eligibility";
|
|
37
37
|
import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider, type ModelMetadata } from "../../generated/model-metadata";
|
|
38
|
-
import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
38
|
+
import { azureVendorModelMetadata, enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
|
|
39
39
|
import {
|
|
40
40
|
captureFastPolicyAuthority,
|
|
41
41
|
fastPolicyForModel,
|
|
@@ -217,9 +217,11 @@ export function routedMaxOutputTokens(
|
|
|
217
217
|
model: CatalogModel,
|
|
218
218
|
metadataId = model.id,
|
|
219
219
|
metadataModelIdCaseFold?: boolean,
|
|
220
|
+
metadataConfigDir?: string,
|
|
220
221
|
): number | undefined {
|
|
221
222
|
const discovered = positiveSafeInteger(model.maxOutputTokens);
|
|
222
|
-
const generated =
|
|
223
|
+
const generated = positiveSafeInteger(azureVendorModelMetadata(provider.baseUrl, metadataId, metadataConfigDir)?.maxTokens)
|
|
224
|
+
?? generatedMaxOutputTokens(providerName, model.id, metadataId, metadataModelIdCaseFold);
|
|
223
225
|
const configured = positiveSafeInteger(
|
|
224
226
|
modelRecordValue(provider.modelMaxOutputTokens, model.id),
|
|
225
227
|
);
|
|
@@ -255,6 +257,7 @@ export function applyProviderConfigHints(
|
|
|
255
257
|
providerCap?: number,
|
|
256
258
|
metadataModelIdCaseFold?: boolean,
|
|
257
259
|
effectiveAlias?: string | null,
|
|
260
|
+
metadataConfigDir?: string,
|
|
258
261
|
): CatalogModel {
|
|
259
262
|
const staticPolicy = resolveModelPolicy({
|
|
260
263
|
providerName: name,
|
|
@@ -276,13 +279,14 @@ export function applyProviderConfigHints(
|
|
|
276
279
|
: model.providerAlias;
|
|
277
280
|
const configuredCap = staticPolicy.model.contextWindow ?? configuredContextWindow(prov, model.id);
|
|
278
281
|
const configuredMaxInput = staticPolicy.model.maxInputTokens;
|
|
279
|
-
const
|
|
282
|
+
const azureMetadata = azureVendorModelMetadata(prov.baseUrl, model.id, metadataConfigDir);
|
|
283
|
+
const maxOutputTokens = routedMaxOutputTokens(name, prov, model, model.id, metadataModelIdCaseFold, metadataConfigDir);
|
|
280
284
|
const configuredAutoCompact = configuredAutoCompactTokenLimit(prov, model.id);
|
|
281
285
|
// The resolver owns exact capability precedence and legacy exact/colon-family/case-fold fallback.
|
|
282
286
|
// Removing an exact capability row therefore returns this projection to legacy-map inference.
|
|
283
|
-
|
|
284
|
-
?
|
|
285
|
-
|
|
287
|
+
const resolvedModalities = staticPolicy.model.inputModalities ?? model.inputModalities
|
|
288
|
+
?? (azureMetadata?.input ? modelInputModalities({ id: model.id, input_modalities: azureMetadata.input }, undefined) : undefined);
|
|
289
|
+
let inputModalities = resolvedModalities ? [...resolvedModalities] : undefined;
|
|
286
290
|
// The shared vision-sidecar consumer predicate keeps catalog advertisement and request-time
|
|
287
291
|
// planning aligned. The catalog must still advertise image input — the Codex app
|
|
288
292
|
// gates attachments client-side on input_modalities, and a text-only entry would block images
|
|
@@ -319,6 +323,7 @@ export function applyProviderConfigHints(
|
|
|
319
323
|
...(typeof model.maxInputTokens === "number" && model.maxInputTokens > 0 ? { maxInputTokens: model.maxInputTokens } : {}),
|
|
320
324
|
});
|
|
321
325
|
const hintedWindow = projectedLimits.contextWindow
|
|
326
|
+
?? positiveSafeInteger(azureMetadata?.contextWindow)
|
|
322
327
|
?? (providerCap !== undefined ? resolveUnknownRoutedContextWindow(providerCap) : undefined);
|
|
323
328
|
const hinted = {
|
|
324
329
|
...modelWithoutServiceTier,
|
|
@@ -383,8 +388,9 @@ export function catalogHintsFromProviderConfig(
|
|
|
383
388
|
contextCap?: number,
|
|
384
389
|
metadataModelIdCaseFold?: boolean,
|
|
385
390
|
effectiveAlias?: string | null,
|
|
391
|
+
metadataConfigDir?: string,
|
|
386
392
|
): Partial<CatalogModel> {
|
|
387
|
-
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold, effectiveAlias);
|
|
393
|
+
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir);
|
|
388
394
|
const { provider: _provider, id: _id, ...hints } = hinted;
|
|
389
395
|
return hints;
|
|
390
396
|
}
|
|
@@ -396,8 +402,9 @@ export function applyConfigHintsToCachedModels(
|
|
|
396
402
|
contextCap?: number,
|
|
397
403
|
metadataModelIdCaseFold?: boolean,
|
|
398
404
|
effectiveAlias?: string | null,
|
|
405
|
+
metadataConfigDir?: string,
|
|
399
406
|
): CatalogModel[] {
|
|
400
|
-
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold, effectiveAlias));
|
|
407
|
+
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir));
|
|
401
408
|
}
|
|
402
409
|
|
|
403
410
|
/** Catalog slugs whose configured model must not gain a missing synthetic max rung. */
|
|
@@ -230,6 +230,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
230
230
|
seedVertexDefault?: boolean;
|
|
231
231
|
retainComboTargets?: boolean;
|
|
232
232
|
metadataModelIdCaseFold?: boolean;
|
|
233
|
+
metadataConfigDir?: string;
|
|
233
234
|
}): { models: CatalogModel[]; droppedConfiguredIds: string[] } {
|
|
234
235
|
const {
|
|
235
236
|
name,
|
|
@@ -240,6 +241,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
240
241
|
seedVertexDefault,
|
|
241
242
|
retainComboTargets = true,
|
|
242
243
|
metadataModelIdCaseFold,
|
|
244
|
+
metadataConfigDir,
|
|
243
245
|
} = opts;
|
|
244
246
|
const out = [...opts.models];
|
|
245
247
|
const present = new Set(out.map(model => model.id));
|
|
@@ -248,7 +250,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
248
250
|
if (present.has(candidate.id)) continue;
|
|
249
251
|
const dated = out.find(live => isDatedVariantId(live.id, candidate.id));
|
|
250
252
|
if (dated) {
|
|
251
|
-
out.push(applyProviderConfigHints(name, prov, { ...dated, id: candidate.id }, contextCap, metadataModelIdCaseFold));
|
|
253
|
+
out.push(applyProviderConfigHints(name, prov, { ...dated, id: candidate.id }, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir));
|
|
252
254
|
present.add(candidate.id);
|
|
253
255
|
continue;
|
|
254
256
|
}
|
|
@@ -93,7 +93,7 @@ export const CONFIGURED_NATIVE_OPENAI_TEMPLATE_MODEL = NATIVE_GPT61_SOL_MODEL;
|
|
|
93
93
|
*
|
|
94
94
|
* `gpt-daybreak-blue-latest` stays gated. It has no shipped catalog row anywhere, so absence is
|
|
95
95
|
* the only signal that exists for it, and the ungating decision was scoped to the flagships.
|
|
96
|
-
* Evidence: devlog/
|
|
96
|
+
* Evidence: devlog/_fin/260904_flagship_native_always_visible/.
|
|
97
97
|
*/
|
|
98
98
|
export const ACCOUNT_GATED_NATIVE_OPENAI_MODELS: ReadonlySet<string> = new Set([
|
|
99
99
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { RawEntry } from "./parsing";
|
|
2
|
+
|
|
3
|
+
/** Match the opt-in tier spelling without coercing malformed catalog values to strings. */
|
|
4
|
+
function isUltraFast(value: unknown): boolean {
|
|
5
|
+
return typeof value === "string" && value.trim().toLowerCase() === "ultrafast";
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Recognize an explicitly supplied Ultra Fast descriptor by its string-valued id. */
|
|
9
|
+
function isUltraFastDescriptor(value: unknown): boolean {
|
|
10
|
+
return value !== null && typeof value === "object" && "id" in value && isUltraFast(value.id);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Restore only exact-row operator metadata, after fresh provider capabilities are applied. */
|
|
14
|
+
export function preserveOperatorUltraFastTiers(entry: RawEntry, previous: RawEntry | undefined, enabled: boolean): void {
|
|
15
|
+
// A native template or stale row is not authority for this opt-in. Keep all other fresh
|
|
16
|
+
// tier policy, including Fast, while replacing only Ultra Fast from the exact persisted row.
|
|
17
|
+
for (const [field, matches] of [
|
|
18
|
+
["service_tiers", isUltraFastDescriptor],
|
|
19
|
+
["additional_speed_tiers", isUltraFast],
|
|
20
|
+
] as const) {
|
|
21
|
+
const current = entry[field];
|
|
22
|
+
const fresh = Array.isArray(current) ? current.filter(value => !matches(value)) : [];
|
|
23
|
+
const old = previous?.[field];
|
|
24
|
+
const supplied = enabled && Array.isArray(old) ? old.find(matches) : undefined;
|
|
25
|
+
if (supplied !== undefined) {
|
|
26
|
+
entry[field] = [...fresh, structuredClone(supplied)];
|
|
27
|
+
} else if (Array.isArray(current) && fresh.length !== current.length) {
|
|
28
|
+
if (fresh.length > 0) entry[field] = fresh;
|
|
29
|
+
else delete entry[field];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const hasDeclaration = (Array.isArray(entry.service_tiers) && entry.service_tiers.some(isUltraFastDescriptor))
|
|
33
|
+
|| (Array.isArray(entry.additional_speed_tiers) && entry.additional_speed_tiers.some(isUltraFast));
|
|
34
|
+
for (const field of ["service_tier", "default_service_tier"] as const) {
|
|
35
|
+
if (enabled && hasDeclaration && previous && isUltraFast(previous[field])) entry[field] = previous[field];
|
|
36
|
+
else if (isUltraFast(entry[field])) delete entry[field];
|
|
37
|
+
}
|
|
38
|
+
}
|