@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.
- package/gui/dist/assets/{index-DUCH59lJ.css → index-CQ7bIKee.css} +1 -1
- package/gui/dist/assets/{index-CXI1262_.js → index-D_JUZLEC.js} +16 -16
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/client-fingerprint.ts +14 -10
- package/src/adapters/google-antigravity-wire.ts +4 -3
- package/src/adapters/google.ts +1 -1
- package/src/chat/inbound.ts +24 -1
- package/src/codex/shim.ts +100 -5
- package/src/generated/compatibility-version.json +21 -17
- package/src/lib/errors.ts +27 -0
- package/src/providers/registry.ts +2 -0
- package/src/responses/spill-store.ts +20 -1
- package/src/responses/state.ts +159 -3
- package/src/server/chat-completions.ts +4 -2
- package/src/server/effort-policy.ts +18 -0
- package/src/server/responses/core.ts +21 -2
- package/src/server/responses/input-admission.ts +169 -0
- package/src/service-manager-probe.ts +2 -3
- package/src/types.ts +10 -2
- package/src/vision/index.ts +21 -4
- package/src/web-search/index.ts +2 -1
package/gui/dist/index.html
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
20
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
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
|
@@ -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
|
|
44
|
-
/** Pinned fallback Antigravity
|
|
45
|
-
export const
|
|
46
|
-
const
|
|
47
|
-
const
|
|
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
|
|
53
|
-
* `antigravity/
|
|
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 =
|
|
57
|
-
const [osType, arch] =
|
|
58
|
-
return `antigravity/
|
|
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
|
|
7
|
-
* (`antigravity/
|
|
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.
|
|
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
|
|
package/src/adapters/google.ts
CHANGED
|
@@ -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
|
|
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",
|
package/src/chat/inbound.ts
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
908
|
+
const movedOriginal = shimPathFingerprint(target.backupPath);
|
|
834
909
|
if (!movedOriginal || !entry.movedOriginalFingerprint
|
|
835
|
-
|| !sameFingerprint(movedOriginal
|
|
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
|
|
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
|
-
|
|
1838
|
-
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
1989
|
+
"sha256": "a1239057ea7a156c562222a6f385a3bbd3f324cf346964cb6aae8185f47a3e34"
|
|
1990
1990
|
},
|
|
1991
1991
|
{
|
|
1992
1992
|
"path": "src/responses/state.ts",
|
|
1993
|
-
"sha256": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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);
|