@bitkyc08/opencodex 2.52.0-preview.20260911 → 2.52.0-preview.20260912

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 (166) hide show
  1. package/gui/dist/assets/index-D_t6sCWs.js +115 -0
  2. package/gui/dist/assets/index-EdoPnm9_.css +1 -0
  3. package/gui/dist/index.html +2 -2
  4. package/gui/dist/provider-icons/devin.svg +49 -0
  5. package/gui/dist/provider-icons/omo.svg +42 -0
  6. package/package.json +3 -1
  7. package/src/AGENTS.md +1 -1
  8. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +0 -1
  9. package/src/adapters/command-code.ts +0 -1
  10. package/src/adapters/cursor/checkpoint-store.ts +37 -0
  11. package/src/adapters/cursor/live-transport.ts +74 -2
  12. package/src/adapters/cursor.ts +6 -0
  13. package/src/adapters/devin/cloud-direct/auth.ts +264 -0
  14. package/src/adapters/devin/cloud-direct/catalog.ts +306 -0
  15. package/src/adapters/devin/cloud-direct/chat.ts +1274 -0
  16. package/src/adapters/devin/cloud-direct/index.ts +65 -0
  17. package/src/adapters/devin/cloud-direct/metadata.ts +134 -0
  18. package/src/adapters/devin/cloud-direct/wire.ts +206 -0
  19. package/src/adapters/devin/live-models.ts +133 -0
  20. package/src/adapters/devin-cli/acp.ts +204 -0
  21. package/src/adapters/devin-cli/adapter.ts +345 -0
  22. package/src/adapters/devin-cli/binary.ts +69 -0
  23. package/src/adapters/devin-cli/models.ts +57 -0
  24. package/src/adapters/devin.ts +326 -0
  25. package/src/adapters/google.ts +1 -1
  26. package/src/adapters/openai-chat.ts +31 -10
  27. package/src/adapters/openai-responses.ts +16 -1
  28. package/src/adapters/registry.ts +25 -1
  29. package/src/bridge.ts +8 -2
  30. package/src/claude/inbound-cache-stabilize.ts +130 -0
  31. package/src/claude/inbound.ts +45 -5
  32. package/src/cli/account-auth.ts +60 -1
  33. package/src/cli/account-extended.ts +23 -30
  34. package/src/cli/account.ts +2 -1
  35. package/src/cli/capabilities.ts +50 -8
  36. package/src/cli/config-command.ts +2 -2
  37. package/src/cli/dispatch.ts +14 -2
  38. package/src/cli/export-command.ts +11 -25
  39. package/src/cli/help.ts +2 -2
  40. package/src/cli/opencode.ts +5 -0
  41. package/src/cli/registry.ts +15 -4
  42. package/src/clients/config-export/cline.ts +71 -0
  43. package/src/clients/config-export/contracts.ts +10 -1
  44. package/src/clients/config-export/model-metadata.ts +33 -0
  45. package/src/clients/config-export/zcode.ts +23 -12
  46. package/src/clients/config-export.ts +156 -4
  47. package/src/codex/account-store.ts +7 -2
  48. package/src/codex/auth-context.ts +1 -1
  49. package/src/codex/catalog/effort.ts +4 -3
  50. package/src/codex/catalog/metadata.ts +8 -4
  51. package/src/codex/catalog/native-models.ts +24 -4
  52. package/src/codex/catalog/parsing.ts +19 -1
  53. package/src/codex/catalog/provider-fetch.ts +57 -0
  54. package/src/codex/catalog/sync.ts +1 -1
  55. package/src/codex/context-compat.ts +97 -0
  56. package/src/codex/context-owner.ts +201 -0
  57. package/src/codex/history-provider.ts +161 -14
  58. package/src/codex/inject-coordination.ts +3 -2
  59. package/src/codex/inject.ts +128 -9
  60. package/src/codex/pool-rotation.ts +8 -292
  61. package/src/codex/quota.ts +41 -12
  62. package/src/codex/retired-model-migration.ts +41 -0
  63. package/src/codex/routing.ts +327 -22
  64. package/src/codex/warmup.ts +2 -2
  65. package/src/combos/failover.ts +5 -0
  66. package/src/combos/resolve.ts +11 -15
  67. package/src/config.ts +46 -14
  68. package/src/generated/compatibility-version.json +293 -113
  69. package/src/grok/grpc-web.ts +120 -0
  70. package/src/grok/reset-coupon-ledger.ts +139 -0
  71. package/src/grok/reset-coupons.ts +278 -0
  72. package/src/integrations/catalog-refresh.ts +1 -1
  73. package/src/integrations/cline-document.ts +73 -0
  74. package/src/integrations/cline-io.ts +149 -0
  75. package/src/integrations/cline-transaction.ts +42 -0
  76. package/src/integrations/config-io.ts +21 -2
  77. package/src/integrations/journal.ts +43 -1
  78. package/src/integrations/omp-yaml-source.ts +123 -4
  79. package/src/integrations/ownership-policy.ts +7 -0
  80. package/src/integrations/ownership.ts +36 -0
  81. package/src/integrations/registry.ts +27 -0
  82. package/src/integrations/state.ts +5 -2
  83. package/src/integrations/store.ts +6 -0
  84. package/src/integrations/writer.ts +53 -9
  85. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  86. package/src/lib/abort.ts +36 -0
  87. package/src/lib/local-destinations.ts +1 -1
  88. package/src/lib/upstream-retry.ts +36 -1
  89. package/src/oauth/account-quota-rank.ts +11 -0
  90. package/src/oauth/callback-server.ts +10 -5
  91. package/src/oauth/devin/api-base.ts +63 -0
  92. package/src/oauth/devin/login.ts +1 -0
  93. package/src/oauth/devin/register-user.ts +186 -0
  94. package/src/oauth/devin/types.ts +71 -0
  95. package/src/oauth/devin-cli.ts +149 -0
  96. package/src/oauth/devin.ts +170 -0
  97. package/src/oauth/generic-account-failover.ts +169 -1
  98. package/src/oauth/index.ts +20 -1
  99. package/src/oauth/login-cli.ts +19 -5
  100. package/src/oauth/pool-kernel.ts +321 -0
  101. package/src/oauth/pool-settings-capability.ts +127 -9
  102. package/src/oauth/store.ts +7 -3
  103. package/src/oauth/token-guardian.ts +1 -1
  104. package/src/providers/codebuddy-models.ts +0 -3
  105. package/src/providers/command-code-efforts.ts +23 -4
  106. package/src/providers/default-aliases.ts +4 -0
  107. package/src/providers/derive.ts +8 -0
  108. package/src/providers/devin-cli-authmode-migration.ts +57 -0
  109. package/src/providers/key-failover.ts +175 -0
  110. package/src/providers/model-rename-startup.ts +21 -1
  111. package/src/providers/qoder-models.ts +0 -1
  112. package/src/providers/quota-key-accounts.ts +50 -0
  113. package/src/providers/quota-routing-cache.ts +65 -6
  114. package/src/providers/quota-types.ts +7 -0
  115. package/src/providers/quota.ts +113 -30
  116. package/src/providers/registry.ts +237 -77
  117. package/src/providers/stale-context-window-migration.ts +92 -0
  118. package/src/providers/zai-responses-migration.ts +45 -0
  119. package/src/quota/reset-observer.ts +2 -1
  120. package/src/quota/reset-seen-store.ts +9 -1
  121. package/src/remote-control/crypto.ts +442 -0
  122. package/src/remote-control/host.ts +175 -0
  123. package/src/remote-control/index.ts +100 -0
  124. package/src/remote-control/protocol.ts +200 -0
  125. package/src/remote-control/relay.ts +162 -0
  126. package/src/remote-control/workspace-agent-protocol.ts +246 -0
  127. package/src/remote-control/workspace-rpc-framing.ts +128 -0
  128. package/src/remote-control/workspace-tools.ts +237 -0
  129. package/src/remote-control/workspace-utf8.ts +24 -0
  130. package/src/responses/custom-tool-compat.ts +23 -0
  131. package/src/router.ts +6 -1
  132. package/src/routing/compatibility/behavior.ts +3 -0
  133. package/src/server/adapter-resolve.ts +6 -2
  134. package/src/server/auth-cors.ts +71 -6
  135. package/src/server/chat-completions.ts +5 -3
  136. package/src/server/chat-native.ts +9 -0
  137. package/src/server/claude-messages.ts +25 -30
  138. package/src/server/context-history.ts +207 -0
  139. package/src/server/images.ts +28 -1
  140. package/src/server/index.ts +42 -22
  141. package/src/server/live.ts +2 -1
  142. package/src/server/management/agent-settings-routes.ts +7 -1
  143. package/src/server/management/config-routes.ts +3 -3
  144. package/src/server/management/grok-coupon-routes.ts +287 -0
  145. package/src/server/management/integration-routes.ts +6 -1
  146. package/src/server/management/oauth-account-routes.ts +124 -7
  147. package/src/server/management/provider-routes.ts +77 -2
  148. package/src/server/management/route-registry.ts +6 -1
  149. package/src/server/management-api.ts +7 -0
  150. package/src/server/request-log.ts +4 -0
  151. package/src/server/responses/codex-ws-exchange.ts +90 -14
  152. package/src/server/responses/codex-ws-wire.ts +51 -1
  153. package/src/server/responses/compact.ts +8 -1
  154. package/src/server/responses/core.ts +188 -22
  155. package/src/server/responses/ws-upstream.ts +1 -1
  156. package/src/server/responses-undeclared-tool-guard.ts +261 -12
  157. package/src/server/zai-responses-startup.ts +21 -0
  158. package/src/types/config.ts +33 -3
  159. package/src/types/provider.ts +54 -4
  160. package/src/types/request.ts +1 -1
  161. package/src/types/tools.ts +36 -6
  162. package/src/update/job.ts +33 -11
  163. package/src/vision/plan.ts +40 -37
  164. package/src/web-search/index.ts +1 -0
  165. package/gui/dist/assets/index-BoBRSehJ.css +0 -1
  166. package/gui/dist/assets/index-Dx0xv2EA.js +0 -115
package/src/update/job.ts CHANGED
@@ -983,6 +983,11 @@ export interface RestartIo {
983
983
  spawnStart?: (job: UpdateJobState, installer: Installer, port?: number, launcher?: string) => void;
984
984
  /** The package launcher verified after a pnpm group switch or rollback. */
985
985
  packageLauncherPathFn?: () => string;
986
+ /** Exercise pinned-start retries without spawning, reclaiming, or killing real processes. */
987
+ spawnDetachedStartFn?: typeof spawnDetachedStart;
988
+ preparePortForPinnedStartFn?: typeof preparePortForPinnedStart;
989
+ waitForGhostListenClearFn?: typeof waitForGhostListenClear;
990
+ killProxyFn?: typeof killProxy;
986
991
  serviceInstalledFn?: () => boolean;
987
992
  /**
988
993
  * After a service reinstall exits 0, only trust the service path when this is true.
@@ -1332,9 +1337,20 @@ async function restartAfterUpdate(
1332
1337
  }
1333
1338
  }
1334
1339
  const attempts = 3;
1340
+ const now = io.now ?? (() => Date.now());
1341
+ const spawnPinnedStart = io.spawnDetachedStartFn ?? spawnDetachedStart;
1342
+ const preparePort = io.preparePortForPinnedStartFn ?? preparePortForPinnedStart;
1343
+ const waitForGhost = io.waitForGhostListenClearFn ?? waitForGhostListenClear;
1335
1344
  // Longer than published hard-pin reclaim (30s) so a slow start can still report healthy.
1336
1345
  const perAttemptHealthMs = 70_000;
1337
1346
  let lastChild: ChildProcess | null = null;
1347
+ const killSpawnAttempt = (child: ChildProcess | null): void => {
1348
+ // A numeric PID can be reused once this particular child has exited.
1349
+ if (!child?.pid || child.exitCode !== null || child.signalCode !== null) return;
1350
+ if (aliveFn(child.pid)) {
1351
+ try { (io.killProxyFn ?? killProxy)(child.pid); } catch { /* best-effort */ }
1352
+ }
1353
+ };
1338
1354
  for (let attempt = 1; attempt <= attempts; attempt++) {
1339
1355
  if (attempt > 1) {
1340
1356
  updateJob(
@@ -1343,13 +1359,11 @@ async function restartAfterUpdate(
1343
1359
  `Pinned start attempt ${attempt - 1} did not become healthy on port ${port}; `
1344
1360
  + `retrying (${attempt}/${attempts}).`,
1345
1361
  );
1346
- if (lastChild?.pid && aliveFn(lastChild.pid)) {
1347
- try { killProxy(lastChild.pid); } catch { /* best-effort */ }
1348
- }
1362
+ killSpawnAttempt(lastChild);
1349
1363
  lastChild = null;
1350
1364
  }
1351
- preparePortForPinnedStart(job, port, listPids, aliveFn, verifyOcx);
1352
- const ready = await waitForGhostListenClear(
1365
+ preparePort(job, port, listPids, aliveFn, verifyOcx);
1366
+ const ready = await waitForGhost(
1353
1367
  port,
1354
1368
  hostname,
1355
1369
  listPids,
@@ -1365,17 +1379,25 @@ async function restartAfterUpdate(
1365
1379
  );
1366
1380
  continue;
1367
1381
  }
1368
- lastChild = spawnDetachedStart(job, job.installer, port, launcher);
1369
- const healthDeadline = Date.now() + perAttemptHealthMs;
1370
- while (Date.now() < healthDeadline) {
1382
+ const child = spawnPinnedStart(job, job.installer, port, launcher);
1383
+ lastChild = child;
1384
+ const retireChild = () => {
1385
+ if (lastChild === child) lastChild = null;
1386
+ child.removeListener("exit", retireChild);
1387
+ child.removeListener("error", retireChild);
1388
+ child.removeListener("close", retireChild);
1389
+ };
1390
+ child.once("exit", retireChild);
1391
+ child.once("error", retireChild);
1392
+ child.once("close", retireChild);
1393
+ const healthDeadline = now() + perAttemptHealthMs;
1394
+ while (now() < healthDeadline) {
1371
1395
  if (await probe(port, hostname)) return;
1372
1396
  await sleep(500);
1373
1397
  }
1374
1398
  }
1375
1399
  // Exhausted retries: do not leave a hung pinned-start child owning the port.
1376
- if (lastChild?.pid && aliveFn(lastChild.pid)) {
1377
- try { killProxy(lastChild.pid); } catch { /* best-effort */ }
1378
- }
1400
+ killSpawnAttempt(lastChild);
1379
1401
  }
1380
1402
 
1381
1403
  /** Compact listen-holder summary for update-job logs when reclaim fails. */
@@ -11,7 +11,7 @@ import { resolveSidecarAuth } from "../sidecar/auth";
11
11
  import { DEFAULT_VISION_TIMEOUT_MS, MAX_VISION_TIMEOUT_MS, MIN_VISION_TIMEOUT_MS } from "./timeout-bounds";
12
12
  import { carriesImages } from "./image-rewrite";
13
13
 
14
- const DEFAULT_VISION_MODEL = "gpt-5.4-mini";
14
+ const DEFAULT_VISION_MODEL = "gpt-5.6-luna";
15
15
  const DEFAULT_ANTHROPIC_VISION_MODEL = "claude-sonnet-5";
16
16
  const DEFAULT_REASONING: VisionReasoningEffort = "low";
17
17
  export const DEFAULT_MAX_DESCRIPTIONS_PER_TURN = 8;
@@ -92,6 +92,20 @@ function messagesHaveImage(parsed: OcxParsedRequest): boolean {
92
92
  carriesImages(m.role) && Array.isArray(m.content) && (m.content as OcxContentPart[]).some(p => p.type === "image"));
93
93
  }
94
94
 
95
+ /** Shared by auth admission and planning so a routed describer never borrows OpenAI auth. */
96
+ function usableRoutedVisionModel(config: OcxConfig): string | undefined {
97
+ const cfg = config.visionSidecar;
98
+ if (cfg?.backend !== "routed") return undefined;
99
+ const routedModel = cfg.model;
100
+ const sep = routedModel ? routedModel.indexOf("/") : -1;
101
+ if (!routedModel || sep <= 0) return undefined;
102
+ const targetProvider = routedModel.slice(0, sep);
103
+ const targetId = routedModel.slice(sep + 1);
104
+ const targetProviderConfig = config.providers?.[targetProvider];
105
+ return modelAcceptsImageInput(config, { provider: targetProvider, id: targetId }) !== false
106
+ && !(targetProviderConfig && isModelTextOnly(targetProviderConfig, targetId)) ? routedModel : undefined;
107
+ }
108
+
95
109
  export function shouldResolveOpenAiVisionSidecar(
96
110
  config: OcxConfig,
97
111
  provider: OcxProviderConfig,
@@ -101,6 +115,7 @@ export function shouldResolveOpenAiVisionSidecar(
101
115
  if (!isModelTextOnly(provider, modelId) || !messagesHaveImage(parsed)) return false;
102
116
  const cfg = config.visionSidecar ?? {};
103
117
  if (cfg.enabled === false) return false;
118
+ if (usableRoutedVisionModel(config)) return false;
104
119
  return resolveVisionBackend(cfg.backend, findAnthropicVisionProvider(config)) === "openai";
105
120
  }
106
121
 
@@ -140,43 +155,31 @@ export function planVisionSidecar(
140
155
  // fence: the target must not be provably blind, and must not itself be a
141
156
  // model this planner would re-enter for (belt; the terminal marker on the
142
157
  // loopback request is the braces).
143
- if (cfg.backend === "routed") {
144
- const routedModel = cfg.model;
145
- const sep = routedModel ? routedModel.indexOf("/") : -1;
146
- if (routedModel && sep > 0) {
147
- const targetProvider = routedModel.slice(0, sep);
148
- const targetId = routedModel.slice(sep + 1);
149
- const targetProviderConfig = config.providers?.[targetProvider];
150
- const targetVisible = modelAcceptsImageInput(config, { provider: targetProvider, id: targetId }) !== false
151
- && !(targetProviderConfig && isModelTextOnly(targetProviderConfig, targetId));
152
- if (targetVisible) {
153
- return {
154
- backend: "routed",
155
- routedModel,
156
- routedConfig: {
157
- port: config.port,
158
- ...(config.apiKeys ? { apiKeys: config.apiKeys } : {}),
159
- // The self-fetch has to honor the unauthenticated loopback listener AND, with no
160
- // listener, the bind address — so BOTH fields the destination resolver reads have to
161
- // survive the narrowing or it silently resolves to the wrong local socket (#4236).
162
- ...(config.hostname ? { hostname: config.hostname } : {}),
163
- ...(config.unauthenticatedLoopbackListener
164
- ? { unauthenticatedLoopbackListener: config.unauthenticatedLoopbackListener }
165
- : {}),
166
- },
167
- settings: {
168
- model: routedModel,
169
- reasoning: DEFAULT_REASONING,
170
- timeoutMs: resolveVisionTimeoutMs(cfg.timeoutMs),
171
- },
172
- maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(cfg.maxDescriptionsPerTurn),
173
- };
174
- }
175
- }
176
- // Misconfigured routed backend (bare id, unknown provider, or provably
177
- // blind target): fall through to the legacy default order below rather
178
- // than dispatching a describe that cannot work.
158
+ const routedModel = usableRoutedVisionModel(config);
159
+ if (routedModel) {
160
+ return {
161
+ backend: "routed",
162
+ routedModel,
163
+ routedConfig: {
164
+ port: config.port,
165
+ ...(config.apiKeys ? { apiKeys: config.apiKeys } : {}),
166
+ // The self-fetch has to honor the unauthenticated loopback listener AND, with no
167
+ // listener, the bind address — so BOTH fields the destination resolver reads have to
168
+ // survive the narrowing or it silently resolves to the wrong local socket (#4236).
169
+ ...(config.hostname ? { hostname: config.hostname } : {}),
170
+ ...(config.unauthenticatedLoopbackListener
171
+ ? { unauthenticatedLoopbackListener: config.unauthenticatedLoopbackListener }
172
+ : {}),
173
+ },
174
+ settings: {
175
+ model: routedModel,
176
+ reasoning: DEFAULT_REASONING,
177
+ timeoutMs: resolveVisionTimeoutMs(cfg.timeoutMs),
178
+ },
179
+ maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(cfg.maxDescriptionsPerTurn),
180
+ };
179
181
  }
182
+ // A non-dispatchable routed configuration keeps the legacy backend fallback below.
180
183
 
181
184
  const anthropicSidecar = findAnthropicVisionProvider(config);
182
185
  const backend = resolveVisionBackend(cfg.backend, anthropicSidecar);
@@ -201,6 +201,7 @@ export function shouldResolveOpenAiWebSearchSidecar(
201
201
  isPassthrough: boolean,
202
202
  ): boolean {
203
203
  if (!parsed._webSearch || isPassthrough) return false;
204
+ if (!toolChoiceToolPredicate(parsed.options.toolChoice)(buildWebSearchTool())) return false;
204
205
  const cfg = config.webSearchSidecar ?? {};
205
206
  return cfg.enabled !== false && resolveSidecarBackend(cfg.backend) === "openai";
206
207
  }