@bitkyc08/opencodex 2.18.2 → 2.19.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.
@@ -16,8 +16,8 @@
16
16
  } catch (e) {}
17
17
  })();
18
18
  </script>
19
- <script type="module" crossorigin src="/assets/index-CXI1262_.js"></script>
20
- <link rel="stylesheet" crossorigin href="/assets/index-DUCH59lJ.css">
19
+ <script type="module" crossorigin src="/assets/index-D_JUZLEC.js"></script>
20
+ <link rel="stylesheet" crossorigin href="/assets/index-CQ7bIKee.css">
21
21
  </head>
22
22
  <body>
23
23
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bitkyc08/opencodex",
3
- "version": "2.18.2",
3
+ "version": "2.19.0",
4
4
  "description": "Universal provider proxy for OpenAI Codex & Claude Code — use any LLM with Codex CLI/App/SDK and Claude Code",
5
5
  "type": "module",
6
6
  "main": "./bin/package-main.mjs",
@@ -40,20 +40,24 @@ export function claudeCodeSessionId(token: string | undefined): string {
40
40
  return `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-${variant}${h.slice(17, 20)}-${h.slice(20, 32)}`;
41
41
  }
42
42
 
43
- // ── Antigravity CLI ──
44
- /** Pinned fallback Antigravity CLI version (real client fetches a manifest; we pin to avoid the network dependency). */
45
- export const ANTIGRAVITY_CLI_VERSION = "1.1.12";
46
- const ANTIGRAVITY_CLI_CLIENT_NAME = "aidev_client";
47
- const ANTIGRAVITY_CLI_PLATFORM = "darwin/arm64";
43
+ // ── Antigravity IDE ──
44
+ /** Pinned fallback Antigravity IDE language-server version (matches the bundled LS 2.5.5). */
45
+ export const ANTIGRAVITY_IDE_VERSION = "2.5.5";
46
+ const ANTIGRAVITY_IDE_CLIENT_NAME = "aidev_client";
47
+ const ANTIGRAVITY_IDE_PLATFORM = "windows/amd64";
48
48
  /** Secondary Google API client UA the Antigravity client library reports. */
49
49
  export const ANTIGRAVITY_GOOG_API_CLIENT_UA = "google-api-nodejs-client/10.3.0";
50
50
 
51
51
  /**
52
- * The real Antigravity CLI User-Agent, e.g.
53
- * `antigravity/cli/1.1.12 (aidev_client; os_type=darwin; arch=arm64)`.
52
+ * The real Antigravity IDE User-Agent, e.g.
53
+ * `antigravity/ide/2.5.5 (aidev_client; os_type=windows; arch=amd64)`.
54
+ *
55
+ * Must be the IDE client family, NOT `antigravity/cli/...`: the Cloud Code Assist backend gates
56
+ * newer agent models (e.g. `gemini-3.7-flash`) by User-Agent and answers 404 NOT_FOUND to
57
+ * CLI-shaped UAs even with a valid OAuth token. Only `antigravity/ide/<ver>` unlocks them.
54
58
  * A `GOOGLE_ANTIGRAVITY_USER_AGENT` override (set by the caller) takes precedence upstream.
55
59
  */
56
- export function antigravityUserAgent(version = ANTIGRAVITY_CLI_VERSION): string {
57
- const [osType, arch] = ANTIGRAVITY_CLI_PLATFORM.split("/");
58
- return `antigravity/cli/${version} (${ANTIGRAVITY_CLI_CLIENT_NAME}; os_type=${osType}; arch=${arch})`;
60
+ export function antigravityUserAgent(version = ANTIGRAVITY_IDE_VERSION): string {
61
+ const [osType, arch] = ANTIGRAVITY_IDE_PLATFORM.split("/");
62
+ return `antigravity/ide/${version} (${ANTIGRAVITY_IDE_CLIENT_NAME}; os_type=${osType}; arch=${arch})`;
59
63
  }
@@ -3,10 +3,11 @@ import type { OcxContentPart, OcxParsedRequest } from "../types";
3
3
  import { antigravityUserAgent } from "./client-fingerprint";
4
4
 
5
5
  /**
6
- * Antigravity request User-Agent. Mirrors the real Antigravity CLI UA
7
- * (`antigravity/cli/{ver} (aidev_client; os_type=darwin; arch=arm64)`) so the request fingerprint
6
+ * Antigravity request User-Agent. Mirrors the real Antigravity IDE UA
7
+ * (`antigravity/ide/{ver} (aidev_client; os_type=windows; arch=amd64)`) so the request fingerprint
8
8
  * matches the OAuth credential — the prior literal `"antigravity"` was a giveaway no real client
9
- * sends. A `GOOGLE_ANTIGRAVITY_USER_AGENT` override still wins.
9
+ * sends. The IDE client family is also required to unlock newer agent models (the backend 404s
10
+ * CLI-shaped UAs for `gemini-3.7-*`). A `GOOGLE_ANTIGRAVITY_USER_AGENT` override still wins.
10
11
  */
11
12
  export const ANTIGRAVITY_REQUEST_UA = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT || antigravityUserAgent();
12
13
 
@@ -469,7 +469,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
469
469
  const envelope = {
470
470
  model: wireModelId,
471
471
  // The envelope's `userAgent` field is a protocol constant ("antigravity"), distinct from
472
- // the HTTP `User-Agent` header (the real CLI UA). CLIProxyAPI `geminiToAntigravity` hardcodes
472
+ // the HTTP `User-Agent` header (the real IDE UA). CLIProxyAPI `geminiToAntigravity` hardcodes
473
473
  // the body field; only the header carries the versioned client string.
474
474
  userAgent: "antigravity",
475
475
  requestType: "agent",
@@ -14,6 +14,7 @@ function isRec(v: unknown): v is Rec {
14
14
  }
15
15
 
16
16
  const OUTPUT_CONFIG_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max", "ultra"]);
17
+ const OUTPUT_CONFIG_SUMMARIES = new Set(["auto", "concise", "detailed", "none"]);
17
18
 
18
19
  function contentToText(content: unknown): string {
19
20
  if (typeof content === "string") return content;
@@ -205,6 +206,22 @@ function resolveReasoningEffort(raw: Rec): string | undefined {
205
206
  return undefined;
206
207
  }
207
208
 
209
+ /**
210
+ * Chat Completions clients (Grok Build, Copilot, OpenAI-compatible SDKs) expect
211
+ * `delta.reasoning_content` whenever the model thinks. The internal Responses
212
+ * parser hides thinking unless `reasoning.summary` is set and is not `"none"`.
213
+ * Map the common Chat Completions knobs onto that field. When the client only
214
+ * sent an effort, default the summary to `"auto"` so traces are not swallowed.
215
+ */
216
+ function resolveReasoningSummary(raw: Rec): string | undefined {
217
+ if (isRec(raw.reasoning) && typeof raw.reasoning.summary === "string" && OUTPUT_CONFIG_SUMMARIES.has(raw.reasoning.summary)) {
218
+ return raw.reasoning.summary;
219
+ }
220
+ if (raw.include_reasoning === false) return "none";
221
+ if (raw.include_reasoning === true) return "auto";
222
+ return undefined;
223
+ }
224
+
208
225
  /**
209
226
  * Translate an OpenAI Chat Completions request body into a /v1/responses request body.
210
227
  * Throws ChatCompletionsRequestError (-> 400) on malformed input.
@@ -286,7 +303,13 @@ export function chatCompletionsToResponsesBody(raw: unknown): Rec {
286
303
  if (raw.metadata !== undefined) body.metadata = raw.metadata;
287
304
 
288
305
  const effort = resolveReasoningEffort(raw);
289
- if (effort) body.reasoning = { effort };
306
+ const summary = resolveReasoningSummary(raw);
307
+ if (effort || summary !== undefined) {
308
+ body.reasoning = {
309
+ ...(effort ? { effort } : {}),
310
+ summary: summary ?? "auto",
311
+ };
312
+ }
290
313
 
291
314
  const text = responseFormatToText(raw.response_format);
292
315
  if (text) body.text = text;
package/src/codex/shim.ts CHANGED
@@ -8,16 +8,19 @@ import {
8
8
  existsSync,
9
9
  fstatSync,
10
10
  lstatSync,
11
+ linkSync,
11
12
  mkdirSync,
12
13
  mkdtempSync,
13
14
  openSync,
14
15
  readFileSync,
15
16
  readdirSync,
17
+ readlinkSync,
16
18
  readSync,
17
19
  renameSync,
18
20
  rmSync,
19
21
  rmdirSync,
20
22
  statSync,
23
+ symlinkSync,
21
24
  type Stats,
22
25
  unlinkSync,
23
26
  writeFileSync,
@@ -407,6 +410,63 @@ function sameStableShimPathProbe(left: StableShimPathProbe, right: StableShimPat
407
410
  return left.prefix === right.prefix && sameFingerprint(left.fingerprint, right.fingerprint);
408
411
  }
409
412
 
413
+ /**
414
+ * Identity of whatever sits at `path`, read from metadata alone.
415
+ *
416
+ * `stableShimPathProbe` answers a different question: it reads content to decide
417
+ * whether a launcher looks like a healthy shim, and it deliberately returns null
418
+ * for a zero-byte file. That makes it the wrong instrument for rollback
419
+ * bookkeeping. A user can legitimately own an empty `codex` launcher, and a fresh
420
+ * install moves it aside before writing our wrapper; if the move is recorded
421
+ * without a fingerprint, rollback cannot prove the backup is still the file it
422
+ * set aside and refuses to restore it — the launcher stays lost (#1625).
423
+ *
424
+ * Content is irrelevant to that proof, so this reads dev/ino/mode/size/times and
425
+ * re-reads them to reject a path that changed under us, following a symlink to
426
+ * fingerprint its target as well.
427
+ */
428
+ function shimPathFingerprint(path: string): ShimPathFingerprint | null {
429
+ const before = statFingerprint(path, false);
430
+ if (!before) return null;
431
+ if (before.kind !== "symlink") {
432
+ const after = statFingerprint(path, false);
433
+ return after && sameFingerprint(before, after) ? before : null;
434
+ }
435
+ const targetBefore = statFingerprint(path, true);
436
+ if (!targetBefore) return null;
437
+ const targetAfter = statFingerprint(path, true);
438
+ const after = statFingerprint(path, false);
439
+ if (!targetAfter || !after
440
+ || !sameFingerprint(targetBefore, targetAfter)
441
+ || !sameFingerprint(before, after)) return null;
442
+ return { ...before, target: targetBefore };
443
+ }
444
+
445
+ /**
446
+ * Move `from` onto `to` without ever replacing an existing entry.
447
+ *
448
+ * `renameSync` silently clobbers the destination on POSIX, which is wrong for a
449
+ * rollback restore: `sourceOccupied` is sampled before the fingerprint check, so
450
+ * a concurrent installer can publish its own launcher at the original path in
451
+ * between, and the restore would delete it. `link` fails EEXIST instead, which
452
+ * is the no-replace primitive we need and needs no native helper.
453
+ *
454
+ * `link` follows a symlink to its target rather than preserving the link, so a
455
+ * symlink launcher is republished with `symlink`, which is also no-replace: it
456
+ * fails EEXIST on an occupied destination. Checking existence and then renaming
457
+ * would reintroduce exactly the race this function exists to close.
458
+ */
459
+ function restoreWithoutReplacing(from: string, to: string): void {
460
+ const source = lstatSync(from);
461
+ if (source.isSymbolicLink()) {
462
+ symlinkSync(readlinkSync(from), to);
463
+ unlinkSync(from);
464
+ return;
465
+ }
466
+ linkSync(from, to);
467
+ unlinkSync(from);
468
+ }
469
+
410
470
  function isHealthyShimProbe(probe: StableShimPathProbe, platform: NodeJS.Platform): boolean {
411
471
  if (probe.prefix.length < 180 || !probe.prefix.includes(SHIM_MARKER) || !probe.prefix.includes("ensure")) return false;
412
472
  const mode = probe.fingerprint.target?.mode ?? probe.fingerprint.mode;
@@ -644,6 +704,7 @@ let codexShimProbeHookForTests: (() => void) | null = null;
644
704
  let codexShimProbeShellForTests: string | null = null;
645
705
  let codexShimGuardedWriteHookForTests: (() => void) | null = null;
646
706
  let codexShimFreshWriteHookForTests: (() => void) | null = null;
707
+ let codexShimRollbackRestoreHookForTests: ((target: ShimFileState) => void) | null = null;
647
708
  let codexShimProbeObservationMs = CODEX_SHIM_INSTALL_PROBE_TIMEOUT_MS;
648
709
 
649
710
  /** Narrow deterministic seam for transaction rollback tests. */
@@ -671,6 +732,20 @@ export function setCodexShimFreshWriteHookForTests(hook: (() => void) | null): v
671
732
  codexShimFreshWriteHookForTests = hook;
672
733
  }
673
734
 
735
+ /**
736
+ * @internal Test-only seam for the rollback restore race.
737
+ *
738
+ * The window this closes opens after `sourceOccupied` is sampled and closes when
739
+ * the backup is republished, so no earlier hook can reach it: publishing from
740
+ * the fresh-write hook makes `sourceOccupied` true and skips the restore
741
+ * entirely.
742
+ */
743
+ export function setCodexShimRollbackRestoreHookForTests(
744
+ hook: ((target: ShimFileState) => void) | null,
745
+ ): void {
746
+ codexShimRollbackRestoreHookForTests = hook;
747
+ }
748
+
674
749
  function readProbeMetadata(path: string, maxBytes: number): string | null {
675
750
  try {
676
751
  if (!existsSync(path)) return "";
@@ -830,9 +905,9 @@ function rollbackFreshShimInstall(journal: readonly FreshShimInstallJournalEntry
830
905
  }
831
906
  try {
832
907
  if (entry.originalMovedToBackup && existsSync(target.backupPath)) {
833
- const movedOriginal = stableShimPathProbe(target.backupPath);
908
+ const movedOriginal = shimPathFingerprint(target.backupPath);
834
909
  if (!movedOriginal || !entry.movedOriginalFingerprint
835
- || !sameFingerprint(movedOriginal.fingerprint, entry.movedOriginalFingerprint)) {
910
+ || !sameFingerprint(movedOriginal, entry.movedOriginalFingerprint)) {
836
911
  throw new Error("Codex shim fresh-install backup changed during rollback");
837
912
  }
838
913
  if (sourceOccupied) {
@@ -842,7 +917,12 @@ function rollbackFreshShimInstall(journal: readonly FreshShimInstallJournalEntry
842
917
  // lose the command entirely. Keep it in that case — a stray
843
918
  // `codex.opencodex-real` is recoverable, a deleted launcher is not.
844
919
  if (ownsWrapperNow) unlinkSync(target.backupPath);
845
- } else renameSync(target.backupPath, target.originalPath);
920
+ } else {
921
+ // No-replace: sourceOccupied was sampled earlier, so a concurrent
922
+ // installer may have published a launcher at the original path since.
923
+ codexShimRollbackRestoreHookForTests?.(target);
924
+ restoreWithoutReplacing(target.backupPath, target.originalPath);
925
+ }
846
926
  }
847
927
  } catch (error) {
848
928
  errors.push(error instanceof Error ? error : new Error(String(error)));
@@ -1832,10 +1912,25 @@ function installCodexShimInternal(options: InstallCodexShimInternalOptions): { i
1832
1912
  if (existsSync(target.originalPath)) {
1833
1913
  renameSync(target.originalPath, target.backupPath);
1834
1914
  entry.originalMovedToBackup = true;
1915
+ // Metadata-only, and before the content probe: an empty or otherwise
1916
+ // unprobeable launcher must still be restorable during rollback.
1917
+ //
1918
+ // Only Unix reaches the rollback path (Windows rethrows freshApplyError
1919
+ // without rolling back), so only Unix may treat a missing fingerprint as
1920
+ // fatal. Throwing here on Windows would abort AFTER the original moved,
1921
+ // stranding the launcher at its backup path with nothing to restore it.
1922
+ const movedOriginalFingerprint = shimPathFingerprint(target.backupPath);
1923
+ if (movedOriginalFingerprint) entry.movedOriginalFingerprint = movedOriginalFingerprint;
1835
1924
  if (process.platform !== "win32") {
1925
+ if (!movedOriginalFingerprint) {
1926
+ throw new Error("Codex shim fresh install could not fingerprint the staged launcher");
1927
+ }
1836
1928
  const movedOriginal = stableShimPathProbe(target.backupPath);
1837
- if (!movedOriginal) throw new Error("Codex shim fresh install could not fingerprint the staged launcher");
1838
- entry.movedOriginalFingerprint = movedOriginal.fingerprint;
1929
+ // A content probe still runs where it can, purely as a consistency
1930
+ // check: disagreement means the file moved under us mid-install.
1931
+ if (movedOriginal && !sameFingerprint(movedOriginal.fingerprint, movedOriginalFingerprint)) {
1932
+ throw new Error("Codex shim fresh install staged launcher changed while being fingerprinted");
1933
+ }
1839
1934
  }
1840
1935
  }
1841
1936
  if (!target.preserveOnly) {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "c49e32901436d0c5d16ce428689fc389ce9647256fa7a1143afa2d96b046a30d"
13
+ "sha256": "4b1881e599a19c6a02309927d634f2bf2a45a93da5762d63e37e5795d103713e"
14
14
  },
15
15
  {
16
16
  "path": "scripts/model-metadata.source.json",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  {
48
48
  "path": "src/adapters/client-fingerprint.ts",
49
- "sha256": "db85c96fb739ada2ddb2b811722d497cec0cbe692fa816920da6a97d10c202e4"
49
+ "sha256": "6494cb40aefb337db3584489d1544d7ae623445dd3c81e5e34a30c77312f4e69"
50
50
  },
51
51
  {
52
52
  "path": "src/adapters/command-code.ts",
@@ -186,7 +186,7 @@
186
186
  },
187
187
  {
188
188
  "path": "src/adapters/google-antigravity-wire.ts",
189
- "sha256": "1d4b78e8002aba15e94e222ab5e0b4adf1a1a2754922ff967c5be2acaa16a3dc"
189
+ "sha256": "1fa3361241026926754656cf8300ce513fefac86020d796cb985018192499797"
190
190
  },
191
191
  {
192
192
  "path": "src/adapters/google-errors.ts",
@@ -210,7 +210,7 @@
210
210
  },
211
211
  {
212
212
  "path": "src/adapters/google.ts",
213
- "sha256": "5fd50e67930ddac30951c2764b5dc8852252e75690fa4c7068a8f36d85932f82"
213
+ "sha256": "7905d7c039bebc63a2fa968fc934570d5dedf882c474087bcf4d55e317641e63"
214
214
  },
215
215
  {
216
216
  "path": "src/adapters/identity.ts",
@@ -302,7 +302,7 @@
302
302
  },
303
303
  {
304
304
  "path": "src/chat/inbound.ts",
305
- "sha256": "747105ce3fb5cff8d5a8020e5df2a481fa218321a957717c2f6db0417e9fe690"
305
+ "sha256": "fe115207f0d5a4cf397e7e46a81b772f405363bd623481dc3fd4b2eec4bbf87e"
306
306
  },
307
307
  {
308
308
  "path": "src/chat/outbound.ts",
@@ -906,7 +906,7 @@
906
906
  },
907
907
  {
908
908
  "path": "src/codex/shim.ts",
909
- "sha256": "f39249f3748a1c35b116677a0583704cba02cc99e3f320ac46356a24983d250c"
909
+ "sha256": "f779c71050a1b2c3d6c08c22dbb6597d393a106f20a27566cc949203dbdad837"
910
910
  },
911
911
  {
912
912
  "path": "src/codex/subagent-defaults.ts",
@@ -1530,7 +1530,7 @@
1530
1530
  },
1531
1531
  {
1532
1532
  "path": "src/lib/errors.ts",
1533
- "sha256": "0e0a2b5117cf32f981256ec550e1fcf43dc181c026258736a9ef65ec9046a47d"
1533
+ "sha256": "39613acb1eaae98fa448296a9ea6e55e246e7348aebf3110d775ac05febae7df"
1534
1534
  },
1535
1535
  {
1536
1536
  "path": "src/lib/eventstream-decoder.ts",
@@ -1942,7 +1942,7 @@
1942
1942
  },
1943
1943
  {
1944
1944
  "path": "src/providers/registry.ts",
1945
- "sha256": "78f6cda9b3a114e72656e88645b062e96af413f9af7e0241246f37473d086b7f"
1945
+ "sha256": "89cc6f2a0fb31b3b05df612e86f396682bc0e7e50bf4d9d733b870ed1307a880"
1946
1946
  },
1947
1947
  {
1948
1948
  "path": "src/providers/slug-codec.ts",
@@ -1986,11 +1986,11 @@
1986
1986
  },
1987
1987
  {
1988
1988
  "path": "src/responses/spill-store.ts",
1989
- "sha256": "c0cd3343d0803eb6d14203f19d6ee38f33110042d8aa896cc9f60bd422ca8f21"
1989
+ "sha256": "a1239057ea7a156c562222a6f385a3bbd3f324cf346964cb6aae8185f47a3e34"
1990
1990
  },
1991
1991
  {
1992
1992
  "path": "src/responses/state.ts",
1993
- "sha256": "59952393060143aae734770fbf78761a8cc69e5b6335c6452bdf5ac7da686bb4"
1993
+ "sha256": "761a96f2839ee22f7cea11f037424a13bd1dedb0f747f1bc35a81c91ca9b0bbd"
1994
1994
  },
1995
1995
  {
1996
1996
  "path": "src/responses/tool-groups.ts",
@@ -2110,7 +2110,7 @@
2110
2110
  },
2111
2111
  {
2112
2112
  "path": "src/server/chat-completions.ts",
2113
- "sha256": "7f3b9b2f0e9850e687d672266f7e6976f634b151ddbcf729b2373c5d31ef51cc"
2113
+ "sha256": "31a6edac0e224e98bdfa3c46197388f5ef8067e2be2e25751b2a23fb7a1cdc96"
2114
2114
  },
2115
2115
  {
2116
2116
  "path": "src/server/claude-messages.ts",
@@ -2122,7 +2122,7 @@
2122
2122
  },
2123
2123
  {
2124
2124
  "path": "src/server/effort-policy.ts",
2125
- "sha256": "7ca86f1bdd5ab6e6cd58cf3fbb65a6c076359f4bc05a6c790ccceb05d071e350"
2125
+ "sha256": "1678af1658f000101cb2c5f3273388953e642fbaf7e85771d425d057c4fd2a6f"
2126
2126
  },
2127
2127
  {
2128
2128
  "path": "src/server/github-copilot-responses-repair.ts",
@@ -2366,7 +2366,7 @@
2366
2366
  },
2367
2367
  {
2368
2368
  "path": "src/server/responses/core.ts",
2369
- "sha256": "1dde5ae551f6baadb2dd0cca790d43d6ce1ae223568ebbc8d9a0d68c41cf7ef6"
2369
+ "sha256": "d4eb9b247e29412896578bd59492a0983681210439d19188b992011b4963992d"
2370
2370
  },
2371
2371
  {
2372
2372
  "path": "src/server/responses/encrypted-payload.ts",
@@ -2376,6 +2376,10 @@
2376
2376
  "path": "src/server/responses/fetch-helpers.ts",
2377
2377
  "sha256": "1043b5b66d5d8c43bd3600200d86cbd6bb5af5614bc4fa17bebc2134b854ca99"
2378
2378
  },
2379
+ {
2380
+ "path": "src/server/responses/input-admission.ts",
2381
+ "sha256": "961168288033b3bb4f1b9542e7ab329a0350d2c5fc44036d2e6cf39355d3b26c"
2382
+ },
2379
2383
  {
2380
2384
  "path": "src/server/responses/passthrough-error.ts",
2381
2385
  "sha256": "d1de1ae5bedc91406219ff82276158b8203c53191a106051a2e2d65140fcef63"
@@ -2434,7 +2438,7 @@
2434
2438
  },
2435
2439
  {
2436
2440
  "path": "src/service-manager-probe.ts",
2437
- "sha256": "6a9efca4e86bb940654475254c6600f377d1fa96a430f70c47fa0781145fca6f"
2441
+ "sha256": "debddd154561e93532edb423bf53dc917c6619ea980d7061368c22a512b29200"
2438
2442
  },
2439
2443
  {
2440
2444
  "path": "src/service.ts",
@@ -2514,7 +2518,7 @@
2514
2518
  },
2515
2519
  {
2516
2520
  "path": "src/types.ts",
2517
- "sha256": "b04403f46247c96634d0c8c6344c1ecc44979980b0456c28140e3aa1da4ab6ca"
2521
+ "sha256": "4eccad9a6a250af4255297f55de0a8b4fd31058e7f4ea0068eeba4a744f7a3fd"
2518
2522
  },
2519
2523
  {
2520
2524
  "path": "src/update/badge.ts",
@@ -2602,7 +2606,7 @@
2602
2606
  },
2603
2607
  {
2604
2608
  "path": "src/vision/index.ts",
2605
- "sha256": "b3c27892a17ea2176c2b6361830800772a6efea5681fff570b3ca5da77626bbc"
2609
+ "sha256": "eec04f8b8e21e3e0f894eda6a194947e3e4f91129e9b089f31c1a5f98a781bd6"
2606
2610
  },
2607
2611
  {
2608
2612
  "path": "src/vision/reasoning.ts",
@@ -2626,7 +2630,7 @@
2626
2630
  },
2627
2631
  {
2628
2632
  "path": "src/web-search/index.ts",
2629
- "sha256": "d8f043b344f916c63ae8d09323a2f894c1c98b76feae7921e67152fcc1db4b5f"
2633
+ "sha256": "5334eb4bf0a970693eca2fa7e38cacef23c1ec5b79692383aa7364db2c011144"
2630
2634
  },
2631
2635
  {
2632
2636
  "path": "src/web-search/loop.ts",
package/src/lib/errors.ts CHANGED
@@ -43,6 +43,25 @@ function isSubscriptionGateMessage(text: string): boolean {
43
43
  );
44
44
  }
45
45
 
46
+ function isLocalAclHardeningMessage(text: string): boolean {
47
+ const secretPathHardening = text.includes("secret path") && (
48
+ text.includes("acl") ||
49
+ text.includes("harden") ||
50
+ text.includes("permission") ||
51
+ text.includes("access denied") ||
52
+ text.includes("inheritance")
53
+ );
54
+ return (
55
+ text.includes("icacls") ||
56
+ text.includes("acl hardening") ||
57
+ text.includes("ntfs") ||
58
+ secretPathHardening ||
59
+ text.includes("inheritance:r") ||
60
+ /\bwindows\b.*\bacl\b/.test(text) ||
61
+ text.includes("/grant:r")
62
+ );
63
+ }
64
+
46
65
  function isAuthenticationMessage(text: string): boolean {
47
66
  const accessDeniedWithCredentialCue = (
48
67
  text.includes("access denied") ||
@@ -170,6 +189,11 @@ export function classifyError(status: number, type: string, message: string): Oc
170
189
  if (type === "origin_rejected") {
171
190
  return { message, type: "invalid_request_error", code: "origin_rejected" };
172
191
  }
192
+ // Local ACL setup failures can contain provider-like auth wording (for example
193
+ // "access denied" or "authentication") but represent unavailable infrastructure.
194
+ if (status === 503 && isLocalAclHardeningMessage(text)) {
195
+ return { message, type: "server_error", code: "upstream_server_error" };
196
+ }
173
197
  // HTTP 401 and explicit auth failures are authoritative even when provider text
174
198
  // also advertises an upgrade or subscription.
175
199
  if (
@@ -280,6 +304,9 @@ export function inferHttpStatusFromAdapterMessage(message: string): number {
280
304
  lower.includes("too many requests") ||
281
305
  lower.includes("throttling")
282
306
  ) return 429;
307
+ // Local Windows filesystem hardening is infrastructure, not provider authentication.
308
+ // Keep this ahead of auth/permission and timeout keyword inference.
309
+ if (isLocalAclHardeningMessage(lower)) return 503;
283
310
  // Strong authentication signals win when a message contains mixed auth and
284
311
  // subscription/permission wording.
285
312
  if (isAuthenticationMessage(lower)) return 401;
@@ -2420,6 +2420,8 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [
2420
2420
  dashboardUrl: "https://xiaomimimo.com",
2421
2421
  defaultModel: "mimo-auto",
2422
2422
  models: ["mimo-auto"],
2423
+ reasoningEfforts: ["low", "medium", "high"],
2424
+ reasoningEffortMap: { xhigh: "high", max: "high", ultra: "high" },
2423
2425
  note: "No key needed — uses Xiaomi MiMo's free public tier (limited-time offer). A JWT is bootstrapped automatically with an anonymous random client id stored locally. The endpoint contract mirrors the official MiMoCode client and is not publicly documented — Xiaomi may change or restrict it at any time. Prompts may be processed/retained by Xiaomi; do not send confidential material.",
2424
2426
  },
2425
2427
  // Xiaomi MiMo paid token plan. Separate host and wire from both `xiaomi` (Anthropic) and
@@ -36,6 +36,17 @@ export interface ResponseSpillPayload {
36
36
  createdAt: number;
37
37
  clientThreadId?: string;
38
38
  items: unknown[];
39
+ /**
40
+ * Index in `items` where the provider output begins, used by replay-overlap detection
41
+ * in state.ts. Optional so a payload written before this field still loads (it simply
42
+ * never authorizes a skip).
43
+ *
44
+ * Compatibility is FORWARD-ONLY: `validPayload` is a strict key allowlist, so a build
45
+ * predating this field rejects a payload carrying it as corrupt rather than ignoring
46
+ * it. Rolling back across this change invalidates spilled entries, which degrades to a
47
+ * replay miss — an already-handled path — not to corrupted live state.
48
+ */
49
+ providerOutputStart?: number;
39
50
  providers?: OcxProviderContinuationState;
40
51
  }
41
52
 
@@ -261,12 +272,19 @@ function validPayload(value: unknown, responseId: string): value is ResponseSpil
261
272
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
262
273
  const payload = value as Record<string, unknown>;
263
274
  const keys = Object.keys(payload);
264
- if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providers"].includes(key))) return false;
275
+ if (keys.some(key => !["version", "responseId", "createdAt", "clientThreadId", "items", "providerOutputStart", "providers"].includes(key))) return false;
265
276
  if (payload.version !== 1 || payload.responseId !== responseId) return false;
266
277
  if (typeof payload.createdAt !== "number" || !Number.isFinite(payload.createdAt)) return false;
267
278
  if (payload.clientThreadId !== undefined
268
279
  && (typeof payload.clientThreadId !== "string" || payload.clientThreadId.trim().length === 0)) return false;
269
280
  if (!Array.isArray(payload.items)) return false;
281
+ // A malformed boundary must degrade to "never skip", never to a bad index: reject the
282
+ // payload outright so materialization treats it as corrupt rather than trusting it.
283
+ if (payload.providerOutputStart !== undefined) {
284
+ const anchor = payload.providerOutputStart;
285
+ if (typeof anchor !== "number" || !Number.isSafeInteger(anchor)
286
+ || anchor < 0 || anchor > payload.items.length) return false;
287
+ }
270
288
  if (payload.providers !== undefined) {
271
289
  if (!payload.providers || typeof payload.providers !== "object" || Array.isArray(payload.providers)) return false;
272
290
  for (const providerState of Object.values(payload.providers)) {
@@ -289,6 +307,7 @@ export function writeResponseSpillDurably(
289
307
  createdAt: state.createdAt,
290
308
  ...(state.clientThreadId ? { clientThreadId: state.clientThreadId } : {}),
291
309
  items: state.items,
310
+ ...(state.providerOutputStart !== undefined ? { providerOutputStart: state.providerOutputStart } : {}),
292
311
  ...(state.providers ? { providers: state.providers } : {}),
293
312
  };
294
313
  const serialized = JSON.stringify(payload);