@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.
Files changed (246) hide show
  1. package/AGENTS_INSTALL.md +2 -2
  2. package/README.md +31 -0
  3. package/bin/ocx.mjs +10 -1
  4. package/gui/dist/assets/index-DS1NE4Jn.css +1 -0
  5. package/gui/dist/assets/index-Djowl68T.js +112 -0
  6. package/gui/dist/index.html +2 -2
  7. package/package.json +1 -1
  8. package/src/adapters/anthropic-image-codec.ts +304 -0
  9. package/src/adapters/anthropic-image-normalize.ts +8 -298
  10. package/src/adapters/anthropic.ts +30 -7
  11. package/src/adapters/command-code.ts +7 -5
  12. package/src/adapters/cursor/desktop-executor-contract.ts +15 -0
  13. package/src/adapters/cursor/images.ts +36 -6
  14. package/src/adapters/cursor/live-transport.ts +7 -2
  15. package/src/adapters/cursor/native-exec-desktop.ts +2 -15
  16. package/src/adapters/cursor/protobuf-request.ts +13 -3
  17. package/src/adapters/cursor/tool-definitions.ts +5 -670
  18. package/src/adapters/cursor/tool-guidance.ts +236 -0
  19. package/src/adapters/cursor/tool-naming.ts +252 -0
  20. package/src/adapters/cursor/tool-schemas.ts +195 -0
  21. package/src/adapters/cursor/types.ts +6 -3
  22. package/src/adapters/exec-tool-result-normalize.ts +1 -1
  23. package/src/adapters/google-errors.ts +9 -1
  24. package/src/adapters/google.ts +1 -0
  25. package/src/adapters/kiro-calibration.ts +181 -0
  26. package/src/adapters/kiro.ts +135 -3
  27. package/src/adapters/openai-responses.ts +216 -26
  28. package/src/adapters/responses-code-mode.ts +59 -0
  29. package/src/adapters/tool-catalog-nudge.ts +1 -1
  30. package/src/adapters/xai-schema-analysis.ts +86 -0
  31. package/src/adapters/xai-tool-schema.ts +2 -87
  32. package/src/adapters/xai-web-search.ts +1 -1
  33. package/src/bridge.ts +22 -10
  34. package/src/chat/inbound.ts +11 -3
  35. package/src/claude/inbound-content-options.ts +60 -0
  36. package/src/claude/inbound-model-options.ts +142 -0
  37. package/src/claude/inbound-records.ts +7 -0
  38. package/src/claude/inbound.ts +10 -202
  39. package/src/claude/model-info.ts +45 -0
  40. package/src/cli/account-auth.ts +21 -6
  41. package/src/cli/capabilities.ts +13 -2
  42. package/src/cli/claude.ts +232 -39
  43. package/src/cli/config-command.ts +9 -1
  44. package/src/cli/dispatch.ts +5 -1
  45. package/src/cli/doctor.ts +10 -0
  46. package/src/cli/effort.ts +372 -0
  47. package/src/cli/export-command.ts +3 -9
  48. package/src/cli/help.ts +1 -0
  49. package/src/cli/index.ts +13 -0
  50. package/src/cli/init.ts +4 -0
  51. package/src/cli/model-selection-guidance.ts +30 -0
  52. package/src/cli/models-runtime.ts +3 -2
  53. package/src/cli/models.ts +8 -3
  54. package/src/cli/opencode.ts +4 -1
  55. package/src/cli/provider-runtime.ts +65 -0
  56. package/src/cli/provider.ts +8 -0
  57. package/src/cli/registry.ts +16 -2
  58. package/src/cli/runtime-api.ts +3 -1
  59. package/src/cli/star-prompt.ts +22 -6
  60. package/src/cli/status-probes.ts +168 -0
  61. package/src/cli/status.ts +5 -168
  62. package/src/clients/config-export/constants.ts +69 -0
  63. package/src/clients/config-export/contracts.ts +154 -0
  64. package/src/clients/config-export/dsh.ts +132 -0
  65. package/src/clients/config-export/fast-models.ts +29 -0
  66. package/src/clients/config-export/mcode.ts +83 -0
  67. package/src/clients/config-export/model-metadata.ts +108 -0
  68. package/src/clients/config-export/omp.ts +104 -0
  69. package/src/clients/config-export/zcode.ts +92 -0
  70. package/src/clients/config-export.ts +18 -710
  71. package/src/codex/account-lifecycle.ts +20 -3
  72. package/src/codex/account-usability.ts +2 -0
  73. package/src/codex/auth-api.ts +170 -23
  74. package/src/codex/auth-context.ts +328 -24
  75. package/src/codex/catalog/effort.ts +30 -4
  76. package/src/codex/catalog/metadata.ts +39 -9
  77. package/src/codex/catalog/native-models.ts +53 -12
  78. package/src/codex/catalog/parsing.ts +119 -5
  79. package/src/codex/catalog/provider-fetch.ts +150 -20
  80. package/src/codex/catalog/reserve.ts +52 -0
  81. package/src/codex/catalog/sync.ts +89 -16
  82. package/src/codex/convergence-types.ts +1 -0
  83. package/src/codex/convergence.ts +2 -0
  84. package/src/codex/data/upstream-models.json +1 -1
  85. package/src/codex/forward-transport-headers.ts +25 -0
  86. package/src/codex/inject.ts +3 -28
  87. package/src/codex/legacy-config-keys.ts +68 -0
  88. package/src/codex/log-guard/inspect-schema.ts +137 -0
  89. package/src/codex/log-guard/inspect.ts +2 -134
  90. package/src/codex/loopback-target.ts +54 -0
  91. package/src/codex/main-account-cache.ts +63 -1
  92. package/src/codex/main-account-hard-lock.ts +52 -0
  93. package/src/codex/main-account.ts +3 -1
  94. package/src/codex/management-convergence.ts +3 -0
  95. package/src/codex/model-entitlements.ts +54 -4
  96. package/src/codex/project-config-warnings.ts +92 -2
  97. package/src/codex/prompt-layers/encoding.ts +80 -0
  98. package/src/codex/prompt-layers/paths.ts +54 -0
  99. package/src/codex/prompt-layers/revision.ts +55 -0
  100. package/src/codex/prompt-layers/toml-edit.ts +163 -0
  101. package/src/codex/prompt-layers/toml-read.ts +181 -0
  102. package/src/codex/prompt-layers.ts +14 -520
  103. package/src/codex/quota-auto-refresh-state.ts +16 -0
  104. package/src/codex/quota-auto-refresh.ts +219 -0
  105. package/src/codex/quota-types.ts +51 -0
  106. package/src/codex/quota.ts +252 -93
  107. package/src/codex/reserve-availability.ts +177 -0
  108. package/src/codex/routing.ts +28 -9
  109. package/src/codex/shim.ts +53 -11
  110. package/src/codex/subagent-model-fallback.ts +23 -3
  111. package/src/combos/failover.ts +125 -7
  112. package/src/combos/identifiers.ts +89 -0
  113. package/src/combos/index.ts +4 -0
  114. package/src/combos/resolve.ts +80 -9
  115. package/src/combos/types.ts +20 -93
  116. package/src/config/subagent-models.ts +24 -0
  117. package/src/config.ts +156 -13
  118. package/src/generated/compatibility-version.json +454 -166
  119. package/src/generated/model-metadata.ts +1 -1
  120. package/src/integrations/journal.ts +65 -4
  121. package/src/integrations/store.ts +5 -0
  122. package/src/lab/events/limits.ts +4 -0
  123. package/src/lib/destination-policy.ts +31 -2
  124. package/src/lib/errors.ts +39 -0
  125. package/src/lib/provider-outbound.ts +69 -3
  126. package/src/lib/proxy-env.ts +22 -0
  127. package/src/lib/redact-folding.ts +176 -0
  128. package/src/lib/redact.ts +2 -175
  129. package/src/lib/state-store-sweeper.ts +20 -6
  130. package/src/lib/token-estimate.ts +94 -27
  131. package/src/lib/windows-user-principal.ts +53 -5
  132. package/src/oauth/anthropic-routing.ts +99 -3
  133. package/src/oauth/generic-account-failover.ts +36 -13
  134. package/src/oauth/index.ts +127 -26
  135. package/src/oauth/login-cli.ts +5 -0
  136. package/src/oauth/meta-muse.ts +117 -15
  137. package/src/oauth/pool-settings-capability.ts +15 -4
  138. package/src/providers/api-keys.ts +8 -10
  139. package/src/providers/default-aliases.ts +39 -0
  140. package/src/providers/derive.ts +10 -2
  141. package/src/providers/fastwire.ts +36 -7
  142. package/src/providers/initial-model-selection-runtime.ts +90 -0
  143. package/src/providers/initial-model-selection.ts +120 -0
  144. package/src/providers/key-failover.ts +134 -54
  145. package/src/providers/key-store.ts +11 -1
  146. package/src/providers/label.ts +1 -1
  147. package/src/providers/model-discovery.ts +76 -0
  148. package/src/providers/model-rename-startup.ts +72 -8
  149. package/src/providers/openai-sidecar.ts +17 -5
  150. package/src/providers/openai-tiers-destination.ts +102 -0
  151. package/src/providers/openai-tiers.ts +2 -99
  152. package/src/providers/opencode-go-transport.ts +41 -0
  153. package/src/providers/quota-key-accounts.ts +141 -0
  154. package/src/providers/quota-types.ts +9 -0
  155. package/src/providers/quota.ts +535 -104
  156. package/src/providers/registry.ts +60 -17
  157. package/src/providers/xai-responses-opt-in.ts +31 -5
  158. package/src/quota/reset-activation.ts +81 -0
  159. package/src/quota/reset-detector.ts +305 -0
  160. package/src/quota/reset-notify-config.ts +162 -0
  161. package/src/quota/reset-observer.ts +125 -0
  162. package/src/quota/reset-poller.ts +160 -0
  163. package/src/quota/reset-seen-store.ts +385 -0
  164. package/src/quota/reset-sinks.ts +199 -0
  165. package/src/quota/window-mapping.ts +106 -0
  166. package/src/responses/apply-patch-envelope.ts +46 -0
  167. package/src/responses/code-mode-helper-compat.ts +39 -1
  168. package/src/responses/custom-tool-compat.ts +10 -4
  169. package/src/responses/hosted-tool-policy.ts +12 -4
  170. package/src/responses/parser-content.ts +133 -0
  171. package/src/responses/parser-text-format.ts +24 -0
  172. package/src/responses/parser-tools.ts +188 -0
  173. package/src/responses/parser.ts +3 -326
  174. package/src/responses/state.ts +124 -28
  175. package/src/router.ts +48 -13
  176. package/src/routing/analytics.ts +1 -0
  177. package/src/routing/capability.ts +17 -4
  178. package/src/server/auth-cors.ts +7 -1
  179. package/src/server/background-lifecycle.ts +23 -1
  180. package/src/server/chat-completions.ts +25 -3
  181. package/src/server/claude-messages.ts +62 -5
  182. package/src/server/effort-row.ts +1 -1
  183. package/src/server/fast-row.ts +295 -0
  184. package/src/server/gui-static.ts +30 -4
  185. package/src/server/index.ts +107 -21
  186. package/src/server/management/agent-settings-routes.ts +2 -2
  187. package/src/server/management/combo-routes.ts +37 -9
  188. package/src/server/management/config-routes.ts +93 -2
  189. package/src/server/management/integration-routes.ts +108 -0
  190. package/src/server/management/model-routes.ts +13 -3
  191. package/src/server/management/model-rows.ts +20 -1
  192. package/src/server/management/native-integration-routes.ts +4 -1
  193. package/src/server/management/oauth-account-routes.ts +38 -10
  194. package/src/server/management/provider-routes.ts +34 -3
  195. package/src/server/management/quota-reset-routes.ts +57 -0
  196. package/src/server/management/route-registry.ts +7 -4
  197. package/src/server/management/shared.ts +19 -5
  198. package/src/server/management/system-routes.ts +3 -2
  199. package/src/server/management-api.ts +14 -2
  200. package/src/server/ports.ts +12 -2
  201. package/src/server/relay-eager.ts +38 -23
  202. package/src/server/relay.ts +4 -0
  203. package/src/server/request-log.ts +6 -0
  204. package/src/server/responses/codex-ws-correlation.ts +65 -0
  205. package/src/server/responses/codex-ws-exchange.ts +261 -0
  206. package/src/server/responses/codex-ws-metadata.ts +134 -0
  207. package/src/server/responses/codex-ws-pool.ts +162 -0
  208. package/src/server/responses/codex-ws-request.ts +87 -0
  209. package/src/server/responses/codex-ws-session.ts +93 -0
  210. package/src/server/responses/codex-ws-wire.ts +144 -0
  211. package/src/server/responses/collaboration.ts +41 -1
  212. package/src/server/responses/compact.ts +105 -12
  213. package/src/server/responses/core.ts +476 -57
  214. package/src/server/responses/fetch-helpers.ts +10 -3
  215. package/src/server/responses/input-admission.ts +16 -9
  216. package/src/server/responses/responses-field-backfill.ts +1 -1
  217. package/src/server/responses/ws-upstream.ts +34 -318
  218. package/src/server/responses-custom-tool-repair.ts +20 -4
  219. package/src/server/responses-undeclared-tool-guard.ts +100 -8
  220. package/src/server/safe-response-headers.ts +23 -0
  221. package/src/server/search.ts +9 -0
  222. package/src/server/subagent-models-startup.ts +27 -0
  223. package/src/server/system-env-shell.ts +238 -0
  224. package/src/server/system-env.ts +7 -234
  225. package/src/server/ws-bridge.ts +3 -25
  226. package/src/server/xai-responses-startup.ts +21 -0
  227. package/src/service-manager-probe.ts +1 -1
  228. package/src/service.ts +55 -16
  229. package/src/types/config.ts +108 -12
  230. package/src/types/provider.ts +36 -7
  231. package/src/types/tools.ts +26 -1
  232. package/src/types.ts +2 -0
  233. package/src/update/notify.ts +8 -2
  234. package/src/usage/cost.ts +38 -28
  235. package/src/usage/expected-prices.ts +34 -15
  236. package/src/usage/log.ts +2 -0
  237. package/src/usage/model-identity.ts +26 -0
  238. package/src/usage/summary.ts +15 -1
  239. package/src/vision/describe.ts +6 -0
  240. package/src/vision/image-rewrite.ts +108 -0
  241. package/src/vision/index.ts +19 -306
  242. package/src/vision/plan.ts +205 -0
  243. package/src/web-search/executor.ts +6 -0
  244. package/src/web-search/index.ts +8 -1
  245. package/gui/dist/assets/index-BU1tE0sr.js +0 -112
  246. package/gui/dist/assets/index-DL9-iS6J.css +0 -1
package/src/config.ts CHANGED
@@ -4,6 +4,8 @@ import { dirname, join } from "node:path";
4
4
  import { Database } from "bun:sqlite";
5
5
  import * as z from "zod/v4";
6
6
  import { isValidProviderName, hasOwnProvider } from "./config/provider-name";
7
+ import { DEFAULT_SUBAGENT_MODELS, SUBAGENT_MODELS_VERSION } from "./config/subagent-models";
8
+ export { DEFAULT_SUBAGENT_MODELS } from "./config/subagent-models";
7
9
  import {
8
10
  apiKeyTransportConfigError,
9
11
  booleanRecordConfigError,
@@ -523,6 +525,12 @@ const providerConfigSchema = z.object({
523
525
  modelAliases: z.record(z.string(), z.string()).optional(),
524
526
  modelDisplayNames: modelDisplayNamesSchema.optional(),
525
527
  defaultAliases: z.boolean().optional(),
528
+ initialModelSelection: z.object({
529
+ version: z.literal(1),
530
+ registrationId: z.uuid(),
531
+ status: z.enum(["pending", "ready", "all-off"]),
532
+ modelCount: z.number().int().nonnegative().optional(),
533
+ }).optional().catch(undefined),
526
534
  requestPacing: requestPacingSchema.optional().catch(undefined),
527
535
  mcpMaxTools: z.number().int().positive().optional(),
528
536
  mcpMaxSchemaBytes: z.number().int().positive().optional(),
@@ -537,6 +545,7 @@ const providerConfigSchema = z.object({
537
545
  modelSupportsServiceTier: z.record(z.string().min(1), z.boolean()).optional(),
538
546
  preserveResponsesReasoningContent: z.boolean().optional(),
539
547
  decodesNativeCompactionBlobs: z.boolean().optional(),
548
+ allowEncryptedV2AgentTasks: z.boolean().optional(),
540
549
  allowPrivateNetwork: z.boolean().optional(),
541
550
  // The management API accepts `null` as "clear this", so a config written before the POST
542
551
  // canonicalization below can hold one on disk. Rejecting it here would send the operator
@@ -574,6 +583,7 @@ const providerConfigSchema = z.object({
574
583
  }).strict().optional(),
575
584
  responsesSnapshotRepair: z.boolean().optional(),
576
585
  xaiResponsesXSearch: z.boolean().optional(),
586
+ xaiResponsesDefaultVersion: z.number().int().positive().optional().catch(undefined),
577
587
  }).passthrough();
578
588
 
579
589
  export { isValidProviderName, hasOwnProvider } from "./config/provider-name";
@@ -840,6 +850,34 @@ const codexAccountPrioritiesSchema = z.custom<Record<string, unknown>>(
840
850
  }
841
851
  }).pipe(z.record(z.string(), z.number().int()));
842
852
 
853
+ const codexQuotaAutoRefreshEntrySchema = z.object({
854
+ fiveHour: z.boolean().optional(),
855
+ weekly: z.boolean().optional(),
856
+ lastFiveHourResetAt: z.number().finite().nonnegative().optional(),
857
+ lastWeeklyResetAt: z.number().finite().nonnegative().optional(),
858
+ }).strict();
859
+ const CODEX_QUOTA_AUTO_REFRESH_KEY_ERROR =
860
+ "quota auto-refresh keys must be a Codex pool-account id or the main Codex account and cannot be reserved JavaScript object keys";
861
+
862
+ const codexQuotaAutoRefreshSchema = z.custom<Record<string, unknown>>(
863
+ (value): value is Record<string, unknown> => !!value
864
+ && typeof value === "object"
865
+ && !Array.isArray(value)
866
+ && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null),
867
+ { error: "codexQuotaAutoRefresh must be a plain object" },
868
+ ).superRefine((settings, ctx) => {
869
+ // Inspect own entries before z.record parses them; Zod omits __proto__ record keys.
870
+ for (const [accountId, setting] of Object.entries(settings)) {
871
+ if (!isCodexAccountPriorityKey(accountId)) {
872
+ ctx.addIssue({ code: "custom", path: [accountId], message: CODEX_QUOTA_AUTO_REFRESH_KEY_ERROR });
873
+ }
874
+ const parsed = codexQuotaAutoRefreshEntrySchema.safeParse(setting);
875
+ if (!parsed.success) {
876
+ ctx.addIssue({ code: "custom", path: [accountId], message: "invalid quota auto-refresh setting" });
877
+ }
878
+ }
879
+ }).pipe(z.record(z.string(), codexQuotaAutoRefreshEntrySchema));
880
+
843
881
  /**
844
882
  * Deliberately permissive. A user's config is not ours to invalidate: a strict
845
883
  * entry fails the whole parse, and loadConfig's fallback then backs the file up
@@ -998,6 +1036,32 @@ const clientConnectionSchema = z.object({
998
1036
  }).optional(),
999
1037
  }).strict();
1000
1038
 
1039
+ /**
1040
+ * Quota-reset notification section.
1041
+ *
1042
+ * `.strict()` like its neighbour: a typo in an optional feature section should surface as a
1043
+ * rejected write rather than a silently ignored key that leaves the operator believing they
1044
+ * enabled something.
1045
+ *
1046
+ * `pollSeconds` admits 0 (passive-only, no timer) and the resolver clamps anything between 1
1047
+ * and the 60-second floor. Bounds live in the resolver rather than here so a hand-edited value
1048
+ * degrades to a sane one instead of discarding the whole section.
1049
+ */
1050
+ const quotaResetNotifySchema = z.object({
1051
+ enabled: z.boolean().optional(),
1052
+ kinds: z.array(z.enum(["scheduled", "surprise"])).optional(),
1053
+ pollSeconds: z.number().int().min(0).optional(),
1054
+ // `z.string().url()` accepts any scheme. The payload carries account identity and the hook
1055
+ // URL is frequently a bearer-equivalent secret, so an http: sink puts both in cleartext.
1056
+ webhookUrl: z.string().url().refine(
1057
+ value => { try { return new URL(value).protocol === "https:"; } catch { return false; } },
1058
+ { message: "webhookUrl must use https" },
1059
+ ).optional(),
1060
+ allowPrivateNetwork: z.boolean().optional(),
1061
+ timeoutMs: z.number().int().positive().optional(),
1062
+ command: z.array(z.string()).optional(),
1063
+ }).strict();
1064
+
1001
1065
  const configSchema = z.object({
1002
1066
  port: z.number().int().min(0).max(65535).default(10100),
1003
1067
  // A malformed hand edit must disable only remote-role behavior, not discard
@@ -1050,6 +1114,12 @@ const configSchema = z.object({
1050
1114
  defaultModelAliases: z.boolean().optional(),
1051
1115
  // Malformed hand edits disable this opt-in projection without rejecting providers.
1052
1116
  cursorEffortRows: z.boolean().optional().catch(false),
1117
+ // Fast selectors default on; malformed hand edits disable them without rejecting providers.
1118
+ fastRows: z.boolean().default(true).catch(false),
1119
+ // Ultra Fast is opt-in for the same reason and degrades the same way: a malformed hand
1120
+ // edit turns the tier off rather than rejecting the config that carries it.
1121
+ ultraFastTier: z.boolean().optional().catch(false),
1122
+ codexMainAccountHardLock: z.boolean().optional().catch(false),
1053
1123
  // Future versions remain opaque through passthrough-compatible whole-config saves.
1054
1124
  // Only version 1 grants deletion authority in the rebase path.
1055
1125
  configRebaseProvenance: z.unknown().optional(),
@@ -1061,12 +1131,16 @@ const configSchema = z.object({
1061
1131
  openaiProviderTierVersion: z.union([z.literal(1), z.literal(2)]).optional(),
1062
1132
  // Invalid hand edits must not discard an otherwise usable config.
1063
1133
  googleAntigravityStaticCatalogVersion: z.union([z.literal(1), z.literal(2)]).optional().catch(undefined),
1134
+ subagentModelsVersion: z.number().int().positive().optional().catch(undefined),
1135
+ subagentModels: z.array(z.string().min(1)).optional().catch(undefined),
1064
1136
  clientIntegrations: clientIntegrationsSchema.optional().catch(undefined),
1065
1137
  providerContextCaps: z.record(z.string(), z.number().int().positive()).optional(),
1066
1138
  contextCapValue: z.number().int().positive().optional(),
1067
1139
  multiAgentGuidanceEnabled: z.boolean().optional(),
1068
1140
  // Invalid optional recovery config must not discard unrelated provider/account state.
1069
1141
  agentTaskRecovery: agentTaskRecoverySchema.optional().catch(undefined),
1142
+ // Same rationale: a bad notify section must not cost the operator their providers.
1143
+ quotaResetNotify: quotaResetNotifySchema.optional().catch(undefined),
1070
1144
  // These selections pre-date schema validation and used to pass through as
1071
1145
  // unknown fields. Invalid hand edits must disable only the optional
1072
1146
  // delegation/native-default feature, not reject the whole config and hide
@@ -1083,6 +1157,7 @@ const configSchema = z.object({
1083
1157
  codexShimAutoRestore: z.boolean().optional(),
1084
1158
  codexDesktopAuthless: z.boolean().optional().catch(undefined),
1085
1159
  pausedCodexAccountIds: z.array(z.string().regex(/^[a-zA-Z0-9._-]{1,64}$/)).optional(),
1160
+ codexQuotaAutoRefresh: codexQuotaAutoRefreshSchema.optional().catch(undefined),
1086
1161
  codexAccountNamespaces: codexAccountNamespacesSchema.optional(),
1087
1162
  // Selection order is a preference, not a safety control like pause: a malformed
1088
1163
  // map degrades to "no ordering" rather than failing the parse, so a hand-edited
@@ -1514,17 +1589,6 @@ const configSchema = z.object({
1514
1589
  }
1515
1590
  });
1516
1591
 
1517
- /**
1518
- * Default featured subagent models (native GPT) seeded on a fresh install and when `subagentModels`
1519
- * is unset. Codex's spawn_agent advertises the first 5 featured catalog entries, so this seed is a
1520
- * deliberate 5-list: frontier gpt-5.5 first, the gpt-5.6 preview trio, and gpt-5.4-mini as the cheap
1521
- * tier. gpt-5.4 / gpt-5.3-codex-spark stay selectable in the GUI's available list. The user can
1522
- * remove any in the GUI — once they set the list (even to []), it is respected, so removals persist
1523
- * (start-up only seeds the UNSET case). Kept to ids ChatGPT accepts; the start-up seed prefers the
1524
- * live catalog's native slugs.
1525
- */
1526
- export const DEFAULT_SUBAGENT_MODELS = ["gpt-5.5", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", "gpt-5.4-mini"];
1527
-
1528
1592
  export function hardenExistingSecret(path: string): void {
1529
1593
  if (existsSync(path)) {
1530
1594
  try { chmodSync(path, 0o600); } catch { /* best-effort */ }
@@ -1723,6 +1787,17 @@ function warnDegradedCodexAccountPriorities(rawParsed: unknown, validated: OcxCo
1723
1787
  }
1724
1788
  }
1725
1789
 
1790
+ function degradedCodexQuotaAutoRefreshWarning(rawParsed: unknown, validated: OcxConfig): string | null {
1791
+ const raw = rawConfigRecord(rawParsed)?.codexQuotaAutoRefresh;
1792
+ if (raw === undefined || validated.codexQuotaAutoRefresh !== undefined) return null;
1793
+ return "codexQuotaAutoRefresh is invalid — automatic quota-window activation is disabled";
1794
+ }
1795
+
1796
+ function warnDegradedCodexQuotaAutoRefresh(rawParsed: unknown, validated: OcxConfig): void {
1797
+ const warning = degradedCodexQuotaAutoRefreshWarning(rawParsed, validated);
1798
+ if (warning) console.warn(`⚠️ config.json ${warning}`);
1799
+ }
1800
+
1726
1801
  /**
1727
1802
  * The apiKeys schema salvages entry by entry rather than failing the parse, so a
1728
1803
  * dropped key is otherwise invisible — and it will not be re-saved by the next
@@ -1941,6 +2016,27 @@ function warnDegradedOptionalRemoteBlocks(rawParsed: unknown): void {
1941
2016
  }
1942
2017
  }
1943
2018
 
2019
+ function malformedQuotaResetNotifyWarning(rawParsed: unknown): string | null {
2020
+ const raw = rawConfigRecord(rawParsed);
2021
+ if (!raw || !Object.hasOwn(raw, "quotaResetNotify")) return null;
2022
+ const result = quotaResetNotifySchema.safeParse(raw.quotaResetNotify);
2023
+ if (result.success) return null;
2024
+ const field = result.error.issues[0]?.path.join(".");
2025
+ return `quotaResetNotify${field ? `.${field}` : ""} ignored: invalid quota-reset notification configuration`;
2026
+ }
2027
+
2028
+ /**
2029
+ * Warn once per load that the section was dropped.
2030
+ *
2031
+ * This matters more than a usual degradation notice: the failure is SILENT in the direction
2032
+ * that hurts. A dropped section means notifications are off, so the operator sees nothing —
2033
+ * which is exactly what they would see if the feature were working and no reset had happened.
2034
+ */
2035
+ function warnDegradedQuotaResetNotify(rawParsed: unknown): void {
2036
+ const warning = malformedQuotaResetNotifyWarning(rawParsed);
2037
+ if (warning) console.warn(`⚠️ config.json ${warning}. Other settings were preserved.`);
2038
+ }
2039
+
1944
2040
  type NativeSubagentPersistedField = "injectionModel" | "injectionEffort" | "syncCodexSubagentDefaults";
1945
2041
 
1946
2042
  function rawConfigRecord(rawParsed: unknown): Record<string, unknown> | null {
@@ -2090,6 +2186,7 @@ export function loadConfig(): OcxConfig {
2090
2186
  warnDegradedHostname(parsed, config);
2091
2187
  warnDegradedApiKeys(parsed, config);
2092
2188
  warnDegradedCodexAccountPriorities(parsed, config);
2189
+ warnDegradedCodexQuotaAutoRefresh(parsed, config);
2093
2190
  warnDegradedClaudeSubagentEffort(parsed);
2094
2191
  warnDegradedNativeSubagentConfig(parsed, config);
2095
2192
  warnDegradedCodexAccountPicker(parsed);
@@ -2097,13 +2194,14 @@ export function loadConfig(): OcxConfig {
2097
2194
  warnDegradedAgentTaskRecovery(parsed);
2098
2195
  warnDegradedRuntimeRole(parsed);
2099
2196
  warnDegradedOptionalRemoteBlocks(parsed);
2197
+ warnDegradedQuotaResetNotify(parsed);
2100
2198
  return withRefreshedCostOverlays(normalizeClaudeSubagentEffort(normalizeNativeSubagentSync(config, parsed), parsed));
2101
2199
  }
2102
2200
  // Schema validation failed — merge defaults into the raw object instead of
2103
2201
  // discarding it entirely, so pool accounts and providers survive a missing
2104
2202
  // field like defaultProvider.
2105
2203
  const defaults = getDefaultConfig();
2106
- const merged = { ...defaults, ...parsed };
2204
+ const merged = { ...defaults, ...parsed, subagentModelsVersion: parsed.subagentModelsVersion };
2107
2205
  // Ensure providers from both sides survive
2108
2206
  if (parsed.providers && defaults.providers) {
2109
2207
  merged.providers = { ...defaults.providers, ...parsed.providers };
@@ -2116,6 +2214,7 @@ export function loadConfig(): OcxConfig {
2116
2214
  warnDegradedHostname(parsed, config);
2117
2215
  warnDegradedApiKeys(parsed, config);
2118
2216
  warnDegradedCodexAccountPriorities(parsed, config);
2217
+ warnDegradedCodexQuotaAutoRefresh(parsed, config);
2119
2218
  warnDegradedClaudeSubagentEffort(parsed);
2120
2219
  warnDegradedNativeSubagentConfig(parsed, config);
2121
2220
  warnDegradedCodexAccountPicker(parsed);
@@ -2123,6 +2222,7 @@ export function loadConfig(): OcxConfig {
2123
2222
  warnDegradedAgentTaskRecovery(parsed);
2124
2223
  warnDegradedRuntimeRole(parsed);
2125
2224
  warnDegradedOptionalRemoteBlocks(parsed);
2225
+ warnDegradedQuotaResetNotify(parsed);
2126
2226
  return withRefreshedCostOverlays(normalizeClaudeSubagentEffort(normalizeNativeSubagentSync(config, parsed), parsed));
2127
2227
  }
2128
2228
  // Still failing, but if every complaint is about one or more named entries
@@ -2138,6 +2238,7 @@ export function loadConfig(): OcxConfig {
2138
2238
  warnDegradedHostname(parsed, config);
2139
2239
  warnDegradedApiKeys(parsed, config);
2140
2240
  warnDegradedCodexAccountPriorities(parsed, config);
2241
+ warnDegradedCodexQuotaAutoRefresh(parsed, config);
2141
2242
  warnDegradedClaudeSubagentEffort(parsed);
2142
2243
  warnDegradedNativeSubagentConfig(parsed, config);
2143
2244
  warnDegradedCodexAccountPicker(parsed);
@@ -2145,6 +2246,7 @@ export function loadConfig(): OcxConfig {
2145
2246
  warnDegradedAgentTaskRecovery(parsed);
2146
2247
  warnDegradedRuntimeRole(parsed);
2147
2248
  warnDegradedOptionalRemoteBlocks(parsed);
2249
+ warnDegradedQuotaResetNotify(parsed);
2148
2250
  return withRefreshedCostOverlays(normalizeClaudeSubagentEffort(normalizeNativeSubagentSync(config, parsed), parsed));
2149
2251
  }
2150
2252
  }
@@ -2267,6 +2369,8 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
2267
2369
  const warnings = configPlaceholderWarnings(normalized);
2268
2370
  warnings.push(...inheritedFastWireConflictProviderNames(normalized).map(inheritedFastWireConflictWarning));
2269
2371
  warnings.push(...degradedCodexAccountPriorityWarnings(rawParsed, normalized));
2372
+ const quotaAutoRefreshWarning = degradedCodexQuotaAutoRefreshWarning(rawParsed, normalized);
2373
+ if (quotaAutoRefreshWarning) warnings.push(quotaAutoRefreshWarning);
2270
2374
  if (rawEffort !== undefined && !isClaudeSubagentEffort(rawEffort)) {
2271
2375
  warnings.push(`claudeCode.subagentEffort ignored: expected one of ${CLAUDE_SUBAGENT_EFFORTS.join(", ")}`);
2272
2376
  }
@@ -2285,6 +2389,8 @@ function validFileConfigDiagnostics(config: OcxConfig, rawParsed: unknown): Conf
2285
2389
  if (remoteGuiWarning) warnings.push(remoteGuiWarning);
2286
2390
  const clientWarning = malformedClientConnectionWarning(rawParsed);
2287
2391
  if (clientWarning) warnings.push(clientWarning);
2392
+ const notifyWarning = malformedQuotaResetNotifyWarning(rawParsed);
2393
+ if (notifyWarning) warnings.push(notifyWarning);
2288
2394
  if (syncDisabledReason) {
2289
2395
  warnings.push(`syncCodexSubagentDefaults ignored: ${syncDisabledReason}`);
2290
2396
  }
@@ -2306,7 +2412,7 @@ function mergeConfigDefaults(parsed: unknown): unknown {
2306
2412
  if (!parsed || typeof parsed !== "object") return parsed;
2307
2413
  const defaults = getDefaultConfig();
2308
2414
  const raw = parsed as Record<string, unknown>;
2309
- const merged: Record<string, unknown> = { ...defaults, ...raw };
2415
+ const merged: Record<string, unknown> = { ...defaults, ...raw, subagentModelsVersion: raw.subagentModelsVersion };
2310
2416
  if (raw.providers && typeof raw.providers === "object" && defaults.providers) {
2311
2417
  merged.providers = { ...defaults.providers, ...(raw.providers as Record<string, unknown>) };
2312
2418
  }
@@ -2423,6 +2529,16 @@ function clientRolePairError(value: unknown): string | null {
2423
2529
  return null;
2424
2530
  }
2425
2531
 
2532
+ function quotaResetNotifyError(value: unknown): string | null {
2533
+ const raw = rawConfigRecord(value);
2534
+ if (!raw || !Object.hasOwn(raw, "quotaResetNotify") || raw.quotaResetNotify === undefined) return null;
2535
+ const result = quotaResetNotifySchema.safeParse(raw.quotaResetNotify);
2536
+ if (result.success) return null;
2537
+ const issue = result.error.issues[0];
2538
+ const field = issue?.path.join(".");
2539
+ return `schema_invalid: quotaResetNotify${field ? `.${field}` : ""}: ${issue?.message ?? "invalid configuration"}`;
2540
+ }
2541
+
2426
2542
  /**
2427
2543
  * Same reasoning as {@link blankHostnameError}, and more urgent: the read path degrades a
2428
2544
  * malformed selection-order map to undefined, which on a write would drop every entry the
@@ -2449,6 +2565,21 @@ function codexAccountPrioritiesError(value: unknown): string | null {
2449
2565
  return null;
2450
2566
  }
2451
2567
 
2568
+ function codexQuotaAutoRefreshError(value: unknown): string | null {
2569
+ const raw = rawConfigRecord(value);
2570
+ if (!raw || raw.codexQuotaAutoRefresh === undefined) return null;
2571
+ const parsed = codexQuotaAutoRefreshSchema.safeParse(raw.codexQuotaAutoRefresh);
2572
+ if (parsed.success) return null;
2573
+ const details = parsed.error.issues.map(issue => {
2574
+ const path = issue.path.join(".");
2575
+ const message = path === ""
2576
+ ? issue.message.replace(/^codexQuotaAutoRefresh\s*/, "")
2577
+ : issue.message;
2578
+ return `codexQuotaAutoRefresh${path ? `.${path}` : ""}: ${message}`;
2579
+ });
2580
+ return `schema_invalid: ${details.join("; ")}`;
2581
+ }
2582
+
2452
2583
  function googleAntigravityStaticCatalogVersionError(value: unknown): string | null {
2453
2584
  const raw = rawConfigRecord(value);
2454
2585
  if (!raw || !Object.hasOwn(raw, "googleAntigravityStaticCatalogVersion")) return null;
@@ -2581,8 +2712,10 @@ export function validateConfigCandidate(value: unknown): { ok: true; config: Ocx
2581
2712
  ?? appOwnedMemoryBudgetError(value)
2582
2713
  ?? upstreamHostCircuitThresholdError(value)
2583
2714
  ?? agentTaskRecoveryError(value)
2715
+ ?? quotaResetNotifyError(value)
2584
2716
  ?? googleAntigravityStaticCatalogVersionError(value)
2585
2717
  ?? codexAccountPrioritiesError(value)
2718
+ ?? codexQuotaAutoRefreshError(value)
2586
2719
  ?? codexAccountPickerEnabledError(value)
2587
2720
  ?? emptyCompletionRetryError(value)
2588
2721
  ?? oauthOpenBrowserError(value)
@@ -3112,6 +3245,14 @@ export function websocketsEnabled(config: Pick<OcxConfig, "websockets">): boolea
3112
3245
  return config.websockets === true;
3113
3246
  }
3114
3247
 
3248
+ /**
3249
+ * Opt-in Ultra Fast, read with the house `=== true` idiom so an absent key and a
3250
+ * malformed one both mean off.
3251
+ */
3252
+ export function ultraFastTierEnabled(config: Pick<OcxConfig, "ultraFastTier">): boolean {
3253
+ return config.ultraFastTier === true;
3254
+ }
3255
+
3115
3256
  // ---------------------------------------------------------------------------
3116
3257
  // Hand-edit protection for the `claudeCode` subtree (devlog 260726_claude_auth_auto/040 H1).
3117
3258
  //
@@ -3543,6 +3684,7 @@ export function getDefaultConfig(): OcxConfig {
3543
3684
  return {
3544
3685
  port: 10100,
3545
3686
  emptyCompletionRetry: false,
3687
+ fastRows: true,
3546
3688
  managementUsageMaxReadBytes: 64 * 1024 * 1024,
3547
3689
  appOwnedMemoryBudgetMb: DEFAULT_APP_OWNED_MEMORY_BUDGET_BYTES / (1024 * 1024),
3548
3690
  // Fresh/re-initialized configs are already written in the current three-tier
@@ -3559,6 +3701,7 @@ export function getDefaultConfig(): OcxConfig {
3559
3701
  },
3560
3702
  defaultProvider: "openai",
3561
3703
  subagentModels: [...DEFAULT_SUBAGENT_MODELS],
3704
+ subagentModelsVersion: SUBAGENT_MODELS_VERSION,
3562
3705
  multiAgentGuidanceEnabled: true,
3563
3706
  websockets: false,
3564
3707
  codexAutoStart: true,