@cortexkit/opencode-antigravity-auth 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/cli.js +308 -81
  2. package/dist/cli.js.map +4 -4
  3. package/dist/index.js +16634 -15416
  4. package/dist/index.js.map +4 -4
  5. package/dist/src/plugin/account-command-oauth.d.ts +34 -0
  6. package/dist/src/plugin/account-command-oauth.d.ts.map +1 -0
  7. package/dist/src/plugin/account-command-oauth.js +156 -0
  8. package/dist/src/plugin/account-command-oauth.js.map +1 -0
  9. package/dist/src/plugin/auth-loader.d.ts +16 -1
  10. package/dist/src/plugin/auth-loader.d.ts.map +1 -1
  11. package/dist/src/plugin/auth-loader.js +113 -32
  12. package/dist/src/plugin/auth-loader.js.map +1 -1
  13. package/dist/src/plugin/background-quota-refresh.d.ts +146 -0
  14. package/dist/src/plugin/background-quota-refresh.d.ts.map +1 -0
  15. package/dist/src/plugin/background-quota-refresh.js +347 -0
  16. package/dist/src/plugin/background-quota-refresh.js.map +1 -0
  17. package/dist/src/plugin/command-data.d.ts +61 -8
  18. package/dist/src/plugin/command-data.d.ts.map +1 -1
  19. package/dist/src/plugin/command-data.js +231 -39
  20. package/dist/src/plugin/command-data.js.map +1 -1
  21. package/dist/src/plugin/commands.d.ts +12 -9
  22. package/dist/src/plugin/commands.d.ts.map +1 -1
  23. package/dist/src/plugin/commands.js +140 -33
  24. package/dist/src/plugin/commands.js.map +1 -1
  25. package/dist/src/plugin/config/schema.d.ts +2 -0
  26. package/dist/src/plugin/config/schema.d.ts.map +1 -1
  27. package/dist/src/plugin/config/schema.js +24 -0
  28. package/dist/src/plugin/config/schema.js.map +1 -1
  29. package/dist/src/plugin/debug.d.ts +7 -0
  30. package/dist/src/plugin/debug.d.ts.map +1 -1
  31. package/dist/src/plugin/debug.js +25 -0
  32. package/dist/src/plugin/debug.js.map +1 -1
  33. package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -1
  34. package/dist/src/plugin/fetch-interceptor.js +13 -1
  35. package/dist/src/plugin/fetch-interceptor.js.map +1 -1
  36. package/dist/src/plugin/index.d.ts +7 -0
  37. package/dist/src/plugin/index.d.ts.map +1 -1
  38. package/dist/src/plugin/index.js +127 -24
  39. package/dist/src/plugin/index.js.map +1 -1
  40. package/dist/src/plugin/killswitch.d.ts.map +1 -1
  41. package/dist/src/plugin/killswitch.js +12 -9
  42. package/dist/src/plugin/killswitch.js.map +1 -1
  43. package/dist/src/plugin/oauth-methods.d.ts.map +1 -1
  44. package/dist/src/plugin/oauth-methods.js +2 -10
  45. package/dist/src/plugin/oauth-methods.js.map +1 -1
  46. package/dist/src/plugin/quota.d.ts +42 -1
  47. package/dist/src/plugin/quota.d.ts.map +1 -1
  48. package/dist/src/plugin/quota.js +185 -41
  49. package/dist/src/plugin/quota.js.map +1 -1
  50. package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
  51. package/dist/src/plugin/ui/auth-menu.js +2 -4
  52. package/dist/src/plugin/ui/auth-menu.js.map +1 -1
  53. package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
  54. package/dist/src/plugin/ui/quota-status.js +3 -5
  55. package/dist/src/plugin/ui/quota-status.js.map +1 -1
  56. package/dist/src/sidebar-state.d.ts +126 -9
  57. package/dist/src/sidebar-state.d.ts.map +1 -1
  58. package/dist/src/sidebar-state.js +185 -25
  59. package/dist/src/sidebar-state.js.map +1 -1
  60. package/dist/src/tui/command-dialogs.js +101 -3
  61. package/dist/src/tui/command-dialogs.js.map +1 -1
  62. package/dist/src/tui.d.ts.map +1 -1
  63. package/dist/src/tui.js +55 -20
  64. package/dist/src/tui.js.map +1 -1
  65. package/package.json +6 -6
  66. package/src/plugin/command-data.ts +331 -51
  67. package/src/sidebar-state.ts +306 -32
  68. package/src/tui/command-dialogs.tsx +136 -3
  69. package/src/tui-compiled/plugin/command-data.ts +331 -51
  70. package/src/tui-compiled/sidebar-state.ts +306 -32
  71. package/src/tui-compiled/tui/command-dialogs.tsx +189 -69
  72. package/src/tui-compiled/tui.tsx +73 -30
  73. package/src/tui.tsx +82 -28
package/dist/cli.js CHANGED
@@ -1063,25 +1063,25 @@ var RESOLVER_ALIASES = {
1063
1063
  "gpt-oss-120b": "gpt-oss-120b-medium"
1064
1064
  };
1065
1065
  var QUOTA_GROUP_BY_MODEL_ID = {
1066
- "claude-opus-4-6-thinking": "claude",
1067
- "claude-opus-4-6": "claude",
1068
- "claude-sonnet-4-6-thinking": "claude",
1069
- "claude-sonnet-4-6": "claude",
1070
- "gemini-pro-agent": "gemini-pro",
1071
- "gemini-3.1-pro": "gemini-pro",
1072
- "gemini-3.1-pro-low": "gemini-pro",
1073
- "gemini-3.1-pro-high": "gemini-pro",
1074
- "gemini-3-flash": "gemini-flash",
1075
- "gemini-3-flash-agent": "gemini-flash",
1076
- "gemini-3.5-flash-low": "gemini-flash",
1077
- "gemini-3.5-flash-extra-low": "gemini-flash",
1078
- "gemini-3.6-flash-low": "gemini-flash",
1079
- "gemini-3.6-flash-medium": "gemini-flash",
1080
- "gemini-3.6-flash-high": "gemini-flash",
1081
- "gemini-3.6-flash-tiered": "gemini-flash",
1082
- "gemini-3.1-flash-image": "gemini-flash",
1083
- "gpt-oss-120b": "gpt-oss",
1084
- "gpt-oss-120b-medium": "gpt-oss"
1066
+ "claude-opus-4-6-thinking": "non-gemini",
1067
+ "claude-opus-4-6": "non-gemini",
1068
+ "claude-sonnet-4-6-thinking": "non-gemini",
1069
+ "claude-sonnet-4-6": "non-gemini",
1070
+ "gemini-pro-agent": "gemini",
1071
+ "gemini-3.1-pro": "gemini",
1072
+ "gemini-3.1-pro-low": "gemini",
1073
+ "gemini-3.1-pro-high": "gemini",
1074
+ "gemini-3-flash": "gemini",
1075
+ "gemini-3-flash-agent": "gemini",
1076
+ "gemini-3.5-flash-low": "gemini",
1077
+ "gemini-3.5-flash-extra-low": "gemini",
1078
+ "gemini-3.6-flash-low": "gemini",
1079
+ "gemini-3.6-flash-medium": "gemini",
1080
+ "gemini-3.6-flash-high": "gemini",
1081
+ "gemini-3.6-flash-tiered": "gemini",
1082
+ "gemini-3.1-flash-image": "gemini",
1083
+ "gpt-oss-120b": "non-gemini",
1084
+ "gpt-oss-120b-medium": "non-gemini"
1085
1085
  };
1086
1086
  var ANTIGRAVITY_OPENCODE_MODEL_IDS = [
1087
1087
  "antigravity-gemini-3.6-flash",
@@ -1100,20 +1100,13 @@ function getResolverAliasMap() {
1100
1100
  return RESOLVER_ALIASES;
1101
1101
  }
1102
1102
  function getQuotaGroupForModel(modelId) {
1103
- return QUOTA_GROUP_BY_MODEL_ID[modelId.toLowerCase()];
1104
- }
1105
-
1106
- // ../core/dist/transform/model-resolver.js
1107
- var MODEL_ALIASES = getResolverAliasMap();
1108
- function getModelFamily(model) {
1109
- const lower = model.toLowerCase();
1110
- if (lower.includes("claude")) {
1111
- return "claude";
1112
- }
1113
- if (lower.includes("flash")) {
1114
- return "gemini-flash";
1115
- }
1116
- return "gemini-pro";
1103
+ const normalized = modelId.toLowerCase();
1104
+ return QUOTA_GROUP_BY_MODEL_ID[normalized] ?? // Check Claude / GPT-OSS substrings BEFORE the `gemini` substring so
1105
+ // a `gemini-claude-*` alias (which is a Claude route exposed under
1106
+ // a `gemini-` namespace) attributes to the non-gemini pool rather
1107
+ // than the gemini pool. Substring matching is required because the
1108
+ // alias IDs start with `gemini-` but contain `claude`.
1109
+ (normalized.includes("claude") || normalized.includes("gpt-oss") ? "non-gemini" : normalized.startsWith("gemini") || normalized.startsWith("tab_") ? "gemini" : void 0);
1117
1110
  }
1118
1111
 
1119
1112
  // ../core/dist/account-manager.js
@@ -1946,7 +1939,7 @@ async function ensureProjectContext(auth) {
1946
1939
  const effectiveProjectId = parts.projectId || fallbackProjectId;
1947
1940
  return { auth, effectiveProjectId };
1948
1941
  }
1949
- const persistManagedProject = async (managedProjectId) => {
1942
+ const persistManagedProject = async (managedProjectId, capturedTier) => {
1950
1943
  const updatedAuth = {
1951
1944
  ...auth,
1952
1945
  refresh: formatRefreshParts({
@@ -1955,12 +1948,23 @@ async function ensureProjectContext(auth) {
1955
1948
  managedProjectId
1956
1949
  })
1957
1950
  };
1958
- return { auth: updatedAuth, effectiveProjectId: managedProjectId };
1951
+ return {
1952
+ auth: updatedAuth,
1953
+ effectiveProjectId: managedProjectId,
1954
+ capturedTier
1955
+ };
1959
1956
  };
1960
1957
  const loadPayload = await loadManagedProject(accessToken, parts.projectId ?? fallbackProjectId);
1958
+ const capturedTierId = loadPayload?.currentTier?.id;
1959
+ const paidTierId = typeof loadPayload?.paidTier === "string" ? loadPayload.paidTier : loadPayload?.paidTier?.id;
1960
+ const tierFromPayload = capturedTierId ? {
1961
+ id: capturedTierId,
1962
+ ...paidTierId ? { paidId: paidTierId } : {},
1963
+ capturedAt: Date.now()
1964
+ } : void 0;
1961
1965
  const resolvedManagedProjectId = extractManagedProjectId(loadPayload);
1962
1966
  if (resolvedManagedProjectId) {
1963
- return persistManagedProject(resolvedManagedProjectId);
1967
+ return persistManagedProject(resolvedManagedProjectId, tierFromPayload);
1964
1968
  }
1965
1969
  const tierId = getDefaultTierId(loadPayload?.allowedTiers) ?? "free-tier";
1966
1970
  log3.debug("Auto-provisioning managed project", {
@@ -1972,7 +1976,7 @@ async function ensureProjectContext(auth) {
1972
1976
  log3.debug("Successfully provisioned managed project", {
1973
1977
  provisionedProjectId
1974
1978
  });
1975
- return persistManagedProject(provisionedProjectId);
1979
+ return persistManagedProject(provisionedProjectId, tierFromPayload);
1976
1980
  }
1977
1981
  log3.warn("Failed to provision managed project - account may not work correctly", {
1978
1982
  hasProjectId: !!parts.projectId
@@ -1981,9 +1985,17 @@ async function ensureProjectContext(auth) {
1981
1985
  provisionFailedKeys.add(cacheKey);
1982
1986
  }
1983
1987
  if (parts.projectId) {
1984
- return { auth, effectiveProjectId: parts.projectId };
1988
+ return {
1989
+ auth,
1990
+ effectiveProjectId: parts.projectId,
1991
+ capturedTier: tierFromPayload
1992
+ };
1985
1993
  }
1986
- return { auth, effectiveProjectId: fallbackProjectId };
1994
+ return {
1995
+ auth,
1996
+ effectiveProjectId: fallbackProjectId,
1997
+ capturedTier: tierFromPayload
1998
+ };
1987
1999
  };
1988
2000
  if (!cacheKey) {
1989
2001
  return resolveContext();
@@ -2225,15 +2237,13 @@ function classifyQuotaGroup(modelName, displayName) {
2225
2237
  return registryGroup;
2226
2238
  }
2227
2239
  const combined = `${modelName} ${displayName ?? ""}`.toLowerCase();
2228
- if (combined.includes("claude")) {
2229
- return "claude";
2240
+ if (combined.includes("claude") || combined.includes("gpt-oss")) {
2241
+ return "non-gemini";
2230
2242
  }
2231
- const isGemini3 = combined.includes("gemini-3") || combined.includes("gemini 3");
2232
- if (!isGemini3) {
2233
- return null;
2243
+ if (combined.includes("gemini")) {
2244
+ return "gemini";
2234
2245
  }
2235
- const family = getModelFamily(modelName);
2236
- return family === "gemini-flash" ? "gemini-flash" : "gemini-pro";
2246
+ return null;
2237
2247
  }
2238
2248
  function normalizeRemainingFraction(value) {
2239
2249
  if (typeof value !== "number" || !Number.isFinite(value)) {
@@ -2301,6 +2311,147 @@ function aggregateQuota(models) {
2301
2311
  perModel.sort((a, b) => a.modelId.localeCompare(b.modelId));
2302
2312
  return { groups, perModel, modelCount: totalCount };
2303
2313
  }
2314
+ function mostConstrainedWindow(windows) {
2315
+ if (windows.length === 0)
2316
+ return void 0;
2317
+ let best = windows[0];
2318
+ for (let i = 1; i < windows.length; i++) {
2319
+ if (windows[i].remainingFraction < best.remainingFraction) {
2320
+ best = windows[i];
2321
+ }
2322
+ }
2323
+ return {
2324
+ remainingFraction: best.remainingFraction,
2325
+ resetTime: best.resetTime
2326
+ };
2327
+ }
2328
+ function poolForBucketId(bucketId) {
2329
+ if (bucketId.startsWith("gemini-"))
2330
+ return "gemini";
2331
+ if (bucketId.startsWith("3p-"))
2332
+ return "non-gemini";
2333
+ return null;
2334
+ }
2335
+ function parseDescriptionModelCount(description) {
2336
+ const prefixMatch = description.match(/^[^:]+:\s*/);
2337
+ const payload = prefixMatch ? description.slice(prefixMatch[0].length) : description;
2338
+ if (!payload)
2339
+ return 0;
2340
+ const entries = payload.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
2341
+ return entries.length;
2342
+ }
2343
+ function windowDurationMs(window) {
2344
+ switch (window) {
2345
+ case "5h":
2346
+ return 5 * 60 * 60 * 1e3;
2347
+ case "weekly":
2348
+ return 7 * 24 * 60 * 60 * 1e3;
2349
+ default:
2350
+ return Number.MAX_SAFE_INTEGER;
2351
+ }
2352
+ }
2353
+ function aggregateQuotaSummary(response) {
2354
+ const groups = {};
2355
+ let totalCount = 0;
2356
+ for (const group of response.groups) {
2357
+ const windows = [];
2358
+ for (const bucket of group.buckets) {
2359
+ const pool2 = poolForBucketId(bucket.bucketId);
2360
+ if (!pool2)
2361
+ continue;
2362
+ windows.push({
2363
+ window: bucket.window,
2364
+ remainingFraction: normalizeRemainingFraction(bucket.remainingFraction),
2365
+ resetTime: bucket.resetTime
2366
+ });
2367
+ }
2368
+ if (windows.length === 0)
2369
+ continue;
2370
+ windows.sort((a, b) => windowDurationMs(a.window) - windowDurationMs(b.window));
2371
+ const constrained = mostConstrainedWindow(windows);
2372
+ const recognizedBucket = group.buckets.find((bucket) => poolForBucketId(bucket.bucketId));
2373
+ if (!recognizedBucket)
2374
+ continue;
2375
+ const pool = poolForBucketId(recognizedBucket.bucketId);
2376
+ if (!pool || !constrained)
2377
+ continue;
2378
+ const modelCount = group.description ? parseDescriptionModelCount(group.description) : 0;
2379
+ groups[pool] = {
2380
+ remainingFraction: constrained.remainingFraction,
2381
+ resetTime: constrained.resetTime,
2382
+ modelCount,
2383
+ windows
2384
+ };
2385
+ totalCount += modelCount;
2386
+ }
2387
+ return { groups, modelCount: totalCount };
2388
+ }
2389
+ async function fetchQuotaSummary(options) {
2390
+ const timeoutMs = options.timeoutMs ?? QUOTA_MANAGER_DEFAULT_TIMEOUT_MS;
2391
+ const userAgent = options.userAgent ?? buildAntigravityHarnessUserAgent();
2392
+ const transport = options.fetchVia ?? defaultTransport;
2393
+ const errors = [];
2394
+ if (options.endpoints.length === 0) {
2395
+ throw new Error("No endpoints configured for fetchQuotaSummary");
2396
+ }
2397
+ const tryBody = async (endpoint, projectId) => {
2398
+ const body = { project: projectId };
2399
+ try {
2400
+ const response = await transport(`${endpoint}/v1internal:retrieveUserQuotaSummary`, {
2401
+ method: "POST",
2402
+ headers: {
2403
+ "User-Agent": userAgent,
2404
+ Authorization: `Bearer ${options.accessToken}`,
2405
+ "Content-Type": "application/json",
2406
+ "Accept-Encoding": "gzip"
2407
+ },
2408
+ body: JSON.stringify(body)
2409
+ }, { timeoutMs });
2410
+ if (response.ok) {
2411
+ return {
2412
+ ok: true,
2413
+ summary: await response.json()
2414
+ };
2415
+ }
2416
+ const status = response.status;
2417
+ if (status === 403) {
2418
+ errors.push(`retrieveUserQuotaSummary 403 at ${endpoint} (project=${projectId.slice(0, 12)}\u2026)`);
2419
+ return { ok: false, reason: "403" };
2420
+ }
2421
+ if (status === 429 || status >= 500) {
2422
+ const message2 = await response.text().catch(() => "");
2423
+ errors.push(`retrieveUserQuotaSummary ${status} at ${endpoint}${message2 ? `: ${message2.trim().slice(0, 200)}` : ""}`);
2424
+ return { ok: false, reason: "transient" };
2425
+ }
2426
+ const message = await response.text().catch(() => "");
2427
+ errors.push(`retrieveUserQuotaSummary ${status} at ${endpoint}${message ? `: ${message.trim().slice(0, 200)}` : ""}`);
2428
+ return { ok: false, reason: "transient" };
2429
+ } catch (error) {
2430
+ errors.push(`retrieveUserQuotaSummary network error at ${endpoint}: ${error instanceof Error ? error.message : String(error)}`);
2431
+ return { ok: false, reason: "transient" };
2432
+ }
2433
+ };
2434
+ const primary = options.managedProjectId ?? options.projectId;
2435
+ let primaryGot403 = false;
2436
+ if (primary) {
2437
+ for (const endpoint of options.endpoints) {
2438
+ const result = await tryBody(endpoint, primary);
2439
+ if (result.ok)
2440
+ return { summary: result.summary };
2441
+ if (result.reason === "403")
2442
+ primaryGot403 = true;
2443
+ }
2444
+ }
2445
+ const fallbackId = primaryGot403 && options.managedProjectId && options.projectId && options.managedProjectId !== options.projectId ? options.projectId : void 0;
2446
+ if (fallbackId) {
2447
+ for (const endpoint of options.endpoints) {
2448
+ const result = await tryBody(endpoint, fallbackId);
2449
+ if (result.ok)
2450
+ return { summary: result.summary };
2451
+ }
2452
+ }
2453
+ throw new Error(errors.join("; ") || "fetchQuotaSummary failed: no project ID available");
2454
+ }
2304
2455
  function aggregateGeminiCliQuota(response) {
2305
2456
  const models = [];
2306
2457
  if (!response.buckets || response.buckets.length === 0) {
@@ -2383,10 +2534,12 @@ async function fetchAvailableModels(options) {
2383
2534
  async function fetchGeminiCliQuota(options) {
2384
2535
  const timeoutMs = options.timeoutMs ?? QUOTA_MANAGER_DEFAULT_TIMEOUT_MS;
2385
2536
  const userAgent = options.userAgent ?? buildAntigravityHarnessUserAgent();
2537
+ const transport = options.fetchVia ?? defaultTransport;
2538
+ const errors = [];
2386
2539
  for (const endpoint of options.endpoints) {
2387
2540
  const body = options.projectId ? { project: options.projectId } : {};
2388
2541
  try {
2389
- const response = await defaultTransport(`${endpoint}/v1internal:retrieveUserQuota`, {
2542
+ const response = await transport(`${endpoint}/v1internal:retrieveUserQuota`, {
2390
2543
  method: "POST",
2391
2544
  headers: {
2392
2545
  Authorization: `Bearer ${options.accessToken}`,
@@ -2400,18 +2553,26 @@ async function fetchGeminiCliQuota(options) {
2400
2553
  }
2401
2554
  const status = response.status;
2402
2555
  if (status === 429 || status >= 500) {
2556
+ errors.push(`fetchGeminiCliQuota ${status} at ${endpoint}`);
2403
2557
  continue;
2404
2558
  }
2405
2559
  return { buckets: [] };
2406
- } catch {
2560
+ } catch (error) {
2561
+ errors.push(error instanceof Error ? error.message : String(error));
2407
2562
  }
2408
2563
  }
2564
+ if (errors.length > 0) {
2565
+ throw new Error(errors.join("; ") || "fetchGeminiCliQuota failed");
2566
+ }
2409
2567
  return { buckets: [] };
2410
2568
  }
2411
2569
  async function defaultTransport(url, init, options) {
2412
2570
  return fetchWithActiveTimeout(url, init, { timeoutMs: options.timeoutMs });
2413
2571
  }
2414
2572
 
2573
+ // ../core/dist/transform/model-resolver.js
2574
+ var MODEL_ALIASES = getResolverAliasMap();
2575
+
2415
2576
  // src/plugin/oauth-login.ts
2416
2577
  function expectedState(authorizationUrl) {
2417
2578
  try {
@@ -3127,6 +3288,32 @@ async function checkAccountsQuotaStandalone(accounts, options) {
3127
3288
  makeFetchAccountQuota(void 0, ANTIGRAVITY_PROVIDER_ID)
3128
3289
  );
3129
3290
  }
3291
+ async function fetchLegacyModelsFallback(options) {
3292
+ try {
3293
+ const modelsResponse = await fetchAvailableModels({
3294
+ accessToken: options.accessToken,
3295
+ projectId: options.projectId,
3296
+ endpoints: ANTIGRAVITY_ENDPOINT_FALLBACKS,
3297
+ userAgent: buildAntigravityHarnessUserAgent(),
3298
+ timeoutMs: 1e4,
3299
+ ...options.fetchVia ? { fetchVia: options.fetchVia } : {}
3300
+ });
3301
+ if (modelsResponse.models) {
3302
+ return aggregateQuota(modelsResponse.models);
3303
+ }
3304
+ return {
3305
+ groups: {},
3306
+ modelCount: 0,
3307
+ error: "Failed to fetch Antigravity quota (legacy fallback)"
3308
+ };
3309
+ } catch {
3310
+ return {
3311
+ groups: {},
3312
+ modelCount: 0,
3313
+ error: "Failed to fetch Antigravity quota"
3314
+ };
3315
+ }
3316
+ }
3130
3317
  function makeFetchAccountQuota(client, providerId, fetchVia) {
3131
3318
  return async (account, signal) => {
3132
3319
  const index = 0;
@@ -3166,49 +3353,79 @@ function makeFetchAccountQuota(client, providerId, fetchVia) {
3166
3353
  }
3167
3354
  const projectContext = await ensureProjectContext(auth);
3168
3355
  auth = projectContext.auth;
3169
- const updatedAccount = applyAccountUpdates(account, auth);
3356
+ const updatedAccount = applyAccountUpdates(
3357
+ account,
3358
+ auth,
3359
+ projectContext.capturedTier
3360
+ );
3170
3361
  if (rotatedRefresh && client) {
3171
3362
  await persistRotatedRefresh(client, providerId, auth).catch(() => {
3172
3363
  });
3173
3364
  }
3174
- const [antigravityResponse, geminiCliResponse] = await Promise.all([
3175
- fetchAvailableModels({
3176
- accessToken: auth.access ?? "",
3177
- projectId: projectContext.effectiveProjectId,
3178
- endpoints: ANTIGRAVITY_ENDPOINT_FALLBACKS,
3179
- userAgent: buildAntigravityHarnessUserAgent(),
3180
- timeoutMs: 1e4,
3181
- ...fetchVia ? { fetchVia } : {}
3182
- }).catch(() => ({ models: void 0 })),
3183
- fetchGeminiCliQuota({
3184
- accessToken: auth.access ?? "",
3185
- projectId: projectContext.effectiveProjectId,
3186
- endpoints: ANTIGRAVITY_ENDPOINT_FALLBACKS,
3187
- userAgent: buildGeminiCliUserAgent(),
3188
- timeoutMs: 1e4,
3189
- ...fetchVia ? { fetchVia } : {}
3190
- })
3191
- ]);
3192
3365
  let quotaResult;
3193
- if (antigravityResponse.models === void 0) {
3194
- quotaResult = {
3195
- groups: {},
3196
- modelCount: 0,
3197
- error: "Failed to fetch Antigravity quota"
3198
- };
3199
- } else {
3200
- quotaResult = aggregateQuota(antigravityResponse.models);
3201
- }
3366
+ let fellBackToLegacy = false;
3367
+ const authParts = parseRefreshParts(auth.refresh);
3368
+ const managedProjectId = authParts.managedProjectId ?? account.managedProjectId;
3369
+ const fetchSummaryPayload = (async () => {
3370
+ try {
3371
+ const summaryResult = await fetchQuotaSummary({
3372
+ accessToken: auth.access ?? "",
3373
+ managedProjectId,
3374
+ projectId: projectContext.effectiveProjectId,
3375
+ endpoints: ANTIGRAVITY_ENDPOINT_FALLBACKS,
3376
+ userAgent: buildAntigravityHarnessUserAgent(),
3377
+ timeoutMs: 1e4,
3378
+ ...fetchVia ? { fetchVia } : {}
3379
+ });
3380
+ return {
3381
+ result: aggregateQuotaSummary(summaryResult.summary),
3382
+ fellBackToLegacy: summaryResult.fellBackToLegacy ?? false
3383
+ };
3384
+ } catch {
3385
+ return {
3386
+ result: await fetchLegacyModelsFallback({
3387
+ accessToken: auth.access ?? "",
3388
+ projectId: projectContext.effectiveProjectId,
3389
+ fetchVia
3390
+ }),
3391
+ fellBackToLegacy: true
3392
+ };
3393
+ }
3394
+ })();
3395
+ let geminiCliFetchError;
3396
+ const fetchGeminiCliPayload = fetchGeminiCliQuota({
3397
+ accessToken: auth.access ?? "",
3398
+ projectId: projectContext.effectiveProjectId,
3399
+ endpoints: ANTIGRAVITY_ENDPOINT_FALLBACKS,
3400
+ userAgent: buildGeminiCliUserAgent(),
3401
+ timeoutMs: 1e4,
3402
+ ...fetchVia ? { fetchVia } : {}
3403
+ }).catch((error) => {
3404
+ geminiCliFetchError = error instanceof Error ? error.message : String(error);
3405
+ log7.debug("fetchGeminiCliQuota failed", { error: geminiCliFetchError });
3406
+ return { buckets: void 0 };
3407
+ });
3408
+ const [summary, geminiCliResponse] = await Promise.all([
3409
+ fetchSummaryPayload,
3410
+ fetchGeminiCliPayload
3411
+ ]);
3412
+ quotaResult = summary.result;
3413
+ fellBackToLegacy = summary.fellBackToLegacy;
3202
3414
  const geminiCliQuotaResult = aggregateGeminiCliQuota(geminiCliResponse);
3203
3415
  const annotated = geminiCliResponse.buckets === void 0 || geminiCliResponse.buckets.length === 0 ? {
3204
3416
  ...geminiCliQuotaResult,
3205
- error: geminiCliQuotaResult.models.length === 0 ? "No Gemini CLI quota available" : void 0
3417
+ error: (
3418
+ // A real fetch exception is a transient failure, not a
3419
+ // "no CLI configured" scenario — propagate the actual message.
3420
+ geminiCliFetchError ?? (geminiCliQuotaResult.models.length === 0 ? "No Gemini CLI quota available" : void 0)
3421
+ )
3206
3422
  } : geminiCliQuotaResult;
3207
3423
  for (const [family, groupQuota] of Object.entries(quotaResult.groups)) {
3208
3424
  const remainingPercent = (groupQuota.remainingFraction ?? 0) * 100;
3209
3425
  logQuotaStatus(account.email, index, remainingPercent, family);
3210
3426
  }
3211
- logQuotaFetch("complete", 1, "ok=1 errors=0");
3427
+ const legacyTag = fellBackToLegacy ? " legacy=1" : "";
3428
+ logQuotaFetch("complete", 1, `ok=1 errors=0${legacyTag}`);
3212
3429
  return {
3213
3430
  index,
3214
3431
  email: account.email,
@@ -3246,7 +3463,7 @@ function buildAuthFromAccount(account) {
3246
3463
  expires: void 0
3247
3464
  };
3248
3465
  }
3249
- function applyAccountUpdates(account, auth) {
3466
+ function applyAccountUpdates(account, auth, capturedTier) {
3250
3467
  const parts = parseRefreshParts(auth.refresh);
3251
3468
  if (!parts.refreshToken) {
3252
3469
  return void 0;
@@ -3255,9 +3472,19 @@ function applyAccountUpdates(account, auth) {
3255
3472
  ...account,
3256
3473
  refreshToken: parts.refreshToken,
3257
3474
  projectId: parts.projectId ?? account.projectId,
3258
- managedProjectId: parts.managedProjectId ?? account.managedProjectId
3475
+ managedProjectId: parts.managedProjectId ?? account.managedProjectId,
3476
+ // Persist the captured tier alongside the project-context write. Only
3477
+ // present when the loadCodeAssist payload returned a non-empty currentTier.id.
3478
+ ...capturedTier ? {
3479
+ capturedTierId: capturedTier.id,
3480
+ ...capturedTier.paidId !== void 0 ? { capturedPaidTierId: capturedTier.paidId } : {},
3481
+ capturedTierAt: capturedTier.capturedAt
3482
+ } : {}
3259
3483
  };
3260
- const changed = updated.refreshToken !== account.refreshToken || updated.projectId !== account.projectId || updated.managedProjectId !== account.managedProjectId;
3484
+ const changed = updated.refreshToken !== account.refreshToken || updated.projectId !== account.projectId || updated.managedProjectId !== account.managedProjectId || updated.capturedTierId !== account.capturedTierId || updated.capturedPaidTierId !== account.capturedPaidTierId || // capturedAt represents when the tier was LAST CONFIRMED, not when it
3485
+ // changed -- always update it on a successful observation so consumers
3486
+ // can gate staleness on that timestamp even when the id stays the same.
3487
+ capturedTier !== void 0 && updated.capturedTierAt !== account.capturedTierAt;
3261
3488
  return changed ? updated : void 0;
3262
3489
  }
3263
3490
  async function persistRotatedRefresh(client, providerId, auth) {