@eddyskywalker/dsh-chatgpt-subscription 0.1.37 → 0.1.39
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/bin/antigravity-login.mjs +36 -0
- package/lib/client.js +1306 -53
- package/lib/client.js.map +1 -1
- package/lib/index.js +1921 -23
- package/lib/types/client/CodexSubscriptionSection.d.ts.map +1 -1
- package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts +11 -0
- package/lib/types/client/antigravity/AntigravityComposerQuota.d.ts.map +1 -0
- package/lib/types/client/antigravity/AntigravitySection.d.ts +10 -0
- package/lib/types/client/antigravity/AntigravitySection.d.ts.map +1 -0
- package/lib/types/client/antigravity/locales.d.ts +298 -0
- package/lib/types/client/antigravity/locales.d.ts.map +1 -0
- package/lib/types/client/antigravity/styles.d.ts +3 -0
- package/lib/types/client/antigravity/styles.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/locales.d.ts +19 -7
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/mermaid/renderer.d.ts +3 -0
- package/lib/types/client/mermaid/renderer.d.ts.map +1 -0
- package/lib/types/client/mermaid/sanitize.d.ts +2 -0
- package/lib/types/client/mermaid/sanitize.d.ts.map +1 -0
- package/lib/types/client/mermaid/styles.d.ts +3 -0
- package/lib/types/client/mermaid/styles.d.ts.map +1 -0
- package/lib/types/host/antigravity/adapter.d.ts +15 -0
- package/lib/types/host/antigravity/adapter.d.ts.map +1 -0
- package/lib/types/host/antigravity/client.d.ts +21 -0
- package/lib/types/host/antigravity/client.d.ts.map +1 -0
- package/lib/types/host/antigravity/mapper.d.ts +28 -0
- package/lib/types/host/antigravity/mapper.d.ts.map +1 -0
- package/lib/types/host/antigravity/oauth.d.ts +38 -0
- package/lib/types/host/antigravity/oauth.d.ts.map +1 -0
- package/lib/types/host/antigravity/routes.d.ts +6 -0
- package/lib/types/host/antigravity/routes.d.ts.map +1 -0
- package/lib/types/host/antigravity/token-store.d.ts +60 -0
- package/lib/types/host/antigravity/token-store.d.ts.map +1 -0
- package/lib/types/host/antigravity/types.d.ts +46 -0
- package/lib/types/host/antigravity/types.d.ts.map +1 -0
- package/lib/types/host/common/idle-watchdog.d.ts +8 -0
- package/lib/types/host/common/idle-watchdog.d.ts.map +1 -0
- package/lib/types/host/preferences.d.ts.map +1 -1
- package/lib/types/host/responses-client.d.ts +3 -1
- package/lib/types/host/responses-client.d.ts.map +1 -1
- package/lib/types/host/responses-mapper.d.ts +2 -2
- package/lib/types/host/responses-mapper.d.ts.map +1 -1
- package/lib/types/host/routes.d.ts.map +1 -1
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/antigravity-contracts.d.ts +72 -0
- package/lib/types/shared/antigravity-contracts.d.ts.map +1 -0
- package/lib/types/shared/contracts.d.ts +3 -0
- package/lib/types/shared/contracts.d.ts.map +1 -1
- package/lib/types/shared/model-catalog.d.ts +7 -0
- package/lib/types/shared/model-catalog.d.ts.map +1 -1
- package/lib/types/shared/preferences.d.ts +2 -1
- package/lib/types/shared/preferences.d.ts.map +1 -1
- package/package.json +34 -29
package/lib/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
1
2
|
import * as LlmModule from "@deepseek-ai/dsh-llm";
|
|
2
3
|
import { HarnessError, LlmAdapter, LlmError, ProviderRequestId, ReasoningEffortId, attributionHeaders, resolveRetryPolicy } from "@deepseek-ai/dsh-llm";
|
|
3
4
|
import { WebError } from "@deepseek-ai/dsh-web";
|
|
4
5
|
import { createUserMessage } from "@deepseek-ai/dsh-llm/message";
|
|
5
6
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
6
7
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
7
|
-
import http from "node:http";
|
|
8
|
+
import http, { createServer } from "node:http";
|
|
8
9
|
import { execSync, spawn } from "node:child_process";
|
|
9
10
|
import { ProxyAgent, fetch as fetch$1 } from "undici";
|
|
10
11
|
import * as SettingsModule from "@deepseek-ai/dsh-settings";
|
|
11
12
|
import z from "@deepseek-ai/schemastery";
|
|
12
|
-
import { constants } from "node:fs";
|
|
13
|
-
import { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
14
|
-
import { homedir } from "node:os";
|
|
15
|
-
import { dirname, join } from "node:path";
|
|
13
|
+
import fs, { constants } from "node:fs";
|
|
14
|
+
import fsPromises, { chmod, lstat, mkdir, open, rename, stat, unlink } from "node:fs/promises";
|
|
15
|
+
import os, { homedir } from "node:os";
|
|
16
|
+
import path, { dirname, join } from "node:path";
|
|
17
|
+
import { URL as URL$1, URLSearchParams as URLSearchParams$1 } from "node:url";
|
|
16
18
|
//#region src/compat.ts
|
|
17
19
|
/**
|
|
18
20
|
* Compatibility constants for the ChatGPT-backed Codex flow. The backend and
|
|
@@ -55,7 +57,8 @@ const CODEX_MODEL_CATALOG = [
|
|
|
55
57
|
inputModalities: ["text", "image"],
|
|
56
58
|
defaultReasoningEffort: "medium",
|
|
57
59
|
reasoningProfile: "gpt-5.6",
|
|
58
|
-
supportsReasoningSummary: true
|
|
60
|
+
supportsReasoningSummary: true,
|
|
61
|
+
fallbackModelId: "gpt-5.6-terra"
|
|
59
62
|
},
|
|
60
63
|
{
|
|
61
64
|
id: "gpt-5.6-terra",
|
|
@@ -64,7 +67,8 @@ const CODEX_MODEL_CATALOG = [
|
|
|
64
67
|
inputModalities: ["text", "image"],
|
|
65
68
|
defaultReasoningEffort: "medium",
|
|
66
69
|
reasoningProfile: "gpt-5.6",
|
|
67
|
-
supportsReasoningSummary: true
|
|
70
|
+
supportsReasoningSummary: true,
|
|
71
|
+
fallbackModelId: "gpt-5.5"
|
|
68
72
|
},
|
|
69
73
|
{
|
|
70
74
|
id: "gpt-5.6-luna",
|
|
@@ -73,7 +77,8 @@ const CODEX_MODEL_CATALOG = [
|
|
|
73
77
|
inputModalities: ["text", "image"],
|
|
74
78
|
defaultReasoningEffort: "medium",
|
|
75
79
|
reasoningProfile: "gpt-5.6",
|
|
76
|
-
supportsReasoningSummary: true
|
|
80
|
+
supportsReasoningSummary: true,
|
|
81
|
+
fallbackModelId: "gpt-5.5"
|
|
77
82
|
},
|
|
78
83
|
{
|
|
79
84
|
id: "gpt-5.5",
|
|
@@ -91,7 +96,8 @@ const CODEX_MODEL_CATALOG = [
|
|
|
91
96
|
inputModalities: ["text", "image"],
|
|
92
97
|
defaultReasoningEffort: "none",
|
|
93
98
|
reasoningProfile: "standard",
|
|
94
|
-
supportsReasoningSummary: true
|
|
99
|
+
supportsReasoningSummary: true,
|
|
100
|
+
fallbackModelId: "gpt-5.4-mini"
|
|
95
101
|
},
|
|
96
102
|
{
|
|
97
103
|
id: "gpt-5.4-mini",
|
|
@@ -149,14 +155,19 @@ function codexModelSupportsImageInput(model) {
|
|
|
149
155
|
function codexModelSupportsReasoningSummary(model) {
|
|
150
156
|
return resolveCodexCatalogEntry(model).supportsReasoningSummary;
|
|
151
157
|
}
|
|
158
|
+
function resolveCodexFallbackModel(model) {
|
|
159
|
+
const entry = resolveCodexCatalogEntry(model);
|
|
160
|
+
if (!entry.fallbackModelId) return void 0;
|
|
161
|
+
return CODEX_MODEL_CATALOG.find((cand) => cand.id === entry.fallbackModelId);
|
|
162
|
+
}
|
|
152
163
|
//#endregion
|
|
153
164
|
//#region src/host/model-catalog.ts
|
|
154
|
-
const PROVIDER_ID = CODEX_CHATGPT_PROVIDER_ID;
|
|
155
|
-
const PROVIDER_NAME = "Codex(ChatGPT 订阅)";
|
|
165
|
+
const PROVIDER_ID$1 = CODEX_CHATGPT_PROVIDER_ID;
|
|
166
|
+
const PROVIDER_NAME$1 = "Codex(ChatGPT 订阅)";
|
|
156
167
|
function listCodexModels(preferences) {
|
|
157
168
|
const visible = new Set(preferences?.status().visibleModelIds ?? CODEX_MODEL_CATALOG.map((entry) => entry.id));
|
|
158
169
|
return CODEX_MODEL_CATALOG.filter((entry) => visible.has(entry.id)).map((entry) => ({
|
|
159
|
-
provider: PROVIDER_ID,
|
|
170
|
+
provider: PROVIDER_ID$1,
|
|
160
171
|
id: entry.id,
|
|
161
172
|
name: entry.name,
|
|
162
173
|
inputModalities: [...entry.inputModalities]
|
|
@@ -168,7 +179,7 @@ function resolveCodexModel(model, preferences) {
|
|
|
168
179
|
const configuredContextWindow = isConfigurableContextModelId(model) ? status?.contextWindowOverrides[model] : void 0;
|
|
169
180
|
const efforts = reasoningEffortsForModel(model);
|
|
170
181
|
return {
|
|
171
|
-
provider: PROVIDER_ID,
|
|
182
|
+
provider: PROVIDER_ID$1,
|
|
172
183
|
id: model,
|
|
173
184
|
name: entry.id === model ? entry.name : model,
|
|
174
185
|
inputModalities: [...entry.inputModalities],
|
|
@@ -213,7 +224,7 @@ var CodexChatGptAdapter = class extends LlmAdapter {
|
|
|
213
224
|
providerInfo(provider) {
|
|
214
225
|
return {
|
|
215
226
|
id: provider,
|
|
216
|
-
name: PROVIDER_NAME
|
|
227
|
+
name: PROVIDER_NAME$1
|
|
217
228
|
};
|
|
218
229
|
}
|
|
219
230
|
providerRetryPolicy() {
|
|
@@ -1306,6 +1317,7 @@ const DEFAULT_PREFERENCES = {
|
|
|
1306
1317
|
quickQuotaVisible: false,
|
|
1307
1318
|
fastMode: false,
|
|
1308
1319
|
outputVerbosity: null,
|
|
1320
|
+
reasoningSummary: null,
|
|
1309
1321
|
visibleModelIds: [...DEFAULT_VISIBLE_CODEX_MODEL_IDS],
|
|
1310
1322
|
searchProvider: "dsh",
|
|
1311
1323
|
contextWindowOverrides: {
|
|
@@ -1325,6 +1337,9 @@ function isSearchProviderPreference(value) {
|
|
|
1325
1337
|
function isCodexOutputVerbosity(value) {
|
|
1326
1338
|
return value === "low" || value === "medium" || value === "high";
|
|
1327
1339
|
}
|
|
1340
|
+
function isCodexReasoningSummary(value) {
|
|
1341
|
+
return value === "auto" || value === "concise" || value === "detailed" || value === "none";
|
|
1342
|
+
}
|
|
1328
1343
|
function isProxyMode(value) {
|
|
1329
1344
|
return value === "auto" || value === "custom" || value === "direct";
|
|
1330
1345
|
}
|
|
@@ -1341,6 +1356,13 @@ function registerPreferenceStore(settings) {
|
|
|
1341
1356
|
z.const("high"),
|
|
1342
1357
|
z.const(null)
|
|
1343
1358
|
]).default(DEFAULT_PREFERENCES.outputVerbosity),
|
|
1359
|
+
reasoningSummary: z.union([
|
|
1360
|
+
z.const("auto"),
|
|
1361
|
+
z.const("concise"),
|
|
1362
|
+
z.const("detailed"),
|
|
1363
|
+
z.const("none"),
|
|
1364
|
+
z.const(null)
|
|
1365
|
+
]).default(DEFAULT_PREFERENCES.reasoningSummary),
|
|
1344
1366
|
visibleModelIds: z.array(z.string()).default(DEFAULT_PREFERENCES.visibleModelIds),
|
|
1345
1367
|
searchProvider: z.union([z.const("dsh"), z.const(SEARCH_PROVIDER_CODEX)]).default(DEFAULT_PREFERENCES.searchProvider),
|
|
1346
1368
|
contextWindowOverrides: z.object({
|
|
@@ -1374,6 +1396,10 @@ var SettingsPreferenceStore = class {
|
|
|
1374
1396
|
if (patch.outputVerbosity !== null && !isCodexOutputVerbosity(patch.outputVerbosity)) throw new PreferenceError("Unsupported output verbosity preference.");
|
|
1375
1397
|
normalized.outputVerbosity = patch.outputVerbosity;
|
|
1376
1398
|
}
|
|
1399
|
+
if (patch.reasoningSummary !== void 0) {
|
|
1400
|
+
if (patch.reasoningSummary !== null && !isCodexReasoningSummary(patch.reasoningSummary)) throw new PreferenceError("Unsupported reasoning summary preference.");
|
|
1401
|
+
normalized.reasoningSummary = patch.reasoningSummary;
|
|
1402
|
+
}
|
|
1377
1403
|
if (patch.visibleModelIds !== void 0) {
|
|
1378
1404
|
if (patch.visibleModelIds.length === 0 || !patch.visibleModelIds.every(isCodexModelId)) throw new PreferenceError("At least one supported Codex model must be visible.");
|
|
1379
1405
|
normalized.visibleModelIds = [...new Set(patch.visibleModelIds)];
|
|
@@ -1416,6 +1442,137 @@ function withWritable(value) {
|
|
|
1416
1442
|
};
|
|
1417
1443
|
}
|
|
1418
1444
|
//#endregion
|
|
1445
|
+
//#region node_modules/@deepseek-ai/dsh-timeout/lib/index.js
|
|
1446
|
+
/**
|
|
1447
|
+
* Shared timeout arithmetic, signal fusion, and classification. The library
|
|
1448
|
+
* only notifies through abort signals; each capability still owns the mechanism
|
|
1449
|
+
* that stops its work and translates timeout reasons into public outcomes.
|
|
1450
|
+
* @module @deepseek-ai/dsh-timeout
|
|
1451
|
+
*/
|
|
1452
|
+
/**
|
|
1453
|
+
* Internal abort reason carrying a capability-owned code and elapsed deadline.
|
|
1454
|
+
* Providers translate it through {@link timeoutOf} before returning to callers.
|
|
1455
|
+
*/
|
|
1456
|
+
var TimeoutReason = class extends Error {
|
|
1457
|
+
code;
|
|
1458
|
+
timeoutMs;
|
|
1459
|
+
name = "TimeoutReason";
|
|
1460
|
+
/**
|
|
1461
|
+
* @param code Capability-owned timeout code (e.g. `BASH_TIMEOUT`).
|
|
1462
|
+
* @param timeoutMs The deadline that elapsed, in milliseconds.
|
|
1463
|
+
*/
|
|
1464
|
+
constructor(code, timeoutMs) {
|
|
1465
|
+
super(`${code} after ${timeoutMs}ms`);
|
|
1466
|
+
this.code = code;
|
|
1467
|
+
this.timeoutMs = timeoutMs;
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
/** Largest delay Node schedules without clamping it to one millisecond. */
|
|
1471
|
+
const MAX_TIMER_DELAY_MS = 2147483647;
|
|
1472
|
+
function assertTimerDelay(timeoutMs, name) {
|
|
1473
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) throw new Error(`${name} must be a positive finite number no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Create a rearmable idle watchdog for an async iterator. The timer exists only
|
|
1477
|
+
* while {@link IdleWatchdog.next} is outstanding, so consumer think time does
|
|
1478
|
+
* not count as provider idle time. The returned signal is stable for the whole
|
|
1479
|
+
* call and only notifies; the iterator must observe it to terminate its work.
|
|
1480
|
+
*
|
|
1481
|
+
* @param upstream - caller cancellation fused into the stable signal.
|
|
1482
|
+
* @param timeoutMs - positive finite idle interval in milliseconds.
|
|
1483
|
+
* @param code - capability-owned code carried by the timeout reason.
|
|
1484
|
+
* @returns a stable signal, guarded next operation, and timer disposer.
|
|
1485
|
+
*/
|
|
1486
|
+
function idleWatchdog(upstream, timeoutMs, code) {
|
|
1487
|
+
assertTimerDelay(timeoutMs, "idleWatchdog timeoutMs");
|
|
1488
|
+
const timeout = new AbortController();
|
|
1489
|
+
const signal = upstream === void 0 ? timeout.signal : AbortSignal.any([upstream, timeout.signal]);
|
|
1490
|
+
let timer;
|
|
1491
|
+
let outstanding = false;
|
|
1492
|
+
let disposed = false;
|
|
1493
|
+
const arm = () => {
|
|
1494
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1495
|
+
timer = setTimeout(() => {
|
|
1496
|
+
timeout.abort(new TimeoutReason(code, timeoutMs));
|
|
1497
|
+
}, timeoutMs);
|
|
1498
|
+
};
|
|
1499
|
+
return {
|
|
1500
|
+
signal,
|
|
1501
|
+
async next(iterator) {
|
|
1502
|
+
if (disposed) throw new Error("idleWatchdog is disposed");
|
|
1503
|
+
if (outstanding) throw new Error("idleWatchdog next is already outstanding");
|
|
1504
|
+
outstanding = true;
|
|
1505
|
+
arm();
|
|
1506
|
+
try {
|
|
1507
|
+
return await iterator.next();
|
|
1508
|
+
} finally {
|
|
1509
|
+
clearTimeout(timer);
|
|
1510
|
+
timer = void 0;
|
|
1511
|
+
outstanding = false;
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
pulse() {
|
|
1515
|
+
if (disposed || !outstanding) return;
|
|
1516
|
+
arm();
|
|
1517
|
+
},
|
|
1518
|
+
[Symbol.dispose]() {
|
|
1519
|
+
if (disposed) return;
|
|
1520
|
+
disposed = true;
|
|
1521
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1522
|
+
timer = void 0;
|
|
1523
|
+
}
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Recover a timeout reason from a reason-bearing object. Supplying `code`
|
|
1528
|
+
* distinguishes this deadline from a nested upstream deadline; a foreign code
|
|
1529
|
+
* follows the ordinary cancellation path.
|
|
1530
|
+
*
|
|
1531
|
+
* @param x An {@link AbortSignal} or any `{ reason }` carrier (e.g. a caught abort error).
|
|
1532
|
+
* @param code When provided, only a {@link TimeoutReason} with this exact `code` matches.
|
|
1533
|
+
* @returns The matching {@link TimeoutReason}, else `undefined`.
|
|
1534
|
+
*/
|
|
1535
|
+
function timeoutOf(x, code) {
|
|
1536
|
+
const reason = x.reason;
|
|
1537
|
+
if (!(reason instanceof TimeoutReason)) return void 0;
|
|
1538
|
+
return code === void 0 || reason.code === code ? reason : void 0;
|
|
1539
|
+
}
|
|
1540
|
+
//#endregion
|
|
1541
|
+
//#region src/host/common/idle-watchdog.ts
|
|
1542
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
1543
|
+
const STREAM_IDLE_TIMEOUT_CODE$1 = "LLM_STREAM_IDLE_TIMEOUT";
|
|
1544
|
+
/**
|
|
1545
|
+
* 为异步流包裹可复位的空闲超时看门狗。
|
|
1546
|
+
* 当流式 chunk 产出之间的间隔超过指定阈值时,主动终止并抛出带有 TIMEOUT 的 LlmError。
|
|
1547
|
+
*/
|
|
1548
|
+
async function* wrapStreamWithWatchdog(source, upstreamSignal, timeoutMs = DEFAULT_STREAM_IDLE_TIMEOUT_MS, timeoutCode = STREAM_IDLE_TIMEOUT_CODE$1, providerTag = "llm") {
|
|
1549
|
+
const consumer = new AbortController();
|
|
1550
|
+
const watchdog = idleWatchdog(upstreamSignal === void 0 ? consumer.signal : AbortSignal.any([upstreamSignal, consumer.signal]), timeoutMs, timeoutCode);
|
|
1551
|
+
const iterator = source(watchdog.signal)[Symbol.asyncIterator]();
|
|
1552
|
+
let exhausted = false;
|
|
1553
|
+
try {
|
|
1554
|
+
while (true) {
|
|
1555
|
+
const result = await watchdog.next(iterator);
|
|
1556
|
+
if (timeoutOf(watchdog.signal, timeoutCode) !== void 0) throw new LlmError(`${providerTag} stream idle timeout after ${timeoutMs}ms`, "TIMEOUT");
|
|
1557
|
+
if (result.done) {
|
|
1558
|
+
exhausted = true;
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
yield result.value;
|
|
1562
|
+
}
|
|
1563
|
+
} catch (error) {
|
|
1564
|
+
if (timeoutOf(watchdog.signal, timeoutCode) !== void 0) throw new LlmError(`${providerTag} stream idle timeout after ${timeoutMs}ms`, "TIMEOUT", { cause: error });
|
|
1565
|
+
if (upstreamSignal?.aborted) throw new LlmError(`${providerTag} request aborted by caller`, "ABORTED", { cause: error });
|
|
1566
|
+
throw error;
|
|
1567
|
+
} finally {
|
|
1568
|
+
consumer.abort(`${providerTag} stream consumer stopped`);
|
|
1569
|
+
if (!exhausted) try {
|
|
1570
|
+
await iterator.return?.(void 0);
|
|
1571
|
+
} catch {}
|
|
1572
|
+
watchdog[Symbol.dispose]();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
//#endregion
|
|
1419
1576
|
//#region src/host/responses-mapper.ts
|
|
1420
1577
|
function hiddenSandboxControlToolNames(options) {
|
|
1421
1578
|
const retryTools = recentSandboxRetryToolNames(options.messages);
|
|
@@ -1450,7 +1607,7 @@ function normalizeReplayCallIds(items, normalizeCallId) {
|
|
|
1450
1607
|
call_id: normalizeCallId(item.call_id)
|
|
1451
1608
|
} : item);
|
|
1452
1609
|
}
|
|
1453
|
-
async function buildResponsesPayload(options, attachments, localRawImages = {}, outputVerbosity = null, fastMode = false) {
|
|
1610
|
+
async function buildResponsesPayload(options, attachments, localRawImages = {}, outputVerbosity = null, fastMode = false, reasoningSummary = null) {
|
|
1454
1611
|
const sandboxRetryTools = recentSandboxRetryToolNames(options.messages);
|
|
1455
1612
|
const resolveLocalRawImages = supportsImageInput(options);
|
|
1456
1613
|
const normalizeCallId = createCallIdNormalizer();
|
|
@@ -1536,7 +1693,7 @@ async function buildResponsesPayload(options, attachments, localRawImages = {},
|
|
|
1536
1693
|
if (fastMode) payload.service_tier = "priority";
|
|
1537
1694
|
if (options.reasoningEffort !== void 0) payload.reasoning = codexModelSupportsReasoningSummary(options.model) ? {
|
|
1538
1695
|
effort: options.reasoningEffort,
|
|
1539
|
-
summary: "auto"
|
|
1696
|
+
summary: reasoningSummary ?? "auto"
|
|
1540
1697
|
} : { effort: options.reasoningEffort };
|
|
1541
1698
|
return payload;
|
|
1542
1699
|
}
|
|
@@ -1843,6 +2000,7 @@ var ResponsesClient = class {
|
|
|
1843
2000
|
onGenerationFinished;
|
|
1844
2001
|
outputVerbosity;
|
|
1845
2002
|
fastMode;
|
|
2003
|
+
reasoningSummary;
|
|
1846
2004
|
constructor(oauth, attachments, options = {}) {
|
|
1847
2005
|
this.oauth = oauth;
|
|
1848
2006
|
this.attachments = attachments;
|
|
@@ -1851,14 +2009,37 @@ var ResponsesClient = class {
|
|
|
1851
2009
|
this.onGenerationFinished = options.onGenerationFinished ?? (() => void 0);
|
|
1852
2010
|
this.outputVerbosity = options.outputVerbosity ?? (() => null);
|
|
1853
2011
|
this.fastMode = options.fastMode ?? (() => false);
|
|
2012
|
+
this.reasoningSummary = options.reasoningSummary ?? (() => null);
|
|
1854
2013
|
}
|
|
1855
2014
|
localRawImages;
|
|
1856
2015
|
async *stream(options) {
|
|
1857
|
-
const payload = await buildResponsesPayload(options, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode());
|
|
1858
2016
|
const hiddenSandboxControls = hiddenSandboxControlToolNames(options);
|
|
1859
2017
|
const sessionId = stableSessionId(options.sessionId);
|
|
2018
|
+
let currentModel = options.model;
|
|
2019
|
+
let attemptOptions = options;
|
|
2020
|
+
let response;
|
|
2021
|
+
while (true) {
|
|
2022
|
+
const payload = await buildResponsesPayload(attemptOptions, this.attachments, this.localRawImages, this.outputVerbosity(), this.fastMode(), this.reasoningSummary());
|
|
2023
|
+
try {
|
|
2024
|
+
response = await this.send(payload, sessionId, options.signal);
|
|
2025
|
+
break;
|
|
2026
|
+
} catch (error) {
|
|
2027
|
+
if (error instanceof LlmError && (error.code === "NOT_FOUND" || error.status === 404)) {
|
|
2028
|
+
const fallback = resolveCodexFallbackModel(currentModel);
|
|
2029
|
+
if (fallback && fallback.id !== currentModel) {
|
|
2030
|
+
currentModel = fallback.id;
|
|
2031
|
+
attemptOptions = {
|
|
2032
|
+
...attemptOptions,
|
|
2033
|
+
model: fallback.id
|
|
2034
|
+
};
|
|
2035
|
+
continue;
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
throw error;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1860
2041
|
try {
|
|
1861
|
-
yield*
|
|
2042
|
+
yield* wrapStreamWithWatchdog((watchdogSignal) => parseResponsesStream(response, watchdogSignal, hiddenSandboxControls), options.signal, 3e5, "LLM_STREAM_IDLE_TIMEOUT", "Codex");
|
|
1862
2043
|
} finally {
|
|
1863
2044
|
this.onGenerationFinished();
|
|
1864
2045
|
}
|
|
@@ -2203,6 +2384,7 @@ async function responseError(response) {
|
|
|
2203
2384
|
...response.status === 429 ? { providerRetryAfterMs: retryAfterMs(response.headers) } : {}
|
|
2204
2385
|
};
|
|
2205
2386
|
if (response.status === 401) return new LlmError("ChatGPT sign-in has expired. Sign in again.", "AUTH", options);
|
|
2387
|
+
if (response.status === 404) return new LlmError(`Codex model or resource not found (${response.status})${detail ? `: ${detail}` : "."}`, "NOT_FOUND", options);
|
|
2206
2388
|
if (response.status === 429) return new LlmError("Codex rate limit reached.", "RATE_LIMIT", options);
|
|
2207
2389
|
if (response.status >= 500) return new LlmError(`Codex service error (${response.status}).`, "SERVER_ERROR", options);
|
|
2208
2390
|
return new LlmError(`Codex request failed (${response.status})${detail ? `: ${detail}` : "."}`, "PROVIDER_ERROR", options);
|
|
@@ -2651,6 +2833,20 @@ function registerRoutes(ctx, oauth, usage, preferences, proxyManager) {
|
|
|
2651
2833
|
});
|
|
2652
2834
|
return;
|
|
2653
2835
|
}
|
|
2836
|
+
if (request.method === "GET" && url.pathname === `/api/dsh-chatgpt-subscription/mermaid.min.js`) {
|
|
2837
|
+
try {
|
|
2838
|
+
const mermaidPath = createRequire(import.meta.url).resolve("mermaid/dist/mermaid.min.js");
|
|
2839
|
+
response.writeHead(200, {
|
|
2840
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
2841
|
+
"Cache-Control": "public, max-age=86400"
|
|
2842
|
+
});
|
|
2843
|
+
fs.createReadStream(mermaidPath).pipe(response);
|
|
2844
|
+
} catch {
|
|
2845
|
+
response.writeHead(404, { "Content-Type": "text/plain" });
|
|
2846
|
+
response.end("Not found");
|
|
2847
|
+
}
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2654
2850
|
if (request.method !== "POST") {
|
|
2655
2851
|
jsonError(response, 405, {
|
|
2656
2852
|
code: "bad-request",
|
|
@@ -2854,7 +3050,7 @@ function field(value, name) {
|
|
|
2854
3050
|
const candidate = value[name];
|
|
2855
3051
|
return typeof candidate === "string" && candidate !== "" ? candidate : null;
|
|
2856
3052
|
}
|
|
2857
|
-
function isRecord(value) {
|
|
3053
|
+
function isRecord$1(value) {
|
|
2858
3054
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2859
3055
|
}
|
|
2860
3056
|
function readPreferencesUpdate(value, current) {
|
|
@@ -2875,12 +3071,16 @@ function readPreferencesUpdate(value, current) {
|
|
|
2875
3071
|
if (value.outputVerbosity !== null && value.outputVerbosity !== "low" && value.outputVerbosity !== "medium" && value.outputVerbosity !== "high") throw new PreferenceError("outputVerbosity must be null, low, medium, or high.");
|
|
2876
3072
|
patch.outputVerbosity = value.outputVerbosity;
|
|
2877
3073
|
}
|
|
3074
|
+
if ("reasoningSummary" in value) {
|
|
3075
|
+
if (value.reasoningSummary !== null && !isCodexReasoningSummary(value.reasoningSummary)) throw new PreferenceError("reasoningSummary must be null, auto, concise, detailed, or none.");
|
|
3076
|
+
patch.reasoningSummary = value.reasoningSummary;
|
|
3077
|
+
}
|
|
2878
3078
|
if ("searchProvider" in value) {
|
|
2879
3079
|
if (value.searchProvider !== "dsh" && value.searchProvider !== "codex") throw new PreferenceError("searchProvider must be dsh or codex.");
|
|
2880
3080
|
patch.searchProvider = value.searchProvider;
|
|
2881
3081
|
}
|
|
2882
3082
|
if ("contextWindowOverrides" in value) {
|
|
2883
|
-
if (!isRecord(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
3083
|
+
if (!isRecord$1(value.contextWindowOverrides)) throw new PreferenceError("contextWindowOverrides must be an object.");
|
|
2884
3084
|
const overrides = {};
|
|
2885
3085
|
for (const [model, contextWindow] of Object.entries(value.contextWindowOverrides)) {
|
|
2886
3086
|
if (!isConfigurableContextModelId(model)) throw new PreferenceError("Only GPT-5.6 context windows can be changed.");
|
|
@@ -3307,6 +3507,1691 @@ function currentConfig(entry) {
|
|
|
3307
3507
|
return typeof config === "object" && config !== null && !Array.isArray(config) ? config : {};
|
|
3308
3508
|
}
|
|
3309
3509
|
//#endregion
|
|
3510
|
+
//#region src/host/antigravity/types.ts
|
|
3511
|
+
const PROVIDER_NAME = "Antigravity";
|
|
3512
|
+
const PROVIDER_ID = "antigravity";
|
|
3513
|
+
const STREAM_IDLE_TIMEOUT_MS = 3e5;
|
|
3514
|
+
const STREAM_IDLE_TIMEOUT_CODE = "LLM_STREAM_IDLE_TIMEOUT";
|
|
3515
|
+
const DISCOVERY_TIMEOUT_MS = 8e3;
|
|
3516
|
+
const PROJECT_CACHE_TTL_MS = 1800 * 1e3;
|
|
3517
|
+
const OAUTH_CALLBACK_TIMEOUT_MS = 300 * 1e3;
|
|
3518
|
+
const ENDPOINT_FALLBACKS = ["https://cloudcode-pa.googleapis.com", "https://daily-cloudcode-pa.sandbox.googleapis.com"];
|
|
3519
|
+
const REDIRECT_PATH = "/oauth-callback";
|
|
3520
|
+
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
3521
|
+
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
3522
|
+
const SCOPES = [
|
|
3523
|
+
"https://www.googleapis.com/auth/aicode",
|
|
3524
|
+
"https://www.googleapis.com/auth/cloud-platform",
|
|
3525
|
+
"https://www.googleapis.com/auth/userinfo.email",
|
|
3526
|
+
"https://www.googleapis.com/auth/userinfo.profile",
|
|
3527
|
+
"https://www.googleapis.com/auth/cclog",
|
|
3528
|
+
"https://www.googleapis.com/auth/experimentsandconfigs"
|
|
3529
|
+
];
|
|
3530
|
+
const DEFAULT_CLIENT_ID = Buffer.from("MTA3MTAwNjA2MDU5MS10bWhzc2luMmgyMWxjcmUyMzV2dG9sb2poNGc0MDNlcC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbQ==", "base64").toString("utf8");
|
|
3531
|
+
const DEFAULT_CLIENT_SECRET = Buffer.from("R09DU1BYLUs1OEZXUjQ4NkxkTEoxbUxCOHNYQzR6NnFEQWY=", "base64").toString("utf8");
|
|
3532
|
+
const ANTIGRAVITY_SYSTEM_INSTRUCTION = "You are Antigravity, a powerful agentic AI coding assistant designed by Google DeepMind. You are pair programming with a user to solve coding tasks. Be concise, practical, and tool-aware.";
|
|
3533
|
+
const ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION = "CRITICAL: NEVER output rule checks, formatting guidelines, constraint checklists, or thinking/personality preambles in the final response. Output only the final response.";
|
|
3534
|
+
const GEMINI_ROLE = {
|
|
3535
|
+
user: "user",
|
|
3536
|
+
model: "model"
|
|
3537
|
+
};
|
|
3538
|
+
const TOOL_CALLING_MODE = {
|
|
3539
|
+
none: "NONE",
|
|
3540
|
+
any: "ANY",
|
|
3541
|
+
auto: "AUTO",
|
|
3542
|
+
validated: "VALIDATED"
|
|
3543
|
+
};
|
|
3544
|
+
const ROUTING = {
|
|
3545
|
+
"gemini-3.8-flash": {
|
|
3546
|
+
off: "gemini-3.8-flash-tiered",
|
|
3547
|
+
routing: {
|
|
3548
|
+
minimal: "gemini-3.8-flash-tiered",
|
|
3549
|
+
low: "gemini-3.8-flash-tiered",
|
|
3550
|
+
medium: "gemini-3.8-flash-tiered",
|
|
3551
|
+
high: "gemini-3.8-flash-tiered",
|
|
3552
|
+
xhigh: "gemini-3.8-flash-tiered"
|
|
3553
|
+
},
|
|
3554
|
+
defaultRequestId: "gemini-3.8-flash-tiered",
|
|
3555
|
+
fallbackCandidates: ["gemini-3.7-flash-tiered", "gemini-3.6-flash-low"]
|
|
3556
|
+
},
|
|
3557
|
+
"claude-opus-4-6": {
|
|
3558
|
+
off: "claude-opus-4-6-thinking",
|
|
3559
|
+
routing: {
|
|
3560
|
+
minimal: "claude-opus-4-6-thinking",
|
|
3561
|
+
low: "claude-opus-4-6-thinking",
|
|
3562
|
+
medium: "claude-opus-4-6-thinking",
|
|
3563
|
+
high: "claude-opus-4-6-thinking",
|
|
3564
|
+
xhigh: "claude-opus-4-6-thinking"
|
|
3565
|
+
},
|
|
3566
|
+
defaultRequestId: "claude-opus-4-6-thinking",
|
|
3567
|
+
fallbackCandidates: ["claude-sonnet-4-6"]
|
|
3568
|
+
},
|
|
3569
|
+
"claude-sonnet-4-6": {
|
|
3570
|
+
off: "claude-sonnet-4-6",
|
|
3571
|
+
routing: {
|
|
3572
|
+
minimal: "claude-sonnet-4-6",
|
|
3573
|
+
low: "claude-sonnet-4-6",
|
|
3574
|
+
medium: "claude-sonnet-4-6",
|
|
3575
|
+
high: "claude-sonnet-4-6",
|
|
3576
|
+
xhigh: "claude-sonnet-4-6"
|
|
3577
|
+
},
|
|
3578
|
+
defaultRequestId: "claude-sonnet-4-6"
|
|
3579
|
+
},
|
|
3580
|
+
"gemini-3.7-flash": {
|
|
3581
|
+
off: "gemini-3.7-flash-tiered",
|
|
3582
|
+
routing: {
|
|
3583
|
+
minimal: "gemini-3.7-flash-tiered",
|
|
3584
|
+
low: "gemini-3.7-flash-tiered",
|
|
3585
|
+
medium: "gemini-3.7-flash-tiered",
|
|
3586
|
+
high: "gemini-3.7-flash-tiered",
|
|
3587
|
+
xhigh: "gemini-3.7-flash-tiered"
|
|
3588
|
+
},
|
|
3589
|
+
defaultRequestId: "gemini-3.7-flash-tiered",
|
|
3590
|
+
fallbackCandidates: ["gemini-3.6-flash-low"]
|
|
3591
|
+
},
|
|
3592
|
+
"gemini-3.6-flash": {
|
|
3593
|
+
off: "gemini-3.6-flash-low",
|
|
3594
|
+
routing: {
|
|
3595
|
+
minimal: "gemini-3.6-flash-low",
|
|
3596
|
+
low: "gemini-3.6-flash-low",
|
|
3597
|
+
medium: "gemini-3.6-flash-medium",
|
|
3598
|
+
high: "gemini-3.6-flash-high",
|
|
3599
|
+
xhigh: "gemini-3.6-flash-high"
|
|
3600
|
+
},
|
|
3601
|
+
defaultRequestId: "gemini-3.6-flash-high"
|
|
3602
|
+
},
|
|
3603
|
+
"gemini-3.5-flash": {
|
|
3604
|
+
off: "gemini-3.5-flash-extra-low",
|
|
3605
|
+
routing: {
|
|
3606
|
+
minimal: "gemini-3.5-flash-extra-low",
|
|
3607
|
+
low: "gemini-3.5-flash-low",
|
|
3608
|
+
medium: "gemini-3.5-flash-low",
|
|
3609
|
+
high: "gemini-3-flash-agent",
|
|
3610
|
+
xhigh: "gemini-3-flash-agent"
|
|
3611
|
+
},
|
|
3612
|
+
defaultRequestId: "gemini-3-flash-agent"
|
|
3613
|
+
},
|
|
3614
|
+
"gemini-3.1-pro": {
|
|
3615
|
+
off: "gemini-3.1-pro-low",
|
|
3616
|
+
routing: {
|
|
3617
|
+
minimal: "gemini-3.1-pro-low",
|
|
3618
|
+
low: "gemini-3.1-pro-low",
|
|
3619
|
+
medium: "gemini-pro-agent",
|
|
3620
|
+
high: "gemini-pro-agent",
|
|
3621
|
+
xhigh: "gemini-pro-agent"
|
|
3622
|
+
},
|
|
3623
|
+
defaultRequestId: "gemini-pro-agent"
|
|
3624
|
+
},
|
|
3625
|
+
"gemini-3.1-flash-image": {
|
|
3626
|
+
off: "gemini-3.1-flash-image",
|
|
3627
|
+
routing: {
|
|
3628
|
+
minimal: "gemini-3.1-flash-image",
|
|
3629
|
+
low: "gemini-3.1-flash-image",
|
|
3630
|
+
medium: "gemini-3.1-flash-image",
|
|
3631
|
+
high: "gemini-3.1-flash-image",
|
|
3632
|
+
xhigh: "gemini-3.1-flash-image"
|
|
3633
|
+
},
|
|
3634
|
+
defaultRequestId: "gemini-3.1-flash-image"
|
|
3635
|
+
},
|
|
3636
|
+
"gemini-3-flash": {
|
|
3637
|
+
off: "gemini-3-flash",
|
|
3638
|
+
routing: {
|
|
3639
|
+
minimal: "gemini-3-flash",
|
|
3640
|
+
low: "gemini-3-flash",
|
|
3641
|
+
medium: "gemini-3-flash",
|
|
3642
|
+
high: "gemini-3-flash",
|
|
3643
|
+
xhigh: "gemini-3-flash"
|
|
3644
|
+
},
|
|
3645
|
+
defaultRequestId: "gemini-3-flash"
|
|
3646
|
+
},
|
|
3647
|
+
"gemini-2.5-pro": {
|
|
3648
|
+
off: "gemini-2.5-pro",
|
|
3649
|
+
routing: {
|
|
3650
|
+
minimal: "gemini-2.5-pro",
|
|
3651
|
+
low: "gemini-2.5-pro",
|
|
3652
|
+
medium: "gemini-2.5-pro",
|
|
3653
|
+
high: "gemini-2.5-pro",
|
|
3654
|
+
xhigh: "gemini-2.5-pro"
|
|
3655
|
+
},
|
|
3656
|
+
defaultRequestId: "gemini-2.5-pro"
|
|
3657
|
+
},
|
|
3658
|
+
"gemini-2.5-flash": {
|
|
3659
|
+
off: "gemini-2.5-flash",
|
|
3660
|
+
routing: {
|
|
3661
|
+
minimal: "gemini-2.5-flash",
|
|
3662
|
+
low: "gemini-2.5-flash",
|
|
3663
|
+
medium: "gemini-2.5-flash",
|
|
3664
|
+
high: "gemini-2.5-flash",
|
|
3665
|
+
xhigh: "gemini-2.5-flash"
|
|
3666
|
+
},
|
|
3667
|
+
defaultRequestId: "gemini-2.5-flash"
|
|
3668
|
+
},
|
|
3669
|
+
"gpt-oss-120b": {
|
|
3670
|
+
off: "gpt-oss-120b-medium",
|
|
3671
|
+
routing: {
|
|
3672
|
+
minimal: "gpt-oss-120b-medium",
|
|
3673
|
+
low: "gpt-oss-120b-medium",
|
|
3674
|
+
medium: "gpt-oss-120b-medium",
|
|
3675
|
+
high: "gpt-oss-120b-medium",
|
|
3676
|
+
xhigh: "gpt-oss-120b-medium"
|
|
3677
|
+
},
|
|
3678
|
+
defaultRequestId: "gpt-oss-120b-medium"
|
|
3679
|
+
}
|
|
3680
|
+
};
|
|
3681
|
+
const RUNTIME_MAX_OUTPUT_TOKENS = {
|
|
3682
|
+
"gemini-3.8-flash": 65536,
|
|
3683
|
+
"gemini-3.8-flash-tiered": 65536,
|
|
3684
|
+
"gemini-3.7-flash": 65536,
|
|
3685
|
+
"gemini-3.7-flash-tiered": 65536,
|
|
3686
|
+
"gemini-3.7-flash-low": 65536,
|
|
3687
|
+
"gemini-3.7-flash-medium": 65536,
|
|
3688
|
+
"gemini-3.7-flash-high": 65536,
|
|
3689
|
+
"gemini-3.6-flash": 65536,
|
|
3690
|
+
"gemini-3.6-flash-low": 65536,
|
|
3691
|
+
"gemini-3.6-flash-medium": 65536,
|
|
3692
|
+
"gemini-3.6-flash-high": 65536,
|
|
3693
|
+
"gemini-3.5-flash": 65536,
|
|
3694
|
+
"gemini-3.5-flash-extra-low": 65536,
|
|
3695
|
+
"gemini-3.5-flash-low": 65536,
|
|
3696
|
+
"gemini-3-flash-agent": 65536,
|
|
3697
|
+
"gemini-3.1-pro": 65535,
|
|
3698
|
+
"gemini-3.1-pro-low": 65535,
|
|
3699
|
+
"gemini-3.1-pro-high": 65535,
|
|
3700
|
+
"gemini-pro-agent": 65535,
|
|
3701
|
+
"claude-opus-4-6": 64e3,
|
|
3702
|
+
"claude-opus-4-6-thinking": 64e3,
|
|
3703
|
+
"claude-sonnet-4-6": 64e3,
|
|
3704
|
+
"gpt-oss-120b": 32768,
|
|
3705
|
+
"gpt-oss-120b-medium": 32768
|
|
3706
|
+
};
|
|
3707
|
+
const MODELS = [
|
|
3708
|
+
{
|
|
3709
|
+
id: "gemini-3.8-flash",
|
|
3710
|
+
name: "Gemini 3.8 Flash",
|
|
3711
|
+
inputModalities: ["text", "image"],
|
|
3712
|
+
contextWindow: 1048576,
|
|
3713
|
+
maxTokens: 65536,
|
|
3714
|
+
reasoningEfforts: [
|
|
3715
|
+
"low",
|
|
3716
|
+
"medium",
|
|
3717
|
+
"high"
|
|
3718
|
+
]
|
|
3719
|
+
},
|
|
3720
|
+
{
|
|
3721
|
+
id: "gemini-3.7-flash",
|
|
3722
|
+
name: "Gemini 3.7 Flash",
|
|
3723
|
+
inputModalities: ["text", "image"],
|
|
3724
|
+
contextWindow: 1048576,
|
|
3725
|
+
maxTokens: 65536,
|
|
3726
|
+
reasoningEfforts: [
|
|
3727
|
+
"low",
|
|
3728
|
+
"medium",
|
|
3729
|
+
"high"
|
|
3730
|
+
]
|
|
3731
|
+
},
|
|
3732
|
+
{
|
|
3733
|
+
id: "gemini-3.6-flash",
|
|
3734
|
+
name: "Gemini 3.6 Flash",
|
|
3735
|
+
inputModalities: ["text", "image"],
|
|
3736
|
+
contextWindow: 1048576,
|
|
3737
|
+
maxTokens: 65536,
|
|
3738
|
+
reasoningEfforts: [
|
|
3739
|
+
"low",
|
|
3740
|
+
"medium",
|
|
3741
|
+
"high"
|
|
3742
|
+
]
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
id: "gemini-3.5-flash",
|
|
3746
|
+
name: "Gemini 3.5 Flash",
|
|
3747
|
+
inputModalities: ["text", "image"],
|
|
3748
|
+
contextWindow: 1048576,
|
|
3749
|
+
maxTokens: 65536,
|
|
3750
|
+
reasoningEfforts: [
|
|
3751
|
+
"low",
|
|
3752
|
+
"medium",
|
|
3753
|
+
"high"
|
|
3754
|
+
]
|
|
3755
|
+
},
|
|
3756
|
+
{
|
|
3757
|
+
id: "gemini-3.1-pro",
|
|
3758
|
+
name: "Gemini 3.1 Pro",
|
|
3759
|
+
inputModalities: ["text", "image"],
|
|
3760
|
+
contextWindow: 1048576,
|
|
3761
|
+
maxTokens: 65535,
|
|
3762
|
+
reasoningEfforts: ["low", "high"]
|
|
3763
|
+
},
|
|
3764
|
+
{
|
|
3765
|
+
id: "gemini-3.1-flash-image",
|
|
3766
|
+
name: "Gemini 3.1 Flash Image",
|
|
3767
|
+
inputModalities: ["text", "image"],
|
|
3768
|
+
contextWindow: 1048576,
|
|
3769
|
+
maxTokens: 8192
|
|
3770
|
+
},
|
|
3771
|
+
{
|
|
3772
|
+
id: "gemini-3-flash",
|
|
3773
|
+
name: "Gemini 3 Flash",
|
|
3774
|
+
inputModalities: ["text", "image"],
|
|
3775
|
+
contextWindow: 1048576,
|
|
3776
|
+
maxTokens: 65536
|
|
3777
|
+
},
|
|
3778
|
+
{
|
|
3779
|
+
id: "gemini-2.5-pro",
|
|
3780
|
+
name: "Gemini 2.5 Pro",
|
|
3781
|
+
inputModalities: ["text", "image"],
|
|
3782
|
+
contextWindow: 1048576,
|
|
3783
|
+
maxTokens: 65535
|
|
3784
|
+
},
|
|
3785
|
+
{
|
|
3786
|
+
id: "gemini-2.5-flash",
|
|
3787
|
+
name: "Gemini 2.5 Flash",
|
|
3788
|
+
inputModalities: ["text", "image"],
|
|
3789
|
+
contextWindow: 1048576,
|
|
3790
|
+
maxTokens: 65536
|
|
3791
|
+
},
|
|
3792
|
+
{
|
|
3793
|
+
id: "claude-opus-4-6",
|
|
3794
|
+
name: "Claude Opus 4.6",
|
|
3795
|
+
inputModalities: ["text", "image"],
|
|
3796
|
+
contextWindow: 1048576,
|
|
3797
|
+
maxTokens: 64e3,
|
|
3798
|
+
reasoningEfforts: [
|
|
3799
|
+
"low",
|
|
3800
|
+
"medium",
|
|
3801
|
+
"high"
|
|
3802
|
+
]
|
|
3803
|
+
},
|
|
3804
|
+
{
|
|
3805
|
+
id: "claude-sonnet-4-6",
|
|
3806
|
+
name: "Claude Sonnet 4.6",
|
|
3807
|
+
inputModalities: ["text", "image"],
|
|
3808
|
+
contextWindow: 1048576,
|
|
3809
|
+
maxTokens: 64e3,
|
|
3810
|
+
reasoningEfforts: [
|
|
3811
|
+
"low",
|
|
3812
|
+
"medium",
|
|
3813
|
+
"high"
|
|
3814
|
+
]
|
|
3815
|
+
},
|
|
3816
|
+
{
|
|
3817
|
+
id: "gpt-oss-120b",
|
|
3818
|
+
name: "GPT-OSS 120B",
|
|
3819
|
+
inputModalities: ["text"],
|
|
3820
|
+
contextWindow: 262144,
|
|
3821
|
+
maxTokens: 32768,
|
|
3822
|
+
reasoningEfforts: [
|
|
3823
|
+
"low",
|
|
3824
|
+
"medium",
|
|
3825
|
+
"high"
|
|
3826
|
+
]
|
|
3827
|
+
}
|
|
3828
|
+
];
|
|
3829
|
+
//#endregion
|
|
3830
|
+
//#region src/host/antigravity/token-store.ts
|
|
3831
|
+
const ANTIGRAVITY_PREFERENCES_NAMESPACE = "dsh-antigravity";
|
|
3832
|
+
function registerAntigravityPreferenceStore(settings, fallbackStore = new FileModelSettingsStore()) {
|
|
3833
|
+
if (!settings) return {
|
|
3834
|
+
status: () => ({
|
|
3835
|
+
enabledModelIds: MODELS.map((m) => m.id),
|
|
3836
|
+
catalogModels: [],
|
|
3837
|
+
contextWindowOverrides: {},
|
|
3838
|
+
defaultReasoningEffort: null
|
|
3839
|
+
}),
|
|
3840
|
+
update: async (patch) => fallbackStore.updateSettings(patch)
|
|
3841
|
+
};
|
|
3842
|
+
const ns = SettingsModule.settingsNamespace ? SettingsModule.settingsNamespace(ANTIGRAVITY_PREFERENCES_NAMESPACE) : ANTIGRAVITY_PREFERENCES_NAMESPACE;
|
|
3843
|
+
const scope = settings.register.call(settings, ns, z.object({
|
|
3844
|
+
enabledModelIds: z.array(z.string()).default(MODELS.map((m) => m.id)),
|
|
3845
|
+
contextWindowOverrides: z.dict(z.number()).default({}),
|
|
3846
|
+
defaultReasoningEffort: z.union([
|
|
3847
|
+
z.const("low"),
|
|
3848
|
+
z.const("medium"),
|
|
3849
|
+
z.const("high"),
|
|
3850
|
+
z.const(null)
|
|
3851
|
+
]).default(null)
|
|
3852
|
+
}));
|
|
3853
|
+
return {
|
|
3854
|
+
status: () => {
|
|
3855
|
+
const val = scope.get();
|
|
3856
|
+
return {
|
|
3857
|
+
enabledModelIds: val.enabledModelIds,
|
|
3858
|
+
catalogModels: [],
|
|
3859
|
+
contextWindowOverrides: val.contextWindowOverrides,
|
|
3860
|
+
defaultReasoningEffort: val.defaultReasoningEffort
|
|
3861
|
+
};
|
|
3862
|
+
},
|
|
3863
|
+
update: async (patch) => {
|
|
3864
|
+
const current = scope.get();
|
|
3865
|
+
const normalized = {
|
|
3866
|
+
enabledModelIds: patch.enabledModelIds ?? current.enabledModelIds,
|
|
3867
|
+
contextWindowOverrides: patch.contextWindowOverrides ? {
|
|
3868
|
+
...current.contextWindowOverrides,
|
|
3869
|
+
...patch.contextWindowOverrides
|
|
3870
|
+
} : current.contextWindowOverrides,
|
|
3871
|
+
defaultReasoningEffort: patch.defaultReasoningEffort !== void 0 ? patch.defaultReasoningEffort : current.defaultReasoningEffort
|
|
3872
|
+
};
|
|
3873
|
+
await scope.update(normalized);
|
|
3874
|
+
fallbackStore.updateSettings(patch).catch(() => void 0);
|
|
3875
|
+
return {
|
|
3876
|
+
...normalized,
|
|
3877
|
+
catalogModels: []
|
|
3878
|
+
};
|
|
3879
|
+
}
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3882
|
+
function dshHomeDir() {
|
|
3883
|
+
return process.env.DSH_HOME?.trim() || path.join(os.homedir(), ".dsh");
|
|
3884
|
+
}
|
|
3885
|
+
function credentialPath() {
|
|
3886
|
+
return path.join(dshHomeDir(), "storages", "antigravity-oauth.json");
|
|
3887
|
+
}
|
|
3888
|
+
function modelSettingsPath() {
|
|
3889
|
+
return path.join(dshHomeDir(), "storages", "antigravity-models.json");
|
|
3890
|
+
}
|
|
3891
|
+
var FileCredentialStore = class {
|
|
3892
|
+
filePath;
|
|
3893
|
+
constructor(filePath = credentialPath()) {
|
|
3894
|
+
this.filePath = filePath;
|
|
3895
|
+
}
|
|
3896
|
+
path() {
|
|
3897
|
+
return this.filePath;
|
|
3898
|
+
}
|
|
3899
|
+
async read() {
|
|
3900
|
+
try {
|
|
3901
|
+
const content = await fsPromises.readFile(this.filePath, "utf8");
|
|
3902
|
+
const parsed = JSON.parse(content);
|
|
3903
|
+
if (typeof parsed === "object" && parsed !== null && ("access_token" in parsed || "access" in parsed)) return parsed;
|
|
3904
|
+
return null;
|
|
3905
|
+
} catch {
|
|
3906
|
+
return null;
|
|
3907
|
+
}
|
|
3908
|
+
}
|
|
3909
|
+
async write(credentials) {
|
|
3910
|
+
await fsPromises.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
3911
|
+
const tmp = `${this.filePath}.tmp.${Date.now()}`;
|
|
3912
|
+
await fsPromises.writeFile(tmp, JSON.stringify(credentials, null, 2), "utf8");
|
|
3913
|
+
await fsPromises.rename(tmp, this.filePath);
|
|
3914
|
+
}
|
|
3915
|
+
async delete() {
|
|
3916
|
+
try {
|
|
3917
|
+
await fsPromises.unlink(this.filePath);
|
|
3918
|
+
} catch (err) {
|
|
3919
|
+
if (err.code !== "ENOENT") throw err;
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
};
|
|
3923
|
+
var FileModelSettingsStore = class {
|
|
3924
|
+
filePath;
|
|
3925
|
+
constructor(filePath = modelSettingsPath()) {
|
|
3926
|
+
this.filePath = filePath;
|
|
3927
|
+
}
|
|
3928
|
+
path() {
|
|
3929
|
+
return this.filePath;
|
|
3930
|
+
}
|
|
3931
|
+
async read() {
|
|
3932
|
+
try {
|
|
3933
|
+
const content = await fsPromises.readFile(this.filePath, "utf8");
|
|
3934
|
+
const parsed = JSON.parse(content);
|
|
3935
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
3936
|
+
const record = parsed;
|
|
3937
|
+
return {
|
|
3938
|
+
enabledModelIds: Array.isArray(record.enabledModelIds) ? record.enabledModelIds.filter((id) => typeof id === "string") : MODELS.map((m) => m.id),
|
|
3939
|
+
catalogModels: Array.isArray(record.catalogModels) ? record.catalogModels : [],
|
|
3940
|
+
contextWindowOverrides: typeof record.contextWindowOverrides === "object" && record.contextWindowOverrides !== null ? record.contextWindowOverrides : {},
|
|
3941
|
+
defaultReasoningEffort: record.defaultReasoningEffort === "low" || record.defaultReasoningEffort === "medium" || record.defaultReasoningEffort === "high" ? record.defaultReasoningEffort : null
|
|
3942
|
+
};
|
|
3943
|
+
}
|
|
3944
|
+
} catch {}
|
|
3945
|
+
return {
|
|
3946
|
+
enabledModelIds: MODELS.map((m) => m.id),
|
|
3947
|
+
catalogModels: [],
|
|
3948
|
+
contextWindowOverrides: {},
|
|
3949
|
+
defaultReasoningEffort: null
|
|
3950
|
+
};
|
|
3951
|
+
}
|
|
3952
|
+
async write(settings) {
|
|
3953
|
+
await fsPromises.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
3954
|
+
const tmp = `${this.filePath}.tmp.${Date.now()}`;
|
|
3955
|
+
await fsPromises.writeFile(tmp, JSON.stringify(settings, null, 2), "utf8");
|
|
3956
|
+
await fsPromises.rename(tmp, this.filePath);
|
|
3957
|
+
}
|
|
3958
|
+
async updateSettings(patch) {
|
|
3959
|
+
const current = await this.read();
|
|
3960
|
+
const next = {
|
|
3961
|
+
...current,
|
|
3962
|
+
...patch.enabledModelIds !== void 0 ? { enabledModelIds: patch.enabledModelIds } : {},
|
|
3963
|
+
...patch.contextWindowOverrides !== void 0 ? { contextWindowOverrides: {
|
|
3964
|
+
...current.contextWindowOverrides || {},
|
|
3965
|
+
...patch.contextWindowOverrides
|
|
3966
|
+
} } : {},
|
|
3967
|
+
...patch.defaultReasoningEffort !== void 0 ? { defaultReasoningEffort: patch.defaultReasoningEffort } : {}
|
|
3968
|
+
};
|
|
3969
|
+
await this.write(next);
|
|
3970
|
+
return next;
|
|
3971
|
+
}
|
|
3972
|
+
async setEnabledModelIds(enabledModelIds) {
|
|
3973
|
+
return this.updateSettings({ enabledModelIds });
|
|
3974
|
+
}
|
|
3975
|
+
async setCatalogModels(catalogModels, options) {
|
|
3976
|
+
const current = await this.read();
|
|
3977
|
+
const next = {
|
|
3978
|
+
...current,
|
|
3979
|
+
enabledModelIds: options?.enabledModelIds ?? current.enabledModelIds,
|
|
3980
|
+
catalogModels
|
|
3981
|
+
};
|
|
3982
|
+
await this.write(next);
|
|
3983
|
+
return next;
|
|
3984
|
+
}
|
|
3985
|
+
};
|
|
3986
|
+
//#endregion
|
|
3987
|
+
//#region src/host/antigravity/client.ts
|
|
3988
|
+
const projectCache = /* @__PURE__ */ new Map();
|
|
3989
|
+
let cachedQuota;
|
|
3990
|
+
const PLATFORM = process.platform === "darwin" ? "MACOS" : process.platform === "win32" ? "WINDOWS" : "LINUX";
|
|
3991
|
+
function defaultUserAgent() {
|
|
3992
|
+
return `antigravity/1.15.8 ${process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "windows" : "linux"}/${process.arch === "x64" ? "amd64" : process.arch}`;
|
|
3993
|
+
}
|
|
3994
|
+
function antigravityHeaders(token) {
|
|
3995
|
+
return {
|
|
3996
|
+
Authorization: `Bearer ${token}`,
|
|
3997
|
+
"Content-Type": "application/json",
|
|
3998
|
+
Accept: "text/event-stream",
|
|
3999
|
+
"User-Agent": process.env.DSH_ANTIGRAVITY_USER_AGENT || defaultUserAgent(),
|
|
4000
|
+
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
|
|
4001
|
+
"Client-Metadata": JSON.stringify({
|
|
4002
|
+
ideType: "ANTIGRAVITY",
|
|
4003
|
+
platform: PLATFORM,
|
|
4004
|
+
pluginType: "GEMINI"
|
|
4005
|
+
})
|
|
4006
|
+
};
|
|
4007
|
+
}
|
|
4008
|
+
function jsonHeaders(token) {
|
|
4009
|
+
return {
|
|
4010
|
+
...antigravityHeaders(token),
|
|
4011
|
+
Accept: "application/json"
|
|
4012
|
+
};
|
|
4013
|
+
}
|
|
4014
|
+
function endpointCandidates() {
|
|
4015
|
+
const custom = process.env.DSH_ANTIGRAVITY_ENDPOINT?.trim();
|
|
4016
|
+
if (custom) return [custom];
|
|
4017
|
+
return ENDPOINT_FALLBACKS;
|
|
4018
|
+
}
|
|
4019
|
+
function extractProjectId(data) {
|
|
4020
|
+
if (typeof data !== "object" || data === null) return void 0;
|
|
4021
|
+
const record = data;
|
|
4022
|
+
const direct = record.antigravityProjectId ?? record.projectId ?? record.backendProjectId ?? record.userDefinedCloudaicompanionProject ?? record.cloudaicompanionProject ?? record.project;
|
|
4023
|
+
if (typeof direct === "string" && direct.length > 0) return direct;
|
|
4024
|
+
if (typeof direct === "object" && direct !== null && "id" in direct && typeof direct.id === "string") return direct.id;
|
|
4025
|
+
for (const key of [
|
|
4026
|
+
"projects",
|
|
4027
|
+
"projectIds",
|
|
4028
|
+
"cloudaicompanionProjects"
|
|
4029
|
+
]) {
|
|
4030
|
+
const list = record[key];
|
|
4031
|
+
if (Array.isArray(list)) for (const item of list) {
|
|
4032
|
+
const nested = extractProjectId(item);
|
|
4033
|
+
if (nested) return nested;
|
|
4034
|
+
if (typeof item === "string" && item.length > 0) return item;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
async function listCloudAICompanionProjects(token) {
|
|
4039
|
+
for (const endpoint of endpointCandidates()) try {
|
|
4040
|
+
const response = await fetch(`${endpoint}/v1internal:listCloudAICompanionProjects`, {
|
|
4041
|
+
method: "POST",
|
|
4042
|
+
headers: antigravityHeaders(token),
|
|
4043
|
+
body: JSON.stringify({}),
|
|
4044
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
|
|
4045
|
+
});
|
|
4046
|
+
if (!response.ok) continue;
|
|
4047
|
+
return extractProjectId(await response.json());
|
|
4048
|
+
} catch {}
|
|
4049
|
+
}
|
|
4050
|
+
async function loadCodeAssist(token) {
|
|
4051
|
+
const cached = projectCache.get(token);
|
|
4052
|
+
if (cached && cached.expiresAt > Date.now()) return cached.projectId;
|
|
4053
|
+
const body = JSON.stringify({ metadata: {
|
|
4054
|
+
ideType: "ANTIGRAVITY",
|
|
4055
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
4056
|
+
pluginType: "GEMINI"
|
|
4057
|
+
} });
|
|
4058
|
+
for (const endpoint of endpointCandidates()) try {
|
|
4059
|
+
const response = await fetch(`${endpoint}/v1internal:loadCodeAssist`, {
|
|
4060
|
+
method: "POST",
|
|
4061
|
+
headers: antigravityHeaders(token),
|
|
4062
|
+
body,
|
|
4063
|
+
signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS)
|
|
4064
|
+
});
|
|
4065
|
+
if (!response.ok) continue;
|
|
4066
|
+
const project = extractProjectId(await response.json());
|
|
4067
|
+
if (project) {
|
|
4068
|
+
projectCache.set(token, {
|
|
4069
|
+
projectId: project,
|
|
4070
|
+
expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
|
|
4071
|
+
});
|
|
4072
|
+
return project;
|
|
4073
|
+
}
|
|
4074
|
+
const listProj = await listCloudAICompanionProjects(token);
|
|
4075
|
+
if (listProj) {
|
|
4076
|
+
projectCache.set(token, {
|
|
4077
|
+
projectId: listProj,
|
|
4078
|
+
expiresAt: Date.now() + PROJECT_CACHE_TTL_MS
|
|
4079
|
+
});
|
|
4080
|
+
return listProj;
|
|
4081
|
+
}
|
|
4082
|
+
} catch {}
|
|
4083
|
+
}
|
|
4084
|
+
async function postJson(path, token, body) {
|
|
4085
|
+
for (const endpoint of endpointCandidates()) try {
|
|
4086
|
+
const response = await fetch(`${endpoint}${path}`, {
|
|
4087
|
+
method: "POST",
|
|
4088
|
+
headers: jsonHeaders(token),
|
|
4089
|
+
body: JSON.stringify(body)
|
|
4090
|
+
});
|
|
4091
|
+
if (response.ok) return {
|
|
4092
|
+
endpoint,
|
|
4093
|
+
status: response.status,
|
|
4094
|
+
data: await response.json()
|
|
4095
|
+
};
|
|
4096
|
+
} catch {}
|
|
4097
|
+
throw new Error(`Failed to call Antigravity API ${path}`);
|
|
4098
|
+
}
|
|
4099
|
+
function parseQuotaSummary(data) {
|
|
4100
|
+
const summary = typeof data === "object" && data !== null ? data : {};
|
|
4101
|
+
const rawGroups = Array.isArray(summary.groups) ? summary.groups : [];
|
|
4102
|
+
const groups = [];
|
|
4103
|
+
for (const group of rawGroups) {
|
|
4104
|
+
if (typeof group !== "object" || group === null) continue;
|
|
4105
|
+
const groupRec = group;
|
|
4106
|
+
const buckets = [];
|
|
4107
|
+
const rawBuckets = Array.isArray(groupRec.buckets) ? groupRec.buckets : [];
|
|
4108
|
+
for (const bucket of rawBuckets) {
|
|
4109
|
+
if (typeof bucket !== "object" || bucket === null) continue;
|
|
4110
|
+
const bRec = bucket;
|
|
4111
|
+
const remaining = typeof bRec.remainingFraction === "number" ? Math.max(0, Math.min(1, bRec.remainingFraction)) : 0;
|
|
4112
|
+
buckets.push({
|
|
4113
|
+
bucketId: String(bRec.bucketId || bRec.displayName || "limit"),
|
|
4114
|
+
displayName: String(bRec.displayName || bRec.bucketId || "Limit"),
|
|
4115
|
+
window: typeof bRec.window === "string" ? bRec.window : void 0,
|
|
4116
|
+
resetTime: typeof bRec.resetTime === "string" ? bRec.resetTime : void 0,
|
|
4117
|
+
description: typeof bRec.description === "string" ? bRec.description : void 0,
|
|
4118
|
+
remainingFraction: remaining
|
|
4119
|
+
});
|
|
4120
|
+
}
|
|
4121
|
+
if (buckets.length > 0 || groupRec.displayName) groups.push({
|
|
4122
|
+
displayName: String(groupRec.displayName || "Quota group"),
|
|
4123
|
+
description: typeof groupRec.description === "string" ? groupRec.description : void 0,
|
|
4124
|
+
buckets
|
|
4125
|
+
});
|
|
4126
|
+
}
|
|
4127
|
+
return {
|
|
4128
|
+
groups,
|
|
4129
|
+
description: typeof summary.description === "string" ? summary.description : void 0
|
|
4130
|
+
};
|
|
4131
|
+
}
|
|
4132
|
+
function parseCatalogModels(data) {
|
|
4133
|
+
if (typeof data !== "object" || data === null) return [];
|
|
4134
|
+
const record = data;
|
|
4135
|
+
const rawModels = typeof record.models === "object" && record.models !== null ? record.models : {};
|
|
4136
|
+
const list = [];
|
|
4137
|
+
for (const [modelId, info] of Object.entries(rawModels)) {
|
|
4138
|
+
if (typeof info !== "object" || info === null) continue;
|
|
4139
|
+
const rec = info;
|
|
4140
|
+
if (rec.isInternal || modelId.startsWith("chat_")) continue;
|
|
4141
|
+
list.push({
|
|
4142
|
+
id: modelId,
|
|
4143
|
+
name: typeof rec.displayName === "string" ? rec.displayName : modelId,
|
|
4144
|
+
description: typeof rec.description === "string" ? rec.description : void 0
|
|
4145
|
+
});
|
|
4146
|
+
}
|
|
4147
|
+
return list;
|
|
4148
|
+
}
|
|
4149
|
+
async function fetchAccountQuota(store = new FileCredentialStore(), modelSettings) {
|
|
4150
|
+
const { token, projectId: credentialProjectId } = await ensureApiKey(store);
|
|
4151
|
+
const [assistResult, summaryResult] = await Promise.all([postJson("/v1internal:loadCodeAssist", token, { metadata: {
|
|
4152
|
+
ideType: "ANTIGRAVITY",
|
|
4153
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
4154
|
+
pluginType: "GEMINI"
|
|
4155
|
+
} }).catch(() => null), postJson("/v1internal:retrieveUserQuotaSummary", token, {}).catch(() => null)]);
|
|
4156
|
+
const discoveredProject = assistResult ? extractProjectId(assistResult.data) : void 0;
|
|
4157
|
+
const projectId = credentialProjectId || discoveredProject || "antigravity-default";
|
|
4158
|
+
const modelsData = (await postJson("/v1internal:fetchAvailableModels", token, { project: projectId }).catch(() => null))?.data;
|
|
4159
|
+
const { groups, description } = summaryResult ? parseQuotaSummary(summaryResult.data) : { groups: [] };
|
|
4160
|
+
const catalogModels = modelsData ? parseCatalogModels(modelsData) : [];
|
|
4161
|
+
const assistData = assistResult?.data || {};
|
|
4162
|
+
const currentTier = assistData.currentTier;
|
|
4163
|
+
const paidTier = assistData.paidTier;
|
|
4164
|
+
const planLabel = paidTier?.name || currentTier?.name || void 0;
|
|
4165
|
+
cachedQuota = {
|
|
4166
|
+
projectId,
|
|
4167
|
+
endpoint: summaryResult?.endpoint || "https://cloudcode-pa.googleapis.com",
|
|
4168
|
+
planLabel,
|
|
4169
|
+
productTier: currentTier,
|
|
4170
|
+
paidTier,
|
|
4171
|
+
groups,
|
|
4172
|
+
groupDescription: description,
|
|
4173
|
+
models: catalogModels.map((m) => ({
|
|
4174
|
+
modelId: m.id,
|
|
4175
|
+
displayName: m.name,
|
|
4176
|
+
description: m.description
|
|
4177
|
+
})),
|
|
4178
|
+
catalogModels,
|
|
4179
|
+
fetchedAt: Date.now()
|
|
4180
|
+
};
|
|
4181
|
+
if (modelSettings && catalogModels.length > 0) {
|
|
4182
|
+
const current = await modelSettings.read();
|
|
4183
|
+
const isFirstTime = current.catalogModels.length === 0 && current.enabledModelIds.length === 0;
|
|
4184
|
+
const catalogIds = new Set(catalogModels.map((m) => m.id));
|
|
4185
|
+
const mergedEnabled = isFirstTime ? catalogModels.map((m) => m.id) : current.enabledModelIds.filter((id) => catalogIds.has(id));
|
|
4186
|
+
await modelSettings.setCatalogModels(catalogModels, { enabledModelIds: mergedEnabled });
|
|
4187
|
+
}
|
|
4188
|
+
return cachedQuota;
|
|
4189
|
+
}
|
|
4190
|
+
function getCachedQuota() {
|
|
4191
|
+
return cachedQuota;
|
|
4192
|
+
}
|
|
4193
|
+
//#endregion
|
|
4194
|
+
//#region src/host/antigravity/oauth.ts
|
|
4195
|
+
let webLoginFlow = { status: "idle" };
|
|
4196
|
+
function antigravityEnv(namePart) {
|
|
4197
|
+
const full = `DSH_ANTIGRAVITY_${namePart}`;
|
|
4198
|
+
return process.env[full];
|
|
4199
|
+
}
|
|
4200
|
+
function callbackPort() {
|
|
4201
|
+
const configured = Number(antigravityEnv("CALLBACK_PORT"));
|
|
4202
|
+
if (Number.isInteger(configured) && configured > 0 && configured <= 65535) return configured;
|
|
4203
|
+
return 51121;
|
|
4204
|
+
}
|
|
4205
|
+
function resolveCallbackHost(raw = antigravityEnv("CALLBACK_HOST")) {
|
|
4206
|
+
const host = (raw || "localhost").trim().toLowerCase();
|
|
4207
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") return host;
|
|
4208
|
+
return "localhost";
|
|
4209
|
+
}
|
|
4210
|
+
function redirectUri() {
|
|
4211
|
+
return `http://${resolveCallbackHost()}:${callbackPort()}${"/oauth-callback".startsWith("/"), REDIRECT_PATH}`;
|
|
4212
|
+
}
|
|
4213
|
+
function clientId() {
|
|
4214
|
+
return antigravityEnv("CLIENT_ID")?.trim() || DEFAULT_CLIENT_ID;
|
|
4215
|
+
}
|
|
4216
|
+
function clientSecret() {
|
|
4217
|
+
return antigravityEnv("CLIENT_SECRET")?.trim() || DEFAULT_CLIENT_SECRET;
|
|
4218
|
+
}
|
|
4219
|
+
function base64Url(buffer) {
|
|
4220
|
+
return buffer.toString("base64url");
|
|
4221
|
+
}
|
|
4222
|
+
function generatePKCE() {
|
|
4223
|
+
const verifier = base64Url(randomBytes(32));
|
|
4224
|
+
return {
|
|
4225
|
+
verifier,
|
|
4226
|
+
challenge: base64Url(createHash("sha256").update(verifier).digest())
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
function escapeHtml(text) {
|
|
4230
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4231
|
+
}
|
|
4232
|
+
function sanitizeOAuthProviderError(text) {
|
|
4233
|
+
return escapeHtml(text.slice(0, 300).replace(/[\r\n\t]+/g, " "));
|
|
4234
|
+
}
|
|
4235
|
+
function openBrowser(url) {
|
|
4236
|
+
try {
|
|
4237
|
+
if (process.platform === "darwin") spawn("open", [url], {
|
|
4238
|
+
stdio: "ignore",
|
|
4239
|
+
detached: true
|
|
4240
|
+
});
|
|
4241
|
+
else if (process.platform === "win32") spawn("cmd", [
|
|
4242
|
+
"/c",
|
|
4243
|
+
"start",
|
|
4244
|
+
"",
|
|
4245
|
+
url
|
|
4246
|
+
], {
|
|
4247
|
+
stdio: "ignore",
|
|
4248
|
+
detached: true
|
|
4249
|
+
});
|
|
4250
|
+
else spawn("xdg-open", [url], {
|
|
4251
|
+
stdio: "ignore",
|
|
4252
|
+
detached: true
|
|
4253
|
+
});
|
|
4254
|
+
} catch {}
|
|
4255
|
+
}
|
|
4256
|
+
async function getUserEmail(token) {
|
|
4257
|
+
try {
|
|
4258
|
+
const response = await fetch("https://www.googleapis.com/oauth2/v1/userinfo?alt=json", { headers: { Authorization: `Bearer ${token}` } });
|
|
4259
|
+
if (!response.ok) return void 0;
|
|
4260
|
+
const data = await response.json();
|
|
4261
|
+
return typeof data.email === "string" ? data.email : void 0;
|
|
4262
|
+
} catch {
|
|
4263
|
+
return;
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
function startCallbackServer(expectedState) {
|
|
4267
|
+
return new Promise((resolve, reject) => {
|
|
4268
|
+
let settled = false;
|
|
4269
|
+
let timeout;
|
|
4270
|
+
let resolveCode;
|
|
4271
|
+
let rejectCode;
|
|
4272
|
+
const codePromise = new Promise((res, rej) => {
|
|
4273
|
+
resolveCode = res;
|
|
4274
|
+
rejectCode = rej;
|
|
4275
|
+
});
|
|
4276
|
+
const finish = (fn) => {
|
|
4277
|
+
if (settled) return;
|
|
4278
|
+
settled = true;
|
|
4279
|
+
if (timeout) clearTimeout(timeout);
|
|
4280
|
+
fn();
|
|
4281
|
+
};
|
|
4282
|
+
const callbackUrl = redirectUri();
|
|
4283
|
+
const server = createServer((request, response) => {
|
|
4284
|
+
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
4285
|
+
response.writeHead(405, { "Content-Type": "text/plain; charset=utf-8" });
|
|
4286
|
+
response.end("Method Not Allowed");
|
|
4287
|
+
return;
|
|
4288
|
+
}
|
|
4289
|
+
const url = new URL$1(request.url || "", callbackUrl);
|
|
4290
|
+
if (url.pathname !== "/oauth-callback") {
|
|
4291
|
+
response.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
4292
|
+
response.end("Antigravity OAuth callback route not found.");
|
|
4293
|
+
return;
|
|
4294
|
+
}
|
|
4295
|
+
const providerError = url.searchParams.get("error");
|
|
4296
|
+
const code = url.searchParams.get("code");
|
|
4297
|
+
const state = url.searchParams.get("state");
|
|
4298
|
+
if (providerError) {
|
|
4299
|
+
const safe = escapeHtml(providerError.slice(0, 200));
|
|
4300
|
+
response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
4301
|
+
response.end(`Antigravity authentication failed: ${safe}`);
|
|
4302
|
+
finish(() => rejectCode(/* @__PURE__ */ new Error(`OAuth error: ${providerError.slice(0, 200)}`)));
|
|
4303
|
+
return;
|
|
4304
|
+
}
|
|
4305
|
+
if (!code || !state) {
|
|
4306
|
+
response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
4307
|
+
response.end("Antigravity authentication failed: missing code or state.");
|
|
4308
|
+
finish(() => rejectCode(/* @__PURE__ */ new Error("Missing code or state in OAuth callback")));
|
|
4309
|
+
return;
|
|
4310
|
+
}
|
|
4311
|
+
if (state !== expectedState) {
|
|
4312
|
+
response.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
4313
|
+
response.end("Antigravity authentication failed: invalid state.");
|
|
4314
|
+
finish(() => rejectCode(/* @__PURE__ */ new Error("OAuth state mismatch")));
|
|
4315
|
+
return;
|
|
4316
|
+
}
|
|
4317
|
+
response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
4318
|
+
response.end("Antigravity authentication complete. You can close this window and return to DSH.");
|
|
4319
|
+
finish(() => resolveCode({
|
|
4320
|
+
code,
|
|
4321
|
+
state
|
|
4322
|
+
}));
|
|
4323
|
+
});
|
|
4324
|
+
server.on("error", reject);
|
|
4325
|
+
server.listen(callbackPort(), resolveCallbackHost(), () => {
|
|
4326
|
+
timeout = setTimeout(() => {
|
|
4327
|
+
finish(() => rejectCode(/* @__PURE__ */ new Error("OAuth callback timed out waiting for browser login")));
|
|
4328
|
+
server.close();
|
|
4329
|
+
}, OAUTH_CALLBACK_TIMEOUT_MS);
|
|
4330
|
+
resolve({
|
|
4331
|
+
server,
|
|
4332
|
+
waitForCode: () => codePromise
|
|
4333
|
+
});
|
|
4334
|
+
});
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
async function exchangeOAuthCode(code, verifier, callbackUrl) {
|
|
4338
|
+
const tokenResponse = await fetch(TOKEN_URL, {
|
|
4339
|
+
method: "POST",
|
|
4340
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4341
|
+
body: new URLSearchParams$1({
|
|
4342
|
+
client_id: clientId(),
|
|
4343
|
+
client_secret: clientSecret(),
|
|
4344
|
+
code,
|
|
4345
|
+
grant_type: "authorization_code",
|
|
4346
|
+
redirect_uri: callbackUrl,
|
|
4347
|
+
code_verifier: verifier
|
|
4348
|
+
}).toString()
|
|
4349
|
+
});
|
|
4350
|
+
if (!tokenResponse.ok) throw new Error(`Token exchange failed: ${sanitizeOAuthProviderError(await tokenResponse.text())}`);
|
|
4351
|
+
const tokenData = await tokenResponse.json();
|
|
4352
|
+
const refreshToken = typeof tokenData.refresh_token === "string" ? tokenData.refresh_token : void 0;
|
|
4353
|
+
const accessToken = typeof tokenData.access_token === "string" ? tokenData.access_token : "";
|
|
4354
|
+
const expiresIn = typeof tokenData.expires_in === "number" ? tokenData.expires_in : 3600;
|
|
4355
|
+
if (!refreshToken) throw new Error("No refresh token received. Re-run login and allow offline access.");
|
|
4356
|
+
const [email, discoveredProject] = await Promise.all([getUserEmail(accessToken), loadCodeAssist(accessToken).catch(() => void 0)]);
|
|
4357
|
+
return {
|
|
4358
|
+
refresh: refreshToken,
|
|
4359
|
+
refresh_token: refreshToken,
|
|
4360
|
+
access: accessToken,
|
|
4361
|
+
access_token: accessToken,
|
|
4362
|
+
expires: Date.now() + expiresIn * 1e3 - 300 * 1e3,
|
|
4363
|
+
expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3,
|
|
4364
|
+
projectId: discoveredProject || void 0,
|
|
4365
|
+
email
|
|
4366
|
+
};
|
|
4367
|
+
}
|
|
4368
|
+
async function beginWebLogin(store) {
|
|
4369
|
+
if (webLoginFlow.status === "pending") return { ...webLoginFlow };
|
|
4370
|
+
const { verifier, challenge } = generatePKCE();
|
|
4371
|
+
const state = base64Url(randomBytes(32));
|
|
4372
|
+
const { server, waitForCode } = await startCallbackServer(state);
|
|
4373
|
+
const callbackUrl = redirectUri();
|
|
4374
|
+
webLoginFlow = {
|
|
4375
|
+
status: "pending",
|
|
4376
|
+
authUrl: `${AUTH_URL}?${new URLSearchParams$1({
|
|
4377
|
+
client_id: clientId(),
|
|
4378
|
+
response_type: "code",
|
|
4379
|
+
redirect_uri: callbackUrl,
|
|
4380
|
+
scope: SCOPES.join(" "),
|
|
4381
|
+
code_challenge: challenge,
|
|
4382
|
+
code_challenge_method: "S256",
|
|
4383
|
+
state,
|
|
4384
|
+
access_type: "offline",
|
|
4385
|
+
prompt: "consent"
|
|
4386
|
+
}).toString()}`,
|
|
4387
|
+
startedAt: Date.now(),
|
|
4388
|
+
error: void 0
|
|
4389
|
+
};
|
|
4390
|
+
(async () => {
|
|
4391
|
+
try {
|
|
4392
|
+
const { code, state: returnedState } = await waitForCode();
|
|
4393
|
+
if (returnedState !== state) throw new Error("OAuth state mismatch");
|
|
4394
|
+
const credentials = await exchangeOAuthCode(code, verifier, callbackUrl);
|
|
4395
|
+
await store.write(credentials);
|
|
4396
|
+
webLoginFlow.status = "complete";
|
|
4397
|
+
webLoginFlow.email = credentials.email;
|
|
4398
|
+
webLoginFlow.completedAt = Date.now();
|
|
4399
|
+
} catch (error) {
|
|
4400
|
+
webLoginFlow.status = "error";
|
|
4401
|
+
webLoginFlow.error = error instanceof Error ? error.message : String(error);
|
|
4402
|
+
webLoginFlow.completedAt = Date.now();
|
|
4403
|
+
} finally {
|
|
4404
|
+
server.close();
|
|
4405
|
+
}
|
|
4406
|
+
})();
|
|
4407
|
+
return { ...webLoginFlow };
|
|
4408
|
+
}
|
|
4409
|
+
function getWebLoginStatus() {
|
|
4410
|
+
return { ...webLoginFlow };
|
|
4411
|
+
}
|
|
4412
|
+
async function refreshAntigravityToken(credentials) {
|
|
4413
|
+
const refreshToken = credentials.refresh || credentials.refresh_token;
|
|
4414
|
+
if (!refreshToken) throw new Error("Missing Antigravity refresh token.");
|
|
4415
|
+
const response = await fetch(TOKEN_URL, {
|
|
4416
|
+
method: "POST",
|
|
4417
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
4418
|
+
body: new URLSearchParams$1({
|
|
4419
|
+
client_id: clientId(),
|
|
4420
|
+
client_secret: clientSecret(),
|
|
4421
|
+
refresh_token: refreshToken,
|
|
4422
|
+
grant_type: "refresh_token"
|
|
4423
|
+
}).toString()
|
|
4424
|
+
});
|
|
4425
|
+
if (!response.ok) throw new Error(`Token refresh failed: ${sanitizeOAuthProviderError(await response.text())}`);
|
|
4426
|
+
const data = await response.json();
|
|
4427
|
+
const accessToken = typeof data.access_token === "string" ? data.access_token : "";
|
|
4428
|
+
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : 3600;
|
|
4429
|
+
const nextRefreshToken = typeof data.refresh_token === "string" ? data.refresh_token : refreshToken;
|
|
4430
|
+
return {
|
|
4431
|
+
...credentials,
|
|
4432
|
+
refresh: nextRefreshToken,
|
|
4433
|
+
refresh_token: nextRefreshToken,
|
|
4434
|
+
access: accessToken,
|
|
4435
|
+
access_token: accessToken,
|
|
4436
|
+
expires: Date.now() + expiresIn * 1e3 - 300 * 1e3,
|
|
4437
|
+
expires_at: Date.now() + expiresIn * 1e3 - 300 * 1e3
|
|
4438
|
+
};
|
|
4439
|
+
}
|
|
4440
|
+
async function ensureApiKey(store) {
|
|
4441
|
+
let credentials = await store.read();
|
|
4442
|
+
if (!credentials) throw new Error("Not logged into Antigravity. Please log in from Settings > Antigravity.");
|
|
4443
|
+
const expires = credentials.expires || credentials.expires_at || 0;
|
|
4444
|
+
if (!(credentials.access || credentials.access_token) || expires <= Date.now() + 6e4) {
|
|
4445
|
+
credentials = await refreshAntigravityToken(credentials);
|
|
4446
|
+
await store.write(credentials);
|
|
4447
|
+
}
|
|
4448
|
+
return {
|
|
4449
|
+
token: credentials.access || credentials.access_token,
|
|
4450
|
+
projectId: credentials.projectId
|
|
4451
|
+
};
|
|
4452
|
+
}
|
|
4453
|
+
async function loginAndSave(store, signal, onUrl) {
|
|
4454
|
+
const { verifier, challenge } = generatePKCE();
|
|
4455
|
+
const state = base64Url(randomBytes(32));
|
|
4456
|
+
const { server, waitForCode } = await startCallbackServer(state);
|
|
4457
|
+
const callbackUrl = redirectUri();
|
|
4458
|
+
const authUrl = `${AUTH_URL}?${new URLSearchParams$1({
|
|
4459
|
+
client_id: clientId(),
|
|
4460
|
+
response_type: "code",
|
|
4461
|
+
redirect_uri: callbackUrl,
|
|
4462
|
+
scope: SCOPES.join(" "),
|
|
4463
|
+
code_challenge: challenge,
|
|
4464
|
+
code_challenge_method: "S256",
|
|
4465
|
+
state,
|
|
4466
|
+
access_type: "offline",
|
|
4467
|
+
prompt: "consent"
|
|
4468
|
+
}).toString()}`;
|
|
4469
|
+
try {
|
|
4470
|
+
if (onUrl) onUrl(authUrl);
|
|
4471
|
+
openBrowser(authUrl);
|
|
4472
|
+
if (signal?.aborted) throw new Error("OAuth login aborted");
|
|
4473
|
+
const { code, state: returnedState } = await waitForCode();
|
|
4474
|
+
if (returnedState !== state) throw new Error("OAuth state mismatch");
|
|
4475
|
+
const credentials = await exchangeOAuthCode(code, verifier, callbackUrl);
|
|
4476
|
+
await store.write(credentials);
|
|
4477
|
+
return credentials;
|
|
4478
|
+
} finally {
|
|
4479
|
+
server.close();
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
//#endregion
|
|
4483
|
+
//#region src/host/antigravity/mapper.ts
|
|
4484
|
+
let toolCallCounter = 0;
|
|
4485
|
+
function sanitizeText(text) {
|
|
4486
|
+
return text.replace(/\0/g, "");
|
|
4487
|
+
}
|
|
4488
|
+
function isRecord(value) {
|
|
4489
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4490
|
+
}
|
|
4491
|
+
function asString(value) {
|
|
4492
|
+
return typeof value === "string" ? value : void 0;
|
|
4493
|
+
}
|
|
4494
|
+
function safeJsonParse(text) {
|
|
4495
|
+
try {
|
|
4496
|
+
return JSON.parse(text);
|
|
4497
|
+
} catch {
|
|
4498
|
+
return;
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4501
|
+
function sanitizeToolCallId(id, fallbackName) {
|
|
4502
|
+
return String(id || "").replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64) || `${fallbackName || "tool"}_${Date.now()}_${++toolCallCounter}`;
|
|
4503
|
+
}
|
|
4504
|
+
function toolCallIdNeeded(modelId, runtimeModel) {
|
|
4505
|
+
return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-") || runtimeModel.startsWith("claude-") || runtimeModel.startsWith("gpt-oss-");
|
|
4506
|
+
}
|
|
4507
|
+
function parseArguments(raw) {
|
|
4508
|
+
if (isRecord(raw)) return raw;
|
|
4509
|
+
if (raw === void 0 || raw === null || raw === "") return {};
|
|
4510
|
+
const parsed = typeof raw === "string" ? safeJsonParse(raw) : raw;
|
|
4511
|
+
return isRecord(parsed) ? parsed : {};
|
|
4512
|
+
}
|
|
4513
|
+
function imageBlockToPart(block) {
|
|
4514
|
+
let data = asString(block.data) || asString(block.base64);
|
|
4515
|
+
const source = isRecord(block.source) ? block.source : void 0;
|
|
4516
|
+
if (!data && source) data = asString(source.data) || asString(source.base64);
|
|
4517
|
+
let mimeType = asString(block.mimeType) || asString(block.mediaType) || (source ? asString(source.mimeType) || asString(source.mediaType) : void 0) || "image/png";
|
|
4518
|
+
if (data?.startsWith("data:")) {
|
|
4519
|
+
const match = data.match(/^data:([^;,]+);base64,(.*)$/s);
|
|
4520
|
+
if (match) {
|
|
4521
|
+
mimeType = match[1] || mimeType;
|
|
4522
|
+
data = match[2] || "";
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
return data ? { inlineData: {
|
|
4526
|
+
mimeType,
|
|
4527
|
+
data
|
|
4528
|
+
} } : void 0;
|
|
4529
|
+
}
|
|
4530
|
+
function contentToUserParts(content) {
|
|
4531
|
+
if (typeof content === "string") return [{ text: sanitizeText(content) }];
|
|
4532
|
+
if (!Array.isArray(content)) return [];
|
|
4533
|
+
const parts = [];
|
|
4534
|
+
for (const block of content) if (isRecord(block) && block.type === "text" && typeof block.text === "string") parts.push({ text: sanitizeText(block.text) });
|
|
4535
|
+
else if (isRecord(block) && block.type === "image") {
|
|
4536
|
+
const img = imageBlockToPart(block);
|
|
4537
|
+
if (img) parts.push(img);
|
|
4538
|
+
}
|
|
4539
|
+
return parts;
|
|
4540
|
+
}
|
|
4541
|
+
function toolResultText(blocks) {
|
|
4542
|
+
if (!Array.isArray(blocks)) return "";
|
|
4543
|
+
return blocks.map((block) => {
|
|
4544
|
+
if (!isRecord(block)) return "";
|
|
4545
|
+
if (block.type === "text" && typeof block.text === "string") return sanitizeText(block.text);
|
|
4546
|
+
if (block.type === "tool-result") return toolResultText(block.content);
|
|
4547
|
+
return "";
|
|
4548
|
+
}).join("");
|
|
4549
|
+
}
|
|
4550
|
+
function replayBlockFor(message, index) {
|
|
4551
|
+
const source = message.source;
|
|
4552
|
+
if (!source || source.kind !== "model") return void 0;
|
|
4553
|
+
const state = source.replayState;
|
|
4554
|
+
if (!isRecord(state)) return void 0;
|
|
4555
|
+
const resp = isRecord(state.response) ? state.response : void 0;
|
|
4556
|
+
if (resp) {
|
|
4557
|
+
if (Array.isArray(resp.outputItems)) return resp.outputItems[index];
|
|
4558
|
+
if (Array.isArray(resp.blocks)) return resp.blocks[index];
|
|
4559
|
+
}
|
|
4560
|
+
if (Array.isArray(state.blocks)) return state.blocks[index];
|
|
4561
|
+
}
|
|
4562
|
+
function assistantParts(message, model, runtimeModel, toolNames) {
|
|
4563
|
+
const parts = [];
|
|
4564
|
+
if (!Array.isArray(message.content)) return parts;
|
|
4565
|
+
for (let index = 0; index < message.content.length; index++) {
|
|
4566
|
+
const block = message.content[index];
|
|
4567
|
+
if (!isRecord(block)) continue;
|
|
4568
|
+
const replay = replayBlockFor(message, index);
|
|
4569
|
+
if (block.type === "text" && String(block.text || "").trim()) parts.push({ text: sanitizeText(String(block.text)) });
|
|
4570
|
+
else if (block.type === "reasoning" && String(block.text || "").trim()) {
|
|
4571
|
+
const sig = asString(replay?.thinkingSignature) || asString(replay?.thought_signature) || asString(block.thought_signature);
|
|
4572
|
+
if (sig) parts.push({
|
|
4573
|
+
thought: true,
|
|
4574
|
+
text: sanitizeText(String(block.text)),
|
|
4575
|
+
thought_signature: sig,
|
|
4576
|
+
thoughtSignature: sig
|
|
4577
|
+
});
|
|
4578
|
+
else parts.push({ text: sanitizeText(String(block.text)) });
|
|
4579
|
+
} else if (block.type === "tool-call") {
|
|
4580
|
+
const toolId = String(block.id || "");
|
|
4581
|
+
const toolName = String(block.name || "");
|
|
4582
|
+
toolNames.set(toolId, toolName);
|
|
4583
|
+
const sig = asString(replay?.thought_signature) || asString(replay?.thoughtSignature) || asString(block.thought_signature) || asString(block.thoughtSignature) || "skip_thought_signature_validator";
|
|
4584
|
+
parts.push({
|
|
4585
|
+
functionCall: {
|
|
4586
|
+
name: toolName,
|
|
4587
|
+
args: parseArguments(block.arguments),
|
|
4588
|
+
...toolCallIdNeeded(model.id, runtimeModel) ? { id: sanitizeToolCallId(toolId, toolName) } : {}
|
|
4589
|
+
},
|
|
4590
|
+
thought_signature: sig,
|
|
4591
|
+
thoughtSignature: sig
|
|
4592
|
+
});
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
return parts;
|
|
4596
|
+
}
|
|
4597
|
+
function pushToolResult(contents, result, toolNames, model, runtimeModel) {
|
|
4598
|
+
const toolCallId = String(result.toolCallId || "");
|
|
4599
|
+
const toolName = toolNames.get(toolCallId) || "unknown";
|
|
4600
|
+
const responseText = toolResultText(result.content) || (result.isError ? "Tool failed" : "");
|
|
4601
|
+
const part = { functionResponse: {
|
|
4602
|
+
name: toolName,
|
|
4603
|
+
response: result.isError ? { error: responseText } : { output: responseText },
|
|
4604
|
+
...toolCallIdNeeded(model.id, runtimeModel) ? { id: sanitizeToolCallId(toolCallId, toolName) } : {}
|
|
4605
|
+
} };
|
|
4606
|
+
const last = contents[contents.length - 1];
|
|
4607
|
+
if (last?.role === GEMINI_ROLE.user && last.parts.some((entry) => "functionResponse" in entry)) last.parts.push(part);
|
|
4608
|
+
else contents.push({
|
|
4609
|
+
role: GEMINI_ROLE.user,
|
|
4610
|
+
parts: [part]
|
|
4611
|
+
});
|
|
4612
|
+
}
|
|
4613
|
+
function convertMessages(options, model, runtimeModel) {
|
|
4614
|
+
const contents = [];
|
|
4615
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
4616
|
+
for (const message of options.messages) {
|
|
4617
|
+
const role = message.role || (message.source?.kind === "model" ? "assistant" : "user");
|
|
4618
|
+
if (role === "assistant" || message.source?.kind === "model") {
|
|
4619
|
+
const parts = assistantParts(message, model, runtimeModel, toolNames);
|
|
4620
|
+
if (parts.length) contents.push({
|
|
4621
|
+
role: GEMINI_ROLE.model,
|
|
4622
|
+
parts
|
|
4623
|
+
});
|
|
4624
|
+
continue;
|
|
4625
|
+
}
|
|
4626
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
4627
|
+
const userParts = contentToUserParts(content.filter((b) => !isRecord(b) || b.type !== "tool-result"));
|
|
4628
|
+
if (role === "system") {
|
|
4629
|
+
if (userParts.length) contents.push({
|
|
4630
|
+
role: GEMINI_ROLE.user,
|
|
4631
|
+
parts: userParts
|
|
4632
|
+
});
|
|
4633
|
+
continue;
|
|
4634
|
+
}
|
|
4635
|
+
if (userParts.length) contents.push({
|
|
4636
|
+
role: GEMINI_ROLE.user,
|
|
4637
|
+
parts: userParts
|
|
4638
|
+
});
|
|
4639
|
+
for (const b of content) if (isRecord(b) && b.type === "tool-result") pushToolResult(contents, b, toolNames, model, runtimeModel);
|
|
4640
|
+
}
|
|
4641
|
+
return contents;
|
|
4642
|
+
}
|
|
4643
|
+
function stripMetaSchema(schema) {
|
|
4644
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema)) return schema;
|
|
4645
|
+
const omit = /* @__PURE__ */ new Set([
|
|
4646
|
+
"$schema",
|
|
4647
|
+
"$id",
|
|
4648
|
+
"$anchor",
|
|
4649
|
+
"$dynamicAnchor",
|
|
4650
|
+
"$vocabulary",
|
|
4651
|
+
"$comment",
|
|
4652
|
+
"$defs",
|
|
4653
|
+
"definitions"
|
|
4654
|
+
]);
|
|
4655
|
+
const out = {};
|
|
4656
|
+
for (const [k, v] of Object.entries(schema)) if (!omit.has(k)) out[k] = stripMetaSchema(v);
|
|
4657
|
+
return out;
|
|
4658
|
+
}
|
|
4659
|
+
function convertTools(tools) {
|
|
4660
|
+
if (!tools || tools.length === 0) return void 0;
|
|
4661
|
+
return [{ functionDeclarations: tools.map((tool) => ({
|
|
4662
|
+
name: tool.name,
|
|
4663
|
+
description: tool.description || "",
|
|
4664
|
+
parameters: stripMetaSchema(tool.parameters) || {
|
|
4665
|
+
type: "object",
|
|
4666
|
+
properties: {}
|
|
4667
|
+
}
|
|
4668
|
+
})) }];
|
|
4669
|
+
}
|
|
4670
|
+
function mapToolChoiceMode(toolChoice) {
|
|
4671
|
+
if (toolChoice === "none") return TOOL_CALLING_MODE.none;
|
|
4672
|
+
if (toolChoice === "any" || toolChoice === "required") return TOOL_CALLING_MODE.any;
|
|
4673
|
+
return TOOL_CALLING_MODE.auto;
|
|
4674
|
+
}
|
|
4675
|
+
function getMaxOutputTokens(modelId, runtimeModel) {
|
|
4676
|
+
return RUNTIME_MAX_OUTPUT_TOKENS[runtimeModel] || RUNTIME_MAX_OUTPUT_TOKENS[modelId] || 65536;
|
|
4677
|
+
}
|
|
4678
|
+
function buildRequest(options, model, projectId, runtimeModel, effort) {
|
|
4679
|
+
const request = {
|
|
4680
|
+
contents: convertMessages(options, model, runtimeModel),
|
|
4681
|
+
systemInstruction: {
|
|
4682
|
+
role: GEMINI_ROLE.user,
|
|
4683
|
+
parts: [
|
|
4684
|
+
{ text: ANTIGRAVITY_SYSTEM_INSTRUCTION },
|
|
4685
|
+
{ text: `Please ignore following [ignore]${ANTIGRAVITY_SYSTEM_INSTRUCTION}[/ignore]` },
|
|
4686
|
+
{ text: ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION },
|
|
4687
|
+
...options.system ? [{ text: sanitizeText(options.system) }] : []
|
|
4688
|
+
]
|
|
4689
|
+
}
|
|
4690
|
+
};
|
|
4691
|
+
const generationConfig = {};
|
|
4692
|
+
if (options.temperature !== void 0) generationConfig.temperature = options.temperature;
|
|
4693
|
+
if (runtimeModel === "gemini-3.8-flash-tiered" || runtimeModel === "gemini-3.7-flash-tiered") {
|
|
4694
|
+
const selected = effort || "off";
|
|
4695
|
+
generationConfig.thinkingConfig = { thinkingLevel: selected === "high" || selected === "xhigh" ? "HIGH" : selected === "medium" ? "MEDIUM" : "LOW" };
|
|
4696
|
+
}
|
|
4697
|
+
const maxAllowed = getMaxOutputTokens(model.id, runtimeModel);
|
|
4698
|
+
generationConfig.maxOutputTokens = options.maxTokens !== void 0 ? Math.min(options.maxTokens, maxAllowed) : maxAllowed;
|
|
4699
|
+
request.generationConfig = generationConfig;
|
|
4700
|
+
const toolChoice = options.toolChoice;
|
|
4701
|
+
const tools = convertTools(options.tools);
|
|
4702
|
+
if (tools) {
|
|
4703
|
+
request.tools = tools;
|
|
4704
|
+
if (toolChoice) request.toolConfig = { functionCallingConfig: { mode: mapToolChoiceMode(toolChoice) } };
|
|
4705
|
+
}
|
|
4706
|
+
if (options.sessionId) request.sessionId = String(options.sessionId);
|
|
4707
|
+
return {
|
|
4708
|
+
project: projectId,
|
|
4709
|
+
model: runtimeModel,
|
|
4710
|
+
request,
|
|
4711
|
+
requestType: "agent",
|
|
4712
|
+
userAgent: "antigravity",
|
|
4713
|
+
requestId: `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4716
|
+
function createStreamState() {
|
|
4717
|
+
return {
|
|
4718
|
+
blocks: [],
|
|
4719
|
+
replayBlocks: [],
|
|
4720
|
+
currentBlock: null,
|
|
4721
|
+
hasContent: false,
|
|
4722
|
+
hasToolCall: false
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
function processStreamLine(line, state) {
|
|
4726
|
+
if (!line.startsWith("data:")) return [];
|
|
4727
|
+
const json = line.slice(5).trim();
|
|
4728
|
+
if (!json || json === "[DONE]") return [];
|
|
4729
|
+
const chunk = safeJsonParse(json);
|
|
4730
|
+
if (!isRecord(chunk)) return [];
|
|
4731
|
+
const responseData = isRecord(chunk.response) ? chunk.response : chunk;
|
|
4732
|
+
const candidate = (Array.isArray(responseData.candidates) ? responseData.candidates : [])[0];
|
|
4733
|
+
const content = isRecord(candidate?.content) ? candidate.content : void 0;
|
|
4734
|
+
const parts = Array.isArray(content?.parts) ? content.parts : [];
|
|
4735
|
+
const out = [];
|
|
4736
|
+
const closeCurrentBlock = () => {
|
|
4737
|
+
if (!state.currentBlock) return;
|
|
4738
|
+
const index = state.blocks.length - 1;
|
|
4739
|
+
if (state.currentBlock.type === "text") out.push({
|
|
4740
|
+
type: "block-end",
|
|
4741
|
+
index,
|
|
4742
|
+
block: {
|
|
4743
|
+
type: "text",
|
|
4744
|
+
text: state.currentBlock.text
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4747
|
+
else out.push({
|
|
4748
|
+
type: "block-end",
|
|
4749
|
+
index,
|
|
4750
|
+
block: {
|
|
4751
|
+
type: "reasoning",
|
|
4752
|
+
text: state.currentBlock.text
|
|
4753
|
+
}
|
|
4754
|
+
});
|
|
4755
|
+
state.currentBlock = null;
|
|
4756
|
+
};
|
|
4757
|
+
for (const part of parts) {
|
|
4758
|
+
if (!isRecord(part)) continue;
|
|
4759
|
+
if (part.text !== void 0 && typeof part.text === "string") {
|
|
4760
|
+
const isThinking = part.thought === true;
|
|
4761
|
+
const blockType = isThinking ? "reasoning" : "text";
|
|
4762
|
+
if (!state.currentBlock || state.currentBlock.type !== blockType) {
|
|
4763
|
+
closeCurrentBlock();
|
|
4764
|
+
state.currentBlock = {
|
|
4765
|
+
type: blockType,
|
|
4766
|
+
text: ""
|
|
4767
|
+
};
|
|
4768
|
+
const index = state.blocks.length;
|
|
4769
|
+
state.blocks.push({
|
|
4770
|
+
type: blockType,
|
|
4771
|
+
text: ""
|
|
4772
|
+
});
|
|
4773
|
+
state.replayBlocks.push({ type: blockType });
|
|
4774
|
+
out.push({
|
|
4775
|
+
type: "block-start",
|
|
4776
|
+
index,
|
|
4777
|
+
blockType
|
|
4778
|
+
});
|
|
4779
|
+
}
|
|
4780
|
+
const delta = sanitizeText(part.text);
|
|
4781
|
+
state.currentBlock.text += delta;
|
|
4782
|
+
state.hasContent = true;
|
|
4783
|
+
if (isThinking && part.thoughtSignature) {
|
|
4784
|
+
state.currentBlock.thinkingSignature = part.thoughtSignature;
|
|
4785
|
+
state.replayBlocks[state.blocks.length - 1].thinkingSignature = part.thoughtSignature;
|
|
4786
|
+
} else if (!isThinking && part.thoughtSignature) {
|
|
4787
|
+
state.currentBlock.textSignature = part.thoughtSignature;
|
|
4788
|
+
state.replayBlocks[state.blocks.length - 1].textSignature = part.thoughtSignature;
|
|
4789
|
+
}
|
|
4790
|
+
out.push({
|
|
4791
|
+
type: isThinking ? "reasoning-delta" : "text-delta",
|
|
4792
|
+
index: state.blocks.length - 1,
|
|
4793
|
+
text: delta
|
|
4794
|
+
});
|
|
4795
|
+
}
|
|
4796
|
+
if (isRecord(part.functionCall)) {
|
|
4797
|
+
closeCurrentBlock();
|
|
4798
|
+
const fc = part.functionCall;
|
|
4799
|
+
const toolName = asString(fc.name) || "";
|
|
4800
|
+
const toolId = sanitizeToolCallId(asString(fc.id) || "", toolName);
|
|
4801
|
+
const argsText = JSON.stringify(isRecord(fc.args) ? fc.args : {});
|
|
4802
|
+
const index = state.blocks.length;
|
|
4803
|
+
const sig = asString(part.thought_signature) || asString(part.thoughtSignature) || asString(part.thinkingSignature) || asString(fc.thought_signature) || asString(fc.thoughtSignature) || state.currentBlock?.thinkingSignature;
|
|
4804
|
+
const block = {
|
|
4805
|
+
type: "tool-call",
|
|
4806
|
+
id: toolId,
|
|
4807
|
+
name: toolName,
|
|
4808
|
+
arguments: argsText,
|
|
4809
|
+
...sig ? {
|
|
4810
|
+
thought_signature: sig,
|
|
4811
|
+
thoughtSignature: sig
|
|
4812
|
+
} : {}
|
|
4813
|
+
};
|
|
4814
|
+
state.blocks.push(block);
|
|
4815
|
+
state.replayBlocks.push({
|
|
4816
|
+
type: "tool-call",
|
|
4817
|
+
...sig ? {
|
|
4818
|
+
thought_signature: sig,
|
|
4819
|
+
thoughtSignature: sig
|
|
4820
|
+
} : {}
|
|
4821
|
+
});
|
|
4822
|
+
state.hasContent = true;
|
|
4823
|
+
state.hasToolCall = true;
|
|
4824
|
+
out.push({
|
|
4825
|
+
type: "block-start",
|
|
4826
|
+
index,
|
|
4827
|
+
blockType: "tool-call"
|
|
4828
|
+
});
|
|
4829
|
+
out.push({
|
|
4830
|
+
type: "tool-call-delta",
|
|
4831
|
+
index,
|
|
4832
|
+
id: toolId,
|
|
4833
|
+
name: toolName,
|
|
4834
|
+
argumentsDelta: argsText
|
|
4835
|
+
});
|
|
4836
|
+
out.push({
|
|
4837
|
+
type: "block-end",
|
|
4838
|
+
index,
|
|
4839
|
+
block
|
|
4840
|
+
});
|
|
4841
|
+
}
|
|
4842
|
+
}
|
|
4843
|
+
if (responseData.usageMetadata && isRecord(responseData.usageMetadata)) {
|
|
4844
|
+
const u = responseData.usageMetadata;
|
|
4845
|
+
const usage = {
|
|
4846
|
+
inputTokens: Math.max(0, (u.promptTokenCount || 0) - (u.cachedContentTokenCount || 0)),
|
|
4847
|
+
outputTokens: (u.candidatesTokenCount || 0) + (u.thoughtsTokenCount || 0),
|
|
4848
|
+
...u.cachedContentTokenCount ? { cacheReadTokens: u.cachedContentTokenCount } : {}
|
|
4849
|
+
};
|
|
4850
|
+
out.push({
|
|
4851
|
+
type: "usage",
|
|
4852
|
+
usage
|
|
4853
|
+
});
|
|
4854
|
+
}
|
|
4855
|
+
const finishReason = candidate?.finishReason;
|
|
4856
|
+
if (finishReason) {
|
|
4857
|
+
closeCurrentBlock();
|
|
4858
|
+
const reason = state.hasToolCall ? { kind: "tool-calls" } : finishReason === "MAX_TOKENS" ? { kind: "max-tokens" } : { kind: "stop" };
|
|
4859
|
+
out.push({
|
|
4860
|
+
type: "finish",
|
|
4861
|
+
reason,
|
|
4862
|
+
replayState: { response: {
|
|
4863
|
+
outputItems: state.replayBlocks,
|
|
4864
|
+
blocks: state.replayBlocks
|
|
4865
|
+
} }
|
|
4866
|
+
});
|
|
4867
|
+
}
|
|
4868
|
+
return out;
|
|
4869
|
+
}
|
|
4870
|
+
function closeStream(state) {
|
|
4871
|
+
const out = [];
|
|
4872
|
+
if (state.currentBlock) {
|
|
4873
|
+
const index = state.blocks.length - 1;
|
|
4874
|
+
if (state.currentBlock.type === "text") out.push({
|
|
4875
|
+
type: "block-end",
|
|
4876
|
+
index,
|
|
4877
|
+
block: {
|
|
4878
|
+
type: "text",
|
|
4879
|
+
text: state.currentBlock.text
|
|
4880
|
+
}
|
|
4881
|
+
});
|
|
4882
|
+
else out.push({
|
|
4883
|
+
type: "block-end",
|
|
4884
|
+
index,
|
|
4885
|
+
block: {
|
|
4886
|
+
type: "reasoning",
|
|
4887
|
+
text: state.currentBlock.text
|
|
4888
|
+
}
|
|
4889
|
+
});
|
|
4890
|
+
state.currentBlock = null;
|
|
4891
|
+
}
|
|
4892
|
+
return out;
|
|
4893
|
+
}
|
|
4894
|
+
//#endregion
|
|
4895
|
+
//#region src/host/antigravity/adapter.ts
|
|
4896
|
+
var AntigravityAdapter = class extends LlmAdapter {
|
|
4897
|
+
store;
|
|
4898
|
+
modelSettings;
|
|
4899
|
+
preferences;
|
|
4900
|
+
constructor(store = new FileCredentialStore(), modelSettings = new FileModelSettingsStore(), preferences) {
|
|
4901
|
+
super();
|
|
4902
|
+
this.store = store;
|
|
4903
|
+
this.modelSettings = modelSettings;
|
|
4904
|
+
this.preferences = preferences;
|
|
4905
|
+
}
|
|
4906
|
+
providerInfo(provider) {
|
|
4907
|
+
return {
|
|
4908
|
+
id: provider,
|
|
4909
|
+
name: PROVIDER_NAME
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
async listModels(provider) {
|
|
4913
|
+
const prov = provider || "antigravity";
|
|
4914
|
+
const settings = this.preferences ? this.preferences.status() : await this.modelSettings.read();
|
|
4915
|
+
const enabledSet = new Set(settings.enabledModelIds);
|
|
4916
|
+
const available = MODELS.filter((m) => enabledSet.has(m.id));
|
|
4917
|
+
const overrides = settings.contextWindowOverrides || {};
|
|
4918
|
+
return available.map((model) => ({
|
|
4919
|
+
provider: prov,
|
|
4920
|
+
id: model.id,
|
|
4921
|
+
name: model.name,
|
|
4922
|
+
inputModalities: model.inputModalities,
|
|
4923
|
+
context: { contextWindow: overrides[model.id] || model.contextWindow },
|
|
4924
|
+
defaultMaxTokens: model.maxTokens,
|
|
4925
|
+
...model.reasoningEfforts ? { reasoningEfforts: model.reasoningEfforts } : {}
|
|
4926
|
+
}));
|
|
4927
|
+
}
|
|
4928
|
+
async resolveModel(provider, modelId, signal) {
|
|
4929
|
+
if (signal?.aborted) throw new LlmError("antigravity model resolution aborted", "ABORTED");
|
|
4930
|
+
const model = MODELS.find((m) => m.id === modelId) || {
|
|
4931
|
+
id: modelId,
|
|
4932
|
+
name: modelId,
|
|
4933
|
+
inputModalities: ["text", "image"],
|
|
4934
|
+
contextWindow: 128e3,
|
|
4935
|
+
maxTokens: 65536
|
|
4936
|
+
};
|
|
4937
|
+
const settings = this.preferences ? this.preferences.status() : await this.modelSettings.read();
|
|
4938
|
+
const overrides = settings.contextWindowOverrides || {};
|
|
4939
|
+
const efforts = model.reasoningEfforts || [
|
|
4940
|
+
"low",
|
|
4941
|
+
"medium",
|
|
4942
|
+
"high"
|
|
4943
|
+
];
|
|
4944
|
+
const defaultEffort = settings.defaultReasoningEffort || "medium";
|
|
4945
|
+
return {
|
|
4946
|
+
provider,
|
|
4947
|
+
id: model.id,
|
|
4948
|
+
name: model.name,
|
|
4949
|
+
inputModalities: model.inputModalities,
|
|
4950
|
+
context: { contextWindow: overrides[model.id] || model.contextWindow },
|
|
4951
|
+
defaultMaxTokens: model.maxTokens,
|
|
4952
|
+
...model.reasoningEfforts ? { reasoning: {
|
|
4953
|
+
efforts: efforts.map((effort) => ({
|
|
4954
|
+
id: ReasoningEffortId(effort),
|
|
4955
|
+
name: effort
|
|
4956
|
+
})),
|
|
4957
|
+
defaultEffort: ReasoningEffortId(defaultEffort)
|
|
4958
|
+
} } : {}
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
async prepareCall(provider, model, signal) {
|
|
4962
|
+
return {
|
|
4963
|
+
model: await this.resolveModel(provider, model, signal),
|
|
4964
|
+
stream: (options) => this.stream(options)
|
|
4965
|
+
};
|
|
4966
|
+
}
|
|
4967
|
+
async *stream(options) {
|
|
4968
|
+
const model = MODELS.find((m) => m.id === options.model) || {
|
|
4969
|
+
id: options.model,
|
|
4970
|
+
name: options.model,
|
|
4971
|
+
inputModalities: ["text", "image"],
|
|
4972
|
+
contextWindow: 128e3,
|
|
4973
|
+
maxTokens: 65536
|
|
4974
|
+
};
|
|
4975
|
+
const settings = await this.modelSettings.read();
|
|
4976
|
+
const effectiveEffort = options.reasoningEffort || settings.defaultReasoningEffort || void 0;
|
|
4977
|
+
const effectiveOptions = effectiveEffort ? {
|
|
4978
|
+
...options,
|
|
4979
|
+
reasoningEffort: effectiveEffort
|
|
4980
|
+
} : options;
|
|
4981
|
+
yield* wrapStreamWithWatchdog((watchdogSignal) => this.requestStream(effectiveOptions, model, watchdogSignal), options.signal, STREAM_IDLE_TIMEOUT_MS, STREAM_IDLE_TIMEOUT_CODE, "Antigravity");
|
|
4982
|
+
}
|
|
4983
|
+
async *requestStream(options, model, signal) {
|
|
4984
|
+
const { token, projectId: defaultProj } = await ensureApiKey(this.store);
|
|
4985
|
+
const projectId = defaultProj || "antigravity-default";
|
|
4986
|
+
const effort = String(options.reasoningEffort || "medium").toLowerCase();
|
|
4987
|
+
const routing = ROUTING[model.id];
|
|
4988
|
+
const initialRuntime = routing?.routing[effort] || routing?.defaultRequestId || model.id;
|
|
4989
|
+
const fallbackRuntime = routing?.off && routing.off !== initialRuntime ? routing.off : void 0;
|
|
4990
|
+
const candidates = [initialRuntime];
|
|
4991
|
+
if (fallbackRuntime && !candidates.includes(fallbackRuntime)) candidates.push(fallbackRuntime);
|
|
4992
|
+
if (routing?.fallbackCandidates) {
|
|
4993
|
+
for (const fc of routing.fallbackCandidates) if (!candidates.includes(fc)) candidates.push(fc);
|
|
4994
|
+
}
|
|
4995
|
+
let response;
|
|
4996
|
+
candidates[0];
|
|
4997
|
+
for (const runtimeModel of candidates) {
|
|
4998
|
+
const body = JSON.stringify(buildRequest(options, model, projectId, runtimeModel, effort));
|
|
4999
|
+
const headers = {
|
|
5000
|
+
...antigravityHeaders(token),
|
|
5001
|
+
...model.id.startsWith("claude-") ? { "anthropic-beta": "interleaved-thinking-2025-05-14" } : {}
|
|
5002
|
+
};
|
|
5003
|
+
for (const endpoint of endpointCandidates()) try {
|
|
5004
|
+
response = await fetch(`${endpoint}/v1internal:streamGenerateContent?alt=sse`, {
|
|
5005
|
+
method: "POST",
|
|
5006
|
+
headers,
|
|
5007
|
+
body,
|
|
5008
|
+
signal
|
|
5009
|
+
});
|
|
5010
|
+
if (response.ok) break;
|
|
5011
|
+
if (response.status === 404) break;
|
|
5012
|
+
} catch (err) {
|
|
5013
|
+
if (signal.aborted) throw new LlmError("Antigravity request aborted", "ABORTED", { cause: err });
|
|
5014
|
+
}
|
|
5015
|
+
if (response && response.ok) break;
|
|
5016
|
+
}
|
|
5017
|
+
if (!response || !response.ok) {
|
|
5018
|
+
const status = response?.status ?? 500;
|
|
5019
|
+
const errText = await response?.text().catch(() => "");
|
|
5020
|
+
if (status === 429) throw new LlmError(`Antigravity 账号配额已耗尽或请求受限 (429 RESOURCE_EXHAUSTED)。请在插件设置页查看配额剩余百分比及重置倒计时。原始响应: ${errText || "No response"}`, "RATE_LIMIT", { status: 429 });
|
|
5021
|
+
throw new LlmError(`Antigravity API error (${status}): ${errText || "No response"}`, "PROVIDER_ERROR", { status });
|
|
5022
|
+
}
|
|
5023
|
+
if (!response.body) throw new LlmError("Antigravity returned empty response body", "PROVIDER_ERROR");
|
|
5024
|
+
const reader = response.body.getReader();
|
|
5025
|
+
const decoder = new TextDecoder();
|
|
5026
|
+
const state = createStreamState();
|
|
5027
|
+
let buffer = "";
|
|
5028
|
+
try {
|
|
5029
|
+
while (true) {
|
|
5030
|
+
const { done, value } = await reader.read();
|
|
5031
|
+
if (done) break;
|
|
5032
|
+
buffer += decoder.decode(value, { stream: true });
|
|
5033
|
+
const lines = buffer.split("\n");
|
|
5034
|
+
buffer = lines.pop() || "";
|
|
5035
|
+
for (const line of lines) {
|
|
5036
|
+
const trimmed = line.trim();
|
|
5037
|
+
if (!trimmed) continue;
|
|
5038
|
+
const chunks = processStreamLine(trimmed, state);
|
|
5039
|
+
for (const chunk of chunks) yield chunk;
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
if (buffer.trim()) {
|
|
5043
|
+
const chunks = processStreamLine(buffer.trim(), state);
|
|
5044
|
+
for (const chunk of chunks) yield chunk;
|
|
5045
|
+
}
|
|
5046
|
+
for (const chunk of closeStream(state)) yield chunk;
|
|
5047
|
+
} finally {
|
|
5048
|
+
reader.cancel().catch(() => void 0);
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
};
|
|
5052
|
+
//#endregion
|
|
5053
|
+
//#region src/host/antigravity/routes.ts
|
|
5054
|
+
function sendJson(response, status, body) {
|
|
5055
|
+
response.writeHead(status, { "Content-Type": "application/json" });
|
|
5056
|
+
response.end(JSON.stringify(body));
|
|
5057
|
+
}
|
|
5058
|
+
function sendMethodNotAllowed(response) {
|
|
5059
|
+
sendJson(response, 405, {
|
|
5060
|
+
ok: false,
|
|
5061
|
+
error: "Method Not Allowed"
|
|
5062
|
+
});
|
|
5063
|
+
}
|
|
5064
|
+
async function readRequestJson(request) {
|
|
5065
|
+
return new Promise((resolve, reject) => {
|
|
5066
|
+
let raw = "";
|
|
5067
|
+
request.on("data", (chunk) => {
|
|
5068
|
+
raw += String(chunk);
|
|
5069
|
+
if (raw.length > 64 * 1024) reject(/* @__PURE__ */ new Error("Request body too large"));
|
|
5070
|
+
});
|
|
5071
|
+
request.on("end", () => {
|
|
5072
|
+
try {
|
|
5073
|
+
resolve(raw ? JSON.parse(raw) : {});
|
|
5074
|
+
} catch (err) {
|
|
5075
|
+
reject(err);
|
|
5076
|
+
}
|
|
5077
|
+
});
|
|
5078
|
+
request.on("error", reject);
|
|
5079
|
+
});
|
|
5080
|
+
}
|
|
5081
|
+
async function getAntigravityWebStatus(store, modelSettings, preferences) {
|
|
5082
|
+
const credentials = await store.read();
|
|
5083
|
+
const settings = preferences ? preferences.status() : await modelSettings.read();
|
|
5084
|
+
const quota = getCachedQuota();
|
|
5085
|
+
const enabledSet = new Set(settings.enabledModelIds);
|
|
5086
|
+
const overrides = settings.contextWindowOverrides || {};
|
|
5087
|
+
const models = MODELS.map((m) => ({
|
|
5088
|
+
id: m.id,
|
|
5089
|
+
name: m.name,
|
|
5090
|
+
enabled: enabledSet.has(m.id),
|
|
5091
|
+
defaultContextWindow: m.contextWindow,
|
|
5092
|
+
contextWindow: overrides[m.id] || m.contextWindow,
|
|
5093
|
+
reasoningEfforts: m.reasoningEfforts
|
|
5094
|
+
}));
|
|
5095
|
+
return {
|
|
5096
|
+
authenticated: !!(credentials?.access || credentials?.access_token),
|
|
5097
|
+
email: credentials?.email,
|
|
5098
|
+
projectId: credentials?.projectId,
|
|
5099
|
+
hasCredentials: !!credentials,
|
|
5100
|
+
storagePath: store.path(),
|
|
5101
|
+
lastFetchedAt: quota?.fetchedAt,
|
|
5102
|
+
quota,
|
|
5103
|
+
models,
|
|
5104
|
+
contextWindowOverrides: overrides,
|
|
5105
|
+
defaultReasoningEffort: settings.defaultReasoningEffort || null
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
function registerAntigravityRoutes(ctx, store, modelSettings, preferences) {
|
|
5109
|
+
return ctx.webServer.register({
|
|
5110
|
+
kind: "prefix",
|
|
5111
|
+
path: "/antigravity/api",
|
|
5112
|
+
handler: async (request, response) => {
|
|
5113
|
+
const path = new URL(request.url || "/", "http://dsh.local").pathname.replace(/^\/antigravity\/api\/?/, "");
|
|
5114
|
+
try {
|
|
5115
|
+
if (path === "status" || path === "") {
|
|
5116
|
+
if (request.method !== "GET") return sendMethodNotAllowed(response);
|
|
5117
|
+
return sendJson(response, 200, {
|
|
5118
|
+
ok: true,
|
|
5119
|
+
value: await getAntigravityWebStatus(store, modelSettings, preferences)
|
|
5120
|
+
});
|
|
5121
|
+
}
|
|
5122
|
+
if (path === "login") {
|
|
5123
|
+
if (request.method !== "POST") return sendMethodNotAllowed(response);
|
|
5124
|
+
return sendJson(response, 200, {
|
|
5125
|
+
ok: true,
|
|
5126
|
+
value: await beginWebLogin(store)
|
|
5127
|
+
});
|
|
5128
|
+
}
|
|
5129
|
+
if (path === "login/status") {
|
|
5130
|
+
if (request.method !== "GET") return sendMethodNotAllowed(response);
|
|
5131
|
+
return sendJson(response, 200, {
|
|
5132
|
+
ok: true,
|
|
5133
|
+
value: getWebLoginStatus()
|
|
5134
|
+
});
|
|
5135
|
+
}
|
|
5136
|
+
if (path === "quota") {
|
|
5137
|
+
if (request.method !== "GET" && request.method !== "POST") return sendMethodNotAllowed(response);
|
|
5138
|
+
const quota = await fetchAccountQuota(store, modelSettings);
|
|
5139
|
+
return sendJson(response, 200, {
|
|
5140
|
+
ok: true,
|
|
5141
|
+
value: {
|
|
5142
|
+
...await getAntigravityWebStatus(store, modelSettings, preferences),
|
|
5143
|
+
quota
|
|
5144
|
+
}
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
if (path === "settings") {
|
|
5148
|
+
if (request.method !== "POST") return sendMethodNotAllowed(response);
|
|
5149
|
+
const body = await readRequestJson(request);
|
|
5150
|
+
if (preferences) await preferences.update(body);
|
|
5151
|
+
else await modelSettings.updateSettings(body);
|
|
5152
|
+
return sendJson(response, 200, {
|
|
5153
|
+
ok: true,
|
|
5154
|
+
value: await getAntigravityWebStatus(store, modelSettings, preferences)
|
|
5155
|
+
});
|
|
5156
|
+
}
|
|
5157
|
+
if (path === "models") {
|
|
5158
|
+
if (request.method === "GET") return sendJson(response, 200, {
|
|
5159
|
+
ok: true,
|
|
5160
|
+
value: (await getAntigravityWebStatus(store, modelSettings, preferences)).models
|
|
5161
|
+
});
|
|
5162
|
+
if (request.method === "POST") {
|
|
5163
|
+
const body = await readRequestJson(request);
|
|
5164
|
+
if (Array.isArray(body.enabledModelIds) || body.contextWindowOverrides || body.defaultReasoningEffort !== void 0) if (preferences) await preferences.update(body);
|
|
5165
|
+
else await modelSettings.updateSettings(body);
|
|
5166
|
+
return sendJson(response, 200, {
|
|
5167
|
+
ok: true,
|
|
5168
|
+
value: await getAntigravityWebStatus(store, modelSettings, preferences)
|
|
5169
|
+
});
|
|
5170
|
+
}
|
|
5171
|
+
return sendMethodNotAllowed(response);
|
|
5172
|
+
}
|
|
5173
|
+
if (path === "logout") {
|
|
5174
|
+
if (request.method !== "POST") return sendMethodNotAllowed(response);
|
|
5175
|
+
await store.delete();
|
|
5176
|
+
return sendJson(response, 200, {
|
|
5177
|
+
ok: true,
|
|
5178
|
+
value: await getAntigravityWebStatus(store, modelSettings)
|
|
5179
|
+
});
|
|
5180
|
+
}
|
|
5181
|
+
return sendJson(response, 404, {
|
|
5182
|
+
ok: false,
|
|
5183
|
+
error: "not-found"
|
|
5184
|
+
});
|
|
5185
|
+
} catch (err) {
|
|
5186
|
+
return sendJson(response, 500, {
|
|
5187
|
+
ok: false,
|
|
5188
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5189
|
+
});
|
|
5190
|
+
}
|
|
5191
|
+
}
|
|
5192
|
+
});
|
|
5193
|
+
}
|
|
5194
|
+
//#endregion
|
|
3310
5195
|
//#region src/index.ts
|
|
3311
5196
|
const inject = [
|
|
3312
5197
|
"webServer",
|
|
@@ -3320,6 +5205,18 @@ const inject = [
|
|
|
3320
5205
|
function apply(ctx) {
|
|
3321
5206
|
const store = createPlatformTokenStore();
|
|
3322
5207
|
const preferences = registerPreferenceStore(ctx.settings);
|
|
5208
|
+
const antigravityStore = new FileCredentialStore();
|
|
5209
|
+
const antigravityModelSettings = new FileModelSettingsStore();
|
|
5210
|
+
const antigravityPreferences = registerAntigravityPreferenceStore(ctx.settings, antigravityModelSettings);
|
|
5211
|
+
const antigravityAdapter = new AntigravityAdapter(antigravityStore, antigravityModelSettings, antigravityPreferences);
|
|
5212
|
+
ctx.effect(() => {
|
|
5213
|
+
const disposeAntigravityAdapter = ctx.llm.registerAdapter([PROVIDER_ID], antigravityAdapter);
|
|
5214
|
+
const disposeAntigravityRoutes = registerAntigravityRoutes(ctx, antigravityStore, antigravityModelSettings, antigravityPreferences);
|
|
5215
|
+
return () => {
|
|
5216
|
+
disposeAntigravityRoutes();
|
|
5217
|
+
disposeAntigravityAdapter();
|
|
5218
|
+
};
|
|
5219
|
+
}, "dsh-antigravity: adapter, routes, and lifecycle");
|
|
3323
5220
|
ctx.effect(() => {
|
|
3324
5221
|
const proxyManager = new ProxyManager({
|
|
3325
5222
|
getPreferences: () => preferences.status(),
|
|
@@ -3336,7 +5233,8 @@ function apply(ctx) {
|
|
|
3336
5233
|
localRawImages: { baseUrl: localWebServerBaseUrl(ctx.webServer.host, ctx.webServer.port) },
|
|
3337
5234
|
onGenerationFinished: () => usage.invalidate(),
|
|
3338
5235
|
outputVerbosity: () => preferences.status().outputVerbosity,
|
|
3339
|
-
fastMode: () => preferences.status().fastMode
|
|
5236
|
+
fastMode: () => preferences.status().fastMode,
|
|
5237
|
+
reasoningSummary: () => preferences.status().reasoningSummary
|
|
3340
5238
|
}), preferences);
|
|
3341
5239
|
const searchSwitcher = new SearchProviderSwitcher(ctx.loader);
|
|
3342
5240
|
const applySearchPreference = (searchProvider = preferences.status().searchProvider) => {
|
|
@@ -3345,7 +5243,7 @@ function apply(ctx) {
|
|
|
3345
5243
|
});
|
|
3346
5244
|
};
|
|
3347
5245
|
const disposeRoutes = registerRoutes(ctx, oauth, usage, preferences, proxyManager);
|
|
3348
|
-
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID], adapter);
|
|
5246
|
+
const disposeAdapter = ctx.llm.registerAdapter([PROVIDER_ID$1], adapter);
|
|
3349
5247
|
const disposeImageTool = ctx.tools.register(createCodexImageTool(oauth, ctx.attachments, { fetchFn: proxyFetch }));
|
|
3350
5248
|
let disposeWebProviders = () => {};
|
|
3351
5249
|
const registerWebProviders = () => {
|
|
@@ -3377,4 +5275,4 @@ function localWebServerBaseUrl(host, port) {
|
|
|
3377
5275
|
return `http://${host === "0.0.0.0" ? "127.0.0.1" : host}:${port}`;
|
|
3378
5276
|
}
|
|
3379
5277
|
//#endregion
|
|
3380
|
-
export { CodexChatGptAdapter, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, detectSystemProxy, inject, mapCodexUsage, parseCodexUsage, parseResponsesStream };
|
|
5278
|
+
export { AntigravityAdapter, CodexChatGptAdapter, FileCredentialStore, FileModelSettingsStore, LinuxFileTokenStore, MacKeychainTokenStore, OAuthService, ProxyManager, ResponsesClient, UsageService, WindowsDpapiTokenStore, apply, beginWebLogin, createCodexFetchProvider, createCodexImageTool, createCodexSearchProvider, createPlatformTokenStore, credentialPath, detectSystemProxy, fetchAccountQuota, getCachedQuota, inject, loginAndSave, mapCodexUsage, modelSettingsPath, parseCodexUsage, parseResponsesStream, refreshAntigravityToken };
|