@bitkyc08/opencodex 2.80.0 → 2.82.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 +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/index-D3FBCNiP.js +94 -0
- package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +8 -3
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -6
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +78 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-listener.ts +71 -4
- 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-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +18 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +15 -1
- package/src/cli/doctor.ts +16 -3
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +25 -14
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +48 -6
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/cli/resolve.ts +25 -2
- 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/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +11 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +33 -7
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +37 -6
- package/src/codex/catalog/gather-capture.ts +10 -3
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +58 -44
- 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/control-plane-models.ts +6 -0
- package/src/codex/convergence.ts +2 -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/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-decision-contract.ts +25 -2
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -219
- package/src/combos/types.ts +1 -1
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/diagnostics.ts +27 -16
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +42 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +11 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +566 -298
- package/src/generated/model-metadata.ts +7 -6
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +30 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +19 -3
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +17 -5
- 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/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +244 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- 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/upstream-retry.ts +17 -5
- 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/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +90 -18
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +96 -175
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +68 -10
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +23 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +23 -33
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +20 -3
- package/src/responses/state/metrics.ts +95 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +13 -5
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +68 -76
- package/src/responses/task-input.ts +17 -0
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native.ts +64 -28
- package/src/server/claude-messages.ts +59 -8
- package/src/server/gui-static.ts +38 -4
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +12 -5
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +5 -2
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +123 -38
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- 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 +85 -14
- package/src/server/management/context.ts +2 -2
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/shared.ts +3 -3
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +91 -45
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +133 -38
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +36 -2
- package/src/server/responses/adapter-continuation.ts +31 -19
- package/src/server/responses/adapter-dispatch.ts +75 -24
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- 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 +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +24 -4
- package/src/server/responses/core-combo.ts +38 -25
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +8 -1
- package/src/server/responses/core-replay.ts +78 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +30 -4
- package/src/server/responses/passthrough-dispatch.ts +39 -9
- package/src/server/responses/request-prepare.ts +41 -5
- package/src/server/responses/request-send-budget.ts +150 -50
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +13 -3
- package/src/server/responses/sidecar-execution.ts +113 -22
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +33 -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/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- 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/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- 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/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +33 -28
- package/src/types/provider.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types/wire.ts +1 -0
- package/src/types.ts +4 -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/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -7
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-MbDVfUeC.js +0 -54
- package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
- package/gui/dist/assets/index-UJt6npXk.js +0 -94
- package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
|
@@ -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
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CODEX_INTERNAL_OPENAI_MODELS, isCodexControlPlaneModel } from "../control-plane-models";
|
|
2
|
+
import { COMBO_NAMESPACE } from "../../combos";
|
|
3
|
+
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
4
|
+
import { NATIVE_RESERVE_MODEL, SUPPORTED_NATIVE_OPENAI_SLUGS } from "./native-models";
|
|
5
|
+
import { isReserveCatalogProjection } from "./reserve";
|
|
6
|
+
import { pinnedNativeModelRows } from "./pinned-models";
|
|
7
|
+
import type { RawEntry } from "./parsing";
|
|
8
|
+
|
|
9
|
+
/** Hidden ordinary natives qualify; Reserve, account choices and routed aliases do not. */
|
|
10
|
+
export function hasOrdinaryNativeOpenAiRow(entries: readonly RawEntry[]): boolean {
|
|
11
|
+
return entries.some(entry => typeof entry.slug === "string"
|
|
12
|
+
&& !entry.slug.includes("/")
|
|
13
|
+
&& SUPPORTED_NATIVE_OPENAI_SLUGS.has(entry.slug)
|
|
14
|
+
&& entry.slug !== NATIVE_RESERVE_MODEL
|
|
15
|
+
&& !isReserveCatalogProjection(entry)
|
|
16
|
+
&& entry.opencodex_catalog_kind !== CODEX_NATIVE_ALIAS_CATALOG_KIND
|
|
17
|
+
&& entry.owned_by !== COMBO_NAMESPACE);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Keep internal reviewer metadata separate from native synthesis, picker ordering and account clones. */
|
|
21
|
+
export function withCodexControlPlaneRows(
|
|
22
|
+
entries: RawEntry[],
|
|
23
|
+
sourceRows: readonly RawEntry[],
|
|
24
|
+
includeNativeOpenAi: boolean,
|
|
25
|
+
wsEnabled: boolean,
|
|
26
|
+
): RawEntry[] {
|
|
27
|
+
const models = entries.filter(entry => !isCodexControlPlaneModel(entry.slug));
|
|
28
|
+
if (!includeNativeOpenAi || !hasOrdinaryNativeOpenAiRow(models)) return models;
|
|
29
|
+
for (const slug of CODEX_INTERNAL_OPENAI_MODELS) {
|
|
30
|
+
const source = sourceRows.find(entry => entry.slug === slug)
|
|
31
|
+
?? pinnedNativeModelRows().find(entry => entry.slug === slug);
|
|
32
|
+
// A pin without the row degrades to Codex's own task-model fallback rather than failing the
|
|
33
|
+
// whole catalog write.
|
|
34
|
+
if (!source) continue;
|
|
35
|
+
const row: RawEntry = structuredClone(source);
|
|
36
|
+
row.visibility = "hide";
|
|
37
|
+
if (wsEnabled) row.supports_websockets = true;
|
|
38
|
+
else {
|
|
39
|
+
delete row.supports_websockets;
|
|
40
|
+
delete row.prefer_websockets;
|
|
41
|
+
}
|
|
42
|
+
models.push(row);
|
|
43
|
+
}
|
|
44
|
+
return models;
|
|
45
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { hasOrdinaryNativeOpenAiRow } from "./control-plane";
|
|
2
|
+
import { isCodexControlPlaneModel } from "../control-plane-models";
|
|
1
3
|
import { execFileSync } from "node:child_process";
|
|
2
4
|
import { createHash } from "node:crypto";
|
|
3
5
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
@@ -45,6 +47,7 @@ import {
|
|
|
45
47
|
displayCodexRuntimePath,
|
|
46
48
|
persistEffortClamp,
|
|
47
49
|
resolveAndPersistCodexRuntime,
|
|
50
|
+
getCodexRuntimeSnapshot,
|
|
48
51
|
UNCLAMPABLE_REASONING_EFFORTS,
|
|
49
52
|
type EffortClampDiagnostic,
|
|
50
53
|
} from "../runtime";
|
|
@@ -84,8 +87,14 @@ export function shouldApplyNativeEffortClamp(
|
|
|
84
87
|
&& isCanonicalOpenAiForwardProvider(provider);
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
export
|
|
88
|
-
|
|
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>();
|
|
89
98
|
if (slugs.length === 0) return out;
|
|
90
99
|
const catalog = readCatalog(readCodexCatalogPath());
|
|
91
100
|
if (!catalog) return out;
|
|
@@ -99,11 +108,19 @@ export function catalogModelEfforts(slugs: readonly string[]): Map<string, strin
|
|
|
99
108
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
100
109
|
: [];
|
|
101
110
|
const efforts = levels.flatMap(l => typeof l.effort === "string" ? [l.effort] : []);
|
|
102
|
-
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 });
|
|
103
116
|
}
|
|
104
117
|
return out;
|
|
105
118
|
}
|
|
106
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
|
+
|
|
107
124
|
export function catalogEntryEfforts(entry: RawEntry): string[] {
|
|
108
125
|
const levels = Array.isArray(entry.supported_reasoning_levels)
|
|
109
126
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
@@ -364,7 +381,9 @@ export function supportedCodexReasoningEffortsFromObservedCatalog(
|
|
|
364
381
|
}
|
|
365
382
|
|
|
366
383
|
export function codexSupportedReasoningEfforts(deps: BundledCatalogDeps = {}): ReadonlySet<string> | null {
|
|
367
|
-
return supportedCodexReasoningEffortsFromObservedCatalog(
|
|
384
|
+
return supportedCodexReasoningEffortsFromObservedCatalog(
|
|
385
|
+
deps.observedCatalog !== undefined ? deps.observedCatalog : loadBundledCodexCatalog(deps),
|
|
386
|
+
);
|
|
368
387
|
}
|
|
369
388
|
|
|
370
389
|
export function clampedDefaultEffort(original: string, surviving: readonly string[]): string {
|
|
@@ -385,7 +404,7 @@ export function clampEntryToCodexSupportedEfforts(
|
|
|
385
404
|
entry: RawEntry,
|
|
386
405
|
supported: ReadonlySet<string> | null,
|
|
387
406
|
): void {
|
|
388
|
-
if (!supported) return;
|
|
407
|
+
if (!supported || isCodexControlPlaneModel(entry.slug)) return;
|
|
389
408
|
const levels = Array.isArray(entry.supported_reasoning_levels)
|
|
390
409
|
? entry.supported_reasoning_levels as Array<{ effort?: string }>
|
|
391
410
|
: null;
|
|
@@ -532,6 +551,14 @@ export function clampCatalogModelsToObservedCodexSupport(
|
|
|
532
551
|
else index += 1;
|
|
533
552
|
}
|
|
534
553
|
|
|
554
|
+
// Only Reserve rows are currently omitted above; ordinary natives keep a fallback ladder.
|
|
555
|
+
// Also repair a persisted orphan so a removed Reserve can never leave a reviewer behind.
|
|
556
|
+
if (!hasOrdinaryNativeOpenAiRow(models)) {
|
|
557
|
+
for (let index = models.length - 1; index >= 0; index -= 1) {
|
|
558
|
+
if (isCodexControlPlaneModel(models[index]!.slug)) models.splice(index, 1);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
535
562
|
return {
|
|
536
563
|
removedEfforts: [...removed].sort(),
|
|
537
564
|
affectedModels: affected,
|
|
@@ -548,7 +575,11 @@ export function clampCatalogModelsToCodexSupport(models: RawEntry[], deps: Bundl
|
|
|
548
575
|
|
|
549
576
|
let runtimePath = "codex";
|
|
550
577
|
let runtimeVersion: string | null = null;
|
|
551
|
-
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) {
|
|
552
583
|
try {
|
|
553
584
|
const resolved = resolveAndPersistCodexRuntime({
|
|
554
585
|
execFileSync: deps.execFileSync,
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
type OAuthActiveTokenObservation,
|
|
33
33
|
} from "../../oauth";
|
|
34
34
|
import { getAccountSet } from "../../oauth/store";
|
|
35
|
+
import { mayResolveModelsOAuth } from "../../oauth/model-discovery-auth";
|
|
35
36
|
import type { OcxConfig, OcxProviderConfig } from "../../types";
|
|
36
37
|
import { modelInList } from "../../types";
|
|
37
38
|
import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
|
|
@@ -147,6 +148,8 @@ export interface CapturedModelsRequest {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
export interface CapturedProviderGather {
|
|
151
|
+
/** Public metadata reads and refreshes stay bound to this admission root. */
|
|
152
|
+
readonly metadataConfigDir: string;
|
|
150
153
|
readonly name: string;
|
|
151
154
|
readonly provider: OcxProviderConfig;
|
|
152
155
|
readonly discovery: ResolvedProviderModelDiscovery;
|
|
@@ -353,6 +356,7 @@ export function captureProviderGather(
|
|
|
353
356
|
retainConfiguredModelIds?: ReadonlySet<string>,
|
|
354
357
|
config?: Pick<OcxConfig, "providers">,
|
|
355
358
|
): CapturedProviderGather {
|
|
359
|
+
const metadataConfigDir = getConfigDir();
|
|
356
360
|
const enriched = detachedClone(withCanonicalOpenAiForwardAuthDefault(name, configured));
|
|
357
361
|
enrichProviderFromRegistry(name, enriched);
|
|
358
362
|
const registryTransportMatch = providerMatchesRegistryTransport(name, enriched);
|
|
@@ -389,15 +393,15 @@ export function captureProviderGather(
|
|
|
389
393
|
? authResolver.resolve(name, provider)
|
|
390
394
|
: undefined;
|
|
391
395
|
// A refreshing capture carries the stored origin so accounts on different hosts keep separate
|
|
392
|
-
// flights.
|
|
396
|
+
// flights. Host-scoped auth rebuilds its request; B retains its configured captured target. Observed capture never
|
|
393
397
|
// reads the live store.
|
|
394
398
|
const oauthApiBaseUrl = observedAuth
|
|
395
399
|
? observedAuth.oauthApiBaseUrl
|
|
396
|
-
: authResolver.kind === "refreshing" && provider.authMode === "oauth"
|
|
400
|
+
: authResolver.kind === "refreshing" && provider.authMode === "oauth" && mayResolveModelsOAuth(name, provider)
|
|
397
401
|
? getOAuthCredentialApiBaseUrl(name)
|
|
398
402
|
: undefined;
|
|
399
403
|
const request = captureModelsRequest(name, provider, oauthApiBaseUrl);
|
|
400
|
-
const refreshingOAuthAccountId = !observedAuth && authResolver.kind === "refreshing" && provider.authMode === "oauth"
|
|
404
|
+
const refreshingOAuthAccountId = !observedAuth && authResolver.kind === "refreshing" && provider.authMode === "oauth" && mayResolveModelsOAuth(name, provider)
|
|
401
405
|
? getAccountSet(name)?.activeAccountId
|
|
402
406
|
: undefined;
|
|
403
407
|
const resolved = resolveProviderModelDiscovery(name, provider);
|
|
@@ -427,6 +431,7 @@ export function captureProviderGather(
|
|
|
427
431
|
const effectiveAlias = effectiveProviderAliasDecision(name, configured, config);
|
|
428
432
|
return Object.freeze({
|
|
429
433
|
name,
|
|
434
|
+
metadataConfigDir,
|
|
430
435
|
provider,
|
|
431
436
|
discovery,
|
|
432
437
|
policy,
|
|
@@ -481,6 +486,7 @@ export function captureGatherFlight(
|
|
|
481
486
|
providerGraphIdentity: keyedGatherIdentity("catalog-gather-provider-graph-v1",
|
|
482
487
|
providers.map(provider => ({
|
|
483
488
|
name: provider.name,
|
|
489
|
+
metadataConfigDir: provider.metadataConfigDir,
|
|
484
490
|
// `fetch` is a caller-owned transport executor, not admitted state: the
|
|
485
491
|
// outbound transport honors it so a caller can supply its own HTTP path.
|
|
486
492
|
// It is the one member of a provider row that is legitimately a function,
|
|
@@ -531,6 +537,7 @@ function providerCatalogFingerprint(name: string, prov: OcxProviderConfig): Reco
|
|
|
531
537
|
live: prov.liveModels ?? null,
|
|
532
538
|
base: prov.baseUrl ?? "",
|
|
533
539
|
adapter: prov.adapter ?? "",
|
|
540
|
+
instance: prov.anthropicOAuthInstance ?? null,
|
|
534
541
|
models: [...(prov.models ?? [])].sort(),
|
|
535
542
|
retain: [...(prov.retainModels ?? [])].sort(),
|
|
536
543
|
selected: [...(prov.selectedModels ?? [])].sort(),
|
|
@@ -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
|
}
|