@bitkyc08/opencodex 2.19.0 → 2.21.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 (130) hide show
  1. package/gui/dist/assets/index-BOFeam5a.js +76 -0
  2. package/gui/dist/assets/index-Xq49CY8F.css +1 -0
  3. package/gui/dist/index.html +2 -2
  4. package/package.json +1 -1
  5. package/src/adapters/anthropic.ts +2 -28
  6. package/src/adapters/google.ts +31 -3
  7. package/src/adapters/mimo-free.ts +17 -0
  8. package/src/adapters/openai-chat.ts +392 -40
  9. package/src/adapters/registry.ts +144 -0
  10. package/src/adapters/responses-tool-schema.ts +67 -0
  11. package/src/bridge.ts +15 -2
  12. package/src/chat/inbound.ts +13 -7
  13. package/src/claude/gateway-cache.ts +41 -4
  14. package/src/cli/claude.ts +3 -2
  15. package/src/cli/combo.ts +3 -0
  16. package/src/cli/dispatch.ts +8 -0
  17. package/src/cli/export-command.ts +2 -2
  18. package/src/cli/help.ts +2 -0
  19. package/src/cli/index.ts +3 -2
  20. package/src/cli/lab.ts +135 -1
  21. package/src/cli/minimax.ts +491 -0
  22. package/src/cli/models-runtime.ts +22 -1
  23. package/src/cli/models.ts +67 -2
  24. package/src/cli/opencode.ts +2 -1
  25. package/src/cli/registry.ts +22 -2
  26. package/src/clients/config-export.ts +125 -7
  27. package/src/codex/app-server-processes.ts +57 -2
  28. package/src/codex/app-server-restart-service.ts +232 -0
  29. package/src/codex/catalog/aggregation.ts +10 -1
  30. package/src/codex/catalog/effort.ts +15 -3
  31. package/src/codex/catalog/parsing.ts +3 -1
  32. package/src/codex/catalog/provider-fetch.ts +45 -5
  33. package/src/codex/catalog/sync.ts +74 -4
  34. package/src/codex/convergence.ts +2 -0
  35. package/src/combos/index.ts +1 -0
  36. package/src/combos/request.ts +30 -0
  37. package/src/combos/types.ts +6 -0
  38. package/src/config.ts +52 -0
  39. package/src/generated/compatibility-version.json +241 -85
  40. package/src/images/loop.ts +26 -6
  41. package/src/integrations/registry.ts +7 -0
  42. package/src/lab/conformance/jcs.ts +42 -2
  43. package/src/lab/conformance/negative-controls.ts +6 -2
  44. package/src/lab/conformance/runner.ts +16 -5
  45. package/src/lab/fabric/observe.ts +49 -14
  46. package/src/lab/index.ts +16 -0
  47. package/src/lab/ledger/purge.ts +152 -83
  48. package/src/lab/ledger/store.ts +168 -54
  49. package/src/lab/observe/from-conformance.ts +8 -6
  50. package/src/lab/observe/from-live.ts +8 -2
  51. package/src/lab/paths.ts +23 -0
  52. package/src/lab/public/bundle.ts +217 -0
  53. package/src/lab/public/community-authority.ts +175 -0
  54. package/src/lab/public/community-files.ts +29 -0
  55. package/src/lab/public/community.ts +479 -0
  56. package/src/lab/public/file-safety.ts +155 -0
  57. package/src/lab/public/ids.ts +26 -0
  58. package/src/lab/public/index.ts +16 -0
  59. package/src/lab/public/mutation-lock.ts +424 -0
  60. package/src/lab/public/operator.ts +353 -0
  61. package/src/lab/public/origin-purge.ts +79 -0
  62. package/src/lab/public/origin.ts +203 -0
  63. package/src/lab/public/privacy.ts +143 -0
  64. package/src/lab/public/private-file.ts +261 -0
  65. package/src/lab/public/project.ts +124 -0
  66. package/src/lab/public/purge-test-fault.ts +21 -0
  67. package/src/lab/public/purge.ts +223 -0
  68. package/src/lab/public/registry.ts +44 -0
  69. package/src/lab/public/revocation.ts +252 -0
  70. package/src/lab/public/signature.ts +219 -0
  71. package/src/lab/public/storage.ts +105 -0
  72. package/src/lab/public/strict-json.ts +206 -0
  73. package/src/lab/public/time.ts +26 -0
  74. package/src/lab/public/types.ts +172 -0
  75. package/src/lab/public/validate.ts +391 -0
  76. package/src/lib/codex-restart-contract.ts +120 -0
  77. package/src/lib/lab-activation.ts +109 -47
  78. package/src/lib/lab-live-pinned-sender.ts +16 -5
  79. package/src/lib/pinned-http.ts +70 -16
  80. package/src/lib/self-launch-argv.ts +15 -0
  81. package/src/lib/state-store-registrations.ts +2 -0
  82. package/src/lib/upstream-reachability.ts +4 -0
  83. package/src/lib/windows-elevation.ts +10 -1
  84. package/src/providers/derive.ts +24 -4
  85. package/src/providers/registry.ts +14 -4
  86. package/src/providers/request-pacing.ts +310 -0
  87. package/src/providers/service-tier.ts +143 -0
  88. package/src/providers/static-model-discovery.ts +86 -0
  89. package/src/reasoning-effort.ts +27 -1
  90. package/src/responses/custom-tool-compat.ts +4 -1
  91. package/src/responses/parser.ts +7 -1
  92. package/src/responses/provider-opaque-metadata.ts +73 -0
  93. package/src/responses/schema.ts +6 -0
  94. package/src/router.ts +23 -6
  95. package/src/routing/capability.ts +4 -2
  96. package/src/routing/compatibility/behavior.ts +5 -1
  97. package/src/server/adapter-resolve.ts +2 -32
  98. package/src/server/auth-cors.ts +50 -6
  99. package/src/server/chat-completions.ts +74 -36
  100. package/src/server/chat-native-sse.ts +331 -0
  101. package/src/server/chat-native.ts +371 -0
  102. package/src/server/management/combo-routes.ts +16 -2
  103. package/src/server/management/config-routes.ts +6 -4
  104. package/src/server/management/context.ts +17 -0
  105. package/src/server/management/lab-routes.ts +181 -19
  106. package/src/server/management/model-routes.ts +76 -2
  107. package/src/server/management/model-rows.ts +8 -0
  108. package/src/server/management/provider-capability-config.ts +48 -0
  109. package/src/server/management/provider-routes.ts +76 -4
  110. package/src/server/management/system-restart.ts +4 -2
  111. package/src/server/management/system-routes.ts +38 -0
  112. package/src/server/relay.ts +17 -3
  113. package/src/server/responses/compact.ts +4 -1
  114. package/src/server/responses/core.ts +257 -46
  115. package/src/server/responses/empty-completion-guard.ts +276 -0
  116. package/src/server/responses/fetch-helpers.ts +35 -4
  117. package/src/server/responses/pacing-overload.ts +13 -0
  118. package/src/server/responses/policy-fallback.ts +16 -2
  119. package/src/server/responses/terminal-guard.ts +1 -1
  120. package/src/server/responses/upstream-error.ts +5 -0
  121. package/src/server/responses.ts +17 -2
  122. package/src/server/system-env.ts +1 -1
  123. package/src/types.ts +84 -5
  124. package/src/update/index.ts +6 -5
  125. package/src/update/job.ts +5 -6
  126. package/src/update/notify.ts +5 -3
  127. package/src/usage/log.ts +11 -1
  128. package/src/web-search/loop.ts +21 -5
  129. package/gui/dist/assets/index-CQ7bIKee.css +0 -1
  130. package/gui/dist/assets/index-D_JUZLEC.js +0 -76
@@ -10,6 +10,7 @@ import {
10
10
  } from "./npm-cache-preflight.mjs";
11
11
  import { handoffWindowsTrayForUpdate, planWindowsTrayUpdate } from "./tray-update-plan.mjs";
12
12
  import { withProcessRuntimeProvenance } from "../lib/bun-runtime";
13
+ import { selfLaunchArgv } from "../lib/self-launch-argv";
13
14
 
14
15
  /**
15
16
  * A `codex-history-backup-*.json` surviving a stop means the native-history restore was
@@ -248,7 +249,7 @@ export async function runUpdate(): Promise<void> {
248
249
  if (serviceWasInstalled || readPid() || readRuntimePort()) {
249
250
  console.log("⏹ Stopping the running proxy before updating...");
250
251
  const stopStdio = updateChildStdio();
251
- const stop = spawnSync(process.execPath, [process.argv[1], "stop"], {
252
+ const stop = spawnSync(process.execPath, selfLaunchArgv(["stop"]), {
252
253
  stdio: stopStdio,
253
254
  encoding: stopStdio === "pipe" ? "utf8" : undefined,
254
255
  windowsHide: true,
@@ -298,13 +299,13 @@ export async function runUpdate(): Promise<void> {
298
299
  console.warn(`⚠️ Shim repair skipped: ${e instanceof Error ? e.message : e}`);
299
300
  }
300
301
  if (trayWasInstalled) {
301
- const trayArgs = [process.argv[1], ...planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs];
302
+ const trayArgs = selfLaunchArgv(planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs);
302
303
  const tray = spawnSync(process.execPath, trayArgs, { stdio: "inherit", windowsHide: true });
303
304
  if (tray.status === 0) {
304
305
  console.log("🔧 Refreshed Windows tray startup paths.");
305
306
  } else {
306
307
  console.warn("⚠️ Windows tray refresh failed. Run 'ocx tray install'.");
307
- if (trayWasRunning) spawnSync(process.execPath, [process.argv[1], "tray", "start"], { stdio: "ignore", windowsHide: true });
308
+ if (trayWasRunning) spawnSync(process.execPath, selfLaunchArgv(["tray", "start"]), { stdio: "ignore", windowsHide: true });
308
309
  }
309
310
  }
310
311
  // The stop above unloaded any managed service; repair it with the NEW files
@@ -328,7 +329,7 @@ export async function runUpdate(): Promise<void> {
328
329
  process.env.OCX_BAKE_PORT = String(capturedListen.port);
329
330
  try {
330
331
  const svcStdio = updateChildStdio();
331
- const svc = spawnSync(process.execPath, [process.argv[1], ...serviceReinstallArgs()], {
332
+ const svc = spawnSync(process.execPath, selfLaunchArgv(serviceReinstallArgs()), {
332
333
  stdio: svcStdio,
333
334
  encoding: svcStdio === "pipe" ? "utf8" : undefined,
334
335
  windowsHide: true,
@@ -372,7 +373,7 @@ export async function runUpdate(): Promise<void> {
372
373
  : " Run 'ocx service repair' to refresh the background service and see why it failed.");
373
374
  const env = { ...process.env };
374
375
  delete env.OCX_SERVICE;
375
- const child = spawn(process.execPath, [process.argv[1], "start", "--port", String(capturedListen.port)], {
376
+ const child = spawn(process.execPath, selfLaunchArgv(["start", "--port", String(capturedListen.port)]), {
376
377
  detached: true,
377
378
  stdio: "ignore",
378
379
  windowsHide: true,
package/src/update/job.ts CHANGED
@@ -13,6 +13,7 @@ import {
13
13
  verifyPidIdentity,
14
14
  } from "../config";
15
15
  import { isProcessAlive, killProxy } from "../lib/process-control";
16
+ import { selfLaunchArgv } from "../lib/self-launch-argv";
16
17
  import {
17
18
  buildWindowsElevatedArgumentList,
18
19
  resolveTrustedWindowsPowerShellExe,
@@ -549,14 +550,12 @@ export function spawnGuiUpdateWorker(
549
550
  channel: Channel,
550
551
  restart: boolean,
551
552
  ): UpdateWorkerProcess {
552
- const script = process.argv[1];
553
- const args = [
554
- script,
553
+ const args = selfLaunchArgv([
555
554
  "__gui-update-worker",
556
555
  jobId,
557
556
  channel,
558
557
  restart ? "restart" : "no-restart",
559
- ];
558
+ ]);
560
559
  if (process.platform !== "win32") {
561
560
  return spawn(process.execPath, args, {
562
561
  detached: true,
@@ -1866,11 +1865,11 @@ export async function runGuiUpdateWorker(
1866
1865
  }
1867
1866
 
1868
1867
  if (trayWasInstalled) {
1869
- const trayArgs = [process.argv[1], ...planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs];
1868
+ const trayArgs = selfLaunchArgv(planWindowsTrayUpdate({ installed: trayWasInstalled, running: trayWasRunning }).installArgs);
1870
1869
  const tray = runLoggedCommand(job, process.execPath, trayArgs, 20_000);
1871
1870
  if (tray.status !== 0) {
1872
1871
  updateJob(job, {}, "Windows tray refresh failed; run 'ocx tray install'.");
1873
- if (trayWasRunning) runLoggedCommand(job, process.execPath, [process.argv[1], "tray", "start"], 15_000);
1872
+ if (trayWasRunning) runLoggedCommand(job, process.execPath, selfLaunchArgv(["tray", "start"]), 15_000);
1874
1873
  }
1875
1874
  }
1876
1875
 
@@ -4,6 +4,7 @@ import { join } from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { atomicWriteFile, getConfigDir } from "../config";
6
6
  import { hasStarPromptRun } from "../cli/star-prompt";
7
+ import { selfLaunchArgv } from "../lib/self-launch-argv";
7
8
  import {
8
9
  type Channel,
9
10
  currentVersion,
@@ -166,9 +167,10 @@ function cacheIsStale(cache: VersionCache | null): boolean {
166
167
  export function triggerBackgroundRefreshIfStale(channel: Channel, cache: VersionCache | null): void {
167
168
  if (!cacheIsStale(cache)) return;
168
169
  try {
169
- const entry = process.argv[1];
170
- if (!entry || !existsSync(entry)) return;
171
- const child = spawn(process.execPath, [entry, "__refresh-version", channel], {
170
+ const commandArgs = ["__refresh-version", channel];
171
+ const args = selfLaunchArgv(commandArgs);
172
+ if (args.length > commandArgs.length && (!args[0] || !existsSync(args[0]))) return;
173
+ const child = spawn(process.execPath, args, {
172
174
  detached: true,
173
175
  stdio: "ignore",
174
176
  windowsHide: true,
package/src/usage/log.ts CHANGED
@@ -27,7 +27,8 @@ export type AttemptRecoveryKind =
27
27
  | "key-429"
28
28
  | "rate-limit-429"
29
29
  | "anthropic-oauth-429"
30
- | "image-413";
30
+ | "image-413"
31
+ | "empty-completion";
31
32
 
32
33
  export interface PersistedUsageAttempt {
33
34
  ordinal: number;
@@ -36,6 +37,12 @@ export interface PersistedUsageAttempt {
36
37
  adapter: string;
37
38
  status: number;
38
39
  durationMs: number;
40
+ /**
41
+ * True only when the upstream stream died after its 200 head was committed,
42
+ * so the row must not meter as a success the client never received.
43
+ * Absent on ordinary attempts so old rows keep their exact shape.
44
+ */
45
+ streamAborted?: boolean;
39
46
  /** TTFT relative to THIS attempt's start (WP4); unset for non-streaming/tool-only. */
40
47
  firstOutputMs?: number;
41
48
  sendCount: number;
@@ -202,6 +209,7 @@ const ATTEMPT_RECOVERY_KINDS = new Set<AttemptRecoveryKind>([
202
209
  "rate-limit-429",
203
210
  "anthropic-oauth-429",
204
211
  "image-413",
212
+ "empty-completion",
205
213
  ]);
206
214
  const USAGE_STATUSES = new Set<UsageStatus>([
207
215
  "reported",
@@ -277,6 +285,8 @@ function normalizeUsageAttempt(raw: unknown): PersistedUsageAttempt | null {
277
285
  adapter: attempt.adapter,
278
286
  status: attempt.status,
279
287
  durationMs: attempt.durationMs,
288
+ // Absent by default; only the literal `true` marker survives the round trip.
289
+ ...(attempt.streamAborted === true ? { streamAborted: true } : {}),
280
290
  ...(isNonNegativeFiniteNumber(attempt.firstOutputMs)
281
291
  ? { firstOutputMs: attempt.firstOutputMs }
282
292
  : {}),
@@ -1,6 +1,7 @@
1
1
  import type { AdapterRequest, IncomingMeta, ProviderAdapter } from "../adapters/base";
2
- import type { AdapterEvent, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxThinkingContent, OcxUsage, RateLimitRetryPolicy } from "../types";
2
+ import type { AdapterEvent, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxProviderOpaqueToolCallMetadata, OcxThinkingContent, OcxUsage, RateLimitRetryPolicy } from "../types";
3
3
  import { namespacedToolName, toolChoiceToolPredicate } from "../types";
4
+ import { cloneProviderOpaqueToolCallMetadata } from "../responses/provider-opaque-metadata";
4
5
  import type { AttemptRecoveryKind } from "../usage/log";
5
6
  import { bridgeToResponsesSSE } from "../bridge";
6
7
  import { runWebSearch, type SidecarOutcome, type SidecarOutcomeRecorder, type SidecarSettings } from "./executor";
@@ -32,6 +33,11 @@ interface WebSearchCall {
32
33
  // empty array means the model called the tool with neither `query` nor `queries` (handled as an
33
34
  // empty-query placeholder).
34
35
  queries: string[];
36
+ /**
37
+ * Provider-opaque metadata from the originating part (issue #1735). Stored PER CALL so a
38
+ * signature can never migrate to a different call when the model batches several.
39
+ */
40
+ providerMetadata?: OcxProviderOpaqueToolCallMetadata;
35
41
  }
36
42
 
37
43
  /**
@@ -69,7 +75,7 @@ export function scanEventsForWebSearch(events: AdapterEvent[]): {
69
75
  const passthrough: AdapterEvent[] = [];
70
76
  let hasRealToolCall = false;
71
77
  let hasMalformedToolCall = false;
72
- let pending: { name: string; id: string; argsBuf: string; closed: boolean; events: AdapterEvent[] } | null = null;
78
+ let pending: { name: string; id: string; argsBuf: string; closed: boolean; events: AdapterEvent[]; providerMetadata?: OcxProviderOpaqueToolCallMetadata } | null = null;
73
79
  const isBlank = (value: string): boolean => value.trim().length === 0;
74
80
  const flushPending = (): void => {
75
81
  // A pending call that never saw tool_call_end is structurally malformed.
@@ -84,7 +90,7 @@ export function scanEventsForWebSearch(events: AdapterEvent[]): {
84
90
  if (e.type === "tool_call_start") {
85
91
  flushPending();
86
92
  if (isBlank(e.id) || isBlank(e.name)) hasMalformedToolCall = true;
87
- pending = { name: e.name, id: e.id, argsBuf: "", closed: false, events: [e] };
93
+ pending = { name: e.name, id: e.id, argsBuf: "", closed: false, events: [e], providerMetadata: e.providerMetadata };
88
94
  } else if (e.type === "tool_call_delta") {
89
95
  // Orphan delta (no open call) is malformed.
90
96
  if (!pending) hasMalformedToolCall = true;
@@ -100,7 +106,7 @@ export function scanEventsForWebSearch(events: AdapterEvent[]): {
100
106
  pending.events.push(e);
101
107
  pending.closed = true;
102
108
  if (pending.name === WEB_SEARCH_TOOL_NAME) {
103
- calls.push({ id: pending.id, queries: parseQueries(pending.argsBuf) });
109
+ calls.push({ id: pending.id, queries: parseQueries(pending.argsBuf), providerMetadata: pending.providerMetadata });
104
110
  } else {
105
111
  passthrough.push(...pending.events);
106
112
  if (!isBlank(pending.id) && !isBlank(pending.name)) hasRealToolCall = true;
@@ -678,7 +684,17 @@ export async function runWithWebSearch(deps: WebSearchLoopDeps): Promise<Respons
678
684
  // Signed thinking must precede tool_use on replay (Anthropic extended thinking), and
679
685
  // unsigned raw reasoning has to ride along for providers that require it back (#688).
680
686
  ...precedingThinking,
681
- { type: "toolCall" as const, id: call.id, name: WEB_SEARCH_TOOL_NAME, arguments: callArgs },
687
+ {
688
+ type: "toolCall" as const,
689
+ id: call.id,
690
+ name: WEB_SEARCH_TOOL_NAME,
691
+ arguments: callArgs,
692
+ // Re-attach the signature to the rebuilt call so a sidecar turn keeps Gemini
693
+ // reasoning continuity instead of relying on the same-process replay cache.
694
+ ...(cloneProviderOpaqueToolCallMetadata(call.providerMetadata)
695
+ ? { providerMetadata: cloneProviderOpaqueToolCallMetadata(call.providerMetadata) }
696
+ : {}),
697
+ },
682
698
  ],
683
699
  timestamp: now,
684
700
  });