@bitkyc08/opencodex 2.37.0 → 2.39.0-preview.20260901

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.
Files changed (131) hide show
  1. package/bin/ocx.mjs +127 -16
  2. package/gui/dist/assets/index-6HZDkIpX.js +112 -0
  3. package/gui/dist/assets/index-uvENYLin.css +1 -0
  4. package/gui/dist/index.html +2 -2
  5. package/gui/dist/provider-icons/aside.svg +3 -0
  6. package/gui/dist/provider-icons/baseten.svg +13 -0
  7. package/gui/dist/provider-icons/bizrouter.svg +41 -0
  8. package/gui/dist/provider-icons/cerebras.svg +26 -0
  9. package/gui/dist/provider-icons/deepinfra.svg +75 -0
  10. package/gui/dist/provider-icons/deepseek-harness.svg +3 -0
  11. package/gui/dist/provider-icons/digitalocean.svg +10 -0
  12. package/gui/dist/provider-icons/featherless.svg +4 -0
  13. package/gui/dist/provider-icons/gajae-code.svg +410 -0
  14. package/gui/dist/provider-icons/hermes-agent.svg +207 -0
  15. package/gui/dist/provider-icons/hyperbolic.svg +18 -0
  16. package/gui/dist/provider-icons/kilo.svg +13 -0
  17. package/gui/dist/provider-icons/litellm.svg +1 -0
  18. package/gui/dist/provider-icons/minimax.svg +1 -0
  19. package/gui/dist/provider-icons/nanogpt.svg +74 -0
  20. package/gui/dist/provider-icons/nebius.svg +1 -0
  21. package/gui/dist/provider-icons/neuralwatt.svg +27 -0
  22. package/gui/dist/provider-icons/nous.svg +149 -0
  23. package/gui/dist/provider-icons/novita.svg +32 -0
  24. package/gui/dist/provider-icons/oh-my-pi.svg +11 -0
  25. package/gui/dist/provider-icons/openclaw.svg +54 -0
  26. package/gui/dist/provider-icons/orcarouter.svg +175 -0
  27. package/gui/dist/provider-icons/parallel.svg +13 -0
  28. package/gui/dist/provider-icons/prime-agent.svg +21 -0
  29. package/gui/dist/provider-icons/sambanova.svg +276 -0
  30. package/gui/dist/provider-icons/scaleway.svg +11 -0
  31. package/gui/dist/provider-icons/siliconflow.svg +18 -0
  32. package/gui/dist/provider-icons/synthetic.svg +12 -0
  33. package/gui/dist/provider-icons/together.svg +18 -0
  34. package/gui/dist/provider-icons/umans.svg +30 -0
  35. package/gui/dist/provider-icons/venice.svg +165 -0
  36. package/gui/dist/provider-icons/vultr.svg +15 -0
  37. package/gui/dist/provider-icons/zai.svg +218 -0
  38. package/gui/dist/provider-icons/zcode.svg +219 -0
  39. package/gui/dist/provider-icons/zenmux.svg +1 -0
  40. package/package.json +1 -1
  41. package/src/adapters/cursor/live-models.ts +1 -0
  42. package/src/adapters/cursor/protobuf-request.ts +4 -1
  43. package/src/adapters/cursor/tool-definitions.ts +36 -4
  44. package/src/adapters/openai-responses.ts +72 -10
  45. package/src/bridge.ts +15 -15
  46. package/src/cli/capabilities.ts +14 -0
  47. package/src/cli/codex-cli-update.ts +96 -0
  48. package/src/cli/codex-shim-autorestore.ts +3 -0
  49. package/src/cli/dispatch.ts +54 -18
  50. package/src/cli/export-command.ts +18 -17
  51. package/src/cli/help.ts +2 -2
  52. package/src/cli/index.ts +423 -32
  53. package/src/cli/integrations.ts +24 -2
  54. package/src/cli/launcher-context.ts +53 -2
  55. package/src/cli/opencode.ts +126 -33
  56. package/src/cli/registry.ts +16 -10
  57. package/src/cli/system-command.ts +6 -1
  58. package/src/cli/uninstall-plan.ts +86 -0
  59. package/src/clients/config-export.ts +293 -28
  60. package/src/codex/account-store.ts +131 -12
  61. package/src/codex/auth-api.ts +202 -33
  62. package/src/codex/autostart-health.ts +3 -3
  63. package/src/codex/catalog/provider-fetch.ts +96 -10
  64. package/src/codex/catalog/sync.ts +4 -3
  65. package/src/codex/cli-install-provenance.ts +795 -0
  66. package/src/codex/convergence.ts +4 -3
  67. package/src/codex/credential-mutation-epoch.ts +11 -0
  68. package/src/codex/history-job.ts +10 -0
  69. package/src/codex/history-manifest.ts +35 -2
  70. package/src/codex/history-provider.ts +196 -19
  71. package/src/codex/history-worker.ts +3 -0
  72. package/src/codex/main-account.ts +2 -0
  73. package/src/codex/model-entitlements.ts +430 -27
  74. package/src/codex/native-profile-manager.ts +4 -0
  75. package/src/codex/quota-401-recovery.ts +190 -0
  76. package/src/codex/quota-recovery-timing.ts +28 -0
  77. package/src/codex/quota.ts +6 -0
  78. package/src/codex/reset-credit-operation-ledger.ts +1411 -0
  79. package/src/codex/reset-credit-recovery.ts +20 -2
  80. package/src/codex/routing.ts +68 -16
  81. package/src/codex/shim.ts +204 -18
  82. package/src/codex/subagent-model-fallback.ts +4 -1
  83. package/src/codex/user-identity.ts +2 -1
  84. package/src/config/paths.ts +18 -3
  85. package/src/config/pending-teardown-names.d.mts +8 -0
  86. package/src/config/pending-teardown-names.mjs +69 -0
  87. package/src/config/pending-teardown.ts +286 -0
  88. package/src/config.ts +23 -0
  89. package/src/generated/compatibility-version.json +155 -67
  90. package/src/integrations/journal.ts +12 -1
  91. package/src/integrations/registry.ts +112 -0
  92. package/src/integrations/state.ts +67 -5
  93. package/src/integrations/writer.ts +107 -16
  94. package/src/lib/bounded-subprocess.ts +36 -0
  95. package/src/lib/process-control.ts +30 -5
  96. package/src/lib/state-store-registrations.ts +8 -0
  97. package/src/lib/strict-semver.ts +47 -0
  98. package/src/lib/windows-elevation.ts +32 -1
  99. package/src/lib/windows-secret-acl.ts +47 -25
  100. package/src/lib/windows-service-mutation-lock.ts +133 -0
  101. package/src/lib/windows-user-principal.ts +15 -17
  102. package/src/oauth/index.ts +220 -13
  103. package/src/oauth/store.ts +220 -18
  104. package/src/responses/spill-store.ts +354 -29
  105. package/src/responses/state.ts +775 -8
  106. package/src/server/index.ts +4 -3
  107. package/src/server/lifecycle.ts +5 -1
  108. package/src/server/management/integration-routes.ts +36 -5
  109. package/src/server/management/model-rows.ts +11 -2
  110. package/src/server/management/provider-routes.ts +4 -0
  111. package/src/server/management/system-restart.ts +5 -5
  112. package/src/server/management-api.ts +72 -13
  113. package/src/server/responses/encrypted-payload.ts +18 -1
  114. package/src/server/startup-action-control.ts +3 -2
  115. package/src/server/stop-teardown.ts +84 -0
  116. package/src/service.ts +749 -53
  117. package/src/sidecar/candidates.ts +1 -1
  118. package/src/update/codex-cli-update-launch-policy.d.mts +18 -0
  119. package/src/update/codex-cli-update-launch-policy.mjs +30 -0
  120. package/src/update/index.ts +39 -6
  121. package/src/update/job.ts +10 -11
  122. package/src/update/proxy-liveness-probe.d.mts +6 -0
  123. package/src/update/proxy-liveness-probe.mjs +84 -0
  124. package/src/update/stop-contract.d.mts +2 -0
  125. package/src/update/stop-contract.mjs +15 -0
  126. package/src/update/stop-decision.d.mts +10 -0
  127. package/src/update/stop-decision.mjs +34 -0
  128. package/src/vision/eligibility.ts +19 -1
  129. package/src/vision/index.ts +4 -18
  130. package/gui/dist/assets/index-CowztZdo.js +0 -112
  131. package/gui/dist/assets/index-jqE_VOKI.css +0 -1
@@ -30,6 +30,7 @@ import {
30
30
  import type { OcxConfig, OcxProviderConfig } from "../../types";
31
31
  import { modelInList } from "../../types";
32
32
  import { CODEX_REASONING_LEVELS, codexEffortRank, configuredReasoningEfforts, modelRecordValue, sanitizeCodexReasoningEfforts } from "../../reasoning-effort";
33
+ import { isModelVisionSidecarConsumer } from "../../vision/eligibility";
33
34
  import { getModelMetadata, getModelMetadataCaseInsensitive, listModelMetadata, resolveMetadataProvider } from "../../generated/model-metadata";
34
35
  import { enrichProviderFromRegistry, shouldCaseFoldMetadataModelId } from "../../providers/derive";
35
36
  import {
@@ -42,6 +43,7 @@ import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryT
42
43
  import { parseAntigravityAvailableModels, registerAntigravityDiscoveredWireModels } from "../../providers/antigravity-models";
43
44
  import { applyProviderContextCap, providerContextCap, resolveUnknownRoutedContextWindow } from "../../providers/context-cap";
44
45
  import { clampAutoCompactTokenLimit } from "../../providers/auto-compact-budget";
46
+ import { effectiveModelAliases } from "../../providers/default-aliases";
45
47
  import { routedSlug, slugEquals, slugEquivalenceKey, slugsEquivalent } from "../../providers/slug-codec";
46
48
  import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
47
49
  import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
@@ -669,11 +671,14 @@ export function applyProviderConfigHints(name: string, prov: OcxProviderConfig,
669
671
  const configuredMaxInput = configuredMaxInputTokens(prov, model.id);
670
672
  const configuredAutoCompact = configuredAutoCompactTokenLimit(prov, model.id);
671
673
  let inputModalities = configuredInputModalities(prov, model.id);
672
- // Vision-sidecar coverage: `noVisionModels` marks models whose images the PROXY describes
673
- // (src/vision/index.ts). The catalog must still advertise image input for them — the Codex app
674
+ // The shared vision-sidecar consumer predicate keeps catalog advertisement and request-time
675
+ // planning aligned. The catalog must still advertise image input — the Codex app
674
676
  // gates attachments client-side on input_modalities, and a text-only entry would block images
675
- // before the sidecar ever runs ("This model does not support image inputs").
676
- if (modelInList(prov.noVisionModels, model.id)) {
677
+ // before the sidecar ever runs ("This model does not support image inputs"). Discovery-derived
678
+ // text-only rows stay untouched: the runtime predicate only reads these two config sources, so
679
+ // it would not convert those.
680
+ const sidecarCovered = isModelVisionSidecarConsumer(prov, model.id);
681
+ if (sidecarCovered) {
677
682
  const base = inputModalities ?? model.inputModalities ?? ["text"];
678
683
  inputModalities = base.includes("image") ? [...base] : [...base, "image"];
679
684
  }
@@ -935,9 +940,70 @@ export function resolveComboCatalogMember(
935
940
  };
936
941
  }
937
942
 
943
+ const DATED_VARIANT_YYYYMMDD = /^(\d{4})(\d{2})(\d{2})$/;
944
+ const DATED_VARIANT_YYMMDD = /^(2\d)(\d{2})(\d{2})$/;
945
+ const DATED_VARIANT_MMDD_OR_YYMM = /^(\d{2})(\d{2})$/;
946
+
947
+ /** Whether a Gregorian year contains February 29th. */
948
+ function isLeapYear(year: number): boolean {
949
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
950
+ }
951
+
952
+ /**
953
+ * Whether a month/day pair exists in the given year. Without a year, February 29th is
954
+ * accepted because it occurs in at least one calendar year.
955
+ */
956
+ function isValidCalendarDate(year: number | undefined, month: number, day: number): boolean {
957
+ if (year !== undefined && (year < 1 || year > 9999)) return false;
958
+ if (month < 1 || month > 12 || day < 1) return false;
959
+ const daysInMonth = [
960
+ 31, year === undefined || isLeapYear(year) ? 29 : 28, 31, 30, 31, 30,
961
+ 31, 31, 30, 31, 30, 31,
962
+ ];
963
+ return day <= daysInMonth[month - 1]!;
964
+ }
965
+
966
+ /**
967
+ * Release-date suffixes providers actually publish: `YYYYMMDD` (`-20251001`), `YYMMDD`
968
+ * (`-260806`), `MMDD` (`-0813`) and `YYMM` (`-2512`). A `\d{8}`-only rule matched none of
969
+ * the dated ids on a real multi-provider install, so DeepSeek, Kimi, Mistral, Qwen and
970
+ * Solar aliases all fell through to `droppedConfiguredIds` (#3024).
971
+ *
972
+ * Calendar validation rejects impossible month-end and leap-day values as well as ordinary
973
+ * numeric suffixes such as `-2048`, `-4096` and `-8192`. `-1024` is the one irreducible
974
+ * collision — it is a valid `MMDD` (October 24th) — so it reads as dated. That is a known,
975
+ * accepted cost; the test table pins it so it cannot become a surprise later.
976
+ *
977
+ * Hyphenated ISO suffixes (`-2024-08-06`, `-05-06`) are deliberately out of scope: a
978
+ * hyphenated suffix is ambiguous against ordinary name segments and needs its own call.
979
+ */
980
+ function isDatedVariantSuffix(suffix: string): boolean {
981
+ const yyyyMmDd = DATED_VARIANT_YYYYMMDD.exec(suffix);
982
+ if (yyyyMmDd) {
983
+ return isValidCalendarDate(
984
+ Number(yyyyMmDd[1]), Number(yyyyMmDd[2]), Number(yyyyMmDd[3]),
985
+ );
986
+ }
987
+
988
+ const yyMmDd = DATED_VARIANT_YYMMDD.exec(suffix);
989
+ if (yyMmDd) {
990
+ return isValidCalendarDate(
991
+ 2000 + Number(yyMmDd[1]), Number(yyMmDd[2]), Number(yyMmDd[3]),
992
+ );
993
+ }
994
+
995
+ const mmDdOrYyMm = DATED_VARIANT_MMDD_OR_YYMM.exec(suffix);
996
+ if (!mmDdOrYyMm) return false;
997
+ const first = Number(mmDdOrYyMm[1]);
998
+ const second = Number(mmDdOrYyMm[2]);
999
+ return isValidCalendarDate(undefined, first, second)
1000
+ || (first >= 20 && first <= 29 && second >= 1 && second <= 12);
1001
+ }
1002
+
1003
+ /** Whether `liveId` is a supported dated release of the configured base id. */
938
1004
  export function isDatedVariantId(liveId: string, configuredId: string): boolean {
939
1005
  if (!liveId.startsWith(`${configuredId}-`)) return false;
940
- return /^\d{8}$/.test(liveId.slice(configuredId.length + 1));
1006
+ return isDatedVariantSuffix(liveId.slice(configuredId.length + 1));
941
1007
  }
942
1008
 
943
1009
  export const lastDropWarnSignature = new Map<string, string>();
@@ -2112,9 +2178,10 @@ async function gatherRoutedModelsUncached(
2112
2178
  ...(base.codexToolMode === undefined && replaced.codexToolMode !== undefined ? { codexToolMode: replaced.codexToolMode } : {}),
2113
2179
  ...(base.capabilities === undefined && replaced.capabilities !== undefined ? { capabilities: replaced.capabilities } : {}),
2114
2180
  } : base;
2115
- // Vision-sidecar coverage ONLY: if the custom model is in the enriched provider's
2116
- // noVisionModels, advertise image input so the Codex app lets images reach the sidecar
2117
- // (#349/#344). Deliberately NOT the full applyProviderConfigHints pass — custom rows are a
2181
+ // Vision-sidecar coverage only: when the enriched provider's shared predicate matches
2182
+ // noVisionModels or text-without-image modelInputModalities, advertise image input so the
2183
+ // Codex app lets images reach the sidecar (#349/#344). Deliberately NOT the full
2184
+ // applyProviderConfigHints pass — custom rows are a
2118
2185
  // user override, so their explicit contextWindow / inputModalities / reasoning fields must be
2119
2186
  // preserved verbatim (the hint pass would cap context and overwrite modalities from registry).
2120
2187
  const mergedContext = typeof merged.contextWindow === "number" && merged.contextWindow > 0
@@ -2140,7 +2207,8 @@ async function gatherRoutedModelsUncached(
2140
2207
  }
2141
2208
  : mergedWithHardBounds;
2142
2209
  const enrichedProvider = enrichedByName.get(cm.provider) ?? rawProvider;
2143
- if (enrichedProvider && modelInList(enrichedProvider.noVisionModels, mergedWithAutoCompact.id)) {
2210
+ // Reuse the request-time consumer predicate so custom rows cannot drift from catalog hints.
2211
+ if (enrichedProvider && isModelVisionSidecarConsumer(enrichedProvider, mergedWithAutoCompact.id)) {
2144
2212
  const current = mergedWithAutoCompact.inputModalities ?? ["text"];
2145
2213
  if (!current.includes("image")) {
2146
2214
  return { ...mergedWithAutoCompact, inputModalities: [...current, "image"] };
@@ -2151,6 +2219,21 @@ async function gatherRoutedModelsUncached(
2151
2219
  // Custom rows override discovered rows that encode to the same Codex-facing slug.
2152
2220
  const customKeys = new Set(customModels.map(c => routedSlug(c.provider, c.id)));
2153
2221
  const deduped = all.filter(m => !customKeys.has(routedSlug(m.provider, m.id)));
2222
+ const models = [...deduped, ...customModels];
2223
+ // ponytail: catalog-scale scan; index ids by provider if catalog growth makes this measurable.
2224
+ const aliasDisplayNames = new Map(activeProviders.flatMap(({ name, provider }) => {
2225
+ const providerModels = models.filter(model => model.provider === name);
2226
+ const aliases = [...effectiveModelAliases(config, provider, providerModels.map(model => model.id))];
2227
+ return aliases.flatMap(([id, { alias }]) => {
2228
+ const exact = providerModels.filter(model => model.id === id);
2229
+ const matches = exact.length > 0
2230
+ ? exact
2231
+ : providerModels.filter(model => model.id.toLowerCase() === id.toLowerCase());
2232
+ return matches.length === 1
2233
+ ? [[`${name}/${matches[0]!.id}`, `${provider.alias || name}/${alias}`] as const]
2234
+ : [];
2235
+ });
2236
+ }));
2154
2237
  const providerModelOutcomes = providerResults.map(result => (
2155
2238
  result.outcome.provider === OPENAI_API_PROVIDER_ID
2156
2239
  && capture.openAiApiPolicy.state === "captured"
@@ -2159,7 +2242,10 @@ async function gatherRoutedModelsUncached(
2159
2242
  : result.outcome
2160
2243
  ));
2161
2244
  return {
2162
- models: [...deduped, ...customModels],
2245
+ models: models.map(model => {
2246
+ const displayName = aliasDisplayNames.get(`${model.provider}/${model.id}`);
2247
+ return displayName && !model.displayName ? { ...model, displayName } : model;
2248
+ }),
2163
2249
  comboOmissions: localOmissions,
2164
2250
  providerAuthOutcomes: localProviderAuthOutcomes,
2165
2251
  providerModelOutcomes,
@@ -35,6 +35,7 @@ import { codexAccountNamespaceEntries, isMainCodexAccountTarget } from "../accou
35
35
  import { MAIN_CODEX_ACCOUNT_ID } from "../main-account";
36
36
  import {
37
37
  availableAccountGatedNativeModels,
38
+ codexModelEntitlementStateForAccount,
38
39
  isCodexModelEntitlementSnapshotCurrent,
39
40
  resolveCodexModelEntitlements,
40
41
  type CodexModelEntitlementSnapshot,
@@ -1613,10 +1614,10 @@ function writeRetainedCatalogSync({
1613
1614
  ? new Map([...accountBoundNativeOpenAiSlugsBySelector(config, observedAccountNativeEntries)].map(([selector, slugs]) => {
1614
1615
  const target = accountTargets.get(selector);
1615
1616
  const accountId = target && isMainCodexAccountTarget(target) ? MAIN_CODEX_ACCOUNT_ID : target;
1616
- const entitled = accountId ? modelEntitlements.modelsByAccount.get(accountId) : undefined;
1617
- const confirmed = accountId ? modelEntitlements.confirmedAccountIds.has(accountId) : false;
1618
1617
  return [selector, slugs.filter(slug => (
1619
- !ACCOUNT_GATED_NATIVE_OPENAI_MODELS.has(slug) || (confirmed && entitled?.has(slug) === true)
1618
+ !ACCOUNT_GATED_NATIVE_OPENAI_MODELS.has(slug)
1619
+ || (accountId !== undefined
1620
+ && codexModelEntitlementStateForAccount(modelEntitlements, accountId, slug) === "granted")
1620
1621
  ))] as const;
1621
1622
  }))
1622
1623
  : new Map<string, readonly string[]>();