copilot-api-plus 1.0.55 → 1.0.57

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/dist/main.js CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { n as ensurePaths, t as PATHS } from "./paths-BdbyVdad.js";
3
3
  import { t as state } from "./state-CRpaW-qc.js";
4
- import { c as githubHeaders, n as GITHUB_API_BASE_URL, o as copilotBaseUrl, s as copilotHeaders } from "./get-user-wlP5uMaW.js";
5
- import { n as forwardError, t as HTTPError } from "./error-BYsVGb6T.js";
6
- import { a as cacheModels, c as isNullish, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken } from "./token-Bg5qiNBd.js";
7
- import { d as getValidAccessToken, g as rotateAccount, l as getCurrentProjectId, n as clearAntigravityAuth, o as getAntigravityAuthPath, r as disableCurrentAccount, s as getApiKey } from "./auth-D_mymhYC.js";
4
+ import { c as githubHeaders, n as GITHUB_API_BASE_URL, o as copilotBaseUrl, s as copilotHeaders } from "./get-user-DOv07Myc.js";
5
+ import { n as forwardError, t as HTTPError } from "./error-CvvAyU1E.js";
6
+ import { a as cacheModels, c as isNullish, i as setupGitHubToken, l as sleep, n as refreshCopilotToken, o as cacheVSCodeVersion, r as setupCopilotToken, s as findModel, t as clearGithubToken } from "./token-D8U-wBLK.js";
7
+ import { C as recordAccountFailure, _ as rotateAccount, f as getValidAccessToken, l as getCurrentAccountIndex, n as clearAntigravityAuth, o as getAntigravityAuthPath, r as disableCurrentAccount, s as getApiKey, u as getCurrentProjectId, w as recordAccountSuccess } from "./auth-C7a3n_4O.js";
8
8
  import { n as getZenAuthPath, t as clearZenAuth } from "./auth-DIDShcrD.js";
9
- import { n as getAntigravityUsage, r as isThinkingModel, t as getAntigravityModels } from "./get-models-DhYpjJVG.js";
9
+ import { n as getAntigravityUsage, r as isThinkingModel, t as getAntigravityModels } from "./get-models-DmIjteNk.js";
10
10
  import { createRequire } from "node:module";
11
11
  import { defineCommand, runMain } from "citty";
12
12
  import consola from "consola";
@@ -531,7 +531,7 @@ function initProxyFromEnv() {
531
531
  * Add a new Antigravity account via OAuth
532
532
  */
533
533
  async function addAccount() {
534
- const { setupAntigravity, loadAntigravityAuth } = await import("./auth-DreFwlx2.js");
534
+ const { setupAntigravity, loadAntigravityAuth } = await import("./auth-BfBWPtWx.js");
535
535
  const existingAuth = await loadAntigravityAuth();
536
536
  if (existingAuth && existingAuth.accounts.length > 0) {
537
537
  const enabledCount = existingAuth.accounts.filter((a) => a.enable).length;
@@ -543,7 +543,7 @@ async function addAccount() {
543
543
  * List all Antigravity accounts
544
544
  */
545
545
  async function listAccounts() {
546
- const { loadAntigravityAuth } = await import("./auth-DreFwlx2.js");
546
+ const { loadAntigravityAuth } = await import("./auth-BfBWPtWx.js");
547
547
  const auth = await loadAntigravityAuth();
548
548
  if (!auth || auth.accounts.length === 0) {
549
549
  consola.info("No Antigravity accounts configured");
@@ -564,7 +564,7 @@ async function listAccounts() {
564
564
  * Remove an Antigravity account by index
565
565
  */
566
566
  async function removeAccount(index) {
567
- const { loadAntigravityAuth, saveAntigravityAuth } = await import("./auth-DreFwlx2.js");
567
+ const { loadAntigravityAuth, saveAntigravityAuth } = await import("./auth-BfBWPtWx.js");
568
568
  const auth = await loadAntigravityAuth();
569
569
  if (!auth || auth.accounts.length === 0) {
570
570
  consola.error("No Antigravity accounts configured");
@@ -583,7 +583,7 @@ async function removeAccount(index) {
583
583
  * Clear all Antigravity accounts
584
584
  */
585
585
  async function clearAccounts() {
586
- const { clearAntigravityAuth } = await import("./auth-DreFwlx2.js");
586
+ const { clearAntigravityAuth } = await import("./auth-BfBWPtWx.js");
587
587
  if (await consola.prompt("Are you sure you want to remove all Antigravity accounts?", {
588
588
  type: "confirm",
589
589
  initial: false
@@ -1225,6 +1225,214 @@ function modelLogger() {
1225
1225
  };
1226
1226
  }
1227
1227
  //#endregion
1228
+ //#region src/services/antigravity/background-detection.ts
1229
+ /**
1230
+ * Antigravity Background Detection
1231
+ *
1232
+ * Detects agent/background requests and optionally downgrades
1233
+ * expensive models to cheaper alternatives to preserve quota.
1234
+ *
1235
+ * Detection uses multi-signal scoring — not a single heuristic:
1236
+ * 1. Explicit header: X-Request-Type: background (definitive)
1237
+ * 2. tool_calls / tool role pattern (strong agent signal)
1238
+ * 3. High density of assistant messages (weak signal alone)
1239
+ *
1240
+ * Controlled by env var: ANTIGRAVITY_BACKGROUND_DOWNGRADE=1
1241
+ */
1242
+ const BACKGROUND_DOWNGRADE_ENABLED = process.env.ANTIGRAVITY_BACKGROUND_DOWNGRADE === "1";
1243
+ /**
1244
+ * Score threshold for downgrade (0–1).
1245
+ * 0.6 means we need at least one strong signal.
1246
+ */
1247
+ const AGENT_SCORE_THRESHOLD = .6;
1248
+ /**
1249
+ * Model downgrade mapping: expensive → cheaper
1250
+ */
1251
+ const DOWNGRADE_MAP = {
1252
+ "claude-sonnet-4-5": "gemini-2.5-flash",
1253
+ "claude-sonnet-4-5-thinking": "gemini-2.5-flash-thinking",
1254
+ "claude-opus-4-5-thinking": "claude-sonnet-4-5-thinking",
1255
+ "gemini-2.5-pro": "gemini-2.5-flash",
1256
+ "gemini-3-pro-high": "gemini-3-flash",
1257
+ "gemini-3-pro-low": "gemini-3-flash"
1258
+ };
1259
+ /**
1260
+ * Multi-signal agent detection.
1261
+ *
1262
+ * Scores the request on a 0–1 scale:
1263
+ * - Explicit header "X-Request-Type: background" → 1.0 (instant)
1264
+ * - Any message with tool_calls field → +0.5
1265
+ * - Any message with role "tool" → +0.4
1266
+ * - assistant messages ≥ 60% of all messages → +0.2
1267
+ * - More than 6 total messages → +0.1
1268
+ *
1269
+ * Score ≥ 0.6 → treated as agent/background request.
1270
+ */
1271
+ function detectAgent(messages, headers) {
1272
+ const signals = [];
1273
+ const requestType = headers?.get("x-request-type");
1274
+ if (requestType === "background" || requestType === "agent") return {
1275
+ isAgent: true,
1276
+ score: 1,
1277
+ signals: ["explicit-header"]
1278
+ };
1279
+ let score = 0;
1280
+ const total = messages.length;
1281
+ if (total === 0) return {
1282
+ isAgent: false,
1283
+ score: 0,
1284
+ signals: []
1285
+ };
1286
+ if (messages.some((m) => m.tool_calls !== void 0 && m.tool_calls !== null)) {
1287
+ score += .5;
1288
+ signals.push("tool_calls");
1289
+ }
1290
+ if (messages.some((m) => m.role === "tool")) {
1291
+ score += .4;
1292
+ signals.push("tool-role");
1293
+ }
1294
+ const assistantCount = messages.filter((m) => m.role === "assistant").length;
1295
+ if (total >= 3 && assistantCount / total >= .6) {
1296
+ score += .2;
1297
+ signals.push("high-assistant-density");
1298
+ }
1299
+ if (total > 6) {
1300
+ score += .1;
1301
+ signals.push("long-conversation");
1302
+ }
1303
+ return {
1304
+ isAgent: score >= AGENT_SCORE_THRESHOLD,
1305
+ score: Math.min(score, 1),
1306
+ signals
1307
+ };
1308
+ }
1309
+ /**
1310
+ * Optionally downgrade a model for background/agent requests.
1311
+ *
1312
+ * Returns the original model if:
1313
+ * - ANTIGRAVITY_BACKGROUND_DOWNGRADE is not "1"
1314
+ * - The request is not detected as agent/background
1315
+ * - No downgrade mapping exists for the model
1316
+ */
1317
+ function maybeDowngradeModel(model, messages, headers) {
1318
+ if (!BACKGROUND_DOWNGRADE_ENABLED) return model;
1319
+ const result = detectAgent(messages, headers);
1320
+ if (!result.isAgent) return model;
1321
+ const downgraded = DOWNGRADE_MAP[model];
1322
+ if (!downgraded) return model;
1323
+ consola.info(`[background-detection] agent detected (score=${result.score.toFixed(2)}, signals=[${result.signals.join(",")}]), downgrading ${model} → ${downgraded}`);
1324
+ return downgraded;
1325
+ }
1326
+ //#endregion
1327
+ //#region src/services/antigravity/circuit-breaker.ts
1328
+ const FAILURE_THRESHOLD = 3;
1329
+ const RESET_TIMEOUT_MS = 3e4;
1330
+ const HALF_OPEN_SUCCESS_THRESHOLD = 2;
1331
+ const CircuitState = {
1332
+ CLOSED: "CLOSED",
1333
+ OPEN: "OPEN",
1334
+ HALF_OPEN: "HALF_OPEN"
1335
+ };
1336
+ const breakers = /* @__PURE__ */ new Map();
1337
+ function getOrCreate(family) {
1338
+ const existing = breakers.get(family);
1339
+ if (existing) return existing;
1340
+ const fresh = {
1341
+ state: CircuitState.CLOSED,
1342
+ failureCount: 0,
1343
+ successCount: 0,
1344
+ lastFailureTime: 0
1345
+ };
1346
+ breakers.set(family, fresh);
1347
+ return fresh;
1348
+ }
1349
+ function getModelFamily(model) {
1350
+ if (model.includes("claude")) return "claude";
1351
+ if (model.includes("gemini")) return "gemini";
1352
+ return "other";
1353
+ }
1354
+ function canExecute(family) {
1355
+ const breaker = getOrCreate(family);
1356
+ switch (breaker.state) {
1357
+ case CircuitState.CLOSED: return true;
1358
+ case CircuitState.OPEN:
1359
+ if (Date.now() - breaker.lastFailureTime >= RESET_TIMEOUT_MS) {
1360
+ consola.info(`[circuit-breaker] ${family}: OPEN -> HALF_OPEN`);
1361
+ breaker.state = CircuitState.HALF_OPEN;
1362
+ breaker.successCount = 0;
1363
+ return true;
1364
+ }
1365
+ return false;
1366
+ case CircuitState.HALF_OPEN: return true;
1367
+ default: return true;
1368
+ }
1369
+ }
1370
+ function recordSuccess(family) {
1371
+ const breaker = getOrCreate(family);
1372
+ switch (breaker.state) {
1373
+ case CircuitState.HALF_OPEN:
1374
+ breaker.successCount++;
1375
+ if (breaker.successCount >= HALF_OPEN_SUCCESS_THRESHOLD) {
1376
+ consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> CLOSED`);
1377
+ breaker.state = CircuitState.CLOSED;
1378
+ breaker.failureCount = 0;
1379
+ breaker.successCount = 0;
1380
+ }
1381
+ break;
1382
+ case CircuitState.CLOSED:
1383
+ breaker.failureCount = 0;
1384
+ break;
1385
+ default: break;
1386
+ }
1387
+ }
1388
+ function recordFailure(family) {
1389
+ const breaker = getOrCreate(family);
1390
+ breaker.failureCount++;
1391
+ breaker.lastFailureTime = Date.now();
1392
+ switch (breaker.state) {
1393
+ case CircuitState.CLOSED:
1394
+ if (breaker.failureCount >= FAILURE_THRESHOLD) {
1395
+ consola.info(`[circuit-breaker] ${family}: CLOSED -> OPEN`);
1396
+ breaker.state = CircuitState.OPEN;
1397
+ }
1398
+ break;
1399
+ case CircuitState.HALF_OPEN:
1400
+ consola.info(`[circuit-breaker] ${family}: HALF_OPEN -> OPEN`);
1401
+ breaker.state = CircuitState.OPEN;
1402
+ break;
1403
+ default: break;
1404
+ }
1405
+ }
1406
+ function getBackoffDelay(family, baseDelay) {
1407
+ const breaker = breakers.get(family);
1408
+ if (!breaker || breaker.failureCount === 0) return baseDelay;
1409
+ const delay = baseDelay * Math.pow(2, breaker.failureCount - 1);
1410
+ return Math.min(delay, 3e4);
1411
+ }
1412
+ function parseRetryDelay$3(errorText) {
1413
+ try {
1414
+ const errorData = JSON.parse(errorText);
1415
+ const details = errorData.error?.details ?? [];
1416
+ for (const detail of details) {
1417
+ if (detail["@type"]?.includes("RetryInfo") && detail.retryDelay) {
1418
+ const match = /(\d+(?:\.\d+)?)s/.exec(detail.retryDelay);
1419
+ if (match) return Math.ceil(Number.parseFloat(match[1]) * 1e3);
1420
+ }
1421
+ if (detail.quotaResetDelay) {
1422
+ const match = /(\d+(?:\.\d+)?)(?:ms|s)/.exec(detail.quotaResetDelay);
1423
+ if (match) {
1424
+ const value = Number.parseFloat(match[1]);
1425
+ return detail.quotaResetDelay.includes("ms") ? Math.ceil(value) : Math.ceil(value * 1e3);
1426
+ }
1427
+ }
1428
+ }
1429
+ const message = errorData.error?.message ?? "";
1430
+ const resetMatch = /quota will reset after (\d+(?:\.\d+)?)s/i.exec(message);
1431
+ if (resetMatch) return Math.ceil(Number.parseFloat(resetMatch[1]) * 1e3);
1432
+ } catch {}
1433
+ return 500;
1434
+ }
1435
+ //#endregion
1228
1436
  //#region src/services/antigravity/stream-parser.ts
1229
1437
  /**
1230
1438
  * Create initial stream state
@@ -1582,12 +1790,17 @@ function createErrorResponse$1(message, type, status, details) {
1582
1790
  * Create chat completion with Antigravity or standard Gemini API
1583
1791
  * Priority: API Key > OAuth
1584
1792
  */
1585
- async function createAntigravityChatCompletion(request) {
1793
+ async function createAntigravityChatCompletion(request, requestHeaders) {
1794
+ const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
1795
+ const effectiveRequest = effectiveModel !== request.model ? {
1796
+ ...request,
1797
+ model: effectiveModel
1798
+ } : request;
1586
1799
  const apiKey = getApiKey();
1587
- if (apiKey) return await createWithApiKey(request, apiKey);
1800
+ if (apiKey) return await createWithApiKey(effectiveRequest, apiKey);
1588
1801
  const accessToken = await getValidAccessToken();
1589
1802
  if (!accessToken) return createErrorResponse$1("No valid authentication available. Please set GEMINI_API_KEY environment variable or run OAuth login.", "auth_error", 401);
1590
- return await createWithOAuth(request, accessToken);
1803
+ return await createWithOAuth(effectiveRequest, accessToken);
1591
1804
  }
1592
1805
  /**
1593
1806
  * Create chat completion using API Key (standard Gemini API)
@@ -1602,7 +1815,7 @@ async function createWithApiKey(request, apiKey) {
1602
1815
  headers: { "Content-Type": "application/json" },
1603
1816
  body: JSON.stringify(body)
1604
1817
  });
1605
- if (!response.ok) return await handleApiError$1(response);
1818
+ if (!response.ok) return (await handleApiError$1(response)).response;
1606
1819
  return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
1607
1820
  } catch (error) {
1608
1821
  consola.error("Gemini API request error:", error);
@@ -1612,29 +1825,63 @@ async function createWithApiKey(request, apiKey) {
1612
1825
  /**
1613
1826
  * Create chat completion using OAuth (Antigravity private API)
1614
1827
  * Note: Both Gemini and Claude models use the same endpoint and Gemini-style format
1828
+ *
1829
+ * Features:
1830
+ * - Circuit breaker per model family
1831
+ * - Retry with exponential backoff on 429/503
1832
+ * - Token refresh between attempts
1615
1833
  */
1616
- async function createWithOAuth(request, accessToken) {
1834
+ const MAX_RETRIES$4 = 5;
1835
+ async function createWithOAuth(request, _initialAccessToken) {
1617
1836
  const endpoint = request.stream ? ANTIGRAVITY_STREAM_URL : ANTIGRAVITY_NO_STREAM_URL;
1618
1837
  const body = buildAntigravityRequestBody(request);
1619
- consola.debug(`Antigravity request to ${endpoint} with model ${request.model}`);
1620
- try {
1621
- const response = await fetch(endpoint, {
1622
- method: "POST",
1623
- headers: {
1624
- Host: ANTIGRAVITY_API_HOST,
1625
- "User-Agent": ANTIGRAVITY_USER_AGENT$1,
1626
- Authorization: `Bearer ${accessToken}`,
1627
- "Content-Type": "application/json",
1628
- "Accept-Encoding": "gzip"
1629
- },
1630
- body: JSON.stringify(body)
1631
- });
1632
- if (!response.ok) return await handleApiError$1(response);
1633
- return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
1634
- } catch (error) {
1635
- consola.error("Antigravity request error:", error);
1636
- return createErrorResponse$1(`Request failed: ${String(error)}`, "request_error", 500);
1838
+ const bodyString = JSON.stringify(body);
1839
+ for (let attempt = 0; attempt <= MAX_RETRIES$4; attempt++) {
1840
+ const family = getModelFamily(request.model);
1841
+ if (!canExecute(family)) {
1842
+ consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
1843
+ await sleep(1e3);
1844
+ continue;
1845
+ }
1846
+ const accessToken = await getValidAccessToken();
1847
+ if (!accessToken) return createErrorResponse$1("No valid Antigravity access token available.", "auth_error", 401);
1848
+ try {
1849
+ const response = await fetch(endpoint, {
1850
+ method: "POST",
1851
+ headers: {
1852
+ Host: ANTIGRAVITY_API_HOST,
1853
+ "User-Agent": ANTIGRAVITY_USER_AGENT$1,
1854
+ Authorization: `Bearer ${accessToken}`,
1855
+ "Content-Type": "application/json",
1856
+ "Accept-Encoding": "gzip"
1857
+ },
1858
+ body: bodyString
1859
+ });
1860
+ if (response.ok) {
1861
+ recordSuccess(family);
1862
+ recordAccountSuccess(await getCurrentAccountIndex());
1863
+ return request.stream ? transformStreamResponse$1(response, request.model) : await transformNonStreamResponse$1(response, request.model);
1864
+ }
1865
+ const errorResult = await handleApiError$1(response);
1866
+ if (errorResult.shouldRetry && attempt < MAX_RETRIES$4) {
1867
+ recordFailure(family);
1868
+ recordAccountFailure(await getCurrentAccountIndex());
1869
+ const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
1870
+ consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$4})`);
1871
+ await sleep(backoffDelay);
1872
+ continue;
1873
+ }
1874
+ return errorResult.response;
1875
+ } catch (error) {
1876
+ consola.error("Antigravity request error:", error);
1877
+ if (attempt < MAX_RETRIES$4) {
1878
+ await sleep(500);
1879
+ continue;
1880
+ }
1881
+ return createErrorResponse$1(`Request failed: ${String(error)}`, "request_error", 500);
1882
+ }
1637
1883
  }
1884
+ return createErrorResponse$1("Max retries exceeded", "api_error", 429);
1638
1885
  }
1639
1886
  /**
1640
1887
  * Handle API error response
@@ -1643,8 +1890,19 @@ async function handleApiError$1(response) {
1643
1890
  const errorText = await response.text();
1644
1891
  consola.error(`Antigravity error: ${response.status} ${errorText}`);
1645
1892
  if (response.status === 403) await disableCurrentAccount();
1646
- if (response.status === 429 || response.status === 503) await rotateAccount();
1647
- return createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText);
1893
+ if (response.status === 429 || response.status === 503) {
1894
+ await rotateAccount();
1895
+ return {
1896
+ shouldRetry: true,
1897
+ retryDelayMs: parseRetryDelay$3(errorText),
1898
+ response: createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText)
1899
+ };
1900
+ }
1901
+ return {
1902
+ shouldRetry: false,
1903
+ retryDelayMs: 0,
1904
+ response: createErrorResponse$1(`Antigravity API error: ${response.status}`, "api_error", response.status, errorText)
1905
+ };
1648
1906
  }
1649
1907
  /**
1650
1908
  * Generate request ID
@@ -1809,7 +2067,7 @@ function extractNonStreamContent(parts) {
1809
2067
  const app$1 = new Hono();
1810
2068
  app$1.post("/", async (c) => {
1811
2069
  const body = await c.req.json();
1812
- const response = await createAntigravityChatCompletion(body);
2070
+ const response = await createAntigravityChatCompletion(body, c.req.raw.headers);
1813
2071
  const headers = new Headers(response.headers);
1814
2072
  if (body.stream) return new Response(response.body, {
1815
2073
  status: response.status,
@@ -2049,31 +2307,6 @@ function rotateEndpoint() {
2049
2307
  consola.info(`Rotating endpoint: ${ANTIGRAVITY_ENDPOINTS[oldIndex]} → ${ANTIGRAVITY_ENDPOINTS[currentEndpointIndex]}`);
2050
2308
  }
2051
2309
  const ANTIGRAVITY_USER_AGENT = "antigravity/1.11.3 windows/amd64";
2052
- const rateLimitTracker = {};
2053
- function getModelFamily(model) {
2054
- if (model.includes("claude")) return "claude";
2055
- if (model.includes("gemini")) return "gemini";
2056
- return "other";
2057
- }
2058
- function trackRateLimit(model) {
2059
- const family = getModelFamily(model);
2060
- if (!rateLimitTracker[family]) rateLimitTracker[family] = {
2061
- lastLimitTime: 0,
2062
- consecutiveErrors: 0
2063
- };
2064
- rateLimitTracker[family].lastLimitTime = Date.now();
2065
- rateLimitTracker[family].consecutiveErrors++;
2066
- }
2067
- function clearRateLimitTracker(model) {
2068
- const family = getModelFamily(model);
2069
- if (rateLimitTracker[family]) rateLimitTracker[family].consecutiveErrors = 0;
2070
- }
2071
- function getBackoffDelay(model, baseDelay) {
2072
- const info = rateLimitTracker[getModelFamily(model)];
2073
- if (!info) return baseDelay;
2074
- const multiplier = Math.min(Math.pow(2, info.consecutiveErrors - 1), 60);
2075
- return Math.min(baseDelay * multiplier, 3e4);
2076
- }
2077
2310
  /**
2078
2311
  * Extract text from system content (can be string or array)
2079
2312
  */
@@ -2243,18 +2476,29 @@ function createErrorResponse(type, message, status) {
2243
2476
  *
2244
2477
  * Features:
2245
2478
  * - Endpoint fallback (daily → prod)
2246
- * - Per-model-family rate limit tracking
2479
+ * - Circuit breaker per model family (CLOSED/OPEN/HALF_OPEN)
2247
2480
  * - Exponential backoff for consecutive errors
2248
2481
  * - Smart retry for short delays (≤5s on same endpoint)
2249
2482
  */
2250
2483
  const MAX_RETRIES$3 = 5;
2251
2484
  const MAX_ENDPOINT_RETRIES = 2;
2252
- async function executeAntigravityRequest(request) {
2253
- const body = buildGeminiRequest(request, await getCurrentProjectId());
2485
+ async function executeAntigravityRequest(request, requestHeaders) {
2486
+ const effectiveModel = maybeDowngradeModel(request.model, request.messages, requestHeaders);
2487
+ const effectiveRequest = effectiveModel !== request.model ? {
2488
+ ...request,
2489
+ model: effectiveModel
2490
+ } : request;
2491
+ const body = buildGeminiRequest(effectiveRequest, await getCurrentProjectId());
2254
2492
  let endpointRetries = 0;
2255
2493
  for (let attempt = 0; attempt <= MAX_RETRIES$3; attempt++) {
2494
+ const family = getModelFamily(effectiveRequest.model);
2495
+ if (!canExecute(family)) {
2496
+ consola.warn(`[circuit-breaker] ${family} circuit OPEN, waiting for reset...`);
2497
+ await sleep(1e3);
2498
+ continue;
2499
+ }
2256
2500
  const host = getCurrentHost();
2257
- const endpoint = request.stream ? getStreamUrl(host) : getNoStreamUrl(host);
2501
+ const endpoint = effectiveRequest.stream ? getStreamUrl(host) : getNoStreamUrl(host);
2258
2502
  const accessToken = await getValidAccessToken();
2259
2503
  if (!accessToken) return createErrorResponse("authentication_error", "No valid Antigravity access token available.", 401);
2260
2504
  try {
@@ -2270,13 +2514,16 @@ async function executeAntigravityRequest(request) {
2270
2514
  body: JSON.stringify(body)
2271
2515
  });
2272
2516
  if (response.ok) {
2273
- clearRateLimitTracker(request.model);
2274
- return request.stream ? transformStreamResponse(response, request.model) : await transformNonStreamResponse(response, request.model);
2517
+ recordSuccess(getModelFamily(effectiveRequest.model));
2518
+ recordAccountSuccess(await getCurrentAccountIndex());
2519
+ return effectiveRequest.stream ? transformStreamResponse(response, effectiveRequest.model) : await transformNonStreamResponse(response, effectiveRequest.model);
2275
2520
  }
2276
- const errorResult = await handleApiError(response, request.model);
2521
+ const errorResult = await handleApiError(response, effectiveRequest.model);
2277
2522
  if (errorResult.shouldRetry && attempt < MAX_RETRIES$3) {
2278
- trackRateLimit(request.model);
2279
- const backoffDelay = getBackoffDelay(request.model, errorResult.retryDelayMs);
2523
+ const family = getModelFamily(effectiveRequest.model);
2524
+ recordFailure(family);
2525
+ recordAccountFailure(await getCurrentAccountIndex());
2526
+ const backoffDelay = getBackoffDelay(family, errorResult.retryDelayMs);
2280
2527
  if (backoffDelay <= 5e3 || endpointRetries >= MAX_ENDPOINT_RETRIES) {
2281
2528
  consola.info(`Rate limited, retrying in ${backoffDelay}ms (attempt ${attempt + 1}/${MAX_RETRIES$3})`);
2282
2529
  await sleep(backoffDelay);
@@ -2304,38 +2551,8 @@ async function executeAntigravityRequest(request) {
2304
2551
  }
2305
2552
  return createErrorResponse("api_error", "Max retries exceeded", 429);
2306
2553
  }
2307
- async function createAntigravityMessages(request) {
2308
- return antigravityQueue.enqueue(() => executeAntigravityRequest(request));
2309
- }
2310
- /**
2311
- * Parse retry delay from error response
2312
- * Supports multiple formats:
2313
- * - RetryInfo.retryDelay: "3.5s"
2314
- * - quotaResetDelay: "3000ms" or "3s"
2315
- * - message: "Your quota will reset after 3s"
2316
- */
2317
- function parseRetryDelay$3(errorText) {
2318
- try {
2319
- const errorData = JSON.parse(errorText);
2320
- const details = errorData.error?.details ?? [];
2321
- for (const detail of details) {
2322
- if (detail["@type"]?.includes("RetryInfo") && detail.retryDelay) {
2323
- const match = /(\d+(?:\.\d+)?)s/.exec(detail.retryDelay);
2324
- if (match) return Math.ceil(Number.parseFloat(match[1]) * 1e3);
2325
- }
2326
- if (detail.quotaResetDelay) {
2327
- const match = /(\d+(?:\.\d+)?)(?:ms|s)/.exec(detail.quotaResetDelay);
2328
- if (match) {
2329
- const value = Number.parseFloat(match[1]);
2330
- return detail.quotaResetDelay.includes("ms") ? Math.ceil(value) : Math.ceil(value * 1e3);
2331
- }
2332
- }
2333
- }
2334
- const message = errorData.error?.message ?? "";
2335
- const resetMatch = /quota will reset after (\d+(?:\.\d+)?)s/i.exec(message);
2336
- if (resetMatch) return Math.ceil(Number.parseFloat(resetMatch[1]) * 1e3);
2337
- } catch {}
2338
- return 500;
2554
+ async function createAntigravityMessages(request, requestHeaders) {
2555
+ return antigravityQueue.enqueue(() => executeAntigravityRequest(request, requestHeaders));
2339
2556
  }
2340
2557
  /**
2341
2558
  * Handle API error response
@@ -2525,7 +2742,7 @@ antigravityMessagesRoute.post("/", async (c) => {
2525
2742
  try {
2526
2743
  const body = await c.req.json();
2527
2744
  consola.debug("Antigravity message request:", body.model);
2528
- const response = await createAntigravityMessages(body);
2745
+ const response = await createAntigravityMessages(body, c.req.raw.headers);
2529
2746
  if (body.stream) {
2530
2747
  const headers = new Headers();
2531
2748
  headers.set("Content-Type", "text/event-stream");
@@ -3874,7 +4091,7 @@ async function runServer(options) {
3874
4091
  } else if (options.antigravity) {
3875
4092
  consola.info("Google Antigravity mode enabled");
3876
4093
  state.antigravityMode = true;
3877
- const { loadAntigravityAuth, setupAntigravity, getCurrentAccount, hasApiKey, getApiKey, setOAuthCredentials } = await import("./auth-DreFwlx2.js");
4094
+ const { loadAntigravityAuth, setupAntigravity, getCurrentAccount, hasApiKey, getApiKey, setOAuthCredentials } = await import("./auth-BfBWPtWx.js");
3878
4095
  if (options.antigravityClientId && options.antigravityClientSecret) {
3879
4096
  setOAuthCredentials(options.antigravityClientId, options.antigravityClientSecret);
3880
4097
  consola.info("Using provided OAuth credentials from CLI");
@@ -3903,7 +4120,7 @@ async function runServer(options) {
3903
4120
  }
3904
4121
  if (!await getCurrentAccount() && !hasApiKey()) throw new Error("No enabled Antigravity accounts available");
3905
4122
  }
3906
- const { getAntigravityModels } = await import("./get-models-PKzVxQmq.js");
4123
+ const { getAntigravityModels } = await import("./get-models-DIOdRXYx.js");
3907
4124
  const models = await getAntigravityModels();
3908
4125
  state.antigravityModels = models;
3909
4126
  consola.info(`Available Antigravity models: \n${models.data.map((model) => `- ${model.id}`).join("\n")}`);
@@ -3913,7 +4130,7 @@ async function runServer(options) {
3913
4130
  state.githubToken = options.githubToken;
3914
4131
  consola.info("Using provided GitHub token");
3915
4132
  try {
3916
- const { getGitHubUser } = await import("./get-user-CtEiwKow.js");
4133
+ const { getGitHubUser } = await import("./get-user-CGhBmkXO.js");
3917
4134
  const user = await getGitHubUser();
3918
4135
  consola.info(`Logged in as ${user.login}`);
3919
4136
  } catch (error) {
@@ -3924,10 +4141,10 @@ async function runServer(options) {
3924
4141
  try {
3925
4142
  await setupCopilotToken();
3926
4143
  } catch (error) {
3927
- const { HTTPError } = await import("./error-4DW6q2Mo.js");
4144
+ const { HTTPError } = await import("./error-Djpro28X.js");
3928
4145
  if (error instanceof HTTPError && error.response.status === 401) {
3929
4146
  consola.error("Failed to get Copilot token - GitHub token may be invalid or Copilot access revoked");
3930
- const { clearGithubToken } = await import("./token-B_0VZjlS.js");
4147
+ const { clearGithubToken } = await import("./token-CoKq3Guw.js");
3931
4148
  await clearGithubToken();
3932
4149
  consola.info("Please restart to re-authenticate");
3933
4150
  }