@bitkyc08/opencodex 2.42.0 → 2.43.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/AGENTS_INSTALL.md +2 -2
- package/README.md +31 -0
- package/bin/ocx.mjs +10 -1
- package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
- package/gui/dist/assets/index-Djowl68T.js +112 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic-image-codec.ts +304 -0
- package/src/adapters/anthropic-image-normalize.ts +8 -298
- package/src/adapters/anthropic.ts +30 -7
- package/src/adapters/command-code.ts +7 -5
- package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
- package/src/adapters/cursor/images.ts +36 -6
- package/src/adapters/cursor/live-transport.ts +7 -2
- package/src/adapters/cursor/native-exec-desktop.ts +2 -15
- package/src/adapters/cursor/protobuf-request.ts +13 -3
- package/src/adapters/cursor/tool-definitions.ts +5 -670
- package/src/adapters/cursor/tool-guidance.ts +236 -0
- package/src/adapters/cursor/tool-naming.ts +252 -0
- package/src/adapters/cursor/tool-schemas.ts +195 -0
- package/src/adapters/cursor/types.ts +6 -3
- package/src/adapters/exec-tool-result-normalize.ts +1 -1
- package/src/adapters/google-errors.ts +9 -1
- package/src/adapters/google.ts +1 -0
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +216 -26
- package/src/adapters/responses-code-mode.ts +59 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/adapters/xai-schema-analysis.ts +86 -0
- package/src/adapters/xai-tool-schema.ts +2 -87
- package/src/adapters/xai-web-search.ts +1 -1
- package/src/bridge.ts +22 -10
- package/src/chat/inbound.ts +11 -3
- package/src/claude/inbound-content-options.ts +60 -0
- package/src/claude/inbound-model-options.ts +142 -0
- package/src/claude/inbound-records.ts +7 -0
- package/src/claude/inbound.ts +10 -202
- package/src/claude/model-info.ts +45 -0
- package/src/cli/account-auth.ts +21 -6
- package/src/cli/capabilities.ts +13 -2
- package/src/cli/claude.ts +232 -39
- package/src/cli/config-command.ts +9 -1
- package/src/cli/dispatch.ts +5 -1
- package/src/cli/doctor.ts +10 -0
- package/src/cli/effort.ts +372 -0
- package/src/cli/export-command.ts +3 -9
- package/src/cli/help.ts +1 -0
- package/src/cli/index.ts +13 -0
- package/src/cli/init.ts +4 -0
- package/src/cli/model-selection-guidance.ts +30 -0
- package/src/cli/models-runtime.ts +3 -2
- package/src/cli/models.ts +8 -3
- package/src/cli/opencode.ts +4 -1
- package/src/cli/provider-runtime.ts +65 -0
- package/src/cli/provider.ts +8 -0
- package/src/cli/registry.ts +16 -2
- package/src/cli/runtime-api.ts +3 -1
- package/src/cli/star-prompt.ts +22 -6
- package/src/cli/status-probes.ts +168 -0
- package/src/cli/status.ts +5 -168
- package/src/clients/config-export/constants.ts +69 -0
- package/src/clients/config-export/contracts.ts +154 -0
- package/src/clients/config-export/dsh.ts +132 -0
- package/src/clients/config-export/fast-models.ts +29 -0
- package/src/clients/config-export/mcode.ts +83 -0
- package/src/clients/config-export/model-metadata.ts +108 -0
- package/src/clients/config-export/omp.ts +104 -0
- package/src/clients/config-export/zcode.ts +92 -0
- package/src/clients/config-export.ts +18 -710
- package/src/codex/account-lifecycle.ts +20 -3
- package/src/codex/account-usability.ts +2 -0
- package/src/codex/auth-api.ts +170 -23
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +30 -4
- package/src/codex/catalog/metadata.ts +39 -9
- package/src/codex/catalog/native-models.ts +53 -12
- package/src/codex/catalog/parsing.ts +119 -5
- package/src/codex/catalog/provider-fetch.ts +150 -20
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +1 -1
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +3 -28
- package/src/codex/legacy-config-keys.ts +68 -0
- package/src/codex/log-guard/inspect-schema.ts +137 -0
- package/src/codex/log-guard/inspect.ts +2 -134
- package/src/codex/loopback-target.ts +54 -0
- package/src/codex/main-account-cache.ts +63 -1
- package/src/codex/main-account-hard-lock.ts +52 -0
- package/src/codex/main-account.ts +3 -1
- package/src/codex/management-convergence.ts +3 -0
- package/src/codex/model-entitlements.ts +54 -4
- package/src/codex/project-config-warnings.ts +92 -2
- package/src/codex/prompt-layers/encoding.ts +80 -0
- package/src/codex/prompt-layers/paths.ts +54 -0
- package/src/codex/prompt-layers/revision.ts +55 -0
- package/src/codex/prompt-layers/toml-edit.ts +163 -0
- package/src/codex/prompt-layers/toml-read.ts +181 -0
- package/src/codex/prompt-layers.ts +14 -520
- package/src/codex/quota-auto-refresh-state.ts +16 -0
- package/src/codex/quota-auto-refresh.ts +219 -0
- package/src/codex/quota-types.ts +51 -0
- package/src/codex/quota.ts +252 -93
- package/src/codex/reserve-availability.ts +177 -0
- package/src/codex/routing.ts +28 -9
- package/src/codex/shim.ts +53 -11
- package/src/codex/subagent-model-fallback.ts +23 -3
- package/src/combos/failover.ts +125 -7
- package/src/combos/identifiers.ts +89 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/resolve.ts +80 -9
- package/src/combos/types.ts +20 -93
- package/src/config/subagent-models.ts +24 -0
- package/src/config.ts +156 -13
- package/src/generated/compatibility-version.json +454 -166
- package/src/generated/model-metadata.ts +1 -1
- package/src/integrations/journal.ts +65 -4
- package/src/integrations/store.ts +5 -0
- package/src/lab/events/limits.ts +4 -0
- package/src/lib/destination-policy.ts +31 -2
- package/src/lib/errors.ts +39 -0
- package/src/lib/provider-outbound.ts +69 -3
- package/src/lib/proxy-env.ts +22 -0
- package/src/lib/redact-folding.ts +176 -0
- package/src/lib/redact.ts +2 -175
- package/src/lib/state-store-sweeper.ts +20 -6
- package/src/lib/token-estimate.ts +94 -27
- package/src/lib/windows-user-principal.ts +53 -5
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +127 -26
- package/src/oauth/login-cli.ts +5 -0
- package/src/oauth/meta-muse.ts +117 -15
- package/src/oauth/pool-settings-capability.ts +15 -4
- package/src/providers/api-keys.ts +8 -10
- package/src/providers/default-aliases.ts +39 -0
- package/src/providers/derive.ts +10 -2
- package/src/providers/fastwire.ts +36 -7
- package/src/providers/initial-model-selection-runtime.ts +90 -0
- package/src/providers/initial-model-selection.ts +120 -0
- package/src/providers/key-failover.ts +134 -54
- package/src/providers/key-store.ts +11 -1
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery.ts +76 -0
- package/src/providers/model-rename-startup.ts +72 -8
- package/src/providers/openai-sidecar.ts +17 -5
- package/src/providers/openai-tiers-destination.ts +102 -0
- package/src/providers/openai-tiers.ts +2 -99
- package/src/providers/opencode-go-transport.ts +41 -0
- package/src/providers/quota-key-accounts.ts +141 -0
- package/src/providers/quota-types.ts +9 -0
- package/src/providers/quota.ts +535 -104
- package/src/providers/registry.ts +60 -17
- package/src/providers/xai-responses-opt-in.ts +31 -5
- package/src/quota/reset-activation.ts +81 -0
- package/src/quota/reset-detector.ts +305 -0
- package/src/quota/reset-notify-config.ts +162 -0
- package/src/quota/reset-observer.ts +125 -0
- package/src/quota/reset-poller.ts +160 -0
- package/src/quota/reset-seen-store.ts +385 -0
- package/src/quota/reset-sinks.ts +199 -0
- package/src/quota/window-mapping.ts +106 -0
- package/src/responses/apply-patch-envelope.ts +46 -0
- package/src/responses/code-mode-helper-compat.ts +39 -1
- package/src/responses/custom-tool-compat.ts +10 -4
- package/src/responses/hosted-tool-policy.ts +12 -4
- package/src/responses/parser-content.ts +133 -0
- package/src/responses/parser-text-format.ts +24 -0
- package/src/responses/parser-tools.ts +188 -0
- package/src/responses/parser.ts +3 -326
- package/src/responses/state.ts +124 -28
- package/src/router.ts +48 -13
- package/src/routing/analytics.ts +1 -0
- package/src/routing/capability.ts +17 -4
- package/src/server/auth-cors.ts +7 -1
- package/src/server/background-lifecycle.ts +23 -1
- package/src/server/chat-completions.ts +25 -3
- package/src/server/claude-messages.ts +62 -5
- package/src/server/effort-row.ts +1 -1
- package/src/server/fast-row.ts +295 -0
- package/src/server/gui-static.ts +30 -4
- package/src/server/index.ts +107 -21
- package/src/server/management/agent-settings-routes.ts +2 -2
- package/src/server/management/combo-routes.ts +37 -9
- package/src/server/management/config-routes.ts +93 -2
- package/src/server/management/integration-routes.ts +108 -0
- package/src/server/management/model-routes.ts +13 -3
- package/src/server/management/model-rows.ts +20 -1
- package/src/server/management/native-integration-routes.ts +4 -1
- package/src/server/management/oauth-account-routes.ts +38 -10
- package/src/server/management/provider-routes.ts +34 -3
- package/src/server/management/quota-reset-routes.ts +57 -0
- package/src/server/management/route-registry.ts +7 -4
- package/src/server/management/shared.ts +19 -5
- package/src/server/management/system-routes.ts +3 -2
- package/src/server/management-api.ts +14 -2
- package/src/server/ports.ts +12 -2
- package/src/server/relay-eager.ts +38 -23
- package/src/server/relay.ts +4 -0
- package/src/server/request-log.ts +6 -0
- package/src/server/responses/codex-ws-correlation.ts +65 -0
- package/src/server/responses/codex-ws-exchange.ts +261 -0
- package/src/server/responses/codex-ws-metadata.ts +134 -0
- package/src/server/responses/codex-ws-pool.ts +162 -0
- package/src/server/responses/codex-ws-request.ts +87 -0
- package/src/server/responses/codex-ws-session.ts +93 -0
- package/src/server/responses/codex-ws-wire.ts +144 -0
- package/src/server/responses/collaboration.ts +41 -1
- package/src/server/responses/compact.ts +105 -12
- package/src/server/responses/core.ts +476 -57
- package/src/server/responses/fetch-helpers.ts +10 -3
- package/src/server/responses/input-admission.ts +16 -9
- package/src/server/responses/responses-field-backfill.ts +1 -1
- package/src/server/responses/ws-upstream.ts +34 -318
- package/src/server/responses-custom-tool-repair.ts +20 -4
- package/src/server/responses-undeclared-tool-guard.ts +100 -8
- package/src/server/safe-response-headers.ts +23 -0
- package/src/server/search.ts +9 -0
- package/src/server/subagent-models-startup.ts +27 -0
- package/src/server/system-env-shell.ts +238 -0
- package/src/server/system-env.ts +7 -234
- package/src/server/ws-bridge.ts +3 -25
- package/src/server/xai-responses-startup.ts +21 -0
- package/src/service-manager-probe.ts +1 -1
- package/src/service.ts +55 -16
- package/src/types/config.ts +108 -12
- package/src/types/provider.ts +36 -7
- package/src/types/tools.ts +26 -1
- package/src/types.ts +2 -0
- package/src/update/notify.ts +8 -2
- package/src/usage/cost.ts +38 -28
- package/src/usage/expected-prices.ts +34 -15
- package/src/usage/log.ts +2 -0
- package/src/usage/model-identity.ts +26 -0
- package/src/usage/summary.ts +15 -1
- package/src/vision/describe.ts +6 -0
- package/src/vision/image-rewrite.ts +108 -0
- package/src/vision/index.ts +19 -306
- package/src/vision/plan.ts +205 -0
- package/src/web-search/executor.ts +6 -0
- package/src/web-search/index.ts +8 -1
- package/gui/dist/assets/index-BU1tE0sr.js +0 -112
- package/gui/dist/assets/index-DL9-iS6J.css +0 -1
|
@@ -38,10 +38,12 @@ import type { RawEntry } from "./parsing";
|
|
|
38
38
|
import { readCurrentCatalogOrCache, readCurrentCodexCatalog, readCurrentCodexModelsCache, unique } from "./bundled";
|
|
39
39
|
import { trustedAccountBoundNativeCatalogSlug, visibleCodexAccountSelectors } from "./account-models";
|
|
40
40
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
41
|
+
import { RESERVE_METADATA_SOURCE_FIELD } from "./reserve";
|
|
41
42
|
import {
|
|
42
43
|
ACCOUNT_GATED_NATIVE_OPENAI_MODELS,
|
|
43
44
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
44
45
|
NATIVE_GPT6_ASTRA_MODEL,
|
|
46
|
+
NATIVE_RESERVE_MODEL,
|
|
45
47
|
NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS,
|
|
46
48
|
NATIVE_OPENAI_MODELS,
|
|
47
49
|
SELF_DESCRIBED_NATIVE_OPENAI_MODELS,
|
|
@@ -70,10 +72,7 @@ export {
|
|
|
70
72
|
export const DOCUMENTED_NATIVE_OPENAI_ADDITIONS = [
|
|
71
73
|
"gpt-5.3-codex-spark",
|
|
72
74
|
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna",
|
|
73
|
-
//
|
|
74
|
-
// entry an install WITH a live catalog would drop the row that native-models.ts deliberately
|
|
75
|
-
// ungated. Listing it here keeps the bare slug reachable so a request actually dispatches and
|
|
76
|
-
// reports the upstream status.
|
|
75
|
+
// The shipped pin also backfills older installed Codex catalogs that predate Astra.
|
|
77
76
|
NATIVE_GPT6_ASTRA_MODEL,
|
|
78
77
|
];
|
|
79
78
|
|
|
@@ -671,7 +670,7 @@ function isAccountBoundOpenAiNativeSlug(slug: string): boolean {
|
|
|
671
670
|
* malformed and minimal hand-written rows. It cannot distinguish a genuine upstream observation
|
|
672
671
|
* from a complete row typed by hand into `$CODEX_HOME/models_cache.json`: there is no signature,
|
|
673
672
|
* source identity, or server attestation to check. A full-shape forged row is accepted, and
|
|
674
|
-
* `observedFullShapeRowIsAccepted` in tests/native-model-toggle.test.ts pins that so nobody
|
|
673
|
+
* `observedFullShapeRowIsAccepted` in tests/codex-integration/native-model-toggle.test.ts pins that so nobody
|
|
675
674
|
* later mistakes this predicate for a security boundary.
|
|
676
675
|
*
|
|
677
676
|
* That is acceptable here because the file is user-owned and written by Codex itself: anyone
|
|
@@ -700,14 +699,44 @@ function observedAccountBoundNativeSlug(entry: RawEntry): string | undefined {
|
|
|
700
699
|
const accountBound = trustedAccountBoundNativeCatalogSlug(entry);
|
|
701
700
|
const slug = accountBound ?? (typeof entry.slug === "string" ? entry.slug : "");
|
|
702
701
|
if (!isAccountBoundOpenAiNativeSlug(slug)
|
|
703
|
-
|| entry.supported_in_api !== true
|
|
702
|
+
|| (entry.supported_in_api !== true && !(slug === NATIVE_RESERVE_MODEL && entry.supported_in_api === false))
|
|
703
|
+
|| (slug === NATIVE_RESERVE_MODEL && entry[RESERVE_METADATA_SOURCE_FIELD] !== undefined
|
|
704
|
+
&& entry[RESERVE_METADATA_SOURCE_FIELD] !== NATIVE_RESERVE_MODEL)
|
|
704
705
|
|| !hasNativeCatalogRowShape(entry)
|
|
705
|
-
|| (entry.visibility !== "list" && entry[ACCOUNT_BOUND_OBSERVED_NATIVE_MARKER] !== true
|
|
706
|
+
|| (entry.visibility !== "list" && entry[ACCOUNT_BOUND_OBSERVED_NATIVE_MARKER] !== true
|
|
707
|
+
&& !(slug === NATIVE_RESERVE_MODEL && entry.visibility === "hide"))) {
|
|
706
708
|
return undefined;
|
|
707
709
|
}
|
|
708
710
|
return slug;
|
|
709
711
|
}
|
|
710
712
|
|
|
713
|
+
/** Prefer a genuine bare observation; an adapted OCX row must never become native evidence. */
|
|
714
|
+
export function observedReserveCatalogSource(
|
|
715
|
+
entries: readonly RawEntry[],
|
|
716
|
+
mainSelectors: readonly string[],
|
|
717
|
+
): RawEntry | null {
|
|
718
|
+
const actual = entries.filter(entry => observedAccountBoundNativeSlug(entry) === NATIVE_RESERVE_MODEL);
|
|
719
|
+
const bare = actual.find(entry => entry.slug === NATIVE_RESERVE_MODEL);
|
|
720
|
+
const qualified = actual.find(entry => entry[RESERVE_METADATA_SOURCE_FIELD] === NATIVE_RESERVE_MODEL
|
|
721
|
+
&& typeof entry.slug === "string"
|
|
722
|
+
&& mainSelectors.includes(entry.slug.slice(0, entry.slug.indexOf("/"))));
|
|
723
|
+
const selected = bare ?? qualified;
|
|
724
|
+
if (!selected) return null;
|
|
725
|
+
const source = structuredClone(selected);
|
|
726
|
+
if (!bare) {
|
|
727
|
+
const prefix = `${String(source.slug).split("/")[0]} / `;
|
|
728
|
+
if (typeof source.display_name === "string" && source.display_name.startsWith(prefix)) {
|
|
729
|
+
source.display_name = source.display_name.slice(prefix.length);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
source.slug = NATIVE_RESERVE_MODEL;
|
|
733
|
+
delete source.opencodex_catalog_kind;
|
|
734
|
+
delete source[RESERVE_METADATA_SOURCE_FIELD];
|
|
735
|
+
delete source[ACCOUNT_BOUND_OBSERVED_NATIVE_MARKER];
|
|
736
|
+
delete source[ACCOUNT_BOUND_OBSERVED_SELECTORS_MARKER];
|
|
737
|
+
return source;
|
|
738
|
+
}
|
|
739
|
+
|
|
711
740
|
/**
|
|
712
741
|
* Return exact, previously observed account-native rows that are not in the static release set.
|
|
713
742
|
* The result is used only to carry a hidden observation across startup cache invalidation.
|
|
@@ -748,7 +777,8 @@ export function accountBoundNativeOpenAiSlugs(
|
|
|
748
777
|
): string[] {
|
|
749
778
|
const observed = observedEntries.flatMap(entry => {
|
|
750
779
|
const slug = observedAccountBoundNativeSlug(entry);
|
|
751
|
-
|
|
780
|
+
// Reserve belongs only to the Codex-specific opt-in builder, not generic native exports.
|
|
781
|
+
return slug === undefined || slug === NATIVE_RESERVE_MODEL ? [] : [slug];
|
|
752
782
|
});
|
|
753
783
|
return unique([...NATIVE_OPENAI_MODELS, ...observed]);
|
|
754
784
|
}
|
|
@@ -775,7 +805,7 @@ export function accountBoundNativeOpenAiSlugsBySelector(
|
|
|
775
805
|
);
|
|
776
806
|
for (const entry of observedEntries) {
|
|
777
807
|
const slug = observedAccountBoundNativeSlug(entry);
|
|
778
|
-
if (slug === undefined || SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug)) continue;
|
|
808
|
+
if (slug === undefined || slug === NATIVE_RESERVE_MODEL || SUPPORTED_NATIVE_OPENAI_SLUGS.has(slug)) continue;
|
|
779
809
|
const generated = trustedAccountBoundNativeCatalogSlug(entry);
|
|
780
810
|
const generatedSelector = generated === undefined || typeof entry.slug !== "string"
|
|
781
811
|
? undefined
|
|
@@ -1,14 +1,9 @@
|
|
|
1
|
+
/** Reserve wire identity, not a globally available native catalog registration. */
|
|
2
|
+
export const NATIVE_RESERVE_MODEL = "gpt-reserve";
|
|
3
|
+
|
|
1
4
|
/** ChatGPT/Codex wire id observed for the account-native Daybreak Blue surface. */
|
|
2
5
|
export const NATIVE_DAYBREAK_BLUE_MODEL = "gpt-daybreak-blue-latest";
|
|
3
6
|
|
|
4
|
-
/**
|
|
5
|
-
* Leaked Responses API identifier for the announced next-generation OpenAI model
|
|
6
|
-
* (2026-09-03: OpenAI teased the launch on X; community probes report `gpt-6-astra` returning
|
|
7
|
-
* the same 404 as other internal staging slugs where an arbitrary slug returns 400).
|
|
8
|
-
* Registered preemptively so an entitled account can route it the moment it ships, before any
|
|
9
|
-
* codex-rs catalog carries it. Unlike Daybreak it is NOT wire-normalized to a serving id —
|
|
10
|
-
* the leaked slug IS the wire id.
|
|
11
|
-
*/
|
|
12
7
|
/**
|
|
13
8
|
* SHIPPED as of 2026-09-03: openai/codex `ed391d4dd` (#42607, bundled model catalog) and
|
|
14
9
|
* `1f7b99922` (#42619, Amazon Bedrock catalogs). The registration is no longer speculative —
|
|
@@ -27,11 +22,32 @@ export const NATIVE_DAYBREAK_BLUE_MODEL = "gpt-daybreak-blue-latest";
|
|
|
27
22
|
*/
|
|
28
23
|
export const NATIVE_GPT6_ASTRA_MODEL = "gpt-6-astra";
|
|
29
24
|
|
|
30
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* Native ChatGPT/Codex ids whose availability is proven per authenticated account.
|
|
27
|
+
*
|
|
28
|
+
* Membership is expensive: it hides the row from the catalog, `/v1/models`, the dashboard and
|
|
29
|
+
* the desktop projection until an authenticated `/models` roster confirms it, AND it makes
|
|
30
|
+
* `auth-context.ts` refuse the request before it is sent. Both halves fail closed on ABSENCE of
|
|
31
|
+
* evidence, not on a denial.
|
|
32
|
+
*
|
|
33
|
+
* The flagship models are deliberately NOT here (owner decision, 2026-09-04). #3442 made
|
|
34
|
+
* discovery ask upstream under an adequate client version, which guarantees the QUESTION is
|
|
35
|
+
* fair but cannot guarantee an ANSWER: an unconfirmed account, a timed-out fetch, or a shard
|
|
36
|
+
* that has not caught up all produce the same silent disappearance, and a model vanishing from
|
|
37
|
+
* the picker reads as "opencodex lost my model" rather than "upstream did not confirm it".
|
|
38
|
+
* Listing them unconditionally means the request dispatches and the user sees the real upstream
|
|
39
|
+
* status. `disabledModels` remains the visibility lever.
|
|
40
|
+
*
|
|
41
|
+
* The cost, accepted knowingly: Pool routing no longer prefers an account that owns the model,
|
|
42
|
+
* so a multi-account user may take one upstream 400 and one alternate retry where they used to
|
|
43
|
+
* be routed straight to the owner. Nothing unsafe — each account still sends its own credential
|
|
44
|
+
* — and `gpt-6-astra` has shipped this way since 6f634eddc.
|
|
45
|
+
*
|
|
46
|
+
* `gpt-daybreak-blue-latest` stays gated. It has no shipped catalog row anywhere, so absence is
|
|
47
|
+
* the only signal that exists for it, and the ungating decision was scoped to the flagships.
|
|
48
|
+
* Evidence: devlog/_plan/260904_flagship_native_always_visible/.
|
|
49
|
+
*/
|
|
31
50
|
export const ACCOUNT_GATED_NATIVE_OPENAI_MODELS: ReadonlySet<string> = new Set([
|
|
32
|
-
"gpt-5.6-sol",
|
|
33
|
-
"gpt-5.6-terra",
|
|
34
|
-
"gpt-5.6-luna",
|
|
35
51
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
36
52
|
]);
|
|
37
53
|
|
|
@@ -144,3 +160,28 @@ export const NATIVE_OPENAI_MODELS = [
|
|
|
144
160
|
];
|
|
145
161
|
|
|
146
162
|
export const SUPPORTED_NATIVE_OPENAI_SLUGS = new Set(NATIVE_OPENAI_MODELS);
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Natives that retain the physical main account as a read-free sentinel during a native-main
|
|
166
|
+
* drain, instead of reading as unavailable and letting the subagent fallback chain advance.
|
|
167
|
+
*
|
|
168
|
+
* This used to be spelled `ACCOUNT_GATED_NATIVE_OPENAI_MODELS`, which was never what it meant:
|
|
169
|
+
* the sentinel protects the atomic main claim so a routed fallback cannot bypass it, and that
|
|
170
|
+
* has nothing to do with entitlement. The two sets were identical in practice, so the accident
|
|
171
|
+
* went unnoticed until the flagships were ungated (2026-09-04) and the predicate would have
|
|
172
|
+
* flipped false — letting a drain silently rewrite the operator's configured subagent model.
|
|
173
|
+
*
|
|
174
|
+
* It is an explicit list rather than `SUPPORTED_NATIVE_OPENAI_SLUGS`, which would have widened
|
|
175
|
+
* the sentinel to `gpt-5.5`, `gpt-5.4`, `gpt-5.4-mini` and `gpt-5.3-codex-spark` as well. Those
|
|
176
|
+
* models were never covered, and widening would turn "fell back and answered" into a
|
|
177
|
+
* maintenance error for the most commonly configured fallback slug in the repo. Membership is
|
|
178
|
+
* the set the drain behaviour was actually reasoned about: the account-gated natives plus the
|
|
179
|
+
* flagships that just left that set.
|
|
180
|
+
*/
|
|
181
|
+
export const NATIVE_MAIN_DRAIN_SENTINEL_MODELS: ReadonlySet<string> = new Set([
|
|
182
|
+
...ACCOUNT_GATED_NATIVE_OPENAI_MODELS,
|
|
183
|
+
"gpt-5.6-sol",
|
|
184
|
+
"gpt-5.6-terra",
|
|
185
|
+
"gpt-5.6-luna",
|
|
186
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
187
|
+
]);
|
|
@@ -2,7 +2,7 @@ import { execFileSync } from "node:child_process";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { copyFileSync, existsSync, mkdirSync, readFileSync, realpathSync } from "node:fs";
|
|
4
4
|
import { delimiter, dirname, join, resolve } from "node:path";
|
|
5
|
-
import { atomicWriteFile, expandUserPath, getConfigDir, websocketsEnabled } from "../../config";
|
|
5
|
+
import { atomicWriteFile, expandUserPath, getConfigDir, loadConfig, ultraFastTierEnabled, websocketsEnabled } from "../../config";
|
|
6
6
|
import { CODEX_CONFIG_PATH, CODEX_MODELS_CACHE_PATH, DEFAULT_CATALOG_PATH, readRootTomlString, resolveCodexConfigPath } from "../paths";
|
|
7
7
|
import { clearModelCache, DEFAULT_MODEL_CACHE_TTL_MS, getFreshCached, getStaleCached, isModelsFetchCoolingDown, markModelsFetchFailure, setCached } from "../model-cache";
|
|
8
8
|
import { buildModelsRequest, resolveModelsAuthToken } from "../../oauth";
|
|
@@ -35,6 +35,7 @@ import { NATIVE_OPENAI_CONTEXT_OVERRIDES, SUPPORTED_NATIVE_OPENAI_SLUGS, UPSTREA
|
|
|
35
35
|
import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
|
|
36
36
|
import { trustedAccountBoundNativeCatalogSlug } from "./account-models";
|
|
37
37
|
import { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
38
|
+
import { NATIVE_GPT6_ASTRA_MODEL } from "./native-models";
|
|
38
39
|
|
|
39
40
|
export function legacyCatalogBackupPath(): string {
|
|
40
41
|
return join(getConfigDir(), "catalog-backup.json");
|
|
@@ -95,6 +96,8 @@ export const CODEX_PROVIDER_MODEL_CATALOG_KIND = "provider-model-v1";
|
|
|
95
96
|
export interface CatalogModel {
|
|
96
97
|
id: string;
|
|
97
98
|
provider: string;
|
|
99
|
+
/** Canonical or configured short alias for the provider segment. */
|
|
100
|
+
providerAlias?: string | null;
|
|
98
101
|
/** Public Codex-facing slug override (used by combo aliases). */
|
|
99
102
|
alias?: string;
|
|
100
103
|
/** Explicit combo takeover of a bare OpenAI-native catalog id. */
|
|
@@ -303,7 +306,98 @@ const NO_FAST_TIER_NATIVE_SLUGS = new Set([
|
|
|
303
306
|
"gpt-5.3-codex-spark",
|
|
304
307
|
]);
|
|
305
308
|
|
|
309
|
+
/** Does this row already carry an `ultrafast` tier the operator put there themselves? */
|
|
310
|
+
/**
|
|
311
|
+
* Read the opt-in from the live config, ONCE per catalog build.
|
|
312
|
+
*
|
|
313
|
+
* `deriveEntry` and its call sites are pure `RawEntry -> RawEntry` transforms with no
|
|
314
|
+
* config parameter, so the flag is resolved here rather than threaded through all of them.
|
|
315
|
+
* It is memoized because `normalizeRoutedCatalogEntry` runs per entry in a sync loop, and
|
|
316
|
+
* `loadConfig()` chmods the config dir, hardens three secrets, reads the file and runs a
|
|
317
|
+
* full Zod parse — doing that once per catalog row would be a real cost for one boolean.
|
|
318
|
+
* Callers holding a config still pass `opts.ultraFastTier` explicitly, which bypasses this
|
|
319
|
+
* entirely and is what the tests do. A read failure means OFF, matching `.catch(false)`.
|
|
320
|
+
*/
|
|
321
|
+
let ultraFastOptInCache: { value: boolean; at: number } | null = null;
|
|
322
|
+
const ULTRA_FAST_OPT_IN_TTL_MS = 5_000;
|
|
323
|
+
|
|
324
|
+
function ultraFastTierOptIn(): boolean {
|
|
325
|
+
const now = Date.now();
|
|
326
|
+
if (ultraFastOptInCache && now - ultraFastOptInCache.at < ULTRA_FAST_OPT_IN_TTL_MS) {
|
|
327
|
+
return ultraFastOptInCache.value;
|
|
328
|
+
}
|
|
329
|
+
let value = false;
|
|
330
|
+
try {
|
|
331
|
+
value = ultraFastTierEnabled(loadConfig());
|
|
332
|
+
} catch {
|
|
333
|
+
value = false;
|
|
334
|
+
}
|
|
335
|
+
ultraFastOptInCache = { value, at: now };
|
|
336
|
+
return value;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Test seam: drop the memoized opt-in so a config change is observed immediately. */
|
|
340
|
+
export function resetUltraFastTierOptInCache(): void {
|
|
341
|
+
ultraFastOptInCache = null;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function entryDeclaresUltraFast(entry: RawEntry): boolean {
|
|
345
|
+
const tiers = entry.service_tiers;
|
|
346
|
+
const declaredInTiers = Array.isArray(tiers) && tiers.some(tier => (
|
|
347
|
+
!!tier && typeof tier === "object" && "id" in tier
|
|
348
|
+
&& String((tier as { id?: unknown }).id).trim().toLowerCase() === "ultrafast"
|
|
349
|
+
));
|
|
350
|
+
const speeds = entry.additional_speed_tiers;
|
|
351
|
+
const declaredInSpeeds = Array.isArray(speeds) && speeds.some(speed => (
|
|
352
|
+
typeof speed === "string" && speed.trim().toLowerCase() === "ultrafast"
|
|
353
|
+
));
|
|
354
|
+
return declaredInTiers || declaredInSpeeds;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Keep the operator's `ultrafast` and drop everything else.
|
|
359
|
+
*
|
|
360
|
+
* A routed row must not inherit the native template's `priority` tier, which is the whole
|
|
361
|
+
* reason this strip exists. Preserving the supplied tier without this narrowing would
|
|
362
|
+
* smuggle Fast onto third-party providers under an unrelated flag.
|
|
363
|
+
*/
|
|
364
|
+
function retainOnlyUltraFastTier(entry: RawEntry): void {
|
|
365
|
+
const tiers = entry.service_tiers;
|
|
366
|
+
const keptTiers = Array.isArray(tiers)
|
|
367
|
+
? tiers.filter(tier => (
|
|
368
|
+
!!tier && typeof tier === "object" && "id" in tier
|
|
369
|
+
&& String((tier as { id?: unknown }).id).trim().toLowerCase() === "ultrafast"
|
|
370
|
+
))
|
|
371
|
+
: [];
|
|
372
|
+
if (keptTiers.length > 0) entry.service_tiers = keptTiers;
|
|
373
|
+
else delete entry.service_tiers;
|
|
374
|
+
|
|
375
|
+
const speeds = entry.additional_speed_tiers;
|
|
376
|
+
const keptSpeeds = Array.isArray(speeds)
|
|
377
|
+
? speeds.filter(speed => typeof speed === "string" && speed.trim().toLowerCase() === "ultrafast")
|
|
378
|
+
: [];
|
|
379
|
+
if (keptSpeeds.length > 0) entry.additional_speed_tiers = keptSpeeds;
|
|
380
|
+
else delete entry.additional_speed_tiers;
|
|
381
|
+
|
|
382
|
+
// A default of `priority` on a row that now only offers ultrafast would name a tier the
|
|
383
|
+
// row no longer carries.
|
|
384
|
+
if (String(entry.service_tier ?? "").trim().toLowerCase() !== "ultrafast") delete entry.service_tier;
|
|
385
|
+
if (String(entry.default_service_tier ?? "").trim().toLowerCase() !== "ultrafast") {
|
|
386
|
+
delete entry.default_service_tier;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
306
390
|
export function normalizeServiceTiers(entry: RawEntry): RawEntry {
|
|
391
|
+
// Repair only the old built-in Astra speed copy on persisted native/account rows.
|
|
392
|
+
// A custom description (and every other field) remains user-owned.
|
|
393
|
+
const nativeSlug = trustedAccountBoundNativeCatalogSlug(entry) ?? entry.slug;
|
|
394
|
+
if (nativeSlug === NATIVE_GPT6_ASTRA_MODEL && Array.isArray(entry.service_tiers)) {
|
|
395
|
+
entry.service_tiers = entry.service_tiers.map(tier =>
|
|
396
|
+
tier && typeof tier === "object" && tier.id === "priority"
|
|
397
|
+
&& tier.description === "1.5x speed, increased usage"
|
|
398
|
+
? { ...tier, description: "2x speed, increased usage" } : tier,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
307
401
|
// Strip service tiers for models that do not actually support the Fast tier.
|
|
308
402
|
if (typeof entry.slug === "string" && NO_FAST_TIER_NATIVE_SLUGS.has(entry.slug)) {
|
|
309
403
|
delete entry.service_tier;
|
|
@@ -502,6 +596,8 @@ export function ensureStrictCatalogFields(
|
|
|
502
596
|
export type MultiAgentMode = "v1" | "default" | "v2";
|
|
503
597
|
|
|
504
598
|
export interface MultiAgentModeOptions {
|
|
599
|
+
/** Caller-owned source metadata already defines the default for these projected rows. */
|
|
600
|
+
preserveDefaultMultiAgentVersion?: (entry: RawEntry) => boolean;
|
|
505
601
|
/**
|
|
506
602
|
* When the catalog is in v2 mode, stamp ChatGPT-native rows as v1 instead.
|
|
507
603
|
* Routed parents get v2 (plaintext child tasks). Native Sol/Terra stay on v1
|
|
@@ -577,6 +673,7 @@ export function applyMultiAgentMode(
|
|
|
577
673
|
// Restore upstream defaults: clear any stale forced multi_agent_version and
|
|
578
674
|
// re-apply upstream pins from the snapshot for native entries that have one.
|
|
579
675
|
for (const entry of entries) {
|
|
676
|
+
if (options.preserveDefaultMultiAgentVersion?.(entry)) continue;
|
|
580
677
|
const slug = typeof entry.slug === "string" ? entry.slug : "";
|
|
581
678
|
const nativeAlias = entry.opencodex_catalog_kind === CODEX_NATIVE_ALIAS_CATALOG_KIND;
|
|
582
679
|
const routedNativeSlug = slug.startsWith(`${OPENAI_CODEX_PROVIDER_ID}/`)
|
|
@@ -612,17 +709,34 @@ export function normalizeRoutedCatalogEntry(
|
|
|
612
709
|
entry: RawEntry,
|
|
613
710
|
parallelToolCalls = false,
|
|
614
711
|
toolMode?: "code_mode_only" | "shell" | string,
|
|
712
|
+
opts?: { ultraFastTier?: boolean },
|
|
615
713
|
): RawEntry {
|
|
616
714
|
delete entry.model_messages;
|
|
617
715
|
delete entry.tool_mode;
|
|
618
716
|
applyRoutedCodexToolMode(entry, toolMode);
|
|
619
717
|
delete entry.multi_agent_version;
|
|
718
|
+
delete entry.multi_agent_reasoning_effort;
|
|
620
719
|
delete entry.use_responses_lite;
|
|
621
720
|
delete entry.supports_websockets;
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
721
|
+
/*
|
|
722
|
+
* Tier metadata is stripped from routed rows because a row cloned from a native template
|
|
723
|
+
* would otherwise hand a third-party provider OpenAI's tiers.
|
|
724
|
+
*
|
|
725
|
+
* The opt-in carves out exactly one case: an `ultrafast` the OPERATOR put in their own
|
|
726
|
+
* catalog. #3429's reporter added it by hand and watched a regeneration delete it every
|
|
727
|
+
* time. Preserving what they wrote is not the same as advertising a tier — upstream
|
|
728
|
+
* publishes only `priority`, and synthesizing an ultrafast row is what PR #2994 was
|
|
729
|
+
* closed for. So this keeps a supplied tier and still never invents one.
|
|
730
|
+
*/
|
|
731
|
+
const keepUltraFast = (opts?.ultraFastTier ?? ultraFastTierOptIn()) && entryDeclaresUltraFast(entry);
|
|
732
|
+
if (!keepUltraFast) {
|
|
733
|
+
delete entry.additional_speed_tiers;
|
|
734
|
+
delete entry.service_tier;
|
|
735
|
+
delete entry.service_tiers;
|
|
736
|
+
delete entry.default_service_tier;
|
|
737
|
+
} else {
|
|
738
|
+
retainOnlyUltraFastTier(entry);
|
|
739
|
+
}
|
|
626
740
|
// Routed rows cloned from native templates must not inherit OpenAI-only summary delivery.
|
|
627
741
|
// Explicit provider/model metadata is re-applied after this normalization step.
|
|
628
742
|
delete entry.supports_reasoning_summaries;
|