@bitkyc08/opencodex 2.86.0 → 2.87.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/gui/dist/assets/App-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +9 -8
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/passthrough.ts +7 -23
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic.ts +108 -21
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +18 -2
- package/src/adapters/dsh-account.ts +97 -0
- package/src/adapters/google.ts +35 -8
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +4 -2
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +4 -1
- package/src/advisor/context.ts +16 -4
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +79 -31
- package/src/advisor/settings.ts +11 -2
- package/src/bridge/errors.ts +19 -1
- package/src/bridge/internal.ts +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/claude/desktop-3p-library.ts +32 -7
- package/src/claude/desktop-3p.ts +55 -13
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +11 -0
- package/src/claude/inbound.ts +33 -0
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/outbound.ts +38 -13
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +17 -3
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +5 -2
- package/src/cli/capabilities-base.ts +38 -10
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +17 -14
- package/src/cli/dispatch.ts +25 -4
- package/src/cli/doctor.ts +6 -1
- package/src/cli/ensure-desktop-cold-start.ts +60 -7
- package/src/cli/help.ts +3 -2
- package/src/cli/index.ts +1 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +28 -8
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +2 -1
- package/src/clients/config-export.ts +18 -44
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +2 -2
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +9 -1
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +1 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +21 -16
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/parsing.ts +30 -86
- package/src/codex/catalog/provider-models.ts +36 -34
- package/src/codex/catalog/routed-gather.ts +12 -13
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +23 -2
- package/src/codex/desktop-app/picker-freshness.ts +96 -0
- package/src/codex/desktop-app-restart.ts +21 -0
- package/src/codex/desktop-compatibility/installed-build.ts +24 -6
- package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
- package/src/codex/desktop-compatibility/runtime.ts +12 -3
- package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
- package/src/codex/features.ts +85 -45
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/main-account-hard-lock.ts +30 -1
- package/src/codex/native-profile-startup.ts +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +33 -6
- package/src/config/live-reconcile.ts +38 -2
- package/src/config/load-degrade.ts +17 -0
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +52 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +22 -0
- package/src/config/schema/leaf-validators.ts +38 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +608 -284
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +1 -0
- package/src/integrations/registry.ts +8 -0
- package/src/lib/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-upstream-tunnel.ts +18 -53
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +19 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/redact.ts +2 -2
- package/src/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +33 -7
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +184 -109
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- package/src/protocols/encoders/adapter-events.ts +5 -4
- package/src/protocols/encoders/messages.ts +9 -6
- package/src/protocols/provider-summary.ts +2 -1
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/model-rename-migration.ts +2 -2
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +37 -8
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +50 -5
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +24 -2
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/router.ts +8 -321
- package/src/routing/health.ts +37 -22
- package/src/routing/history/indexer.ts +6 -5
- package/src/routing/history/ingest.ts +6 -2
- package/src/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +17 -0
- package/src/server/auth-cors.ts +11 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +55 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +9 -1
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index.ts +1 -1
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/management/advisor-routes.ts +15 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +1 -1
- package/src/server/management/config-routes.ts +75 -4
- package/src/server/management/cursor-integration-routes.ts +1 -1
- package/src/server/management/model-routes.ts +11 -6
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +10 -1
- package/src/server/management-api.ts +3 -1
- package/src/server/messages-native-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +101 -21
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +6 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +13 -1
- package/src/server/responses/adapter-delivery.ts +19 -6
- package/src/server/responses/adapter-dispatch.ts +19 -2
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +2 -0
- package/src/server/responses/core-combo.ts +120 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +5 -2
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +18 -2
- package/src/server/responses/core.ts +2 -115
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +3 -1
- package/src/server/responses/passthrough-dispatch.ts +15 -13
- package/src/server/responses/request-prepare.ts +76 -12
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +104 -27
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +20 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +76 -57
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +72 -9
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/types/config.ts +64 -3
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +29 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +23 -2
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -0
- package/src/update/npm-invocation.mjs +12 -2
- package/src/usage/cost.ts +48 -12
- package/src/usage/expected-prices.ts +3 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +22 -9
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- package/src/web-search/run-turn-loop.ts +9 -1
- package/gui/dist/assets/App-BjlttVLy.js +0 -58
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-BMssP4k3.js +0 -94
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
|
@@ -1,10 +1,9 @@
|
|
|
1
|
+
import { observeSignedInCodexIdentity } from "../signed-in-identity";
|
|
1
2
|
import { lstatSync } from "node:fs";
|
|
2
3
|
import type { OcxConfig } from "../../types";
|
|
3
|
-
import { inspectChatGptDomainClaim, extractEmail } from "../../oauth/chatgpt";
|
|
4
|
-
import { MAIN_CODEX_ACCOUNT_ID, isSelectableCodexPoolAccount } from "../account-id";
|
|
5
4
|
import { readCodexTokensResult } from "../auth-collision";
|
|
6
5
|
import type { CodexTokenReadResult } from "../auth-collision";
|
|
7
|
-
import {
|
|
6
|
+
import { linkedCodexAccountIds } from "../account-identity";
|
|
8
7
|
import { resolveCodexHomeDir } from "../home";
|
|
9
8
|
import { tryAcquireNativeMainProfileClaim } from "../native-main-admission";
|
|
10
9
|
import { withNativeMainSharedClaim } from "../native-main-claim";
|
|
@@ -12,7 +11,6 @@ import { MAX_AUTH_BYTES, readBounded, resolveNativeProfileContext } from "../nat
|
|
|
12
11
|
import { NativeProfileError } from "../native-profile-types";
|
|
13
12
|
import { isNativeMainClaimUnavailable, nativeMainProfileBusyResponse } from "./http";
|
|
14
13
|
|
|
15
|
-
type AccountIdentity = { accountId: string; email: string };
|
|
16
14
|
type MainAuth = CodexTokenReadResult | { status: "api-key-only" };
|
|
17
15
|
|
|
18
16
|
/** A valid API-key login has no ChatGPT identity; anything else unreadable stays unknown. */
|
|
@@ -32,50 +30,6 @@ function readMainAuth(authPath: string): MainAuth {
|
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
32
|
|
|
35
|
-
function identity(accountId: unknown, email: unknown): AccountIdentity | undefined {
|
|
36
|
-
const normalizedEmail = typeof email === "string" ? email.trim().toLowerCase() : undefined;
|
|
37
|
-
return typeof accountId === "string" && accountId.trim() && normalizedEmail
|
|
38
|
-
? { accountId: accountId.trim(), email: normalizedEmail }
|
|
39
|
-
: undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Resolve only existing entries, under native-main ownership, never on the request path. */
|
|
43
|
-
function linkedAccountIds(config: OcxConfig, selectedId: string, main: MainAuth): string[] | undefined {
|
|
44
|
-
// Unknown main identity must not produce a successful but incomplete pause.
|
|
45
|
-
if (main.status === "unreadable" || main.status === "invalid") return undefined;
|
|
46
|
-
const identities = new Map<string, AccountIdentity | undefined>();
|
|
47
|
-
if (main.status === "ok") {
|
|
48
|
-
const tokens = main.tokens;
|
|
49
|
-
if (typeof tokens.access_token !== "string" || typeof tokens.account_id !== "string"
|
|
50
|
-
|| (tokens.id_token !== undefined && typeof tokens.id_token !== "string")) return undefined;
|
|
51
|
-
const claims = [tokens.id_token, tokens.access_token]
|
|
52
|
-
.filter((token): token is string => typeof token === "string")
|
|
53
|
-
.map(inspectChatGptDomainClaim);
|
|
54
|
-
if (claims.some(claim => claim.kind === "invalid")) return undefined;
|
|
55
|
-
const claimIds = claims.flatMap(claim => claim.kind === "valid" ? [claim.accountId] : []);
|
|
56
|
-
// organizations[] describes memberships, not the selected workspace. Never use its first row.
|
|
57
|
-
const accountId = tokens.account_id.trim() || claimIds[0];
|
|
58
|
-
if (claimIds.some(claim => claim !== accountId)) return undefined;
|
|
59
|
-
// Both tokens must name the same member; otherwise the bearer's identity is unknown.
|
|
60
|
-
const emails = new Set([extractEmail(tokens.id_token), extractEmail(undefined, tokens.access_token)]
|
|
61
|
-
.flatMap(email => email?.trim() ? [email.trim().toLowerCase()] : []));
|
|
62
|
-
if (emails.size > 1) return undefined;
|
|
63
|
-
identities.set(MAIN_CODEX_ACCOUNT_ID, identity(accountId, [...emails][0]));
|
|
64
|
-
}
|
|
65
|
-
for (const account of config.codexAccounts ?? []) {
|
|
66
|
-
if (!isSelectableCodexPoolAccount(account)) continue;
|
|
67
|
-
const credential = getCodexAccountCredential(account.id);
|
|
68
|
-
identities.set(account.id, identity(credential?.chatgptAccountId, account.email));
|
|
69
|
-
}
|
|
70
|
-
const selected = identities.get(selectedId);
|
|
71
|
-
// Missing identity evidence never links unrelated logins, including members of one workspace.
|
|
72
|
-
if (!selected) return [selectedId];
|
|
73
|
-
return [selectedId, ...[...identities].flatMap(([id, candidate]) => (
|
|
74
|
-
id !== selectedId && candidate?.accountId === selected.accountId && candidate.email === selected.email
|
|
75
|
-
? [id] : []
|
|
76
|
-
))];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
33
|
/** Hold admission throughout; claim the physical main whenever its home exists. */
|
|
80
34
|
export async function withCodexAccountPauseGroup(
|
|
81
35
|
config: OcxConfig,
|
|
@@ -92,13 +46,15 @@ export async function withCodexAccountPauseGroup(
|
|
|
92
46
|
// Only positive absence permits Pool-only publication. A dangling symlink or access
|
|
93
47
|
// failure is not absence; do not create a home just to coordinate an unused main login.
|
|
94
48
|
if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") return nativeMainProfileBusyResponse();
|
|
95
|
-
const ids =
|
|
49
|
+
const ids = linkedCodexAccountIds(config, selectedId, { status: "missing" });
|
|
50
|
+
if (ids) observeSignedInCodexIdentity();
|
|
96
51
|
return ids ? publish(ids) : nativeMainProfileBusyResponse();
|
|
97
52
|
}
|
|
98
53
|
const context = resolveNativeProfileContext({ codexHome });
|
|
99
54
|
return await withNativeMainSharedClaim(context, async () => {
|
|
100
55
|
const main = readMainAuth(context.authPath);
|
|
101
|
-
const ids =
|
|
56
|
+
const ids = linkedCodexAccountIds(config, selectedId, main);
|
|
57
|
+
if (ids) observeSignedInCodexIdentity(main.status === "ok" ? main.tokens : undefined);
|
|
102
58
|
return ids ? publish(ids) : nativeMainProfileBusyResponse();
|
|
103
59
|
});
|
|
104
60
|
} catch (error) {
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { getSignedInCodexIdentity } from "../signed-in-identity";
|
|
2
|
+
import type { OcxConfig } from "../../types";
|
|
3
|
+
import { captureConfigTopLevelRollback } from "../../config/rebase-provenance";
|
|
4
|
+
import { clearCodexAccountPin, pinnedCodexAccountId } from "../account-priority";
|
|
5
|
+
import { clearThreadAccountMapForAccount, previewCodexAccountForRequest } from "../routing";
|
|
6
|
+
import { forgetManualPreference } from "../routing/active-account";
|
|
7
|
+
import { withCodexAccountPauseGroup } from "./account-pause-group";
|
|
8
|
+
import { MAIN_CODEX_ACCOUNT_ID } from "../account-id";
|
|
9
|
+
import { getRuntimeConfig, saveRuntimeConfig } from "./runtime-config";
|
|
10
|
+
import { jsonResponse } from "./http";
|
|
11
|
+
|
|
12
|
+
/** Management owns the native-main claim; publish before releasing any runtime bindings. */
|
|
13
|
+
export async function handleCodexAccountPreservation(req: Request, config: OcxConfig): Promise<Response> {
|
|
14
|
+
let update: { enabled: boolean; applyToBound: boolean } | undefined;
|
|
15
|
+
if (req.method === "PUT") {
|
|
16
|
+
const body: unknown = await req.json().catch(() => null);
|
|
17
|
+
if (!body || typeof body !== "object" || Array.isArray(body)
|
|
18
|
+
|| !("enabled" in body) || typeof body.enabled !== "boolean"
|
|
19
|
+
|| Object.keys(body).some(key => key !== "enabled" && key !== "applyToBound")
|
|
20
|
+
|| ("applyToBound" in body && typeof body.applyToBound !== "boolean")) {
|
|
21
|
+
return jsonResponse({ error: "enabled and optional applyToBound must be booleans" }, 400);
|
|
22
|
+
}
|
|
23
|
+
const applyToBound = "applyToBound" in body && body.applyToBound === true;
|
|
24
|
+
if (applyToBound && !body.enabled) return jsonResponse({ error: "applyToBound requires enabled: true" }, 400);
|
|
25
|
+
update = { enabled: body.enabled, applyToBound };
|
|
26
|
+
}
|
|
27
|
+
const runtime = getRuntimeConfig(config);
|
|
28
|
+
return withCodexAccountPauseGroup(runtime, MAIN_CODEX_ACCOUNT_ID, affectedAccountIds => {
|
|
29
|
+
const releasedPin = update?.applyToBound === true && affectedAccountIds.includes(pinnedCodexAccountId(runtime) ?? "");
|
|
30
|
+
if (update) {
|
|
31
|
+
const rollback = captureConfigTopLevelRollback(runtime, ["codexPreserveSignedInAccount", "activeCodexAccountPinned"]);
|
|
32
|
+
try {
|
|
33
|
+
runtime.codexPreserveSignedInAccount = update.enabled;
|
|
34
|
+
if (releasedPin) clearCodexAccountPin(runtime);
|
|
35
|
+
saveRuntimeConfig(config, runtime);
|
|
36
|
+
} catch (error) { rollback(); throw error; }
|
|
37
|
+
if (update.applyToBound) {
|
|
38
|
+
for (const id of affectedAccountIds) {
|
|
39
|
+
clearThreadAccountMapForAccount(id);
|
|
40
|
+
forgetManualPreference(id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const enabled = runtime.codexPreserveSignedInAccount === true;
|
|
45
|
+
const nextAccountId = previewCodexAccountForRequest(null, runtime);
|
|
46
|
+
return jsonResponse({
|
|
47
|
+
ok: true, enabled, affectedAccountIds, identityKnown: getSignedInCodexIdentity() !== undefined,
|
|
48
|
+
nextAccountId,
|
|
49
|
+
nextUsesSignedInQuota: nextAccountId !== null && affectedAccountIds.includes(nextAccountId),
|
|
50
|
+
pinnedAccountId: pinnedCodexAccountId(runtime) ?? null,
|
|
51
|
+
bindings: update?.applyToBound ? "released-for-next-request" : "retained",
|
|
52
|
+
releasedPin,
|
|
53
|
+
priorityFailbackEnabled: runtime.codexAccountPriorityFailback === true,
|
|
54
|
+
fallbackConsumesSignedInQuota: true,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { handleCodexAccountPreservation } from "./account-preservation";
|
|
1
2
|
import { persistCodexAccountSelection } from "./account-selection";
|
|
2
3
|
import { CODEX_ACCOUNT_LOG_LABEL_RE, codexAccountLogLabel } from "../account-label";
|
|
4
|
+
import { handleUseRemainingQuotaRoute } from "./use-remaining-route";
|
|
3
5
|
import { poolQuotaHistoryIdentity, readCodexAccountRecord } from "../account-store";
|
|
4
6
|
import { estimateCodexQuotaCapacity, insufficientCodexCapacity } from "../quota-capacity";
|
|
5
7
|
import type { CodexCapacityResult } from "../quota-capacity";
|
|
@@ -34,6 +36,12 @@ export async function handleCodexAuthAPI(
|
|
|
34
36
|
convergeCodexCatalog?: CodexAuthCatalogConvergence,
|
|
35
37
|
principal?: import("../../server/management-auth").ManagementPrincipal,
|
|
36
38
|
): Promise<Response | null> {
|
|
39
|
+
if (url.pathname === "/api/codex-auth/preserve-signed-in" && (req.method === "GET" || req.method === "PUT")) {
|
|
40
|
+
return handleCodexAccountPreservation(req, config);
|
|
41
|
+
}
|
|
42
|
+
if (url.pathname === "/api/codex-auth/accounts/use-remaining" && (req.method === "GET" || req.method === "PUT")) {
|
|
43
|
+
return handleUseRemainingQuotaRoute(req, url, config);
|
|
44
|
+
}
|
|
37
45
|
if (url.pathname === "/api/codex-auth/accounts" && req.method === "GET") {
|
|
38
46
|
const forceRefresh = url.searchParams.get("refresh") === "1" || url.searchParams.get("refresh") === "true";
|
|
39
47
|
return jsonResponse({ accounts: await listCodexAuthAccounts(config, forceRefresh) });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { OcxConfig } from "../../types";
|
|
2
|
+
import { captureConfigTopLevelRollback } from "../../config/rebase-provenance";
|
|
3
|
+
import { MAIN_CODEX_ACCOUNT_ID, isValidCodexAccountId } from "../account-id";
|
|
4
|
+
import { getEffectiveCodexAutoSwitchThreshold } from "../account-auto-switch";
|
|
5
|
+
import { codexAccountUsesRemainingQuota, setCodexAccountUseRemainingQuota } from "../account-use-remaining";
|
|
6
|
+
import { getMainAccountHardLockStatus } from "../main-account-hard-lock";
|
|
7
|
+
import { configuredPoolAccount, getRuntimeConfig, saveRuntimeConfig } from "./runtime-config";
|
|
8
|
+
import { jsonResponse } from "./http";
|
|
9
|
+
|
|
10
|
+
/** Called only behind the existing Codex management authentication boundary. */
|
|
11
|
+
export async function handleUseRemainingQuotaRoute(req: Request, url: URL, config: OcxConfig): Promise<Response> {
|
|
12
|
+
const parsed: unknown = req.method === "PUT" ? await req.json().catch(() => null) : { id: url.searchParams.get("id") };
|
|
13
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return jsonResponse({ error: "body must be an object" }, 400);
|
|
14
|
+
const body = parsed as { id?: unknown; useRemainingQuota?: unknown };
|
|
15
|
+
if (typeof body.id !== "string" || (body.id !== MAIN_CODEX_ACCOUNT_ID && !isValidCodexAccountId(body.id))) {
|
|
16
|
+
return jsonResponse({ error: "Invalid account id format" }, 400);
|
|
17
|
+
}
|
|
18
|
+
const runtime = getRuntimeConfig(config);
|
|
19
|
+
if (body.id !== MAIN_CODEX_ACCOUNT_ID && !configuredPoolAccount(runtime, body.id)) return jsonResponse({ error: "Account not found" }, 404);
|
|
20
|
+
if (req.method === "PUT") {
|
|
21
|
+
if (typeof body.useRemainingQuota !== "boolean") return jsonResponse({ error: "useRemainingQuota must be a boolean" }, 400);
|
|
22
|
+
const rollback = captureConfigTopLevelRollback(runtime, ["codexUseRemainingQuotaAccountIds"]);
|
|
23
|
+
try {
|
|
24
|
+
setCodexAccountUseRemainingQuota(runtime, body.id, body.useRemainingQuota);
|
|
25
|
+
saveRuntimeConfig(config, runtime);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
rollback();
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return jsonResponse({ ok: true, id: body.id,
|
|
32
|
+
useRemainingQuota: codexAccountUsesRemainingQuota(runtime, body.id),
|
|
33
|
+
autoSwitchThreshold: getEffectiveCodexAutoSwitchThreshold(runtime, body.id),
|
|
34
|
+
...(body.id === MAIN_CODEX_ACCOUNT_ID ? { mainAccountHardLock: getMainAccountHardLockStatus(runtime) } : {}),
|
|
35
|
+
});
|
|
36
|
+
}
|
package/src/codex/auth-api.ts
CHANGED
|
@@ -33,7 +33,6 @@ export {
|
|
|
33
33
|
} from "./auth-api/pool-mode-gate";
|
|
34
34
|
export { createResetCreditWhamClient } from "./auth-api/reset-credit-service";
|
|
35
35
|
export type { CodexAuthCatalogConvergence } from "./auth-api/login-flow";
|
|
36
|
-
export { handleCodexAuthAPI } from "./auth-api/routes";
|
|
37
36
|
import { getEffectiveActiveCodexAccountId } from "./routing";
|
|
38
37
|
import { MAIN_CODEX_ACCOUNT_ID } from "./main-account";
|
|
39
38
|
import type { OcxConfig } from "../types";
|
|
@@ -632,7 +632,7 @@ export class CodexRecoveryWithheldError extends CodexAccountCooldownError {
|
|
|
632
632
|
|
|
633
633
|
export type CodexAuthPolicyConfig = Readonly<Pick<OcxConfig,
|
|
634
634
|
"codexMainAccountHardLock" | "codexMainAccountHardLockThresholds" | "codexDesktopAuthless" | "runtimeRole" | "pausedCodexAccountIds"
|
|
635
|
-
| "creditCodexAccountIds" | "codexAccounts"
|
|
635
|
+
| "creditCodexAccountIds" | "codexAccounts" | "codexUseRemainingQuotaAccountIds"
|
|
636
636
|
>>;
|
|
637
637
|
|
|
638
638
|
interface CodexAuthMaterializationOptions {
|
|
@@ -783,7 +783,7 @@ function mainCreditsHoldResetAt(config: Pick<OcxConfig, "creditCodexAccountIds">
|
|
|
783
783
|
}
|
|
784
784
|
|
|
785
785
|
function assertMainAccountPolicy(
|
|
786
|
-
config: Pick<OcxConfig, "codexMainAccountHardLock" | "codexMainAccountHardLockThresholds" | "creditCodexAccountIds"> | undefined,
|
|
786
|
+
config: Pick<OcxConfig, "codexMainAccountHardLock" | "codexMainAccountHardLockThresholds" | "creditCodexAccountIds" | "codexUseRemainingQuotaAccountIds"> | undefined,
|
|
787
787
|
): void {
|
|
788
788
|
if (config) {
|
|
789
789
|
const status = getMainAccountHardLockStatus(config);
|
|
@@ -480,20 +480,37 @@ function configHasProviderAutoReview(config: Pick<OcxConfig, "providers">): bool
|
|
|
480
480
|
provider.autoReviewModel !== undefined || provider.autoReviewModelOverrides !== undefined);
|
|
481
481
|
}
|
|
482
482
|
|
|
483
|
+
/**
|
|
484
|
+
* The global override selector, when the operator enabled it and named a model.
|
|
485
|
+
*
|
|
486
|
+
* Occupies the same slot as Codex's root `auto_review_model` fallback and wins over it: an
|
|
487
|
+
* enabled Model-settings choice is the operator's explicit pick, while a disabled or blank block
|
|
488
|
+
* leaves the root selector in charge. Provider-scoped stamps keep their precedence separately.
|
|
489
|
+
*/
|
|
490
|
+
export function resolveAutoReviewOverrideSelector(
|
|
491
|
+
config?: Pick<OcxConfig, "autoReviewOverride">,
|
|
492
|
+
): string | undefined {
|
|
493
|
+
const block = config?.autoReviewOverride;
|
|
494
|
+
if (block?.enabled !== true) return undefined;
|
|
495
|
+
const model = typeof block.model === "string" ? block.model.trim() : "";
|
|
496
|
+
return model.length > 0 ? model : undefined;
|
|
497
|
+
}
|
|
498
|
+
|
|
483
499
|
/** Apply the root Codex auto-review selector after the final catalog merge. */
|
|
484
500
|
export function finalizeAutoReviewModelOverride(
|
|
485
501
|
models: RawEntry[] | undefined,
|
|
486
502
|
sourceModels: readonly RawEntry[] = [],
|
|
487
|
-
config?: Pick<OcxConfig, "providers">,
|
|
503
|
+
config?: Pick<OcxConfig, "providers" | "autoReviewOverride">,
|
|
488
504
|
): AutoReviewModelOverrideResult {
|
|
489
505
|
if (models && sourceModels.length > 0) preserveNativeAutoReviewModelOverrides(
|
|
490
506
|
models.filter(entry => !isCodexControlPlaneModel(entry.slug)),
|
|
491
507
|
sourceModels.filter(entry => !isCodexControlPlaneModel(entry.slug)),
|
|
492
508
|
);
|
|
509
|
+
const rootValue = resolveAutoReviewOverrideSelector(config) ?? readConfiguredAutoReviewModel();
|
|
493
510
|
if (config && configHasProviderAutoReview(config)) {
|
|
494
|
-
return applyConfiguredAutoReviewModelOverride(models,
|
|
511
|
+
return applyConfiguredAutoReviewModelOverride(models, rootValue, config, sourceModels);
|
|
495
512
|
}
|
|
496
|
-
return applyAutoReviewModelOverride(models,
|
|
513
|
+
return applyAutoReviewModelOverride(models, rootValue, sourceModels);
|
|
497
514
|
}
|
|
498
515
|
/**
|
|
499
516
|
* Why an account-gated native model stopped being offered, but only when the answer is one the
|
|
@@ -48,7 +48,7 @@ import { NATIVE_RESERVE_MODEL } from "./native-models";
|
|
|
48
48
|
import { isReserveCatalogProjection, type ReserveCatalogProjection } from "./reserve";
|
|
49
49
|
import { deriveEntry, finishUpstreamNativeEntry, isExactComboCatalogEntry } from "./derive-entry";
|
|
50
50
|
import { PICKER_ORDER_PRIORITY_BASE, SPAWN_PRIORITY_FIELD } from "./subagent-roster";
|
|
51
|
-
import { applyConfiguredUltraFast } from "./configured-tiers";
|
|
51
|
+
import { applyConfiguredUltraFast, configuredUltraFastSlugs } from "./configured-tiers";
|
|
52
52
|
import { applyNativeUltraFastTier } from "./native-ultrafast";
|
|
53
53
|
|
|
54
54
|
export interface ObservedCatalogEntryBuildInput {
|
|
@@ -111,6 +111,14 @@ export function buildCatalogEntries(
|
|
|
111
111
|
accountNativeSlugs,
|
|
112
112
|
accountNativeSlugsBySelector,
|
|
113
113
|
});
|
|
114
|
+
// Live discovery does not run the disk merge; use the same captured declarations and row fences.
|
|
115
|
+
const configured = configuredUltraFastSlugs(goModels);
|
|
116
|
+
for (const entry of entries) {
|
|
117
|
+
if (typeof entry.slug !== "string" || !entry.slug.includes("/")
|
|
118
|
+
|| isNativeAliasCatalogEntry(entry) || isExactComboCatalogEntry(entry, exactComboSlugs)
|
|
119
|
+
|| trustedAccountBoundNativeCatalogSlug(entry) !== undefined) continue;
|
|
120
|
+
applyConfiguredUltraFast(entry, undefined, configured.has(entry.slug), false);
|
|
121
|
+
}
|
|
114
122
|
applyFullModelPickerOrder(entries, modelPickerOrder);
|
|
115
123
|
return entries;
|
|
116
124
|
}
|
|
@@ -100,7 +100,7 @@ import type {
|
|
|
100
100
|
CatalogSourceEvidence,
|
|
101
101
|
CatalogTrustedOpenAiApiPolicySnapshot,
|
|
102
102
|
} from "../convergence-types";
|
|
103
|
-
import { applyProviderConfigHints, configuredAutoCompactTokenLimit, positiveSafeInteger } from "./model-hints";
|
|
103
|
+
import { applyProviderConfigHints, configuredAutoCompactTokenLimit, positiveSafeInteger, withVisionSidecarInput } from "./model-hints";
|
|
104
104
|
|
|
105
105
|
/** Model ids each provider must retain for combo catalog derivation (OCX-111). */
|
|
106
106
|
export function configuredComboTargetModelsByProvider(
|
|
@@ -219,6 +219,7 @@ export function resolveComboCatalogMember(
|
|
|
219
219
|
callerFallback?: ComboCatalogMemberFallback,
|
|
220
220
|
metadataModelIdCaseFold?: boolean,
|
|
221
221
|
metadataConfigDir?: string,
|
|
222
|
+
visionSidecarEnabled = true,
|
|
222
223
|
): CatalogModel | undefined {
|
|
223
224
|
const existing = memberByKey.get(targetKey(target));
|
|
224
225
|
const prov = providers.get(target.provider);
|
|
@@ -227,6 +228,16 @@ export function resolveComboCatalogMember(
|
|
|
227
228
|
// is unusable for catalog derivation while the provider is off.
|
|
228
229
|
if (prov?.disabled === true) return undefined;
|
|
229
230
|
|
|
231
|
+
/** Preserve custom modality overrides while filling missing provider metadata. */
|
|
232
|
+
const withProviderHints = (model: CatalogModel): CatalogModel => {
|
|
233
|
+
const hinted = prov
|
|
234
|
+
? applyProviderConfigHints(target.provider, prov, model, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir, visionSidecarEnabled)
|
|
235
|
+
: model;
|
|
236
|
+
return model.catalogKind === CODEX_CUSTOM_MODEL_CATALOG_KIND && model.inputModalities !== undefined
|
|
237
|
+
? withVisionSidecarInput({ ...hinted, inputModalities: [...model.inputModalities] }, prov, visionSidecarEnabled)
|
|
238
|
+
: hinted;
|
|
239
|
+
};
|
|
240
|
+
|
|
230
241
|
const withFallbackMetadata = (member: CatalogModel): CatalogModel => {
|
|
231
242
|
const contextWindow = typeof member.contextWindow === "number" && member.contextWindow > 0
|
|
232
243
|
? member.contextWindow
|
|
@@ -276,7 +287,7 @@ export function resolveComboCatalogMember(
|
|
|
276
287
|
// supplies a vision sidecar. Apply the same provider hints used for thin
|
|
277
288
|
// rows before deriving a combo from this complete row.
|
|
278
289
|
const hinted = prov && isModelVisionSidecarConsumer(prov, existing.id)
|
|
279
|
-
?
|
|
290
|
+
? withProviderHints(existing)
|
|
280
291
|
: existing;
|
|
281
292
|
const capped = applyProviderContextCap(hinted.contextWindow, contextCap);
|
|
282
293
|
if (capped === undefined || capped === existing.contextWindow) {
|
|
@@ -298,9 +309,7 @@ export function resolveComboCatalogMember(
|
|
|
298
309
|
id: target.model,
|
|
299
310
|
provider: target.provider,
|
|
300
311
|
};
|
|
301
|
-
const hinted =
|
|
302
|
-
? applyProviderConfigHints(target.provider, prov, base, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir)
|
|
303
|
-
: base;
|
|
312
|
+
const hinted = withProviderHints(base);
|
|
304
313
|
const hintedContext = typeof hinted.contextWindow === "number" && hinted.contextWindow > 0
|
|
305
314
|
? hinted.contextWindow
|
|
306
315
|
: undefined;
|
|
@@ -12,7 +12,7 @@ export function supportsConfiguredUltraFast(policy: ResolvedFastPolicy | undefin
|
|
|
12
12
|
&& policy.fastWire.canonicalToWire.ultrafast === "ultrafast";
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
/** Carry captured capability to
|
|
15
|
+
/** Carry captured capability to catalog projection without treating persisted metadata as authority. */
|
|
16
16
|
export function configuredUltraFastSlugs(models: readonly CatalogModel[]): ReadonlySet<string> {
|
|
17
17
|
const declarations = new Map<string, boolean>();
|
|
18
18
|
for (const model of models) {
|
|
@@ -564,6 +564,7 @@ function providerCatalogFingerprint(name: string, prov: OcxProviderConfig): Reco
|
|
|
564
564
|
};
|
|
565
565
|
}
|
|
566
566
|
|
|
567
|
+
/** Coalesce only gathers with the same model inventory and effective sidecar visibility policy. */
|
|
567
568
|
export function gatherFlightKey(config: OcxConfig): string {
|
|
568
569
|
const providers = Object.entries(config.providers)
|
|
569
570
|
.filter(([, prov]) => prov.disabled !== true)
|
|
@@ -571,6 +572,7 @@ export function gatherFlightKey(config: OcxConfig): string {
|
|
|
571
572
|
.sort((a, b) => String(a.n).localeCompare(String(b.n)));
|
|
572
573
|
const assembly = stableJson({
|
|
573
574
|
providers,
|
|
575
|
+
visionSidecarEnabled: config.visionSidecar?.enabled !== false,
|
|
574
576
|
disabledModels: [...(config.disabledModels ?? [])].sort(),
|
|
575
577
|
combos: config.combos ?? {},
|
|
576
578
|
customModels: (config.customModels ?? []).map((cm) => ({
|
|
@@ -276,6 +276,14 @@ function asLimits(input: NativeContextLimitsInput): NativeContextLimits {
|
|
|
276
276
|
return typeof input === "number" ? { cap: input } : input;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
/** Configured hard ceiling only; an advertised default window is not a user limit. */
|
|
280
|
+
export function nativeOpenAiConfiguredContextLimit(slug: string, input?: NativeContextLimitsInput): number | undefined {
|
|
281
|
+
const limits = asLimits(input);
|
|
282
|
+
const overlay = positiveInt(limits.modelWindows?.[slug]) ?? positiveInt(limits.providerWindow);
|
|
283
|
+
const cap = positiveInt(limits.cap);
|
|
284
|
+
return overlay === undefined ? cap : cap === undefined ? overlay : Math.min(overlay, cap);
|
|
285
|
+
}
|
|
286
|
+
|
|
279
287
|
/** Read both levers out of a config once, for call sites that hold one. */
|
|
280
288
|
export function nativeContextLimits(
|
|
281
289
|
config: Pick<OcxConfig, "providers" | "providerContextCaps">,
|
|
@@ -326,18 +334,12 @@ function longWindowOptInCeiling(slug: string): number | undefined {
|
|
|
326
334
|
|
|
327
335
|
function narrowToLimits(raw: number | undefined, slug: string, input: NativeContextLimitsInput): number | undefined {
|
|
328
336
|
if (raw === undefined) return undefined;
|
|
329
|
-
const
|
|
330
|
-
const overlay = positiveInt(limits.modelWindows?.[slug]) ?? positiveInt(limits.providerWindow);
|
|
331
|
-
const cap = positiveInt(limits.cap);
|
|
337
|
+
const limit = nativeOpenAiConfiguredContextLimit(slug, input);
|
|
332
338
|
const optInCeiling = longWindowOptInCeiling(slug);
|
|
333
339
|
if (optInCeiling !== undefined) {
|
|
334
|
-
|
|
335
|
-
const chosen = overlay ?? cap ?? raw;
|
|
336
|
-
const window = Math.min(chosen, ceiling);
|
|
337
|
-
return overlay !== undefined && cap !== undefined ? Math.min(window, cap) : window;
|
|
340
|
+
return Math.min(limit ?? raw, optInCeiling);
|
|
338
341
|
}
|
|
339
|
-
|
|
340
|
-
return applyProviderContextCap(narrowed, cap) ?? narrowed;
|
|
342
|
+
return applyProviderContextCap(raw, limit);
|
|
341
343
|
}
|
|
342
344
|
|
|
343
345
|
export function nativeOpenAiContextWindow(slug: string, limits?: NativeContextLimitsInput): number | undefined {
|
|
@@ -252,6 +252,17 @@ export function configuredReasoningSummarySupport(prov: OcxProviderConfig | unde
|
|
|
252
252
|
return modelRecordValue(prov.modelReasoningSummaryDelivery, id) !== undefined ? true : undefined;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/** Add attachment metadata at publication time, independently of cached provider evidence. */
|
|
256
|
+
export function withVisionSidecarInput(
|
|
257
|
+
model: CatalogModel,
|
|
258
|
+
provider: OcxProviderConfig | undefined,
|
|
259
|
+
enabled = true,
|
|
260
|
+
): CatalogModel {
|
|
261
|
+
if (!enabled || !provider || !isModelVisionSidecarConsumer(provider, model.id)) return model;
|
|
262
|
+
const modalities = model.inputModalities ?? ["text"];
|
|
263
|
+
return modalities.includes("image") ? model : { ...model, inputModalities: [...modalities, "image"] };
|
|
264
|
+
}
|
|
265
|
+
|
|
255
266
|
/** Reapply configured limits and captured wire capabilities without trusting stale discovery hints. */
|
|
256
267
|
export function applyProviderConfigHints(
|
|
257
268
|
name: string,
|
|
@@ -261,6 +272,7 @@ export function applyProviderConfigHints(
|
|
|
261
272
|
metadataModelIdCaseFold?: boolean,
|
|
262
273
|
effectiveAlias?: string | null,
|
|
263
274
|
metadataConfigDir?: string,
|
|
275
|
+
includeVisionSidecar = true,
|
|
264
276
|
): CatalogModel {
|
|
265
277
|
const staticPolicy = resolveModelPolicy({
|
|
266
278
|
providerName: name,
|
|
@@ -289,18 +301,7 @@ export function applyProviderConfigHints(
|
|
|
289
301
|
// Removing an exact capability row therefore returns this projection to legacy-map inference.
|
|
290
302
|
const resolvedModalities = staticPolicy.model.inputModalities ?? model.inputModalities
|
|
291
303
|
?? (azureMetadata?.input ? modelInputModalities({ id: model.id, input_modalities: azureMetadata.input }, undefined) : undefined);
|
|
292
|
-
|
|
293
|
-
// The shared vision-sidecar consumer predicate keeps catalog advertisement and request-time
|
|
294
|
-
// planning aligned. The catalog must still advertise image input — the Codex app
|
|
295
|
-
// gates attachments client-side on input_modalities, and a text-only entry would block images
|
|
296
|
-
// before the sidecar ever runs ("This model does not support image inputs"). Discovery-derived
|
|
297
|
-
// text-only rows stay untouched: the runtime predicate only reads these two config sources, so
|
|
298
|
-
// it would not convert those.
|
|
299
|
-
const sidecarCovered = isModelVisionSidecarConsumer(prov, model.id);
|
|
300
|
-
if (sidecarCovered) {
|
|
301
|
-
const base = inputModalities ?? model.inputModalities ?? ["text"];
|
|
302
|
-
inputModalities = base.includes("image") ? [...base] : [...base, "image"];
|
|
303
|
-
}
|
|
304
|
+
const inputModalities = resolvedModalities ? [...resolvedModalities] : undefined;
|
|
304
305
|
const reasoningEfforts = configuredReasoningEfforts(prov, model.id);
|
|
305
306
|
const suppressSyntheticMax = modelRecordValue(prov.modelSuppressSyntheticMax, model.id)
|
|
306
307
|
?? (model.antigravityEffortWireModelIds !== undefined);
|
|
@@ -337,7 +338,7 @@ export function applyProviderConfigHints(
|
|
|
337
338
|
?? azureWindow
|
|
338
339
|
?? inferredWindow?.contextWindow
|
|
339
340
|
?? (providerCap !== undefined ? resolveUnknownRoutedContextWindow(providerCap) : undefined);
|
|
340
|
-
const hinted = {
|
|
341
|
+
const hinted = withVisionSidecarInput({
|
|
341
342
|
...modelWithoutServiceTier,
|
|
342
343
|
...(!model.contextWindowInference && previousContextCap !== undefined
|
|
343
344
|
? { contextCap: previousContextCap, contextCapped: previousContextCapped } : {}),
|
|
@@ -368,7 +369,7 @@ export function applyProviderConfigHints(
|
|
|
368
369
|
? { parallelToolCalls: true }
|
|
369
370
|
: {}),
|
|
370
371
|
...(prov.codexToolMode !== undefined ? { codexToolMode: prov.codexToolMode } : {}),
|
|
371
|
-
};
|
|
372
|
+
}, prov, includeVisionSidecar);
|
|
372
373
|
const capped = applyProviderContextCap(hinted.contextWindow, providerCap);
|
|
373
374
|
const withCap = providerCap !== undefined
|
|
374
375
|
? capped !== hinted.contextWindow
|
|
@@ -397,6 +398,7 @@ export function applyProviderConfigHints(
|
|
|
397
398
|
};
|
|
398
399
|
}
|
|
399
400
|
|
|
401
|
+
/** Project a configured row, optionally deferring sidecar attachment metadata until publication. */
|
|
400
402
|
export function catalogHintsFromProviderConfig(
|
|
401
403
|
name: string,
|
|
402
404
|
prov: OcxProviderConfig,
|
|
@@ -405,12 +407,14 @@ export function catalogHintsFromProviderConfig(
|
|
|
405
407
|
metadataModelIdCaseFold?: boolean,
|
|
406
408
|
effectiveAlias?: string | null,
|
|
407
409
|
metadataConfigDir?: string,
|
|
410
|
+
includeVisionSidecar = true,
|
|
408
411
|
): Partial<CatalogModel> {
|
|
409
|
-
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir);
|
|
412
|
+
const hinted = applyProviderConfigHints(name, prov, { id, provider: name }, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir, includeVisionSidecar);
|
|
410
413
|
const { provider: _provider, id: _id, ...hints } = hinted;
|
|
411
414
|
return hints;
|
|
412
415
|
}
|
|
413
416
|
|
|
417
|
+
/** Reapply current hints to cached rows without mutating their provider evidence. */
|
|
414
418
|
export function applyConfigHintsToCachedModels(
|
|
415
419
|
name: string,
|
|
416
420
|
prov: OcxProviderConfig,
|
|
@@ -419,8 +423,9 @@ export function applyConfigHintsToCachedModels(
|
|
|
419
423
|
metadataModelIdCaseFold?: boolean,
|
|
420
424
|
effectiveAlias?: string | null,
|
|
421
425
|
metadataConfigDir?: string,
|
|
426
|
+
includeVisionSidecar = true,
|
|
422
427
|
): CatalogModel[] {
|
|
423
|
-
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir));
|
|
428
|
+
return models.map(model => applyProviderConfigHints(name, prov, model, contextCap, metadataModelIdCaseFold, effectiveAlias, metadataConfigDir, includeVisionSidecar));
|
|
424
429
|
}
|
|
425
430
|
|
|
426
431
|
/** Catalog slugs whose configured model must not gain a missing synthetic max rung. */
|
|
@@ -231,6 +231,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
231
231
|
retainComboTargets?: boolean;
|
|
232
232
|
metadataModelIdCaseFold?: boolean;
|
|
233
233
|
metadataConfigDir?: string;
|
|
234
|
+
includeVisionSidecar?: boolean;
|
|
234
235
|
}): { models: CatalogModel[]; droppedConfiguredIds: string[] } {
|
|
235
236
|
const {
|
|
236
237
|
name,
|
|
@@ -242,6 +243,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
242
243
|
retainComboTargets = true,
|
|
243
244
|
metadataModelIdCaseFold,
|
|
244
245
|
metadataConfigDir,
|
|
246
|
+
includeVisionSidecar,
|
|
245
247
|
} = opts;
|
|
246
248
|
const out = [...opts.models];
|
|
247
249
|
const present = new Set(out.map(model => model.id));
|
|
@@ -250,7 +252,7 @@ export function mergeConfiguredModelsIntoLiveCatalog(opts: {
|
|
|
250
252
|
if (present.has(candidate.id)) continue;
|
|
251
253
|
const dated = out.find(live => isDatedVariantId(live.id, candidate.id));
|
|
252
254
|
if (dated) {
|
|
253
|
-
out.push(applyProviderConfigHints(name, prov, { ...dated, id: candidate.id }, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir));
|
|
255
|
+
out.push(applyProviderConfigHints(name, prov, { ...dated, id: candidate.id }, contextCap, metadataModelIdCaseFold, undefined, metadataConfigDir, includeVisionSidecar));
|
|
254
256
|
present.add(candidate.id);
|
|
255
257
|
continue;
|
|
256
258
|
}
|