@bitkyc08/opencodex 2.41.0 → 2.43.0-preview.20260906
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-VGeQEZ_v.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 +54 -24
- 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/identity.ts +8 -2
- package/src/adapters/kiro-calibration.ts +181 -0
- package/src/adapters/kiro.ts +135 -3
- package/src/adapters/openai-responses.ts +259 -29
- 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 +47 -13
- 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 +49 -9
- package/src/cli/account-extended.ts +7 -1
- package/src/cli/capabilities.ts +15 -4
- 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/observe.ts +3 -1
- 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 +272 -32
- package/src/codex/auth-context.ts +328 -24
- package/src/codex/catalog/effort.ts +44 -5
- package/src/codex/catalog/metadata.ts +149 -14
- package/src/codex/catalog/native-models.ts +116 -4
- package/src/codex/catalog/parsing.ts +122 -8
- package/src/codex/catalog/provider-fetch.ts +154 -23
- package/src/codex/catalog/reserve.ts +52 -0
- package/src/codex/catalog/sync.ts +89 -16
- package/src/codex/catalog.ts +1 -1
- package/src/codex/convergence-types.ts +1 -0
- package/src/codex/convergence.ts +2 -0
- package/src/codex/data/upstream-models.json +169 -0
- package/src/codex/forward-transport-headers.ts +25 -0
- package/src/codex/inject.ts +99 -34
- package/src/codex/injected-marker.ts +30 -4
- package/src/codex/journal.ts +14 -0
- 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 +474 -178
- 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/account-quota-rank.ts +40 -1
- package/src/oauth/anthropic-routing.ts +99 -3
- package/src/oauth/chatgpt-device.ts +187 -0
- package/src/oauth/chatgpt.ts +31 -4
- package/src/oauth/generic-account-failover.ts +36 -13
- package/src/oauth/index.ts +140 -29
- package/src/oauth/log.ts +3 -0
- 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/muse-subscription-usage.ts +95 -0
- 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 +625 -98
- 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 +122 -28
- package/src/server/live.ts +18 -4
- 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 +45 -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 +510 -57
- package/src/server/responses/empty-completion-guard.ts +4 -0
- 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/request.ts +8 -0
- 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-B2YjLA-i.css +0 -1
- package/gui/dist/assets/index-aPup8CKb.js +0 -112
|
@@ -35,7 +35,8 @@ import upstreamModelsSnapshot from "../data/upstream-models.json";
|
|
|
35
35
|
import { generatedModelMetadata, readCatalog, readCodexCatalogPath } from "./parsing";
|
|
36
36
|
import type { CatalogModel, RawEntry } from "./parsing";
|
|
37
37
|
import { UPSTREAM_NATIVE_ENTRIES } from "./metadata";
|
|
38
|
-
import { nativeOpenAiCapabilitySourceSlug } from "./native-models";
|
|
38
|
+
import { nativeOpenAiCapabilitySourceSlug, SELF_DESCRIBED_NATIVE_OPENAI_MODELS, NATIVE_RESERVE_MODEL } from "./native-models";
|
|
39
|
+
import { isReserveCatalogProjection } from "./reserve";
|
|
39
40
|
import { loadBundledCodexCatalog } from "./bundled";
|
|
40
41
|
import type { BundledCatalogDeps, ReadonlyRawCatalog } from "./bundled";
|
|
41
42
|
import { deriveEntry } from "./sync";
|
|
@@ -158,11 +159,14 @@ export function applyCatalogModelMetadata(entry: RawEntry, model?: CatalogModel)
|
|
|
158
159
|
entry.supports_reasoning_summaries = model.supportsReasoningSummaries;
|
|
159
160
|
}
|
|
160
161
|
if (model.supportsServiceTier === true) {
|
|
162
|
+
const nativeFast = model.codexForwardNativeCapabilityAlias && Array.isArray(entry.service_tiers)
|
|
163
|
+
? entry.service_tiers.find(tier => tier?.id === "priority")
|
|
164
|
+
: undefined;
|
|
161
165
|
entry.default_service_tier = null;
|
|
162
166
|
entry.service_tiers = [{
|
|
163
167
|
id: "priority",
|
|
164
168
|
name: "Fast",
|
|
165
|
-
description: model.fastTierDescription ?? "1.5x speed, increased usage",
|
|
169
|
+
description: model.fastTierDescription ?? nativeFast?.description ?? "1.5x speed, increased usage",
|
|
166
170
|
}];
|
|
167
171
|
entry.additional_speed_tiers = ["fast"];
|
|
168
172
|
}
|
|
@@ -258,8 +262,21 @@ export function applyReasoningLevels(
|
|
|
258
262
|
: efforts.find(effort => effort !== "none" && effort !== "minimal") ?? efforts[0];
|
|
259
263
|
}
|
|
260
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Native slugs entitled to the full GPT-5.6-era ladder (low..ultra, with max restored).
|
|
267
|
+
*
|
|
268
|
+
* The name is historical: membership is about the LADDER, not the model generation. `gpt-6-astra`
|
|
269
|
+
* qualifies because upstream ships it with the same six rungs
|
|
270
|
+
* (`supported_reasoning_levels` low/medium/high/xhigh/max/ultra, #42607). It used to qualify only
|
|
271
|
+
* as a side effect of borrowing Sol's capability source; once it became self-described that
|
|
272
|
+
* accident disappeared, and the sync path's else-branch
|
|
273
|
+
* (`applyReasoningLevels(entry, ["low","medium","high","xhigh"])`) would have truncated the
|
|
274
|
+
* shipped ladder, silently dropping `max` and `ultra`.
|
|
275
|
+
*/
|
|
261
276
|
export function isGpt56NativeSlug(slug: string): boolean {
|
|
262
|
-
|
|
277
|
+
if (slug.includes("/")) return false;
|
|
278
|
+
if (SELF_DESCRIBED_NATIVE_OPENAI_MODELS.has(slug)) return true;
|
|
279
|
+
return nativeOpenAiCapabilitySourceSlug(slug).startsWith("gpt-5.6-");
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
export function ensureGpt56ReasoningLevels(entry: RawEntry): void {
|
|
@@ -324,6 +341,10 @@ export function clampedDefaultEffort(original: string, surviving: readonly strin
|
|
|
324
341
|
return (atOrBelow.at(-1) ?? ranked[0]!).effort;
|
|
325
342
|
}
|
|
326
343
|
|
|
344
|
+
function requiresExactReserveEfforts(entry: RawEntry): boolean {
|
|
345
|
+
return entry.slug === NATIVE_RESERVE_MODEL || isReserveCatalogProjection(entry);
|
|
346
|
+
}
|
|
347
|
+
|
|
327
348
|
export function clampEntryToCodexSupportedEfforts(
|
|
328
349
|
entry: RawEntry,
|
|
329
350
|
supported: ReadonlySet<string> | null,
|
|
@@ -334,6 +355,19 @@ export function clampEntryToCodexSupportedEfforts(
|
|
|
334
355
|
: null;
|
|
335
356
|
if (levels && levels.length > 0) {
|
|
336
357
|
const kept = levels.filter(level => typeof level?.effort === "string" && supported.has(level.effort));
|
|
358
|
+
if (requiresExactReserveEfforts(entry)) {
|
|
359
|
+
entry.supported_reasoning_levels = kept;
|
|
360
|
+
if (kept.length === 0) {
|
|
361
|
+
// The list-level clamp removes this incompatible row; never invent another ladder.
|
|
362
|
+
delete entry.default_reasoning_level;
|
|
363
|
+
} else if (!kept.some(level => level.effort === entry.default_reasoning_level)) {
|
|
364
|
+
entry.default_reasoning_level = clampedDefaultEffort(
|
|
365
|
+
typeof entry.default_reasoning_level === "string" ? entry.default_reasoning_level : "",
|
|
366
|
+
kept.map(level => level.effort!),
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
337
371
|
entry.supported_reasoning_levels = kept.length > 0
|
|
338
372
|
? kept
|
|
339
373
|
: CODEX_REASONING_LEVELS
|
|
@@ -364,7 +398,9 @@ export function clampCatalogModelsToObservedCodexSupport(
|
|
|
364
398
|
|
|
365
399
|
const removed = new Set<string>();
|
|
366
400
|
const affected: string[] = [];
|
|
367
|
-
for (
|
|
401
|
+
for (let index = 0; index < models.length;) {
|
|
402
|
+
const entry = models[index]!;
|
|
403
|
+
const hadLadder = Array.isArray(entry.supported_reasoning_levels) && entry.supported_reasoning_levels.length > 0;
|
|
368
404
|
const before = new Set(
|
|
369
405
|
(Array.isArray(entry.supported_reasoning_levels) ? entry.supported_reasoning_levels : [])
|
|
370
406
|
.flatMap(level => typeof (level as { effort?: string })?.effort === "string"
|
|
@@ -386,11 +422,14 @@ export function clampCatalogModelsToObservedCodexSupport(
|
|
|
386
422
|
: null;
|
|
387
423
|
const lost = [...before].filter(effort => !after.has(effort));
|
|
388
424
|
const defaultClamped = Boolean(beforeDefault && beforeDefault !== afterDefault);
|
|
389
|
-
|
|
425
|
+
const omitted = requiresExactReserveEfforts(entry) && hadLadder && after.size === 0;
|
|
426
|
+
if (lost.length > 0 || defaultClamped || omitted) {
|
|
390
427
|
for (const effort of lost) removed.add(effort);
|
|
391
428
|
if (defaultClamped && beforeDefault) removed.add(beforeDefault);
|
|
392
429
|
if (typeof entry.slug === "string") affected.push(entry.slug);
|
|
393
430
|
}
|
|
431
|
+
if (omitted) models.splice(index, 1);
|
|
432
|
+
else index += 1;
|
|
394
433
|
}
|
|
395
434
|
|
|
396
435
|
return {
|
|
@@ -38,13 +38,19 @@ 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,
|
|
45
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
46
|
+
NATIVE_RESERVE_MODEL,
|
|
44
47
|
NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS,
|
|
45
48
|
NATIVE_OPENAI_MODELS,
|
|
49
|
+
SELF_DESCRIBED_NATIVE_OPENAI_MODELS,
|
|
46
50
|
SUPPORTED_NATIVE_OPENAI_SLUGS,
|
|
51
|
+
hasNativeOpenAiCapabilityMetadata,
|
|
47
52
|
isNativeOpenAiCapabilityAliasModel,
|
|
53
|
+
nativeOpenAiAliasPresentation,
|
|
48
54
|
nativeOpenAiCapabilitySourceSlug,
|
|
49
55
|
} from "./native-models";
|
|
50
56
|
import { cachedAvailableAccountGatedNativeModels } from "../model-entitlements";
|
|
@@ -52,16 +58,22 @@ import { MAIN_CODEX_ACCOUNT_ID } from "../main-account";
|
|
|
52
58
|
export { CODEX_NATIVE_ALIAS_CATALOG_KIND } from "./kinds";
|
|
53
59
|
export {
|
|
54
60
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
61
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
55
62
|
NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS,
|
|
56
63
|
NATIVE_OPENAI_MODELS,
|
|
64
|
+
SELF_DESCRIBED_NATIVE_OPENAI_MODELS,
|
|
57
65
|
SUPPORTED_NATIVE_OPENAI_SLUGS,
|
|
66
|
+
hasNativeOpenAiCapabilityMetadata,
|
|
58
67
|
isNativeOpenAiCapabilityAliasModel,
|
|
68
|
+
nativeOpenAiAliasPresentation,
|
|
59
69
|
nativeOpenAiCapabilitySourceSlug,
|
|
60
70
|
} from "./native-models";
|
|
61
71
|
|
|
62
72
|
export const DOCUMENTED_NATIVE_OPENAI_ADDITIONS = [
|
|
63
73
|
"gpt-5.3-codex-spark",
|
|
64
74
|
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna",
|
|
75
|
+
// The shipped pin also backfills older installed Codex catalogs that predate Astra.
|
|
76
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
65
77
|
];
|
|
66
78
|
|
|
67
79
|
export function configuredNativeAliasSlugs(
|
|
@@ -161,6 +173,12 @@ export const NATIVE_OPENAI_CONTEXT_OVERRIDES: Record<string, { contextWindow?: n
|
|
|
161
173
|
// ChatGPT account."`), so the promotion rests on a report from an account that has
|
|
162
174
|
// access rather than on a probe. Treat it as the weaker evidence of the four.
|
|
163
175
|
[NATIVE_DAYBREAK_BLUE_MODEL]: { contextWindow: NATIVE_GPT56_CONTEXT_WINDOW, maxContextWindow: NATIVE_GPT56_MAX_INPUT_TOKENS, maxInputTokens: NATIVE_GPT56_MAX_INPUT_TOKENS },
|
|
176
|
+
// gpt-6-astra ships its own numbers (upstream models.json, #42607): a 272,000 default window
|
|
177
|
+
// against an 872,000 ceiling. It is NOT in NATIVE_GPT56_FAMILY, so it must not inherit that
|
|
178
|
+
// family's measured 922,000 clamp — advertising 922,000 here over-stated the ceiling by 50k.
|
|
179
|
+
// maxInputTokens is clamped to the resolved window by nativeOpenAiMaxInputTokens, so this reads
|
|
180
|
+
// 272,000 by default and 872,000 only under the long-window opt-in.
|
|
181
|
+
[NATIVE_GPT6_ASTRA_MODEL]: { contextWindow: 272_000, maxContextWindow: 872_000, maxInputTokens: 872_000 },
|
|
164
182
|
};
|
|
165
183
|
|
|
166
184
|
const PINNED_UPSTREAM_MODELS: Map<string, RawEntry> = new Map(
|
|
@@ -246,13 +264,36 @@ export function nativeContextLimits(
|
|
|
246
264
|
}
|
|
247
265
|
|
|
248
266
|
/** Apply the user levers to an authoritative value. */
|
|
267
|
+
/**
|
|
268
|
+
* The ceiling a native slug may be RAISED to by a user lever, or undefined when it has no
|
|
269
|
+
* separate long window.
|
|
270
|
+
*
|
|
271
|
+
* This is what makes the dashboard's 1M opt-in work: without an opt-in ceiling a lever can only
|
|
272
|
+
* ever narrow the advertised window, so the toggle would appear to do nothing. The GPT-5.6 family
|
|
273
|
+
* shares one measured ceiling; a self-described native carries its own in
|
|
274
|
+
* `NATIVE_OPENAI_CONTEXT_OVERRIDES.maxContextWindow` (`gpt-6-astra` ships 872,000 against a
|
|
275
|
+
* 272,000 default), and reading it per-slug is what keeps the toggle honest for a model whose
|
|
276
|
+
* ceiling is not the family's.
|
|
277
|
+
*/
|
|
278
|
+
function longWindowOptInCeiling(slug: string): number | undefined {
|
|
279
|
+
if (NATIVE_GPT56_FAMILY.has(slug)) return NATIVE_GPT56_MAX_INPUT_TOKENS;
|
|
280
|
+
const override = NATIVE_OPENAI_CONTEXT_OVERRIDES[slug];
|
|
281
|
+
const defaultWindow = positiveInt(override?.contextWindow);
|
|
282
|
+
const longWindow = positiveInt(override?.maxContextWindow);
|
|
283
|
+
if (defaultWindow === undefined || longWindow === undefined || longWindow <= defaultWindow) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
return longWindow;
|
|
287
|
+
}
|
|
288
|
+
|
|
249
289
|
function narrowToLimits(raw: number | undefined, slug: string, input: NativeContextLimitsInput): number | undefined {
|
|
250
290
|
if (raw === undefined) return undefined;
|
|
251
291
|
const limits = asLimits(input);
|
|
252
292
|
const overlay = positiveInt(limits.modelWindows?.[slug]) ?? positiveInt(limits.providerWindow);
|
|
253
293
|
const cap = positiveInt(limits.cap);
|
|
254
|
-
|
|
255
|
-
|
|
294
|
+
const optInCeiling = longWindowOptInCeiling(slug);
|
|
295
|
+
if (optInCeiling !== undefined) {
|
|
296
|
+
const ceiling = optInCeiling;
|
|
256
297
|
const chosen = overlay ?? cap ?? raw;
|
|
257
298
|
const window = Math.min(chosen, ceiling);
|
|
258
299
|
return overlay !== undefined && cap !== undefined ? Math.min(window, cap) : window;
|
|
@@ -496,15 +537,23 @@ export function applyNativeVisibility(
|
|
|
496
537
|
|
|
497
538
|
function upstreamNativeEntryForSlug(slug: string): RawEntry | undefined {
|
|
498
539
|
const sourceSlug = nativeOpenAiCapabilitySourceSlug(slug);
|
|
499
|
-
|
|
540
|
+
// A self-described native returns its OWN pinned row; the alias-cloning branch below stays
|
|
541
|
+
// reserved for slugs that genuinely borrow another model's identity. The allowlist is explicit
|
|
542
|
+
// rather than "has a pinned entry", which would also admit gpt-5.5/gpt-5.4/gpt-5.4-mini into
|
|
543
|
+
// the sync-replacement authority this map carries.
|
|
544
|
+
if (!sourceSlug.startsWith("gpt-5.6-") && !SELF_DESCRIBED_NATIVE_OPENAI_MODELS.has(slug)) {
|
|
545
|
+
return undefined;
|
|
546
|
+
}
|
|
500
547
|
const source = PINNED_UPSTREAM_MODELS.get(sourceSlug);
|
|
501
548
|
if (!source) return undefined;
|
|
502
|
-
if (slug === sourceSlug) return source;
|
|
549
|
+
if (slug === sourceSlug) return withDerivedBaseInstructions(source);
|
|
503
550
|
|
|
504
551
|
const alias = structuredClone(source) as RawEntry;
|
|
505
552
|
alias.slug = slug;
|
|
506
|
-
|
|
507
|
-
|
|
553
|
+
const presentation = nativeOpenAiAliasPresentation(slug);
|
|
554
|
+
if (!presentation) return undefined; // an alias with no product identity must not ship a wrong one
|
|
555
|
+
alias.display_name = presentation.displayName;
|
|
556
|
+
alias.description = presentation.description;
|
|
508
557
|
if (typeof alias.base_instructions === "string") {
|
|
509
558
|
alias.base_instructions = identifyRoutedModel(alias.base_instructions, slug);
|
|
510
559
|
}
|
|
@@ -521,6 +570,27 @@ function upstreamNativeEntryForSlug(slug: string): RawEntry | undefined {
|
|
|
521
570
|
return alias;
|
|
522
571
|
}
|
|
523
572
|
|
|
573
|
+
/**
|
|
574
|
+
* Backfill `base_instructions` from `model_messages.instructions_template` when upstream ships
|
|
575
|
+
* only the latter.
|
|
576
|
+
*
|
|
577
|
+
* `gpt-6-astra` is the first pinned row to arrive without a top-level `base_instructions`; every
|
|
578
|
+
* other native carries both. That field is not decorative here — `hasNativeCatalogRowShape`,
|
|
579
|
+
* `findNativeTemplate` and `findSupportedNativeTemplate` all test for it, so a row missing it is
|
|
580
|
+
* not recognized as a native catalog row at all. The two fields hold the same prompt upstream, so
|
|
581
|
+
* deriving one from the other preserves upstream's content while keeping this codebase's row
|
|
582
|
+
* shape intact. The pinned JSON is left byte-identical to upstream; only the projection fills in.
|
|
583
|
+
*/
|
|
584
|
+
function withDerivedBaseInstructions(entry: RawEntry): RawEntry {
|
|
585
|
+
if (typeof entry.base_instructions === "string" && entry.base_instructions.length > 0) return entry;
|
|
586
|
+
const messages = entry.model_messages;
|
|
587
|
+
const template = messages && typeof messages === "object" && !Array.isArray(messages)
|
|
588
|
+
? (messages as Record<string, unknown>).instructions_template
|
|
589
|
+
: undefined;
|
|
590
|
+
if (typeof template !== "string" || template.length === 0) return entry;
|
|
591
|
+
return { ...entry, base_instructions: template };
|
|
592
|
+
}
|
|
593
|
+
|
|
524
594
|
export const UPSTREAM_NATIVE_ENTRIES: Map<string, RawEntry> = new Map(
|
|
525
595
|
[...NATIVE_OPENAI_MODELS, ...NATIVE_OPENAI_CAPABILITY_ALIAS_MODELS].flatMap(slug => {
|
|
526
596
|
const entry = upstreamNativeEntryForSlug(slug);
|
|
@@ -536,10 +606,44 @@ export function upstreamNativeEntry(slug: string): RawEntry | null {
|
|
|
536
606
|
return clone;
|
|
537
607
|
}
|
|
538
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Product label for a native slug whose custom row inherits native metadata.
|
|
611
|
+
*
|
|
612
|
+
* An alias carries a hand-written presentation because upstream never described it. A
|
|
613
|
+
* self-described native gets its label from its own pinned row instead, so the two kinds answer
|
|
614
|
+
* through one accessor and no caller has to know which it holds.
|
|
615
|
+
*/
|
|
616
|
+
export function nativeOpenAiCapabilityDisplayName(slug: string): string | undefined {
|
|
617
|
+
const presentation = nativeOpenAiAliasPresentation(slug);
|
|
618
|
+
if (presentation) return presentation.displayName;
|
|
619
|
+
const pinned = UPSTREAM_NATIVE_ENTRIES.get(slug);
|
|
620
|
+
return typeof pinned?.display_name === "string" ? pinned.display_name : undefined;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Slugs whose persisted row may be replaced by the pinned snapshot even when it carries a real
|
|
625
|
+
* display name — because THIS codebase wrote that name from a guess.
|
|
626
|
+
*
|
|
627
|
+
* `shouldUpgradeToUpstreamEntry`'s normal rule ("upgrade only fallback-quality rows, where
|
|
628
|
+
* `display_name === slug`") assumes any row with a real label came from upstream and is therefore
|
|
629
|
+
* authoritative. That assumption broke for `gpt-6-astra`: opencodex shipped a speculative row with
|
|
630
|
+
* a hand-written "GPT-6 Astra" label and a provisional description while the slug was still a leak.
|
|
631
|
+
* Those rows are already on disk in every install that ran that release, and they look genuine, so
|
|
632
|
+
* without this list they would survive every future sync and permanently shadow the real shipped
|
|
633
|
+
* metadata — the wrong label, the wrong 922k ceiling, the wrong priority.
|
|
634
|
+
*
|
|
635
|
+
* Membership is a statement about opencodex's own history, not about upstream. Add a slug only
|
|
636
|
+
* when a released version of this project wrote a fabricated row for it.
|
|
637
|
+
*/
|
|
638
|
+
const SELF_AUTHORED_NATIVE_ROWS: ReadonlySet<string> = new Set([NATIVE_GPT6_ASTRA_MODEL]);
|
|
639
|
+
|
|
539
640
|
export function shouldUpgradeToUpstreamEntry(entry: RawEntry): boolean {
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
641
|
+
if (typeof entry.slug !== "string" || !UPSTREAM_NATIVE_ENTRIES.has(entry.slug)) return false;
|
|
642
|
+
if (entry.display_name === entry.slug) return true;
|
|
643
|
+
// A row this project authored from a guess is not evidence of upstream truth, however genuine
|
|
644
|
+
// its display name looks. Replace it once, from the pin.
|
|
645
|
+
return SELF_AUTHORED_NATIVE_ROWS.has(entry.slug)
|
|
646
|
+
&& entry.display_name !== UPSTREAM_NATIVE_ENTRIES.get(entry.slug)?.display_name;
|
|
543
647
|
}
|
|
544
648
|
|
|
545
649
|
export function nativeOpenAiSlugs(): string[] {
|
|
@@ -566,7 +670,7 @@ function isAccountBoundOpenAiNativeSlug(slug: string): boolean {
|
|
|
566
670
|
* malformed and minimal hand-written rows. It cannot distinguish a genuine upstream observation
|
|
567
671
|
* from a complete row typed by hand into `$CODEX_HOME/models_cache.json`: there is no signature,
|
|
568
672
|
* source identity, or server attestation to check. A full-shape forged row is accepted, and
|
|
569
|
-
* `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
|
|
570
674
|
* later mistakes this predicate for a security boundary.
|
|
571
675
|
*
|
|
572
676
|
* That is acceptable here because the file is user-owned and written by Codex itself: anyone
|
|
@@ -595,14 +699,44 @@ function observedAccountBoundNativeSlug(entry: RawEntry): string | undefined {
|
|
|
595
699
|
const accountBound = trustedAccountBoundNativeCatalogSlug(entry);
|
|
596
700
|
const slug = accountBound ?? (typeof entry.slug === "string" ? entry.slug : "");
|
|
597
701
|
if (!isAccountBoundOpenAiNativeSlug(slug)
|
|
598
|
-
|| 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)
|
|
599
705
|
|| !hasNativeCatalogRowShape(entry)
|
|
600
|
-
|| (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"))) {
|
|
601
708
|
return undefined;
|
|
602
709
|
}
|
|
603
710
|
return slug;
|
|
604
711
|
}
|
|
605
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
|
+
|
|
606
740
|
/**
|
|
607
741
|
* Return exact, previously observed account-native rows that are not in the static release set.
|
|
608
742
|
* The result is used only to carry a hidden observation across startup cache invalidation.
|
|
@@ -643,7 +777,8 @@ export function accountBoundNativeOpenAiSlugs(
|
|
|
643
777
|
): string[] {
|
|
644
778
|
const observed = observedEntries.flatMap(entry => {
|
|
645
779
|
const slug = observedAccountBoundNativeSlug(entry);
|
|
646
|
-
|
|
780
|
+
// Reserve belongs only to the Codex-specific opt-in builder, not generic native exports.
|
|
781
|
+
return slug === undefined || slug === NATIVE_RESERVE_MODEL ? [] : [slug];
|
|
647
782
|
});
|
|
648
783
|
return unique([...NATIVE_OPENAI_MODELS, ...observed]);
|
|
649
784
|
}
|
|
@@ -670,7 +805,7 @@ export function accountBoundNativeOpenAiSlugsBySelector(
|
|
|
670
805
|
);
|
|
671
806
|
for (const entry of observedEntries) {
|
|
672
807
|
const slug = observedAccountBoundNativeSlug(entry);
|
|
673
|
-
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;
|
|
674
809
|
const generated = trustedAccountBoundNativeCatalogSlug(entry);
|
|
675
810
|
const generatedSelector = generated === undefined || typeof entry.slug !== "string"
|
|
676
811
|
? undefined
|
|
@@ -1,11 +1,53 @@
|
|
|
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
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* SHIPPED as of 2026-09-03: openai/codex `ed391d4dd` (#42607, bundled model catalog) and
|
|
9
|
+
* `1f7b99922` (#42619, Amazon Bedrock catalogs). The registration is no longer speculative —
|
|
10
|
+
* `src/codex/data/upstream-models.json` now pins the real row, so this slug is SELF-DESCRIBED
|
|
11
|
+
* and must not borrow another model's capability metadata.
|
|
12
|
+
*
|
|
13
|
+
* Still NOT wire-normalized: unlike Daybreak the slug IS the wire id.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately NOT account-gated (owner decision, 2026-09-04, reaffirmed during rollout).
|
|
16
|
+
* Upstream `available_in_plans` lists 23 plans including `free`, but the model is rolling out,
|
|
17
|
+
* so a given account's Codex surface may still answer
|
|
18
|
+
* `"The 'gpt-6-astra' model is not supported when using Codex with a ChatGPT account."`
|
|
19
|
+
* — the same refusal Daybreak returns. Gating on an entitlement roster would hide the row until
|
|
20
|
+
* that roster catches up; listing it means the request dispatches and the real upstream status
|
|
21
|
+
* is what the user sees. Evidence: devlog/_plan/260904_astra_release_alignment/021.
|
|
22
|
+
*/
|
|
23
|
+
export const NATIVE_GPT6_ASTRA_MODEL = "gpt-6-astra";
|
|
24
|
+
|
|
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
|
+
*/
|
|
5
50
|
export const ACCOUNT_GATED_NATIVE_OPENAI_MODELS: ReadonlySet<string> = new Set([
|
|
6
|
-
"gpt-5.6-sol",
|
|
7
|
-
"gpt-5.6-terra",
|
|
8
|
-
"gpt-5.6-luna",
|
|
9
51
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
10
52
|
]);
|
|
11
53
|
|
|
@@ -25,6 +67,19 @@ const NATIVE_OPENAI_CAPABILITY_SOURCES: Readonly<Record<string, string>> = Objec
|
|
|
25
67
|
[NATIVE_DAYBREAK_BLUE_MODEL]: "gpt-5.6-sol",
|
|
26
68
|
});
|
|
27
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Native slugs that carry their OWN pinned upstream row rather than an alias's borrowed one.
|
|
72
|
+
*
|
|
73
|
+
* Membership authorizes `upstreamNativeEntryForSlug` to return the pinned entry directly. It is
|
|
74
|
+
* an explicit list, not a structural `PINNED_UPSTREAM_MODELS.has(slug)` predicate: the pin also
|
|
75
|
+
* holds `gpt-5.5`, `gpt-5.4` and `gpt-5.4-mini`, and admitting those into
|
|
76
|
+
* `UPSTREAM_NATIVE_ENTRIES` would newly authorize replacing their persisted catalog rows during
|
|
77
|
+
* sync — an invariant that map's own comment reserves for the GPT-5.6 family.
|
|
78
|
+
*/
|
|
79
|
+
export const SELF_DESCRIBED_NATIVE_OPENAI_MODELS: ReadonlySet<string> = new Set([
|
|
80
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
81
|
+
]);
|
|
82
|
+
|
|
28
83
|
/**
|
|
29
84
|
* Native ids whose capability metadata is inherited from another pinned native row.
|
|
30
85
|
*
|
|
@@ -46,10 +101,41 @@ export function isNativeOpenAiCapabilityAliasModel(slug: string): boolean {
|
|
|
46
101
|
return Object.hasOwn(NATIVE_OPENAI_CAPABILITY_SOURCES, slug);
|
|
47
102
|
}
|
|
48
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Native slugs whose Codex-forward CUSTOM row inherits authoritative native metadata.
|
|
106
|
+
*
|
|
107
|
+
* Two shapes qualify and the distinction matters only to `upstreamNativeEntryForSlug`:
|
|
108
|
+
* a capability ALIAS borrows another model's pinned row, while a SELF-DESCRIBED native has its
|
|
109
|
+
* own. Every consumer that asks "does this custom row get real native capabilities and a real
|
|
110
|
+
* product label" wants both, which is why they call this rather than the alias check —
|
|
111
|
+
* `gpt-6-astra` stopped being an alias when its own row was pinned, and gating on
|
|
112
|
+
* `isNativeOpenAiCapabilityAliasModel` alone would have silently demoted it to a bare-slug label
|
|
113
|
+
* with no inherited ladder.
|
|
114
|
+
*/
|
|
115
|
+
export function hasNativeOpenAiCapabilityMetadata(slug: string): boolean {
|
|
116
|
+
return isNativeOpenAiCapabilityAliasModel(slug) || SELF_DESCRIBED_NATIVE_OPENAI_MODELS.has(slug);
|
|
117
|
+
}
|
|
118
|
+
|
|
49
119
|
export function nativeOpenAiCapabilitySourceSlug(slug: string): string {
|
|
50
120
|
return NATIVE_OPENAI_CAPABILITY_SOURCES[slug] ?? slug;
|
|
51
121
|
}
|
|
52
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Presentation identity per capability alias. Capability metadata (context, ladder, modalities)
|
|
125
|
+
* is inherited from the source model; the NAME and description are the alias's own product
|
|
126
|
+
* identity — hardcoding one alias's label would present every other alias as the wrong product.
|
|
127
|
+
*/
|
|
128
|
+
export const NATIVE_OPENAI_ALIAS_PRESENTATION: Readonly<Record<string, { displayName: string; description: string }>> = Object.freeze({
|
|
129
|
+
[NATIVE_DAYBREAK_BLUE_MODEL]: {
|
|
130
|
+
displayName: "Daybreak Blue",
|
|
131
|
+
description: "Frontier general-purpose model with safeguards for defensive cybersecurity work.",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
export function nativeOpenAiAliasPresentation(slug: string): { displayName: string; description: string } | undefined {
|
|
136
|
+
return NATIVE_OPENAI_ALIAS_PRESENTATION[slug];
|
|
137
|
+
}
|
|
138
|
+
|
|
53
139
|
/**
|
|
54
140
|
* Native OpenAI model ids that this release can route and restore with authoritative metadata.
|
|
55
141
|
*
|
|
@@ -70,6 +156,32 @@ export const NATIVE_OPENAI_MODELS = [
|
|
|
70
156
|
"gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex-spark",
|
|
71
157
|
"gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna",
|
|
72
158
|
NATIVE_DAYBREAK_BLUE_MODEL,
|
|
159
|
+
NATIVE_GPT6_ASTRA_MODEL,
|
|
73
160
|
];
|
|
74
161
|
|
|
75
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
|
+
]);
|