@yansigit/opencodex 2.33.0 → 2.33.1
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-CIDo4y4k.js +102 -0
- package/gui/dist/index.html +1 -1
- package/package.json +3 -1
- package/src/adapters/command-code.ts +101 -20
- package/src/adapters/cursor/envelope-echo.ts +162 -0
- package/src/adapters/cursor/live-transport.ts +3 -1
- package/src/adapters/cursor/native-exec-fs.ts +13 -12
- package/src/adapters/cursor/native-exec-network.ts +3 -5
- package/src/adapters/cursor/native-exec-policy.ts +47 -0
- package/src/adapters/cursor/native-exec-shell.ts +13 -25
- package/src/adapters/cursor/native-exec.ts +18 -10
- package/src/adapters/cursor/protobuf-events.ts +28 -2
- package/src/adapters/cursor/protobuf-request.ts +20 -3
- package/src/adapters/cursor/request-builder.ts +7 -0
- package/src/adapters/cursor/tool-definitions.ts +22 -1
- package/src/adapters/cursor/tool-result-normalize.ts +21 -8
- package/src/adapters/cursor/types.ts +7 -0
- package/src/adapters/cursor.ts +114 -0
- package/src/adapters/google-aistudio-parser.ts +49 -0
- package/src/adapters/google.ts +108 -16
- package/src/adapters/openai-responses.ts +1 -0
- package/src/chat/inbound.ts +15 -0
- package/src/cli/index.ts +1 -1
- package/src/codex/catalog/provider-fetch.ts +34 -0
- package/src/generated/compatibility-version.json +91 -47
- package/src/generated/model-metadata.ts +3 -0
- package/src/oauth/aistudio-native-daemon.ts +62 -0
- package/src/oauth/aistudio-session-sync.ts +95 -0
- package/src/oauth/google-aistudio-auth.ts +98 -0
- package/src/oauth/key-providers.ts +8 -0
- package/src/oauth/login-cli.ts +66 -1
- package/src/providers/derive.ts +1 -1
- package/src/providers/quota.ts +90 -38
- package/src/providers/registry.ts +24 -3
- package/src/router.ts +3 -0
- package/src/routing/account-pool/cooldown.ts +8 -0
- package/src/routing/account-pool/index.ts +1 -0
- package/src/server/aistudio-ws-hub.ts +295 -0
- package/src/server/auth-cors.ts +1 -0
- package/src/server/chat-completions.ts +2 -0
- package/src/server/index.ts +94 -0
- package/src/server/management/logs-usage-routes.ts +11 -5
- package/src/server/management/oauth-account-routes.ts +13 -3
- package/src/server/port-reclaim.ts +19 -1
- package/src/server/request-log-conversation.ts +12 -0
- package/src/server/request-log.ts +2 -1
- package/src/server/responses/core.ts +4 -3
- package/src/server/responses/policy-fallback.ts +1 -1
- package/src/server/ws-bridge.ts +2 -1
- package/src/smoke/fingerprint-cache.ts +133 -0
- package/src/smoke/live-scenarios.ts +33 -0
- package/src/smoke/runner.ts +119 -0
- package/src/types/provider.ts +2 -1
- package/src/types/request.ts +2 -0
- package/src/types/tools.ts +31 -7
- package/src/usage/command-code-manifest.ts +116 -0
- package/src/usage/cost.ts +2 -2
- package/src/usage/expected-prices.ts +83 -0
- package/src/usage/log.ts +2 -2
- package/src/usage/summary.ts +34 -12
- package/src/web-search/index.ts +16 -8
- package/gui/dist/assets/index-DKLr4LTE.js +0 -102
package/src/adapters/google.ts
CHANGED
|
@@ -48,6 +48,10 @@ import {
|
|
|
48
48
|
import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
|
|
49
49
|
import { configuredReasoningEfforts, mapReasoningEffort } from "../reasoning-effort";
|
|
50
50
|
import { normalizeAntigravityProviderError } from "../oauth/antigravity-routing";
|
|
51
|
+
import { buildAiStudioHeaders, parseGoogleCookieJar } from "../oauth/google-aistudio-auth";
|
|
52
|
+
import { cookieHeaderFromSession, loadAiStudioSession } from "../oauth/aistudio-session-sync";
|
|
53
|
+
import { parseMakerSuiteChunk } from "./google-aistudio-parser";
|
|
54
|
+
import { globalAiStudioRelayHub } from "../server/aistudio-ws-hub";
|
|
51
55
|
|
|
52
56
|
const INLINE_ERROR_URL_USERINFO = /https?:\/\/[^\s"'<>]*@/gi;
|
|
53
57
|
|
|
@@ -540,6 +544,21 @@ function googlePartThoughtSignature(part: GoogleResponsePart): string | undefine
|
|
|
540
544
|
return typeof nested === "string" && nested.length > 0 ? nested : undefined;
|
|
541
545
|
}
|
|
542
546
|
|
|
547
|
+
function ensureThoughtSignatureBypassSentinel(contents: unknown[], modelId?: string): void {
|
|
548
|
+
if (modelId && !/gemini-(?:3.7|2.5)|thinking/i.test(modelId)) return;
|
|
549
|
+
for (const c of contents as { role?: string; parts?: unknown[] }[]) {
|
|
550
|
+
if (c?.role !== "model" || !Array.isArray(c.parts)) continue;
|
|
551
|
+
for (const p of c.parts) {
|
|
552
|
+
if (p && typeof p === "object") {
|
|
553
|
+
const partObj = p as Record<string, unknown>;
|
|
554
|
+
if (partObj.functionCall && !partObj.thoughtSignature && !partObj.thought_signature) {
|
|
555
|
+
partObj.thoughtSignature = ANTIGRAVITY_SIGNATURE_BYPASS_SENTINEL;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
543
562
|
/**
|
|
544
563
|
* Carry a Gemini thought signature with the exact function-call part that produced it. Google
|
|
545
564
|
* validates the signature against that specific part, so it must ride the individual tool call
|
|
@@ -744,7 +763,46 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
744
763
|
// Direct AI-Studio uses the canonical server transport (fetchWithTransientRetry), which
|
|
745
764
|
// retries transient 5xx responses through providerFetch while preserving multi-key pool
|
|
746
765
|
// 429 rotation and raw error formatting.
|
|
747
|
-
...(provider.googleMode === "
|
|
766
|
+
...(provider.googleMode === "ai-studio-web"
|
|
767
|
+
? {
|
|
768
|
+
fetchResponse: async (request: AdapterRequest, ctx?: AdapterFetchContext): Promise<Response> => {
|
|
769
|
+
if (globalAiStudioRelayHub.hasActiveSessions()) {
|
|
770
|
+
const streamRes = await globalAiStudioRelayHub.dispatchStream(
|
|
771
|
+
{
|
|
772
|
+
url: request.url,
|
|
773
|
+
method: request.method,
|
|
774
|
+
headers: request.headers,
|
|
775
|
+
body: request.body,
|
|
776
|
+
},
|
|
777
|
+
ctx?.abortSignal,
|
|
778
|
+
);
|
|
779
|
+
const encoder = new TextEncoder();
|
|
780
|
+
const bodyStream = new ReadableStream({
|
|
781
|
+
async start(controller) {
|
|
782
|
+
try {
|
|
783
|
+
for await (const chunk of streamRes.chunks) {
|
|
784
|
+
controller.enqueue(encoder.encode(chunk));
|
|
785
|
+
}
|
|
786
|
+
controller.close();
|
|
787
|
+
} catch (err) {
|
|
788
|
+
controller.error(err);
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
return new Response(bodyStream, {
|
|
793
|
+
status: 200,
|
|
794
|
+
headers: { "Content-Type": "text/event-stream" },
|
|
795
|
+
});
|
|
796
|
+
}
|
|
797
|
+
return fetch(request.url, {
|
|
798
|
+
method: request.method,
|
|
799
|
+
headers: request.headers,
|
|
800
|
+
body: request.body,
|
|
801
|
+
signal: ctx?.abortSignal,
|
|
802
|
+
});
|
|
803
|
+
},
|
|
804
|
+
}
|
|
805
|
+
: provider.googleMode === "vertex" || provider.googleMode === "cloud-code-assist"
|
|
748
806
|
? {
|
|
749
807
|
fetchResponse: (request: AdapterRequest, ctx?: AdapterFetchContext): Promise<Response> =>
|
|
750
808
|
(provider.googleMode === "cloud-code-assist" ? fetchAntigravityWithRetry : fetchVertexWithRetry)(request, ctx),
|
|
@@ -882,19 +940,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
882
940
|
const strippedModelTail = /claude/i.test(wireModelId) ? stripTrailingClaudePrefill(contents) : false;
|
|
883
941
|
if (antigravityUsesReplayCache(wireModelId)) {
|
|
884
942
|
applyAntigravityReplay(wireModelId, sessionId, contents);
|
|
885
|
-
|
|
886
|
-
// supply the bypass sentinel so Antigravity does not reject the turn with HTTP 400.
|
|
887
|
-
for (const c of contents as { role?: string; parts?: unknown[] }[]) {
|
|
888
|
-
if (c?.role !== "model" || !Array.isArray(c.parts)) continue;
|
|
889
|
-
for (const p of c.parts) {
|
|
890
|
-
if (p && typeof p === "object") {
|
|
891
|
-
const partObj = p as Record<string, unknown>;
|
|
892
|
-
if (partObj.functionCall && !partObj.thoughtSignature && !partObj.thought_signature) {
|
|
893
|
-
partObj.thoughtSignature = ANTIGRAVITY_SIGNATURE_BYPASS_SENTINEL;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
943
|
+
ensureThoughtSignatureBypassSentinel(contents);
|
|
898
944
|
} else {
|
|
899
945
|
sanitizeAntigravityClaudeSignatures(contents);
|
|
900
946
|
}
|
|
@@ -970,6 +1016,22 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
970
1016
|
return { url, method: "POST", headers, body: JSON.stringify(compiled.body) };
|
|
971
1017
|
}
|
|
972
1018
|
|
|
1019
|
+
if (provider.googleMode === "ai-studio-web") {
|
|
1020
|
+
const base = (provider.baseUrl || "https://alkalimakersuite-pa.clients6.google.com").replace(/\/+$/, "");
|
|
1021
|
+
const url = `${base}/v1internal:${method}${streamParam}`;
|
|
1022
|
+
const cookieInput = provider.apiKey || provider.headers?.["Cookie"] || cookieHeaderFromSession(loadAiStudioSession()) || "";
|
|
1023
|
+
const jar = parseGoogleCookieJar(cookieInput);
|
|
1024
|
+
const aiStudioHeaders = await buildAiStudioHeaders(jar, "https://aistudio.google.com");
|
|
1025
|
+
Object.assign(headers, aiStudioHeaders);
|
|
1026
|
+
const compiled = compileGoogleWireBody({ ...body, model: routedModelId });
|
|
1027
|
+
restoreGoogleToolName = compiled.restoreToolName;
|
|
1028
|
+
if (Array.isArray((compiled.body as { contents?: unknown[] }).contents)) {
|
|
1029
|
+
ensureThoughtSignatureBypassSentinel((compiled.body as { contents: unknown[] }).contents, routedModelId);
|
|
1030
|
+
}
|
|
1031
|
+
emitInTurnGroundingSourcesQueue.push(!!parsed._ccaInTurnGrounding);
|
|
1032
|
+
return { url, method: "POST", headers, body: JSON.stringify(compiled.body) };
|
|
1033
|
+
}
|
|
1034
|
+
|
|
973
1035
|
// ai-studio (default): Generative Language API + x-goog-api-key.
|
|
974
1036
|
const url = `${provider.baseUrl}/v1beta/models/${routedModelId}:${method}${streamParam}`;
|
|
975
1037
|
const apiKey = provider.apiKey?.trim();
|
|
@@ -978,6 +1040,9 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
978
1040
|
|
|
979
1041
|
const compiled = compileGoogleWireBody(body);
|
|
980
1042
|
restoreGoogleToolName = compiled.restoreToolName;
|
|
1043
|
+
if (Array.isArray((compiled.body as { contents?: unknown[] }).contents)) {
|
|
1044
|
+
ensureThoughtSignatureBypassSentinel((compiled.body as { contents: unknown[] }).contents, routedModelId);
|
|
1045
|
+
}
|
|
981
1046
|
emitInTurnGroundingSourcesQueue.push(!!parsed._ccaInTurnGrounding);
|
|
982
1047
|
return { url, method: "POST", headers, body: JSON.stringify(compiled.body) };
|
|
983
1048
|
},
|
|
@@ -1280,6 +1345,16 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
1280
1345
|
if (result === "content") sawContentEvent = true;
|
|
1281
1346
|
continue;
|
|
1282
1347
|
}
|
|
1348
|
+
if (provider.googleMode === "ai-studio-web") {
|
|
1349
|
+
const parsed = parseMakerSuiteChunk(line);
|
|
1350
|
+
if (parsed.text) {
|
|
1351
|
+
sawAnyFrame = true;
|
|
1352
|
+
sawTerminalSignal = true;
|
|
1353
|
+
sawContentEvent = true;
|
|
1354
|
+
yield { type: "text_delta", text: parsed.text };
|
|
1355
|
+
continue;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1283
1358
|
sawLiveness = true;
|
|
1284
1359
|
if (line.startsWith(":") || !line.trim()) continue;
|
|
1285
1360
|
debugDroppedFrame("google", line);
|
|
@@ -1292,7 +1367,24 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
1292
1367
|
if (residual.startsWith(":")) {
|
|
1293
1368
|
yield { type: "heartbeat" };
|
|
1294
1369
|
} else if (!residual.startsWith("data:")) {
|
|
1295
|
-
|
|
1370
|
+
try {
|
|
1371
|
+
const parsedErr = JSON.parse(residual);
|
|
1372
|
+
if (parsedErr.error?.message) {
|
|
1373
|
+
yield { type: "error", message: parsedErr.error.message };
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
} catch (err) {
|
|
1377
|
+
void err;
|
|
1378
|
+
}
|
|
1379
|
+
if (provider.googleMode === "ai-studio-web") {
|
|
1380
|
+
const parsed = parseMakerSuiteChunk(residual);
|
|
1381
|
+
if (parsed.text) {
|
|
1382
|
+
yield { type: "text_delta", text: parsed.text };
|
|
1383
|
+
yield { type: "done" };
|
|
1384
|
+
return;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
yield { type: "error", message: `upstream non-SSE response: ${residual.slice(0, 300)}` };
|
|
1296
1388
|
return;
|
|
1297
1389
|
} else if ((yield* handleDataLine(residual)) === "terminate") return;
|
|
1298
1390
|
}
|
|
@@ -1344,7 +1436,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
1344
1436
|
// buffered adapter entry point, so collect the exact same events parseStream emits
|
|
1345
1437
|
// instead of maintaining a second CCA JSON parser.
|
|
1346
1438
|
const isSse = response.headers.get("content-type")?.includes("text/event-stream") ?? false;
|
|
1347
|
-
if (provider.googleMode === "cloud-code-assist" && isSse) {
|
|
1439
|
+
if ((provider.googleMode === "cloud-code-assist" && isSse) || provider.googleMode === "ai-studio-web") {
|
|
1348
1440
|
const events: AdapterEvent[] = [];
|
|
1349
1441
|
let previousTail: AdapterEvent | undefined;
|
|
1350
1442
|
try {
|
package/src/chat/inbound.ts
CHANGED
|
@@ -10,6 +10,21 @@ export class ChatCompletionsRequestError extends Error {}
|
|
|
10
10
|
type Rec = Record<string, unknown>;
|
|
11
11
|
type ChatCompletionsRoutingBody = Rec & { model: string; messages: unknown[] };
|
|
12
12
|
|
|
13
|
+
/** Session/thread headers the Chat -> Responses bridge must preserve for provider affinity. */
|
|
14
|
+
export const CHAT_RESPONSES_SESSION_HEADERS = [
|
|
15
|
+
"session_id",
|
|
16
|
+
"session-id",
|
|
17
|
+
"x-session-id",
|
|
18
|
+
"thread-id",
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export function copyChatResponsesSessionHeaders(source: Headers, target: Headers): void {
|
|
22
|
+
for (const name of CHAT_RESPONSES_SESSION_HEADERS) {
|
|
23
|
+
const value = source.get(name);
|
|
24
|
+
if (value) target.set(name, value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
function isRec(v: unknown): v is Rec {
|
|
14
29
|
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
15
30
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -164,7 +164,7 @@ async function chooseListenPort(requestedPort?: number): Promise<number> {
|
|
|
164
164
|
// Ghost LISTEN rows with a dead PID can outlive the process for a while.
|
|
165
165
|
// SetTcpEntry(DELETE_TCB) needs elevation (often returns 317), so the only
|
|
166
166
|
// reliable non-admin recovery is to wait for the OS to release the TCB.
|
|
167
|
-
timeoutMs: 60_000,
|
|
167
|
+
timeoutMs: process.platform === "win32" ? 60_000 : 10_000,
|
|
168
168
|
intervalMs: 100,
|
|
169
169
|
scanIntervalMs: 500,
|
|
170
170
|
killOcxHolders: false,
|
|
@@ -47,6 +47,7 @@ import { CODEX_GPT5_IDENTITY_LINE } from "../../adapters/identity";
|
|
|
47
47
|
import { filterCursorConfiguredModelsByLiveDiscovery } from "../../adapters/cursor/discovery";
|
|
48
48
|
import { fetchCursorUsableModels } from "../../adapters/cursor/live-models";
|
|
49
49
|
import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
50
|
+
import { globalAiStudioRelayHub } from "../../server/aistudio-ws-hub";
|
|
50
51
|
import {
|
|
51
52
|
COMBO_NAMESPACE,
|
|
52
53
|
comboModelId,
|
|
@@ -1245,6 +1246,39 @@ async function fetchProviderModelsWithAuth(
|
|
|
1245
1246
|
}
|
|
1246
1247
|
return merged;
|
|
1247
1248
|
};
|
|
1249
|
+
if (prov.googleMode === "ai-studio-web") {
|
|
1250
|
+
clearProviderDiscoveryStatus(name);
|
|
1251
|
+
if (globalAiStudioRelayHub.hasActiveSessions()) {
|
|
1252
|
+
try {
|
|
1253
|
+
const streamRes = await globalAiStudioRelayHub.dispatchStream({
|
|
1254
|
+
url: "https://generativelanguage.googleapis.com/v1beta/models",
|
|
1255
|
+
method: "GET",
|
|
1256
|
+
});
|
|
1257
|
+
let rawBody = "";
|
|
1258
|
+
for await (const chunk of streamRes.chunks) {
|
|
1259
|
+
rawBody += chunk;
|
|
1260
|
+
}
|
|
1261
|
+
const json = JSON.parse(rawBody);
|
|
1262
|
+
if (Array.isArray(json?.models)) {
|
|
1263
|
+
const liveModels: CatalogModel[] = json.models.map((m: any) => {
|
|
1264
|
+
const rawId = typeof m.name === "string" ? m.name.replace(/^models\//, "") : "";
|
|
1265
|
+
return {
|
|
1266
|
+
id: rawId,
|
|
1267
|
+
provider: name,
|
|
1268
|
+
...catalogHintsFromProviderConfig(name, prov, rawId, contextCap),
|
|
1269
|
+
};
|
|
1270
|
+
}).filter((m: any) => Boolean(m.id));
|
|
1271
|
+
if (liveModels.length > 0) {
|
|
1272
|
+
return observed(withConfiguredRetention(liveModels), "authoritative");
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
} catch {
|
|
1276
|
+
/* fallback to configured models */
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return observed(configured, "authoritative");
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1248
1282
|
// Static catalogs never need an OAuth refresh or an upstream model request. Clear any
|
|
1249
1283
|
// discovery failure left by an older live configuration even when the account is logged out.
|
|
1250
1284
|
if (prov.liveModels === false) {
|