@cortexkit/opencode-antigravity-auth 3.0.1 → 3.0.2

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 (138) hide show
  1. package/dist/cli.js.map +2 -2
  2. package/dist/cli.meta.json +3 -3
  3. package/dist/index.d.ts +1 -1
  4. package/dist/index.js +1136 -928
  5. package/dist/index.js.map +4 -4
  6. package/dist/index.meta.json +107 -25
  7. package/dist/server.js +267 -37
  8. package/dist/server.js.map +4 -4
  9. package/dist/server.meta.json +1 -7313
  10. package/dist/src/cli.d.ts +1 -1
  11. package/dist/src/constants.d.ts +1 -1
  12. package/dist/src/ga/rpc/protocol.d.ts +1 -1
  13. package/dist/src/ga/server/index.d.ts +6 -6
  14. package/dist/src/ga/server/index.d.ts.map +1 -1
  15. package/dist/src/ga/server/index.js +29 -1
  16. package/dist/src/ga/server/index.js.map +1 -1
  17. package/dist/src/ga/tui/host-ga.d.ts +2 -2
  18. package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
  19. package/dist/src/hooks/auto-update-checker/index.d.ts +4 -4
  20. package/dist/src/plugin/account-access.d.ts +6 -6
  21. package/dist/src/plugin/account-command-oauth.d.ts +2 -2
  22. package/dist/src/plugin/accounts.d.ts +3 -3
  23. package/dist/src/plugin/auth-doctor.d.ts +2 -2
  24. package/dist/src/plugin/auth-drift.d.ts +2 -2
  25. package/dist/src/plugin/auth-loader.d.ts +8 -8
  26. package/dist/src/plugin/auth-loader.d.ts.map +1 -1
  27. package/dist/src/plugin/auth-loader.js +1 -0
  28. package/dist/src/plugin/auth-loader.js.map +1 -1
  29. package/dist/src/plugin/background-quota-refresh.d.ts +3 -3
  30. package/dist/src/plugin/background-vault-quota-refresh.d.ts +43 -0
  31. package/dist/src/plugin/background-vault-quota-refresh.d.ts.map +1 -0
  32. package/dist/src/plugin/background-vault-quota-refresh.js +155 -0
  33. package/dist/src/plugin/background-vault-quota-refresh.js.map +1 -0
  34. package/dist/src/plugin/cache/index.d.ts +1 -1
  35. package/dist/src/plugin/cache/signature-cache.d.ts +2 -2
  36. package/dist/src/plugin/cache.d.ts +6 -6
  37. package/dist/src/plugin/cli.d.ts +6 -6
  38. package/dist/src/plugin/command-apply.d.ts +1 -1
  39. package/dist/src/plugin/command-data.d.ts +6 -4
  40. package/dist/src/plugin/command-data.d.ts.map +1 -1
  41. package/dist/src/plugin/command-data.js +13 -34
  42. package/dist/src/plugin/command-data.js.map +1 -1
  43. package/dist/src/plugin/commands.d.ts +4 -4
  44. package/dist/src/plugin/config/index.d.ts +2 -2
  45. package/dist/src/plugin/config/loader.d.ts +2 -2
  46. package/dist/src/plugin/config/models.d.ts +2 -2
  47. package/dist/src/plugin/config/writer.d.ts +2 -2
  48. package/dist/src/plugin/core/streaming/index.d.ts +2 -2
  49. package/dist/src/plugin/core/streaming/transformer.d.ts +1 -1
  50. package/dist/src/plugin/debug.d.ts +1 -1
  51. package/dist/src/plugin/dependencies.d.ts +3 -3
  52. package/dist/src/plugin/event-handler.d.ts +3 -3
  53. package/dist/src/plugin/fetch-interceptor.d.ts +10 -10
  54. package/dist/src/plugin/fetch-routing.d.ts +3 -3
  55. package/dist/src/plugin/ga-location-services.d.ts +10 -10
  56. package/dist/src/plugin/google-search-tool.d.ts +1 -1
  57. package/dist/src/plugin/index.d.ts +8 -6
  58. package/dist/src/plugin/index.d.ts.map +1 -1
  59. package/dist/src/plugin/index.js +41 -18
  60. package/dist/src/plugin/index.js.map +1 -1
  61. package/dist/src/plugin/killswitch.d.ts +1 -1
  62. package/dist/src/plugin/lifecycle.d.ts +2 -2
  63. package/dist/src/plugin/logger.d.ts +2 -2
  64. package/dist/src/plugin/oauth-login.d.ts +1 -1
  65. package/dist/src/plugin/oauth-methods.d.ts +9 -9
  66. package/dist/src/plugin/operator-settings.d.ts +3 -3
  67. package/dist/src/plugin/persist-account-pool.d.ts +1 -1
  68. package/dist/src/plugin/quota.d.ts +5 -4
  69. package/dist/src/plugin/quota.d.ts.map +1 -1
  70. package/dist/src/plugin/quota.js +1 -0
  71. package/dist/src/plugin/quota.js.map +1 -1
  72. package/dist/src/plugin/recovery/index.d.ts +3 -3
  73. package/dist/src/plugin/recovery/storage.d.ts +1 -1
  74. package/dist/src/plugin/recovery.d.ts +3 -3
  75. package/dist/src/plugin/refresh-queue.d.ts +3 -3
  76. package/dist/src/plugin/request-helpers.d.ts +1 -1
  77. package/dist/src/plugin/request.d.ts +9 -9
  78. package/dist/src/plugin/session-context.d.ts +1 -1
  79. package/dist/src/plugin/shared/request-services.d.ts +9 -9
  80. package/dist/src/plugin/shared/runtime.d.ts +11 -11
  81. package/dist/src/plugin/shared/source-sidebar.d.ts +1 -1
  82. package/dist/src/plugin/shared/vault-menu.d.ts +1 -1
  83. package/dist/src/plugin/shared/vault-request-credentials.d.ts +3 -1
  84. package/dist/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  85. package/dist/src/plugin/shared/vault-request-credentials.js +1 -1
  86. package/dist/src/plugin/shared/vault-request-credentials.js.map +1 -1
  87. package/dist/src/plugin/stores/signature-store.d.ts +1 -1
  88. package/dist/src/plugin/token.d.ts +1 -1
  89. package/dist/src/plugin/ui/auth-menu.d.ts +4 -4
  90. package/dist/src/plugin/ui/model-status.d.ts +3 -3
  91. package/dist/src/plugin/ui/quota-status.d.ts +2 -2
  92. package/dist/src/plugin/vault-activation.d.ts +1 -1
  93. package/dist/src/plugin/vault-quota-refresh.d.ts +7 -0
  94. package/dist/src/plugin/vault-quota-refresh.d.ts.map +1 -0
  95. package/dist/src/plugin/vault-quota-refresh.js +12 -0
  96. package/dist/src/plugin/vault-quota-refresh.js.map +1 -0
  97. package/dist/src/plugin/vault-sidebar-projection.d.ts +27 -0
  98. package/dist/src/plugin/vault-sidebar-projection.d.ts.map +1 -0
  99. package/dist/src/plugin/vault-sidebar-projection.js +34 -0
  100. package/dist/src/plugin/vault-sidebar-projection.js.map +1 -0
  101. package/dist/src/rpc/notifications.d.ts +1 -1
  102. package/dist/src/rpc/rpc-server.d.ts +1 -1
  103. package/dist/src/server.d.ts +3 -3
  104. package/dist/src/sidebar-state.d.ts +20 -13
  105. package/dist/src/sidebar-state.d.ts.map +1 -1
  106. package/dist/src/sidebar-state.js +20 -10
  107. package/dist/src/sidebar-state.js.map +1 -1
  108. package/dist/src/tui/command-dialogs.d.ts +1 -1
  109. package/dist/src/tui/host-v1.d.ts +3 -3
  110. package/dist/src/tui.d.ts +3 -3
  111. package/dist/src/tui.js +1 -1
  112. package/dist/tui-build-map.json +6 -6
  113. package/dist/types/src/ga/server/index.d.ts.map +1 -1
  114. package/dist/types/src/plugin/auth-loader.d.ts.map +1 -1
  115. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts +43 -0
  116. package/dist/types/src/plugin/background-vault-quota-refresh.d.ts.map +1 -0
  117. package/dist/types/src/plugin/command-data.d.ts +6 -4
  118. package/dist/types/src/plugin/command-data.d.ts.map +1 -1
  119. package/dist/types/src/plugin/index.d.ts +2 -0
  120. package/dist/types/src/plugin/index.d.ts.map +1 -1
  121. package/dist/types/src/plugin/quota.d.ts +1 -0
  122. package/dist/types/src/plugin/quota.d.ts.map +1 -1
  123. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts +2 -0
  124. package/dist/types/src/plugin/shared/vault-request-credentials.d.ts.map +1 -1
  125. package/dist/types/src/plugin/vault-quota-refresh.d.ts +7 -0
  126. package/dist/types/src/plugin/vault-quota-refresh.d.ts.map +1 -0
  127. package/dist/types/src/plugin/vault-sidebar-projection.d.ts +27 -0
  128. package/dist/types/src/plugin/vault-sidebar-projection.d.ts.map +1 -0
  129. package/dist/types/src/sidebar-state.d.ts +20 -13
  130. package/dist/types/src/sidebar-state.d.ts.map +1 -1
  131. package/package.json +2 -2
  132. package/src/plugin/command-data.ts +19 -47
  133. package/src/sidebar-state.ts +34 -15
  134. package/src/tui-compiled/sidebar-state.js +12 -5
  135. package/src/tui-compiled/tui.js +1 -1
  136. package/src/tui-raw/sidebar-state.js +12 -5
  137. package/src/tui-raw/tui.tsx +1 -1
  138. package/src/tui.tsx +1 -1
package/dist/index.js CHANGED
@@ -27149,7 +27149,7 @@ async function replacePoolLogins(repository, results, options = {}) {
27149
27149
  }
27150
27150
 
27151
27151
  // src/sidebar-state.ts
27152
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "node:fs";
27152
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2 } from "node:fs";
27153
27153
  import { homedir as homedir3 } from "node:os";
27154
27154
  import { dirname as dirname14, join as join12 } from "node:path";
27155
27155
 
@@ -27173,7 +27173,7 @@ if (xdgConfig) {
27173
27173
  }
27174
27174
 
27175
27175
  // src/sidebar-state.ts
27176
- var SIDEBAR_STATE_VERSION = 1;
27176
+ var SIDEBAR_STATE_VERSION = 2;
27177
27177
  var DEFAULT_SIDEBAR_STATE = {
27178
27178
  version: SIDEBAR_STATE_VERSION,
27179
27179
  checkedAt: 0,
@@ -27234,7 +27234,7 @@ function normalizeSidebarState(input2) {
27234
27234
  }
27235
27235
  const record2 = input2;
27236
27236
  const version = record2.version;
27237
- if (version !== SIDEBAR_STATE_VERSION) {
27237
+ if (version !== 1 && version !== SIDEBAR_STATE_VERSION) {
27238
27238
  return {
27239
27239
  ...DEFAULT_SIDEBAR_STATE,
27240
27240
  lastError: `unsupported-version:${stringifySafe(version)}`
@@ -27289,6 +27289,7 @@ function normalizeAccount(input2) {
27289
27289
  return {
27290
27290
  id,
27291
27291
  label,
27292
+ ...typeof record2.email === "string" ? { email: record2.email } : {},
27292
27293
  enabled,
27293
27294
  health,
27294
27295
  current,
@@ -27310,6 +27311,7 @@ function normalizeQuota(input2) {
27310
27311
  if (!isObject(input2)) return null;
27311
27312
  const record2 = input2;
27312
27313
  const remaining = toFiniteNumber(record2.remainingPercent);
27314
+ const fraction = toFiniteNumber(record2.remainingFraction);
27313
27315
  if (remaining === null) return null;
27314
27316
  const resetAt = toFiniteNumber(record2.resetAt) ?? void 0;
27315
27317
  const checkedAt = toFiniteNumber(record2.checkedAt) ?? void 0;
@@ -27323,10 +27325,12 @@ function normalizeQuota(input2) {
27323
27325
  );
27324
27326
  const ra = toFiniteNumber(w.resetAt);
27325
27327
  const ca = toFiniteNumber(w.checkedAt);
27328
+ const rf = toFiniteNumber(w.remainingFraction);
27326
27329
  if (win !== "weekly" && win !== "5h" || rp === null) return null;
27327
27330
  return {
27328
27331
  window: win,
27329
27332
  remainingPercent: clampNumber(rp, 0, 100),
27333
+ ...rf !== null ? { remainingFraction: clampNumber(rf, 0, 1) } : {},
27330
27334
  resetAt: ra ?? void 0,
27331
27335
  ...ca !== null && ca >= 0 ? { checkedAt: ca } : {}
27332
27336
  };
@@ -27335,6 +27339,7 @@ function normalizeQuota(input2) {
27335
27339
  }
27336
27340
  return {
27337
27341
  remainingPercent: clampNumber(remaining, 0, 100),
27342
+ ...fraction !== null ? { remainingFraction: clampNumber(fraction, 0, 1) } : {},
27338
27343
  resetAt,
27339
27344
  ...checkedAt !== void 0 ? { checkedAt } : {},
27340
27345
  windows
@@ -27386,6 +27391,7 @@ function stringifySafe(value) {
27386
27391
  }
27387
27392
  function ensureSidebarStateDir(path5 = getSidebarStateFile()) {
27388
27393
  mkdirSync3(dirname14(path5), { recursive: true, mode: SIDEBAR_STATE_DIR_MODE });
27394
+ chmodSync3(dirname14(path5), SIDEBAR_STATE_DIR_MODE);
27389
27395
  }
27390
27396
  function pruneActiveRouting(map, now) {
27391
27397
  const cutoff = now - ACTIVE_ROUTING_MAX_AGE_MS;
@@ -27464,6 +27470,7 @@ function projectQuotaPoolForSidebar(source, checkedAt) {
27464
27470
  0,
27465
27471
  100
27466
27472
  ),
27473
+ remainingFraction: clampNumber(w.remainingFraction, 0, 1),
27467
27474
  resetAt: typeof w.resetTime === "string" && w.resetTime.length > 0 ? (() => {
27468
27475
  const parsed = Date.parse(w.resetTime);
27469
27476
  return Number.isFinite(parsed) ? parsed : void 0;
@@ -27471,6 +27478,7 @@ function projectQuotaPoolForSidebar(source, checkedAt) {
27471
27478
  })) : void 0;
27472
27479
  return {
27473
27480
  remainingPercent,
27481
+ remainingFraction: clampNumber(fraction, 0, 1),
27474
27482
  resetAt,
27475
27483
  windows,
27476
27484
  ...readingAt !== void 0 ? { checkedAt: readingAt } : {}
@@ -27507,6 +27515,7 @@ function redactAccountForSidebar(source) {
27507
27515
  return {
27508
27516
  id,
27509
27517
  label,
27518
+ ...typeof source.email === "string" ? { email: source.email } : {},
27510
27519
  enabled,
27511
27520
  health,
27512
27521
  current,
@@ -28108,6 +28117,7 @@ async function pushSidebarQuotaSnapshot(getAccounts, backoffUntil = 0, getActive
28108
28117
  accounts.map((entry) => ({
28109
28118
  index: entry.index,
28110
28119
  label: entry.label,
28120
+ email: entry.email,
28111
28121
  enabled: entry.enabled,
28112
28122
  current: activeByFamily ? isAccountCurrent(entry.index, activeByFamily) : false,
28113
28123
  coolingDownUntil: entry.coolingDownUntil,
@@ -35826,7 +35836,7 @@ function clearSignatureCache(sessionId) {
35826
35836
  }
35827
35837
 
35828
35838
  // src/plugin/image-saver.ts
35829
- import { chmodSync as chmodSync3, existsSync as existsSync11, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
35839
+ import { chmodSync as chmodSync4, existsSync as existsSync11, mkdirSync as mkdirSync6, writeFileSync as writeFileSync6 } from "node:fs";
35830
35840
  import { homedir as homedir9 } from "node:os";
35831
35841
  import { join as join22 } from "node:path";
35832
35842
  function getImageOutputDir(outputDir) {
@@ -35834,7 +35844,7 @@ function getImageOutputDir(outputDir) {
35834
35844
  if (!existsSync11(resolved)) {
35835
35845
  mkdirSync6(resolved, { recursive: true, mode: 448 });
35836
35846
  }
35837
- chmodSync3(resolved, 448);
35847
+ chmodSync4(resolved, 448);
35838
35848
  return resolved;
35839
35849
  }
35840
35850
  function generateImageFilename(mimeType) {
@@ -35855,7 +35865,7 @@ function saveImageToDisk(base64Data, mimeType, outputDir) {
35855
35865
  const resolvedOutputDir = getImageOutputDir(outputDir);
35856
35866
  const filePath = join22(resolvedOutputDir, generateImageFilename(mimeType));
35857
35867
  writeFileSync6(filePath, Buffer.from(base64Data, "base64"), { mode: 384 });
35858
- chmodSync3(filePath, 384);
35868
+ chmodSync4(filePath, 384);
35859
35869
  return filePath;
35860
35870
  } catch (error) {
35861
35871
  console.error("[image-saver] Failed to save image:", error);
@@ -41008,6 +41018,7 @@ function createAuthLoader({
41008
41018
  return {
41009
41019
  index: entry.index,
41010
41020
  label: entry.label,
41021
+ email: entry.email,
41011
41022
  enabled: entry.enabled,
41012
41023
  current: isAccountCurrent(entry.index, activeByFamily),
41013
41024
  coolingDownUntil: entry.coolingDownUntil,
@@ -41540,494 +41551,1017 @@ var BackgroundQuotaRefresh = class {
41540
41551
  }
41541
41552
  };
41542
41553
 
41543
- // src/plugin/operator-settings.ts
41544
- import { createHash as createHash18 } from "node:crypto";
41545
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
41546
- import { z as z4 } from "zod";
41547
-
41548
- // src/plugin/config/operator-settings-schema.ts
41549
- import { z as z2 } from "zod";
41550
- var OperatorSettingsSchema = z2.object({
41551
- routing: z2.object({
41552
- cli_first: z2.boolean(),
41553
- quota_style_fallback: z2.boolean()
41554
- }),
41555
- killswitch: z2.object({
41556
- enabled: z2.boolean(),
41557
- minimum_remaining_percent: z2.number().min(0).max(100),
41558
- accounts: z2.record(z2.string(), z2.number().min(0).max(100)).optional()
41559
- }),
41560
- log_level: z2.enum(["error", "warn", "info", "debug", "trace"])
41561
- });
41562
- function emptyOperatorSettings() {
41554
+ // src/plugin/shared/vault-request-credentials.ts
41555
+ function vaultAccountRows(routes) {
41556
+ return routes.map((route, index) => ({
41557
+ index,
41558
+ enabled: true,
41559
+ lastUsed: 0,
41560
+ rateLimitResetTimes: {},
41561
+ touchedForQuota: {},
41562
+ route,
41563
+ ...route.email !== void 0 ? { email: route.email } : {}
41564
+ }));
41565
+ }
41566
+ function vaultAccountRowKey(row) {
41567
+ return JSON.stringify([
41568
+ row.route.routeId,
41569
+ row.route.credentialId,
41570
+ row.route.accountIdentity
41571
+ ]);
41572
+ }
41573
+ function refreshVaultAccountRow(prior, fresh) {
41574
+ prior.route = fresh.route;
41575
+ if (fresh.email === void 0) delete prior.email;
41576
+ else prior.email = fresh.email;
41577
+ }
41578
+ var VaultSendGrant = class {
41579
+ credentialId;
41580
+ accountIdentity;
41581
+ recordVersion;
41582
+ projectId;
41583
+ report401;
41584
+ constructor(admission, report) {
41585
+ Object.defineProperty(this, "accessToken", {
41586
+ value: admission.accessToken,
41587
+ enumerable: false
41588
+ });
41589
+ this.credentialId = admission.credentialId;
41590
+ this.accountIdentity = admission.accountIdentity;
41591
+ this.recordVersion = admission.recordVersion;
41592
+ this.projectId = admission.projectId;
41593
+ this.report401 = report;
41594
+ }
41595
+ };
41596
+ function createVaultRequestCredentials(source, onAdmitted) {
41563
41597
  return {
41564
- routing: { cli_first: false, quota_style_fallback: false },
41565
- killswitch: { enabled: false, minimum_remaining_percent: 5 },
41566
- log_level: "info"
41598
+ domain: "vault",
41599
+ async admit({ account, signal }) {
41600
+ const admission = await source.admit(account.route, signal);
41601
+ if (onAdmitted)
41602
+ onAdmitted.admitted(account, onAdmitted.source.attribution(admission));
41603
+ return new VaultSendGrant(
41604
+ admission,
41605
+ (status) => source.reportServedStatus(admission, status)
41606
+ );
41607
+ }
41567
41608
  };
41568
41609
  }
41569
-
41570
- // src/plugin/config/writer.ts
41571
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
41572
- import { z as z3 } from "zod";
41573
- async function writeOperatorConfig(options) {
41574
- const target = existsSync13(options.projectConfigPath) ? options.projectConfigPath : options.userConfigPath;
41575
- await writeOperatorConfigAt(target, options.operator);
41610
+ function present4(value) {
41611
+ return value === null ? void 0 : value;
41576
41612
  }
41577
- async function writeOperatorConfigAt(target, settings) {
41578
- const operator = OperatorSettingsSchema.parse(settings);
41579
- const lock = await acquireFencedFileLock({
41580
- path: target,
41581
- name: "antigravity-operator",
41582
- ttlMs: 5e3,
41583
- renew: false
41584
- });
41585
- if (!lock) {
41586
- throw new Error(
41587
- `Could not acquire operator-config lock at ${target} (already held by another writer).`
41588
- );
41589
- }
41590
- try {
41591
- const existing = readExistingConfig(target);
41592
- const merged = mergeOperator(existing, operator);
41593
- await writeJsonAtomic(target, merged);
41594
- } finally {
41595
- await lock.release().catch(() => {
41596
- });
41613
+ function storedResets(resets) {
41614
+ const stored = {};
41615
+ for (const [key, at] of Object.entries(resets))
41616
+ if (typeof at === "number") stored[key] = at;
41617
+ return stored;
41618
+ }
41619
+ function cachedQuotaOf(quota) {
41620
+ const groups = quota.cachedQuota;
41621
+ if (groups == null) return void 0;
41622
+ const out = {};
41623
+ for (const name of ["gemini", "non-gemini"]) {
41624
+ const group = groups[name];
41625
+ if (group === void 0) continue;
41626
+ out[name] = {
41627
+ modelCount: group.modelCount,
41628
+ ...group.remainingFraction != null ? { remainingFraction: group.remainingFraction } : {},
41629
+ ...group.resetTime != null ? { resetTime: group.resetTime } : {},
41630
+ ...group.windows != null ? {
41631
+ windows: group.windows.map((window) => ({
41632
+ window: window.window,
41633
+ remainingFraction: window.remainingFraction,
41634
+ resetTime: window.resetTime
41635
+ }))
41636
+ } : {}
41637
+ };
41597
41638
  }
41639
+ return out;
41598
41640
  }
41599
- function readExistingConfig(target) {
41600
- if (!existsSync13(target)) return {};
41601
- try {
41602
- const raw = readFileSync11(target, "utf-8");
41603
- const parsed = JSON.parse(raw);
41604
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
41605
- return parsed;
41606
- }
41607
- return {};
41608
- } catch {
41609
- return {};
41641
+ function durableSelectionOf(row) {
41642
+ return {
41643
+ lastUsed: row.lastUsed,
41644
+ ...row.lastSwitchReason !== void 0 ? { lastSwitchReason: row.lastSwitchReason } : {},
41645
+ rateLimitResetTimes: storedResets(row.rateLimitResetTimes),
41646
+ ...row.coolingDownUntil !== void 0 ? { coolingDownUntil: row.coolingDownUntil } : {},
41647
+ ...row.cooldownReason !== void 0 ? { cooldownReason: row.cooldownReason } : {},
41648
+ ...row.fingerprint !== void 0 ? { fingerprint: structuredClone(row.fingerprint) } : {},
41649
+ ...row.fingerprintHistory !== void 0 ? { fingerprintHistory: structuredClone(row.fingerprintHistory) } : {},
41650
+ ...row.verificationRequired !== void 0 ? { verificationRequired: row.verificationRequired } : {},
41651
+ ...row.verificationRequiredAt !== void 0 ? { verificationRequiredAt: row.verificationRequiredAt } : {},
41652
+ ...row.verificationRequiredReason !== void 0 ? { verificationRequiredReason: row.verificationRequiredReason } : {},
41653
+ ...row.verificationUrl !== void 0 ? { verificationUrl: row.verificationUrl } : {},
41654
+ ...row.accountIneligible !== void 0 ? { accountIneligible: row.accountIneligible } : {},
41655
+ ...row.accountIneligibleAt !== void 0 ? { accountIneligibleAt: row.accountIneligibleAt } : {},
41656
+ ...row.accountIneligibleReason !== void 0 ? { accountIneligibleReason: row.accountIneligibleReason } : {},
41657
+ ...row.eligibilityStateUpdatedAt !== void 0 ? { eligibilityStateUpdatedAt: row.eligibilityStateUpdatedAt } : {},
41658
+ ...row.dailyRequestCounts !== void 0 ? { dailyRequestCounts: { ...row.dailyRequestCounts } } : {}
41659
+ };
41660
+ }
41661
+ function applyDurableSelection(row, metadata) {
41662
+ row.lastUsed = metadata.lastUsed;
41663
+ const reason = present4(metadata.lastSwitchReason);
41664
+ if (reason !== void 0) row.lastSwitchReason = reason;
41665
+ const resets = present4(metadata.rateLimitResetTimes);
41666
+ if (resets !== void 0) {
41667
+ const copy = {};
41668
+ for (const [key, at] of Object.entries(resets))
41669
+ if (typeof at === "number") copy[key] = at;
41670
+ row.rateLimitResetTimes = copy;
41671
+ }
41672
+ const until = present4(metadata.coolingDownUntil);
41673
+ const cooldownReason = present4(metadata.cooldownReason);
41674
+ if (until !== void 0 && cooldownReason !== void 0) {
41675
+ row.coolingDownUntil = until;
41676
+ row.cooldownReason = cooldownReason;
41610
41677
  }
41678
+ const fingerprint = present4(metadata.fingerprint);
41679
+ if (fingerprint !== void 0) row.fingerprint = structuredClone(fingerprint);
41680
+ const history = present4(metadata.fingerprintHistory);
41681
+ if (history !== void 0) row.fingerprintHistory = structuredClone(history);
41682
+ const verification = present4(metadata.verificationRequired);
41683
+ if (verification !== void 0) row.verificationRequired = verification;
41684
+ const verificationAt = present4(metadata.verificationRequiredAt);
41685
+ if (verificationAt !== void 0) row.verificationRequiredAt = verificationAt;
41686
+ const verificationReason = present4(metadata.verificationRequiredReason);
41687
+ if (verificationReason !== void 0)
41688
+ row.verificationRequiredReason = verificationReason;
41689
+ const verificationUrl = present4(metadata.verificationUrl);
41690
+ if (verificationUrl !== void 0) row.verificationUrl = verificationUrl;
41691
+ const ineligible = present4(metadata.accountIneligible);
41692
+ if (ineligible !== void 0) row.accountIneligible = ineligible;
41693
+ const ineligibleAt = present4(metadata.accountIneligibleAt);
41694
+ if (ineligibleAt !== void 0) row.accountIneligibleAt = ineligibleAt;
41695
+ const ineligibleReason = present4(metadata.accountIneligibleReason);
41696
+ if (ineligibleReason !== void 0)
41697
+ row.accountIneligibleReason = ineligibleReason;
41698
+ const eligibilityAt = present4(metadata.eligibilityStateUpdatedAt);
41699
+ if (eligibilityAt !== void 0) row.eligibilityStateUpdatedAt = eligibilityAt;
41700
+ const counts = present4(metadata.dailyRequestCounts);
41701
+ if (counts !== void 0) row.dailyRequestCounts = { ...counts };
41611
41702
  }
41612
- function mergeOperator(existing, operator) {
41613
- const next = { ...existing };
41614
- next.operator = operator;
41615
- return next;
41703
+ function stateBelongsTo(state, row) {
41704
+ return state.observed.routeId === row.route.routeId && state.observed.credentialId === row.route.credentialId;
41616
41705
  }
41617
-
41618
- // src/plugin/operator-settings.ts
41619
- var PartialOperatorSettingsSchema = z4.object({
41620
- routing: z4.object({
41621
- cli_first: z4.boolean().optional(),
41622
- quota_style_fallback: z4.boolean().optional()
41623
- }).optional(),
41624
- killswitch: z4.object({
41625
- enabled: z4.boolean().optional(),
41626
- minimum_remaining_percent: z4.number().min(0).max(100).optional(),
41627
- accounts: z4.record(z4.string(), z4.number().min(0).max(100)).optional()
41628
- }).optional(),
41629
- log_level: z4.enum(["error", "warn", "info", "debug", "trace"]).optional()
41630
- });
41631
- function createOperatorSettingsController(options) {
41632
- let cached = null;
41633
- let disposed = false;
41634
- let pending = null;
41635
- const loadFromDisk = () => {
41636
- const existing = readOperatorFile(options.projectConfigPath);
41637
- if (existing) return existing;
41638
- const fromUser = readOperatorFile(options.userConfigPath);
41639
- if (fromUser) return fromUser;
41640
- return emptyOperatorSettings();
41706
+ function createVaultSelectionState(options) {
41707
+ const now = options.now ?? (() => Date.now());
41708
+ const attributions = /* @__PURE__ */ new WeakMap();
41709
+ const changed = /* @__PURE__ */ new Set();
41710
+ let writing = Promise.resolve();
41711
+ const mark = (row) => {
41712
+ changed.add(row);
41641
41713
  };
41642
- const persist = async (next) => {
41643
- if (pending) await pending;
41644
- pending = writeOperatorConfig({
41645
- projectConfigPath: options.projectConfigPath,
41646
- userConfigPath: options.userConfigPath,
41647
- operator: next
41648
- });
41714
+ const report = (error) => {
41649
41715
  try {
41650
- await pending;
41651
- } finally {
41652
- pending = null;
41716
+ options.onError?.(error);
41717
+ } catch {
41653
41718
  }
41654
41719
  };
41655
- return {
41656
- get() {
41657
- if (!cached) cached = loadFromDisk();
41658
- return cached;
41720
+ const writeRow = async (row) => {
41721
+ const attribution = attributions.get(row);
41722
+ if (attribution === void 0) return;
41723
+ changed.delete(row);
41724
+ const selection = durableSelectionOf(row);
41725
+ try {
41726
+ await options.source.commitState(attribution, (current) => ({
41727
+ metadata: {
41728
+ ...current?.metadata ?? { addedAt: now() },
41729
+ ...selection
41730
+ },
41731
+ ...current?.quota !== void 0 ? { quota: current.quota } : {},
41732
+ ...current?.extensions !== void 0 ? { extensions: current.extensions } : {}
41733
+ }));
41734
+ } catch (error) {
41735
+ report(error);
41736
+ }
41737
+ };
41738
+ const flush = () => {
41739
+ writing = writing.then(async () => {
41740
+ for (const row of [...changed]) await writeRow(row);
41741
+ });
41742
+ return writing;
41743
+ };
41744
+ return {
41745
+ sink: {
41746
+ rateLimit: mark,
41747
+ cooldown: mark,
41748
+ switched: mark,
41749
+ lastUsed: mark,
41750
+ usage: mark,
41751
+ accessVerdict: mark,
41752
+ fingerprint: mark,
41753
+ requestSave: () => {
41754
+ void flush();
41755
+ }
41659
41756
  },
41660
- async update(mutator) {
41661
- if (disposed) throw new Error("OperatorSettingsController is disposed");
41662
- const current = cached ?? loadFromDisk();
41663
- const draft = JSON.parse(
41664
- JSON.stringify(current)
41665
- );
41666
- mutator(draft);
41667
- const validated = OperatorSettingsSchema.parse(draft);
41668
- cached = validated;
41669
- await persist(validated);
41757
+ admitted(row, attribution) {
41758
+ attributions.set(row, attribution);
41670
41759
  },
41671
- async dispose() {
41672
- if (disposed) return;
41673
- disposed = true;
41674
- if (pending) {
41675
- try {
41676
- await pending;
41677
- } catch {
41760
+ async hydrate(rows) {
41761
+ const file = await options.readState();
41762
+ for (const row of rows) {
41763
+ const identity = row.route.accountIdentity;
41764
+ if (!Object.hasOwn(file.accounts, identity)) continue;
41765
+ const state = file.accounts[identity];
41766
+ if (state === void 0 || !stateBelongsTo(state, row)) continue;
41767
+ if (state.metadata !== void 0)
41768
+ applyDurableSelection(row, state.metadata);
41769
+ if (state.quota !== void 0) {
41770
+ const cached = cachedQuotaOf(state.quota);
41771
+ if (cached !== void 0) row.cachedQuota = cached;
41772
+ const updatedAt = present4(state.quota.cachedQuotaUpdatedAt);
41773
+ if (updatedAt !== void 0) row.cachedQuotaUpdatedAt = updatedAt;
41678
41774
  }
41679
41775
  }
41776
+ },
41777
+ flush,
41778
+ recordQuota(attribution, quota) {
41779
+ return options.source.commitState(attribution, (current) => ({
41780
+ ...current?.metadata !== void 0 ? { metadata: current.metadata } : {},
41781
+ quota,
41782
+ ...current?.extensions !== void 0 ? { extensions: current.extensions } : {}
41783
+ }));
41680
41784
  }
41681
41785
  };
41682
41786
  }
41683
- function createOperatorSettingsRegistry() {
41684
- const entries = /* @__PURE__ */ new Map();
41685
- const createEntry = (path5) => {
41686
- let state;
41687
- let chain = Promise.resolve();
41688
- const read = () => {
41689
- if (state === void 0) state = readOperatorFile(path5);
41690
- return state;
41691
- };
41692
- return {
41693
- path: path5,
41694
- refs: 0,
41695
- read,
41696
- update(seed, mutator) {
41697
- const run = chain.then(async () => {
41698
- const draft = cloneSettings(read() ?? seed());
41699
- mutator(draft);
41700
- const validated = OperatorSettingsSchema.parse(draft);
41701
- state = validated;
41702
- await writeOperatorConfigAt(path5, validated);
41703
- });
41704
- chain = run.catch(() => {
41705
- });
41706
- return run;
41707
- }
41708
- };
41787
+ function createVaultAccountPool(options) {
41788
+ const { source, durable } = options;
41789
+ const state = durable ? createVaultSelectionState({
41790
+ source: durable.source,
41791
+ readState: durable.readState,
41792
+ ...options.now ? { now: options.now } : {},
41793
+ ...durable.onError ? { onError: durable.onError } : {}
41794
+ }) : void 0;
41795
+ const selector = new AccountSelector({
41796
+ ...options.now ? { now: options.now } : {},
41797
+ ...state ? { sink: state.sink } : {}
41798
+ });
41799
+ const credentials = createVaultRequestCredentials(
41800
+ source,
41801
+ state && durable ? {
41802
+ source: durable.source,
41803
+ admitted: (row, attribution) => state.admitted(row, attribution)
41804
+ } : void 0
41805
+ );
41806
+ const rowsFromSource = async () => {
41807
+ const rows = vaultAccountRows(source.routes());
41808
+ if (state) {
41809
+ const kept = new Set(selector.rows.map(vaultAccountRowKey));
41810
+ const added = rows.filter((row) => !kept.has(vaultAccountRowKey(row)));
41811
+ if (added.length > 0) await state.hydrate(added);
41812
+ }
41813
+ return rows;
41709
41814
  };
41710
- const registry = {
41711
- livePaths: () => [...entries.keys()],
41712
- acquire(path5) {
41713
- const key = canonicalizeOwnedPath(path5);
41714
- let entry = entries.get(key);
41715
- if (!entry) {
41716
- entry = createEntry(key);
41717
- entries.set(key, entry);
41718
- }
41719
- entry.refs++;
41720
- return entry;
41815
+ const replace = async () => {
41816
+ selector.replaceAccounts(await rowsFromSource(), {
41817
+ keyOf: vaultAccountRowKey,
41818
+ refresh: refreshVaultAccountRow
41819
+ });
41820
+ };
41821
+ let firstRead = null;
41822
+ const readOnce = () => {
41823
+ firstRead ??= (async () => {
41824
+ await source.refresh();
41825
+ selector.resetAccounts(await rowsFromSource());
41826
+ })().catch((error) => {
41827
+ firstRead = null;
41828
+ throw error;
41829
+ });
41830
+ return firstRead;
41831
+ };
41832
+ return {
41833
+ selector,
41834
+ credentials,
41835
+ state,
41836
+ async sync() {
41837
+ const pending = firstRead;
41838
+ await readOnce();
41839
+ if (pending !== null) await replace();
41721
41840
  },
41722
- release(entry) {
41723
- entry.refs--;
41724
- if (entry.refs <= 0 && entries.get(entry.path) === entry) {
41725
- entries.delete(entry.path);
41726
- }
41841
+ async refresh() {
41842
+ await readOnce();
41843
+ await source.refresh();
41844
+ await replace();
41845
+ },
41846
+ flush: async () => {
41847
+ await state?.flush();
41727
41848
  }
41728
41849
  };
41729
- return registry;
41730
- }
41731
- var processOperatorSettingsRegistry = createOperatorSettingsRegistry();
41732
- function cloneSettings(settings) {
41733
- return JSON.parse(JSON.stringify(settings));
41734
41850
  }
41735
- function readOperatorFile(path5) {
41736
- try {
41737
- const raw = readFileSync12(path5, "utf-8");
41738
- const parsed = JSON.parse(raw);
41739
- if (parsed === null || typeof parsed !== "object" || !("operator" in parsed)) {
41740
- return null;
41741
- }
41742
- const partial = PartialOperatorSettingsSchema.safeParse(
41743
- parsed.operator
41744
- );
41745
- if (!partial.success) return null;
41746
- return mergeWithDefaults(partial.data);
41747
- } catch {
41748
- return null;
41851
+ function vaultQuotaState(quota, at) {
41852
+ const cachedQuota = {};
41853
+ for (const [name, group] of Object.entries(quota.groups)) {
41854
+ if (group === void 0) continue;
41855
+ cachedQuota[name] = {
41856
+ modelCount: group.modelCount,
41857
+ ...group.remainingFraction !== void 0 ? { remainingFraction: group.remainingFraction } : {},
41858
+ ...group.resetTime !== void 0 ? { resetTime: group.resetTime } : {},
41859
+ ...group.windows !== void 0 ? { windows: group.windows.map((window) => ({ ...window })) } : {}
41860
+ };
41749
41861
  }
41862
+ return { schemaVersion: 1, cachedQuota, cachedQuotaUpdatedAt: at };
41750
41863
  }
41751
- function mergeWithDefaults(partial) {
41752
- const defaults = emptyOperatorSettings();
41753
- return {
41754
- routing: { ...defaults.routing, ...partial.routing ?? {} },
41755
- killswitch: {
41756
- ...defaults.killswitch,
41757
- ...partial.killswitch ?? {},
41758
- accounts: {
41759
- ...defaults.killswitch.accounts ?? {},
41760
- ...partial.killswitch?.accounts ?? {}
41864
+ async function checkVaultQuota(input2) {
41865
+ const state = input2.pool.state;
41866
+ const rows = [...input2.pool.selector.rows];
41867
+ const now = input2.now ?? (() => Date.now());
41868
+ let checked = 0;
41869
+ for (const row of rows) {
41870
+ if (input2.signal.aborted || state === void 0) break;
41871
+ try {
41872
+ const answer = await input2.fetchReading(row.route, input2.signal);
41873
+ const attribution = answer.quotaAttribution;
41874
+ if (attribution === void 0 || answer.quota.error !== void 0)
41875
+ continue;
41876
+ const reading = vaultQuotaState(answer.quota, now());
41877
+ const result = await state.recordQuota(attribution, reading);
41878
+ if (result.status !== "written") continue;
41879
+ checked += 1;
41880
+ const live = input2.pool.selector.rows.find(
41881
+ (candidate) => candidate.route.routeId === attribution.routeId && candidate.route.credentialId === attribution.credentialId && candidate.route.accountIdentity === attribution.accountIdentity
41882
+ );
41883
+ if (live !== void 0) {
41884
+ const cached = cachedQuotaOf(reading);
41885
+ if (cached !== void 0) live.cachedQuota = cached;
41886
+ live.cachedQuotaUpdatedAt = reading.cachedQuotaUpdatedAt ?? void 0;
41761
41887
  }
41762
- },
41763
- log_level: partial.log_level ?? defaults.log_level
41764
- };
41888
+ } catch (error) {
41889
+ try {
41890
+ input2.onError?.(error);
41891
+ } catch {
41892
+ }
41893
+ }
41894
+ }
41895
+ return { checked, notChecked: rows.length - checked };
41765
41896
  }
41766
- function accountKeyForRefreshToken(refreshToken) {
41767
- return createHash18("sha256").update(refreshToken).digest("hex").slice(0, 12);
41897
+
41898
+ // src/plugin/vault-quota-refresh.ts
41899
+ async function checkAndPublishVaultQuota(input2, publishSidebar) {
41900
+ const report = await checkVaultQuota(input2);
41901
+ if (report.checked > 0 && !input2.signal.aborted) await publishSidebar();
41902
+ return report;
41768
41903
  }
41769
41904
 
41770
- // src/plugin/command-apply.ts
41771
- function operatorMenuSettings(settings) {
41772
- return {
41773
- read() {
41774
- const current = settings.get();
41905
+ // src/plugin/vault-sidebar-projection.ts
41906
+ function quotaReadingForVaultRoute(route, state) {
41907
+ const stored = state.accounts[route.accountIdentity];
41908
+ return stored?.observed.routeId === route.routeId && stored.observed.credentialId === route.credentialId ? stored.quota : void 0;
41909
+ }
41910
+ function buildVaultSidebarProjection(pool, state, checkedAt = Date.now()) {
41911
+ const projection = buildSidebarMachineStateFromAccounts(
41912
+ pool.selector.rows.map((row) => {
41913
+ const quota = quotaReadingForVaultRoute(row.route, state);
41775
41914
  return {
41776
- routing: {
41777
- cliFirst: current.routing.cli_first,
41778
- quotaStyleFallback: current.routing.quota_style_fallback
41779
- },
41780
- killswitch: {
41781
- enabled: current.killswitch.enabled,
41782
- minimumRemainingPercent: current.killswitch.minimum_remaining_percent
41783
- }
41915
+ index: row.index,
41916
+ email: row.route.email,
41917
+ enabled: row.enabled,
41918
+ current: false,
41919
+ coolingDownUntil: row.coolingDownUntil,
41920
+ healthScore: pool.selector.healthTracker.getScore(row.index),
41921
+ cachedQuota: quota ? cachedQuotaOf(quota) : void 0,
41922
+ cachedQuotaUpdatedAt: quota?.cachedQuotaUpdatedAt ?? void 0
41784
41923
  };
41785
- },
41786
- updateRouting: (next) => settings.update((draft) => {
41787
- draft.routing.cli_first = next.cliFirst;
41788
- draft.routing.quota_style_fallback = next.quotaStyleFallback;
41789
41924
  }),
41790
- updateKillswitch: (next) => settings.update((draft) => {
41791
- draft.killswitch.enabled = next.enabled;
41792
- draft.killswitch.minimum_remaining_percent = next.minimumRemainingPercent;
41793
- })
41925
+ { checkedAt }
41926
+ );
41927
+ return {
41928
+ ...projection,
41929
+ accounts: projection.accounts.map((account) => ({
41930
+ ...account,
41931
+ id: `vault-${account.id}`
41932
+ }))
41794
41933
  };
41795
41934
  }
41796
- var LOG_LEVEL_CHOICES = ["error", "warn", "info", "debug", "trace"];
41797
- function diagnosticsMenuSection(options) {
41798
- return {
41799
- title: "Diagnostics",
41800
- build() {
41801
- const level = options.settings.get().log_level;
41802
- const dumping = options.dump.isEnabled();
41803
- return {
41804
- lines: [
41805
- `Gemini request dump: ${dumping ? "on" : "off"}`,
41806
- `Log level: ${level}`
41807
- ],
41808
- actions: [
41809
- {
41810
- id: "dump",
41811
- label: dumping ? "Stop dumping requests" : "Dump requests",
41812
- run: async () => {
41813
- options.dump.setEnabled(!dumping);
41814
- return `Gemini request dump ${dumping ? "off" : "on"}`;
41815
- }
41816
- },
41817
- {
41818
- id: "logging",
41819
- label: "Change log level",
41820
- knobs: [
41821
- {
41822
- kind: "choice",
41823
- id: "level",
41824
- label: "Level",
41825
- choices: LOG_LEVEL_CHOICES.map((value) => ({
41826
- value,
41827
- label: value
41828
- })),
41829
- value: level
41935
+
41936
+ // src/plugin/background-vault-quota-refresh.ts
41937
+ var BackgroundVaultQuotaRefresh = class {
41938
+ constructor(options) {
41939
+ this.options = options;
41940
+ this.now = options.now ?? Date.now;
41941
+ this.random = options.random ?? Math.random;
41942
+ this.acquireLock = options.acquireLock ?? acquireFencedFileLock;
41943
+ }
41944
+ options;
41945
+ timer = null;
41946
+ inFlight = null;
41947
+ controller = null;
41948
+ disposed = false;
41949
+ started = false;
41950
+ now;
41951
+ random;
41952
+ acquireLock;
41953
+ start() {
41954
+ if (this.disposed || this.started) return;
41955
+ this.started = true;
41956
+ this.schedule(Math.floor(this.random() * 3e4));
41957
+ }
41958
+ /** Custody notifications cancel admitted work as soon as authority changes. */
41959
+ abortTick() {
41960
+ this.controller?.abort();
41961
+ }
41962
+ async dispose() {
41963
+ this.disposed = true;
41964
+ if (this.timer !== null) clearTimeout(this.timer);
41965
+ this.timer = null;
41966
+ this.abortTick();
41967
+ await this.inFlight;
41968
+ }
41969
+ schedule(delay) {
41970
+ if (this.disposed || !this.started) return;
41971
+ this.timer = setTimeout(() => {
41972
+ this.timer = null;
41973
+ void this.tick();
41974
+ }, delay);
41975
+ this.timer.unref?.();
41976
+ }
41977
+ /** One serialized tick; also used by deterministic composition tests. */
41978
+ tick() {
41979
+ if (this.disposed) return Promise.resolve();
41980
+ if (this.inFlight) return this.inFlight;
41981
+ const controller = new AbortController();
41982
+ this.controller = controller;
41983
+ let extraJitter = 0;
41984
+ this.inFlight = this.runTick(controller).catch(() => {
41985
+ extraJitter = 15e3;
41986
+ }).finally(() => {
41987
+ this.inFlight = null;
41988
+ this.controller = null;
41989
+ this.schedule(
41990
+ this.options.intervalMs + Math.floor(this.random() * this.options.intervalMs * 0.1) + extraJitter
41991
+ );
41992
+ });
41993
+ return this.inFlight;
41994
+ }
41995
+ async assertActive(controller) {
41996
+ if (this.disposed || await this.options.readAuthority() !== "vault")
41997
+ controller.abort();
41998
+ controller.signal.throwIfAborted();
41999
+ }
42000
+ async runTick(controller) {
42001
+ await this.assertActive(controller);
42002
+ ensureSidebarStateDir(this.options.sidebarStateFile);
42003
+ const lock = await this.acquireLock({
42004
+ path: this.options.sidebarStateFile,
42005
+ name: "bg-vault-quota-poll",
42006
+ ttlMs: 6e4,
42007
+ renew: true
42008
+ });
42009
+ if (!lock) return;
42010
+ let released = false;
42011
+ void lock.whenLost().then(() => {
42012
+ if (!released) controller.abort();
42013
+ });
42014
+ try {
42015
+ await this.assertActive(controller);
42016
+ await lock.assertOwned();
42017
+ const { source, pool } = await this.options.currentVaultPool();
42018
+ await this.assertActive(controller);
42019
+ await pool.sync();
42020
+ await this.assertActive(controller);
42021
+ const state = await this.options.readProviderState();
42022
+ await this.assertActive(controller);
42023
+ await lock.assertOwned();
42024
+ const threshold = Math.max(
42025
+ this.options.intervalMs - 6e4,
42026
+ Math.floor(this.options.intervalMs / 2)
42027
+ );
42028
+ const now = this.now();
42029
+ const due = pool.selector.rows.some((row) => {
42030
+ const quota = quotaReadingForVaultRoute(row.route, state);
42031
+ const at = quota?.cachedQuotaUpdatedAt;
42032
+ return quota?.cachedQuota == null || typeof at !== "number" || !Number.isFinite(at) || at < 0 || now - at >= threshold;
42033
+ });
42034
+ if (!due) return;
42035
+ await checkAndPublishVaultQuota(
42036
+ {
42037
+ pool,
42038
+ signal: controller.signal,
42039
+ now: this.now,
42040
+ fetchReading: async (ref, signal) => {
42041
+ await this.assertActive(controller);
42042
+ const reading = await fetchVaultAccountQuota({
42043
+ source,
42044
+ ref,
42045
+ signal,
42046
+ logger: this.options.logger,
42047
+ transport: async (url, init, extra) => {
42048
+ await this.assertActive(controller);
42049
+ await lock.assertOwned();
42050
+ controller.signal.throwIfAborted();
42051
+ return this.options.transport(
42052
+ url,
42053
+ { ...init, signal: extra.signal ?? controller.signal },
42054
+ extra
42055
+ );
41830
42056
  }
41831
- ],
41832
- run: async ({ values }) => {
41833
- const next = LOG_LEVEL_CHOICES.find(
41834
- (candidate) => candidate === values.level
41835
- );
41836
- if (next === void 0)
41837
- return {
41838
- ok: false,
41839
- text: "Choose a log level",
41840
- code: "invalid-input"
41841
- };
41842
- await options.settings.update((draft) => {
41843
- draft.log_level = next;
41844
- });
41845
- options.applyLogLevel(next);
41846
- return `Log level set to ${next}`;
41847
- }
42057
+ });
42058
+ await this.assertActive(controller);
42059
+ await lock.assertOwned();
42060
+ return reading;
41848
42061
  }
41849
- ]
41850
- };
42062
+ },
42063
+ async () => {
42064
+ await this.assertActive(controller);
42065
+ await lock.assertOwned();
42066
+ await this.options.publishSidebar();
42067
+ }
42068
+ );
42069
+ } finally {
42070
+ released = true;
42071
+ await lock.release().catch(() => {
42072
+ });
41851
42073
  }
41852
- };
41853
- }
41854
- var STALE_FAILURES = /* @__PURE__ */ new Set(["attribution", "unknown-row", "id-removed"]);
41855
- function createStoreAccountLimits(input2) {
42074
+ }
42075
+ };
42076
+
42077
+ // src/plugin/operator-settings.ts
42078
+ import { createHash as createHash18 } from "node:crypto";
42079
+ import { existsSync as existsSync14, readFileSync as readFileSync12 } from "node:fs";
42080
+ import { z as z4 } from "zod";
42081
+
42082
+ // src/plugin/config/operator-settings-schema.ts
42083
+ import { z as z2 } from "zod";
42084
+ var OperatorSettingsSchema = z2.object({
42085
+ routing: z2.object({
42086
+ cli_first: z2.boolean(),
42087
+ quota_style_fallback: z2.boolean()
42088
+ }),
42089
+ killswitch: z2.object({
42090
+ enabled: z2.boolean(),
42091
+ minimum_remaining_percent: z2.number().min(0).max(100),
42092
+ accounts: z2.record(z2.string(), z2.number().min(0).max(100)).optional()
42093
+ }),
42094
+ log_level: z2.enum(["error", "warn", "info", "debug", "trace"])
42095
+ });
42096
+ function emptyOperatorSettings() {
41856
42097
  return {
41857
- async read(ref) {
41858
- const read = await input2.repository.read();
41859
- if (read.status !== "ready") return null;
41860
- const row = read.rows.find((candidate) => sameRowRef(candidate.ref, ref));
41861
- const token2 = row?.credential?.refreshToken;
41862
- if (token2 === void 0) return null;
41863
- return input2.settings.get().killswitch.accounts?.[accountKeyForRefreshToken(token2)] ?? null;
41864
- },
41865
- async write(ref, minimumRemainingPercent) {
41866
- try {
41867
- await input2.repository.updateMetadata(ref, async (_current, row) => {
41868
- const token2 = row.credential?.refreshToken;
41869
- if (token2 === void 0)
41870
- throw new Error("The account holds no stored credential");
41871
- const key = accountKeyForRefreshToken(token2);
41872
- await input2.settings.update((draft) => {
41873
- const accounts = { ...draft.killswitch.accounts ?? {} };
41874
- if (minimumRemainingPercent === null) delete accounts[key];
41875
- else accounts[key] = minimumRemainingPercent;
41876
- draft.killswitch.accounts = accounts;
41877
- });
41878
- return { kind: "keep" };
41879
- });
41880
- } catch (error) {
41881
- if (error instanceof AccountRepositoryError && STALE_FAILURES.has(error.failure.kind))
41882
- return "stale";
41883
- throw error;
41884
- }
41885
- return "applied";
41886
- }
42098
+ routing: { cli_first: false, quota_style_fallback: false },
42099
+ killswitch: { enabled: false, minimum_remaining_percent: 5 },
42100
+ log_level: "info"
41887
42101
  };
41888
42102
  }
41889
42103
 
41890
- // src/plugin/prompt-context.ts
41891
- function isRecord15(value) {
41892
- return typeof value === "object" && value !== null;
42104
+ // src/plugin/config/writer.ts
42105
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
42106
+ import { z as z3 } from "zod";
42107
+ async function writeOperatorConfig(options) {
42108
+ const target = existsSync13(options.projectConfigPath) ? options.projectConfigPath : options.userConfigPath;
42109
+ await writeOperatorConfigAt(target, options.operator);
41893
42110
  }
41894
- function extractMessages(response) {
41895
- if (Array.isArray(response)) return response;
41896
- if (isRecord15(response) && Array.isArray(response.data)) return response.data;
41897
- return [];
42111
+ async function writeOperatorConfigAt(target, settings) {
42112
+ const operator = OperatorSettingsSchema.parse(settings);
42113
+ const lock = await acquireFencedFileLock({
42114
+ path: target,
42115
+ name: "antigravity-operator",
42116
+ ttlMs: 5e3,
42117
+ renew: false
42118
+ });
42119
+ if (!lock) {
42120
+ throw new Error(
42121
+ `Could not acquire operator-config lock at ${target} (already held by another writer).`
42122
+ );
42123
+ }
42124
+ try {
42125
+ const existing = readExistingConfig(target);
42126
+ const merged = mergeOperator(existing, operator);
42127
+ await writeJsonAtomic(target, merged);
42128
+ } finally {
42129
+ await lock.release().catch(() => {
42130
+ });
42131
+ }
41898
42132
  }
41899
- function getRole(message) {
41900
- if (!isRecord15(message) || !isRecord15(message.info)) return void 0;
41901
- return typeof message.info.role === "string" ? message.info.role : void 0;
42133
+ function readExistingConfig(target) {
42134
+ if (!existsSync13(target)) return {};
42135
+ try {
42136
+ const raw = readFileSync11(target, "utf-8");
42137
+ const parsed = JSON.parse(raw);
42138
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
42139
+ return parsed;
42140
+ }
42141
+ return {};
42142
+ } catch {
42143
+ return {};
42144
+ }
41902
42145
  }
41903
- function extractFromMessage(message) {
41904
- if (!isRecord15(message) || !isRecord15(message.info)) return null;
41905
- const info = message.info;
41906
- const modelInfo = isRecord15(info.model) ? info.model : void 0;
41907
- const agent = typeof info.agent === "string" ? info.agent : void 0;
41908
- const providerID = typeof modelInfo?.providerID === "string" ? modelInfo.providerID : typeof info.providerID === "string" ? info.providerID : void 0;
41909
- const modelID = typeof modelInfo?.modelID === "string" ? modelInfo.modelID : typeof info.modelID === "string" ? info.modelID : void 0;
41910
- const variant = typeof modelInfo?.variant === "string" ? modelInfo.variant : typeof info.variant === "string" ? info.variant : void 0;
41911
- if (!agent && (!providerID || !modelID) && !variant) return null;
41912
- const context = {};
41913
- if (agent) context.agent = agent;
41914
- if (providerID && modelID) context.model = { providerID, modelID };
41915
- if (variant) context.variant = variant;
41916
- return context;
42146
+ function mergeOperator(existing, operator) {
42147
+ const next = { ...existing };
42148
+ next.operator = operator;
42149
+ return next;
41917
42150
  }
41918
- function mergeContexts(base, patch) {
41919
- return {
41920
- agent: base.agent ?? patch.agent,
41921
- model: base.model ?? patch.model,
41922
- variant: base.variant ?? patch.variant
42151
+
42152
+ // src/plugin/operator-settings.ts
42153
+ var PartialOperatorSettingsSchema = z4.object({
42154
+ routing: z4.object({
42155
+ cli_first: z4.boolean().optional(),
42156
+ quota_style_fallback: z4.boolean().optional()
42157
+ }).optional(),
42158
+ killswitch: z4.object({
42159
+ enabled: z4.boolean().optional(),
42160
+ minimum_remaining_percent: z4.number().min(0).max(100).optional(),
42161
+ accounts: z4.record(z4.string(), z4.number().min(0).max(100)).optional()
42162
+ }).optional(),
42163
+ log_level: z4.enum(["error", "warn", "info", "debug", "trace"]).optional()
42164
+ });
42165
+ function createOperatorSettingsController(options) {
42166
+ let cached = null;
42167
+ let disposed = false;
42168
+ let pending = null;
42169
+ const loadFromDisk = () => {
42170
+ const existing = readOperatorFile(options.projectConfigPath);
42171
+ if (existing) return existing;
42172
+ const fromUser = readOperatorFile(options.userConfigPath);
42173
+ if (fromUser) return fromUser;
42174
+ return emptyOperatorSettings();
42175
+ };
42176
+ const persist = async (next) => {
42177
+ if (pending) await pending;
42178
+ pending = writeOperatorConfig({
42179
+ projectConfigPath: options.projectConfigPath,
42180
+ userConfigPath: options.userConfigPath,
42181
+ operator: next
42182
+ });
42183
+ try {
42184
+ await pending;
42185
+ } finally {
42186
+ pending = null;
42187
+ }
42188
+ };
42189
+ return {
42190
+ get() {
42191
+ if (!cached) cached = loadFromDisk();
42192
+ return cached;
42193
+ },
42194
+ async update(mutator) {
42195
+ if (disposed) throw new Error("OperatorSettingsController is disposed");
42196
+ const current = cached ?? loadFromDisk();
42197
+ const draft = JSON.parse(
42198
+ JSON.stringify(current)
42199
+ );
42200
+ mutator(draft);
42201
+ const validated = OperatorSettingsSchema.parse(draft);
42202
+ cached = validated;
42203
+ await persist(validated);
42204
+ },
42205
+ async dispose() {
42206
+ if (disposed) return;
42207
+ disposed = true;
42208
+ if (pending) {
42209
+ try {
42210
+ await pending;
42211
+ } catch {
42212
+ }
42213
+ }
42214
+ }
41923
42215
  };
41924
42216
  }
41925
- function isComplete(context) {
41926
- return Boolean(context.agent && context.model && context.variant);
42217
+ function createOperatorSettingsRegistry() {
42218
+ const entries = /* @__PURE__ */ new Map();
42219
+ const createEntry = (path5) => {
42220
+ let state;
42221
+ let chain = Promise.resolve();
42222
+ const read = () => {
42223
+ if (state === void 0) state = readOperatorFile(path5);
42224
+ return state;
42225
+ };
42226
+ return {
42227
+ path: path5,
42228
+ refs: 0,
42229
+ read,
42230
+ update(seed, mutator) {
42231
+ const run = chain.then(async () => {
42232
+ const draft = cloneSettings(read() ?? seed());
42233
+ mutator(draft);
42234
+ const validated = OperatorSettingsSchema.parse(draft);
42235
+ state = validated;
42236
+ await writeOperatorConfigAt(path5, validated);
42237
+ });
42238
+ chain = run.catch(() => {
42239
+ });
42240
+ return run;
42241
+ }
42242
+ };
42243
+ };
42244
+ const registry = {
42245
+ livePaths: () => [...entries.keys()],
42246
+ acquire(path5) {
42247
+ const key = canonicalizeOwnedPath(path5);
42248
+ let entry = entries.get(key);
42249
+ if (!entry) {
42250
+ entry = createEntry(key);
42251
+ entries.set(key, entry);
42252
+ }
42253
+ entry.refs++;
42254
+ return entry;
42255
+ },
42256
+ release(entry) {
42257
+ entry.refs--;
42258
+ if (entry.refs <= 0 && entries.get(entry.path) === entry) {
42259
+ entries.delete(entry.path);
42260
+ }
42261
+ }
42262
+ };
42263
+ return registry;
41927
42264
  }
41928
- async function resolvePromptContext(client, sessionId) {
41929
- if (!client || !sessionId) return null;
41930
- const typedClient = client;
41931
- if (typeof typedClient.session?.messages !== "function") return null;
41932
- let messages = [];
42265
+ var processOperatorSettingsRegistry = createOperatorSettingsRegistry();
42266
+ function cloneSettings(settings) {
42267
+ return JSON.parse(JSON.stringify(settings));
42268
+ }
42269
+ function readOperatorFile(path5) {
41933
42270
  try {
41934
- messages = extractMessages(
41935
- await Promise.resolve(
41936
- typedClient.session.messages({
41937
- path: { id: sessionId },
41938
- query: { limit: 100 }
41939
- })
41940
- )
42271
+ const raw = readFileSync12(path5, "utf-8");
42272
+ const parsed = JSON.parse(raw);
42273
+ if (parsed === null || typeof parsed !== "object" || !("operator" in parsed)) {
42274
+ return null;
42275
+ }
42276
+ const partial = PartialOperatorSettingsSchema.safeParse(
42277
+ parsed.operator
41941
42278
  );
42279
+ if (!partial.success) return null;
42280
+ return mergeWithDefaults(partial.data);
41942
42281
  } catch {
41943
42282
  return null;
41944
42283
  }
41945
- if (messages.length === 0) return null;
41946
- let result = {};
41947
- for (let index = messages.length - 1; index >= 0; index--) {
41948
- if (getRole(messages[index]) !== "assistant") continue;
41949
- const context = extractFromMessage(messages[index]);
41950
- if (!context) continue;
41951
- result = mergeContexts(result, context);
41952
- if (isComplete(result)) return result;
41953
- }
41954
- for (let index = messages.length - 1; index >= 0; index--) {
41955
- const context = extractFromMessage(messages[index]);
41956
- if (!context) continue;
41957
- result = mergeContexts(result, context);
41958
- if (isComplete(result)) return result;
41959
- }
41960
- if (!result.agent && !result.model && !result.variant) return null;
41961
- return result;
41962
42284
  }
41963
-
41964
- // src/plugin/commands.ts
41965
- var HANDLED_COMMAND_SENTINEL = "ANTIGRAVITY_COMMAND_HANDLED";
41966
- async function sendIgnoredMessage(client, sessionID, text2) {
41967
- const session = client.session;
41968
- const promptContext = await resolvePromptContext(client, sessionID);
41969
- const request = {
41970
- path: { id: sessionID },
41971
- body: {
41972
- noReply: true,
41973
- parts: [{ type: "text", text: text2, ignored: true }],
41974
- ...promptContext?.agent ? { agent: promptContext.agent } : {},
41975
- ...promptContext?.model ? { model: promptContext.model } : {},
41976
- ...promptContext?.variant ? { variant: promptContext.variant } : {}
41977
- }
42285
+ function mergeWithDefaults(partial) {
42286
+ const defaults = emptyOperatorSettings();
42287
+ return {
42288
+ routing: { ...defaults.routing, ...partial.routing ?? {} },
42289
+ killswitch: {
42290
+ ...defaults.killswitch,
42291
+ ...partial.killswitch ?? {},
42292
+ accounts: {
42293
+ ...defaults.killswitch.accounts ?? {},
42294
+ ...partial.killswitch?.accounts ?? {}
42295
+ }
42296
+ },
42297
+ log_level: partial.log_level ?? defaults.log_level
41978
42298
  };
41979
- if (typeof session?.promptAsync === "function") {
41980
- await session.promptAsync(request);
41981
- return;
41982
- }
41983
- if (typeof session?.prompt === "function") {
41984
- await Promise.resolve(session.prompt(request));
41985
- return;
41986
- }
41987
- throw new Error(
41988
- "OpenCode session prompt API is unavailable for ignored replies."
41989
- );
41990
42299
  }
41991
- function throwHandledCommandSentinel() {
41992
- throw new Error(HANDLED_COMMAND_SENTINEL);
42300
+ function accountKeyForRefreshToken(refreshToken) {
42301
+ return createHash18("sha256").update(refreshToken).digest("hex").slice(0, 12);
41993
42302
  }
41994
- function menuInvocation(push, sessionId) {
42303
+
42304
+ // src/plugin/command-apply.ts
42305
+ function operatorMenuSettings(settings) {
41995
42306
  return {
41996
- ...sessionId !== void 0 ? { sessionId } : {},
41997
- notify(message, kind) {
41998
- push(
41999
- {
42000
- command: ANTIGRAVITY_MENU_COMMAND,
42001
- notify: { message, kind: kind ?? "info" }
42307
+ read() {
42308
+ const current = settings.get();
42309
+ return {
42310
+ routing: {
42311
+ cliFirst: current.routing.cli_first,
42312
+ quotaStyleFallback: current.routing.quota_style_fallback
42002
42313
  },
42003
- sessionId
42004
- );
42005
- }
42314
+ killswitch: {
42315
+ enabled: current.killswitch.enabled,
42316
+ minimumRemainingPercent: current.killswitch.minimum_remaining_percent
42317
+ }
42318
+ };
42319
+ },
42320
+ updateRouting: (next) => settings.update((draft) => {
42321
+ draft.routing.cli_first = next.cliFirst;
42322
+ draft.routing.quota_style_fallback = next.quotaStyleFallback;
42323
+ }),
42324
+ updateKillswitch: (next) => settings.update((draft) => {
42325
+ draft.killswitch.enabled = next.enabled;
42326
+ draft.killswitch.minimum_remaining_percent = next.minimumRemainingPercent;
42327
+ })
42006
42328
  };
42007
42329
  }
42008
- function createAntigravityCommandExecuteBefore(options) {
42009
- const connection = options.connection ?? {
42010
- isTuiConnected
42011
- };
42012
- return async (input2) => {
42013
- if (input2.command !== ANTIGRAVITY_MENU_COMMAND) return;
42014
- const opening = await options.open(input2.sessionID);
42015
- if (opening.kind === "menu") {
42016
- options.push(opening.payload, input2.sessionID);
42017
- } else {
42018
- options.push(
42019
- {
42020
- command: ANTIGRAVITY_MENU_COMMAND,
42021
- notify: { message: opening.message, kind: "warning" }
42022
- },
42023
- input2.sessionID
42024
- );
42025
- }
42026
- if (!connection.isTuiConnected(input2.sessionID)) {
42027
- await sendIgnoredMessage(
42028
- options.client,
42029
- input2.sessionID,
42030
- opening.kind === "menu" ? "The Antigravity menu opens in the OpenCode terminal interface." : opening.message
42330
+ var LOG_LEVEL_CHOICES = ["error", "warn", "info", "debug", "trace"];
42331
+ function diagnosticsMenuSection(options) {
42332
+ return {
42333
+ title: "Diagnostics",
42334
+ build() {
42335
+ const level = options.settings.get().log_level;
42336
+ const dumping = options.dump.isEnabled();
42337
+ return {
42338
+ lines: [
42339
+ `Gemini request dump: ${dumping ? "on" : "off"}`,
42340
+ `Log level: ${level}`
42341
+ ],
42342
+ actions: [
42343
+ {
42344
+ id: "dump",
42345
+ label: dumping ? "Stop dumping requests" : "Dump requests",
42346
+ run: async () => {
42347
+ options.dump.setEnabled(!dumping);
42348
+ return `Gemini request dump ${dumping ? "off" : "on"}`;
42349
+ }
42350
+ },
42351
+ {
42352
+ id: "logging",
42353
+ label: "Change log level",
42354
+ knobs: [
42355
+ {
42356
+ kind: "choice",
42357
+ id: "level",
42358
+ label: "Level",
42359
+ choices: LOG_LEVEL_CHOICES.map((value) => ({
42360
+ value,
42361
+ label: value
42362
+ })),
42363
+ value: level
42364
+ }
42365
+ ],
42366
+ run: async ({ values }) => {
42367
+ const next = LOG_LEVEL_CHOICES.find(
42368
+ (candidate) => candidate === values.level
42369
+ );
42370
+ if (next === void 0)
42371
+ return {
42372
+ ok: false,
42373
+ text: "Choose a log level",
42374
+ code: "invalid-input"
42375
+ };
42376
+ await options.settings.update((draft) => {
42377
+ draft.log_level = next;
42378
+ });
42379
+ options.applyLogLevel(next);
42380
+ return `Log level set to ${next}`;
42381
+ }
42382
+ }
42383
+ ]
42384
+ };
42385
+ }
42386
+ };
42387
+ }
42388
+ var STALE_FAILURES = /* @__PURE__ */ new Set(["attribution", "unknown-row", "id-removed"]);
42389
+ function createStoreAccountLimits(input2) {
42390
+ return {
42391
+ async read(ref) {
42392
+ const read = await input2.repository.read();
42393
+ if (read.status !== "ready") return null;
42394
+ const row = read.rows.find((candidate) => sameRowRef(candidate.ref, ref));
42395
+ const token2 = row?.credential?.refreshToken;
42396
+ if (token2 === void 0) return null;
42397
+ return input2.settings.get().killswitch.accounts?.[accountKeyForRefreshToken(token2)] ?? null;
42398
+ },
42399
+ async write(ref, minimumRemainingPercent) {
42400
+ try {
42401
+ await input2.repository.updateMetadata(ref, async (_current, row) => {
42402
+ const token2 = row.credential?.refreshToken;
42403
+ if (token2 === void 0)
42404
+ throw new Error("The account holds no stored credential");
42405
+ const key = accountKeyForRefreshToken(token2);
42406
+ await input2.settings.update((draft) => {
42407
+ const accounts = { ...draft.killswitch.accounts ?? {} };
42408
+ if (minimumRemainingPercent === null) delete accounts[key];
42409
+ else accounts[key] = minimumRemainingPercent;
42410
+ draft.killswitch.accounts = accounts;
42411
+ });
42412
+ return { kind: "keep" };
42413
+ });
42414
+ } catch (error) {
42415
+ if (error instanceof AccountRepositoryError && STALE_FAILURES.has(error.failure.kind))
42416
+ return "stale";
42417
+ throw error;
42418
+ }
42419
+ return "applied";
42420
+ }
42421
+ };
42422
+ }
42423
+
42424
+ // src/plugin/prompt-context.ts
42425
+ function isRecord15(value) {
42426
+ return typeof value === "object" && value !== null;
42427
+ }
42428
+ function extractMessages(response) {
42429
+ if (Array.isArray(response)) return response;
42430
+ if (isRecord15(response) && Array.isArray(response.data)) return response.data;
42431
+ return [];
42432
+ }
42433
+ function getRole(message) {
42434
+ if (!isRecord15(message) || !isRecord15(message.info)) return void 0;
42435
+ return typeof message.info.role === "string" ? message.info.role : void 0;
42436
+ }
42437
+ function extractFromMessage(message) {
42438
+ if (!isRecord15(message) || !isRecord15(message.info)) return null;
42439
+ const info = message.info;
42440
+ const modelInfo = isRecord15(info.model) ? info.model : void 0;
42441
+ const agent = typeof info.agent === "string" ? info.agent : void 0;
42442
+ const providerID = typeof modelInfo?.providerID === "string" ? modelInfo.providerID : typeof info.providerID === "string" ? info.providerID : void 0;
42443
+ const modelID = typeof modelInfo?.modelID === "string" ? modelInfo.modelID : typeof info.modelID === "string" ? info.modelID : void 0;
42444
+ const variant = typeof modelInfo?.variant === "string" ? modelInfo.variant : typeof info.variant === "string" ? info.variant : void 0;
42445
+ if (!agent && (!providerID || !modelID) && !variant) return null;
42446
+ const context = {};
42447
+ if (agent) context.agent = agent;
42448
+ if (providerID && modelID) context.model = { providerID, modelID };
42449
+ if (variant) context.variant = variant;
42450
+ return context;
42451
+ }
42452
+ function mergeContexts(base, patch) {
42453
+ return {
42454
+ agent: base.agent ?? patch.agent,
42455
+ model: base.model ?? patch.model,
42456
+ variant: base.variant ?? patch.variant
42457
+ };
42458
+ }
42459
+ function isComplete(context) {
42460
+ return Boolean(context.agent && context.model && context.variant);
42461
+ }
42462
+ async function resolvePromptContext(client, sessionId) {
42463
+ if (!client || !sessionId) return null;
42464
+ const typedClient = client;
42465
+ if (typeof typedClient.session?.messages !== "function") return null;
42466
+ let messages = [];
42467
+ try {
42468
+ messages = extractMessages(
42469
+ await Promise.resolve(
42470
+ typedClient.session.messages({
42471
+ path: { id: sessionId },
42472
+ query: { limit: 100 }
42473
+ })
42474
+ )
42475
+ );
42476
+ } catch {
42477
+ return null;
42478
+ }
42479
+ if (messages.length === 0) return null;
42480
+ let result = {};
42481
+ for (let index = messages.length - 1; index >= 0; index--) {
42482
+ if (getRole(messages[index]) !== "assistant") continue;
42483
+ const context = extractFromMessage(messages[index]);
42484
+ if (!context) continue;
42485
+ result = mergeContexts(result, context);
42486
+ if (isComplete(result)) return result;
42487
+ }
42488
+ for (let index = messages.length - 1; index >= 0; index--) {
42489
+ const context = extractFromMessage(messages[index]);
42490
+ if (!context) continue;
42491
+ result = mergeContexts(result, context);
42492
+ if (isComplete(result)) return result;
42493
+ }
42494
+ if (!result.agent && !result.model && !result.variant) return null;
42495
+ return result;
42496
+ }
42497
+
42498
+ // src/plugin/commands.ts
42499
+ var HANDLED_COMMAND_SENTINEL = "ANTIGRAVITY_COMMAND_HANDLED";
42500
+ async function sendIgnoredMessage(client, sessionID, text2) {
42501
+ const session = client.session;
42502
+ const promptContext = await resolvePromptContext(client, sessionID);
42503
+ const request = {
42504
+ path: { id: sessionID },
42505
+ body: {
42506
+ noReply: true,
42507
+ parts: [{ type: "text", text: text2, ignored: true }],
42508
+ ...promptContext?.agent ? { agent: promptContext.agent } : {},
42509
+ ...promptContext?.model ? { model: promptContext.model } : {},
42510
+ ...promptContext?.variant ? { variant: promptContext.variant } : {}
42511
+ }
42512
+ };
42513
+ if (typeof session?.promptAsync === "function") {
42514
+ await session.promptAsync(request);
42515
+ return;
42516
+ }
42517
+ if (typeof session?.prompt === "function") {
42518
+ await Promise.resolve(session.prompt(request));
42519
+ return;
42520
+ }
42521
+ throw new Error(
42522
+ "OpenCode session prompt API is unavailable for ignored replies."
42523
+ );
42524
+ }
42525
+ function throwHandledCommandSentinel() {
42526
+ throw new Error(HANDLED_COMMAND_SENTINEL);
42527
+ }
42528
+ function menuInvocation(push, sessionId) {
42529
+ return {
42530
+ ...sessionId !== void 0 ? { sessionId } : {},
42531
+ notify(message, kind) {
42532
+ push(
42533
+ {
42534
+ command: ANTIGRAVITY_MENU_COMMAND,
42535
+ notify: { message, kind: kind ?? "info" }
42536
+ },
42537
+ sessionId
42538
+ );
42539
+ }
42540
+ };
42541
+ }
42542
+ function createAntigravityCommandExecuteBefore(options) {
42543
+ const connection = options.connection ?? {
42544
+ isTuiConnected
42545
+ };
42546
+ return async (input2) => {
42547
+ if (input2.command !== ANTIGRAVITY_MENU_COMMAND) return;
42548
+ const opening = await options.open(input2.sessionID);
42549
+ if (opening.kind === "menu") {
42550
+ options.push(opening.payload, input2.sessionID);
42551
+ } else {
42552
+ options.push(
42553
+ {
42554
+ command: ANTIGRAVITY_MENU_COMMAND,
42555
+ notify: { message: opening.message, kind: "warning" }
42556
+ },
42557
+ input2.sessionID
42558
+ );
42559
+ }
42560
+ if (!connection.isTuiConnected(input2.sessionID)) {
42561
+ await sendIgnoredMessage(
42562
+ options.client,
42563
+ input2.sessionID,
42564
+ opening.kind === "menu" ? "The Antigravity menu opens in the OpenCode terminal interface." : opening.message
42031
42565
  );
42032
42566
  }
42033
42567
  throwHandledCommandSentinel();
@@ -44935,483 +45469,139 @@ function createRequestExecutor(deps) {
44935
45469
  response,
44936
45470
  cancellation
44937
45471
  );
44938
- inFlight.add(retained.settled);
44939
- void retained.settled.then(() => inFlight.delete(retained.settled));
44940
- return redactCredentialResponse(
44941
- preserveResponseMetadata2(retained.response, response),
44942
- [...selectedCredentials]
44943
- );
44944
- },
44945
- (error) => {
44946
- cancellation.release();
44947
- throw redactCredentialError(error, [...selectedCredentials]);
44948
- }
44949
- ).catch((error) => {
44950
- cancellation.release();
44951
- throw redactCredentialError(error, [...selectedCredentials]);
44952
- });
44953
- inFlight.add(pending);
44954
- void pending.then(
44955
- () => inFlight.delete(pending),
44956
- () => inFlight.delete(pending)
44957
- );
44958
- return pending;
44959
- },
44960
- retire: retire2,
44961
- dispose
44962
- };
44963
- }
44964
- function extractRateLimitBodyInfo(body) {
44965
- if (!body || typeof body !== "object") return { retryDelayMs: null };
44966
- const error = body.error;
44967
- const message = error && typeof error === "object" ? error.message : void 0;
44968
- const details = error && typeof error === "object" ? error.details : void 0;
44969
- let reason;
44970
- if (Array.isArray(details)) {
44971
- for (const detail of details) {
44972
- if (!detail || typeof detail !== "object") continue;
44973
- const type = detail["@type"];
44974
- if (typeof type === "string" && type.includes("google.rpc.ErrorInfo")) {
44975
- const detailReason = detail.reason;
44976
- if (typeof detailReason === "string") {
44977
- reason = detailReason;
44978
- break;
44979
- }
44980
- }
44981
- }
44982
- for (const detail of details) {
44983
- if (!detail || typeof detail !== "object") continue;
44984
- const type = detail["@type"];
44985
- if (typeof type === "string" && type.includes("google.rpc.RetryInfo")) {
44986
- const retryDelay = detail.retryDelay;
44987
- if (typeof retryDelay === "string") {
44988
- const retryDelayMs = parseDurationToMs(retryDelay);
44989
- if (retryDelayMs !== null) {
44990
- return { retryDelayMs, message, reason };
44991
- }
44992
- }
44993
- }
44994
- }
44995
- for (const detail of details) {
44996
- if (!detail || typeof detail !== "object") continue;
44997
- const metadata = detail.metadata;
44998
- if (metadata && typeof metadata === "object") {
44999
- const quotaResetDelay = metadata.quotaResetDelay;
45000
- const quotaResetTime = metadata.quotaResetTimeStamp;
45001
- if (typeof quotaResetDelay === "string") {
45002
- const quotaResetDelayMs = parseDurationToMs(quotaResetDelay);
45003
- if (quotaResetDelayMs !== null) {
45004
- return {
45005
- retryDelayMs: quotaResetDelayMs,
45006
- message,
45007
- quotaResetTime,
45008
- reason
45009
- };
45010
- }
45011
- }
45012
- }
45013
- }
45014
- }
45015
- if (message) {
45016
- const afterMatch = message.match(/reset after\s+([0-9hms.]+)/i);
45017
- const rawDuration = afterMatch?.[1];
45018
- if (rawDuration) {
45019
- const parsed = parseDurationToMs(rawDuration);
45020
- if (parsed !== null) {
45021
- return { retryDelayMs: parsed, message, reason };
45022
- }
45023
- }
45024
- }
45025
- return { retryDelayMs: null, message, reason };
45026
- }
45027
- function parseDurationToMs(duration) {
45028
- const simpleMatch = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i);
45029
- if (simpleMatch) {
45030
- const value = parseFloat(simpleMatch[1]);
45031
- const unit = (simpleMatch[2] || "s").toLowerCase();
45032
- switch (unit) {
45033
- case "h":
45034
- return value * 3600 * 1e3;
45035
- case "m":
45036
- return value * 60 * 1e3;
45037
- case "s":
45038
- return value * 1e3;
45039
- case "ms":
45040
- return value;
45041
- default:
45042
- return value * 1e3;
45043
- }
45044
- }
45045
- const compoundRegex = /(\d+(?:\.\d+)?)(h|m(?!s)|s|ms)/gi;
45046
- let totalMs = 0;
45047
- let matchFound = false;
45048
- let match = null;
45049
- while (true) {
45050
- match = compoundRegex.exec(duration);
45051
- if (match === null) break;
45052
- matchFound = true;
45053
- const value = parseFloat(match[1]);
45054
- const unit = match[2]?.toLowerCase();
45055
- switch (unit) {
45056
- case "h":
45057
- totalMs += value * 3600 * 1e3;
45058
- break;
45059
- case "m":
45060
- totalMs += value * 60 * 1e3;
45061
- break;
45062
- case "s":
45063
- totalMs += value * 1e3;
45064
- break;
45065
- case "ms":
45066
- totalMs += value;
45067
- break;
45068
- }
45069
- }
45070
- return matchFound ? totalMs : null;
45071
- }
45072
-
45073
- // src/plugin/shared/vault-request-credentials.ts
45074
- function vaultAccountRows(routes) {
45075
- return routes.map((route, index) => ({
45076
- index,
45077
- enabled: true,
45078
- lastUsed: 0,
45079
- rateLimitResetTimes: {},
45080
- touchedForQuota: {},
45081
- route,
45082
- ...route.email !== void 0 ? { email: route.email } : {}
45083
- }));
45084
- }
45085
- function vaultAccountRowKey(row) {
45086
- return JSON.stringify([
45087
- row.route.routeId,
45088
- row.route.credentialId,
45089
- row.route.accountIdentity
45090
- ]);
45091
- }
45092
- function refreshVaultAccountRow(prior, fresh) {
45093
- prior.route = fresh.route;
45094
- if (fresh.email === void 0) delete prior.email;
45095
- else prior.email = fresh.email;
45096
- }
45097
- var VaultSendGrant = class {
45098
- credentialId;
45099
- accountIdentity;
45100
- recordVersion;
45101
- projectId;
45102
- report401;
45103
- constructor(admission, report) {
45104
- Object.defineProperty(this, "accessToken", {
45105
- value: admission.accessToken,
45106
- enumerable: false
45107
- });
45108
- this.credentialId = admission.credentialId;
45109
- this.accountIdentity = admission.accountIdentity;
45110
- this.recordVersion = admission.recordVersion;
45111
- this.projectId = admission.projectId;
45112
- this.report401 = report;
45113
- }
45114
- };
45115
- function createVaultRequestCredentials(source, onAdmitted) {
45116
- return {
45117
- domain: "vault",
45118
- async admit({ account, signal }) {
45119
- const admission = await source.admit(account.route, signal);
45120
- if (onAdmitted)
45121
- onAdmitted.admitted(account, onAdmitted.source.attribution(admission));
45122
- return new VaultSendGrant(
45123
- admission,
45124
- (status) => source.reportServedStatus(admission, status)
45125
- );
45126
- }
45127
- };
45128
- }
45129
- function present4(value) {
45130
- return value === null ? void 0 : value;
45131
- }
45132
- function storedResets(resets) {
45133
- const stored = {};
45134
- for (const [key, at] of Object.entries(resets))
45135
- if (typeof at === "number") stored[key] = at;
45136
- return stored;
45137
- }
45138
- function cachedQuotaOf(quota) {
45139
- const groups = quota.cachedQuota;
45140
- if (groups == null) return void 0;
45141
- const out = {};
45142
- for (const name of ["gemini", "non-gemini"]) {
45143
- const group = groups[name];
45144
- if (group === void 0) continue;
45145
- out[name] = {
45146
- modelCount: group.modelCount,
45147
- ...group.remainingFraction != null ? { remainingFraction: group.remainingFraction } : {},
45148
- ...group.resetTime != null ? { resetTime: group.resetTime } : {},
45149
- ...group.windows != null ? {
45150
- windows: group.windows.map((window) => ({
45151
- window: window.window,
45152
- remainingFraction: window.remainingFraction,
45153
- resetTime: window.resetTime
45154
- }))
45155
- } : {}
45156
- };
45157
- }
45158
- return out;
45159
- }
45160
- function durableSelectionOf(row) {
45161
- return {
45162
- lastUsed: row.lastUsed,
45163
- ...row.lastSwitchReason !== void 0 ? { lastSwitchReason: row.lastSwitchReason } : {},
45164
- rateLimitResetTimes: storedResets(row.rateLimitResetTimes),
45165
- ...row.coolingDownUntil !== void 0 ? { coolingDownUntil: row.coolingDownUntil } : {},
45166
- ...row.cooldownReason !== void 0 ? { cooldownReason: row.cooldownReason } : {},
45167
- ...row.fingerprint !== void 0 ? { fingerprint: structuredClone(row.fingerprint) } : {},
45168
- ...row.fingerprintHistory !== void 0 ? { fingerprintHistory: structuredClone(row.fingerprintHistory) } : {},
45169
- ...row.verificationRequired !== void 0 ? { verificationRequired: row.verificationRequired } : {},
45170
- ...row.verificationRequiredAt !== void 0 ? { verificationRequiredAt: row.verificationRequiredAt } : {},
45171
- ...row.verificationRequiredReason !== void 0 ? { verificationRequiredReason: row.verificationRequiredReason } : {},
45172
- ...row.verificationUrl !== void 0 ? { verificationUrl: row.verificationUrl } : {},
45173
- ...row.accountIneligible !== void 0 ? { accountIneligible: row.accountIneligible } : {},
45174
- ...row.accountIneligibleAt !== void 0 ? { accountIneligibleAt: row.accountIneligibleAt } : {},
45175
- ...row.accountIneligibleReason !== void 0 ? { accountIneligibleReason: row.accountIneligibleReason } : {},
45176
- ...row.eligibilityStateUpdatedAt !== void 0 ? { eligibilityStateUpdatedAt: row.eligibilityStateUpdatedAt } : {},
45177
- ...row.dailyRequestCounts !== void 0 ? { dailyRequestCounts: { ...row.dailyRequestCounts } } : {}
45178
- };
45179
- }
45180
- function applyDurableSelection(row, metadata) {
45181
- row.lastUsed = metadata.lastUsed;
45182
- const reason = present4(metadata.lastSwitchReason);
45183
- if (reason !== void 0) row.lastSwitchReason = reason;
45184
- const resets = present4(metadata.rateLimitResetTimes);
45185
- if (resets !== void 0) {
45186
- const copy = {};
45187
- for (const [key, at] of Object.entries(resets))
45188
- if (typeof at === "number") copy[key] = at;
45189
- row.rateLimitResetTimes = copy;
45190
- }
45191
- const until = present4(metadata.coolingDownUntil);
45192
- const cooldownReason = present4(metadata.cooldownReason);
45193
- if (until !== void 0 && cooldownReason !== void 0) {
45194
- row.coolingDownUntil = until;
45195
- row.cooldownReason = cooldownReason;
45196
- }
45197
- const fingerprint = present4(metadata.fingerprint);
45198
- if (fingerprint !== void 0) row.fingerprint = structuredClone(fingerprint);
45199
- const history = present4(metadata.fingerprintHistory);
45200
- if (history !== void 0) row.fingerprintHistory = structuredClone(history);
45201
- const verification = present4(metadata.verificationRequired);
45202
- if (verification !== void 0) row.verificationRequired = verification;
45203
- const verificationAt = present4(metadata.verificationRequiredAt);
45204
- if (verificationAt !== void 0) row.verificationRequiredAt = verificationAt;
45205
- const verificationReason = present4(metadata.verificationRequiredReason);
45206
- if (verificationReason !== void 0)
45207
- row.verificationRequiredReason = verificationReason;
45208
- const verificationUrl = present4(metadata.verificationUrl);
45209
- if (verificationUrl !== void 0) row.verificationUrl = verificationUrl;
45210
- const ineligible = present4(metadata.accountIneligible);
45211
- if (ineligible !== void 0) row.accountIneligible = ineligible;
45212
- const ineligibleAt = present4(metadata.accountIneligibleAt);
45213
- if (ineligibleAt !== void 0) row.accountIneligibleAt = ineligibleAt;
45214
- const ineligibleReason = present4(metadata.accountIneligibleReason);
45215
- if (ineligibleReason !== void 0)
45216
- row.accountIneligibleReason = ineligibleReason;
45217
- const eligibilityAt = present4(metadata.eligibilityStateUpdatedAt);
45218
- if (eligibilityAt !== void 0) row.eligibilityStateUpdatedAt = eligibilityAt;
45219
- const counts = present4(metadata.dailyRequestCounts);
45220
- if (counts !== void 0) row.dailyRequestCounts = { ...counts };
45221
- }
45222
- function stateBelongsTo(state, row) {
45223
- return state.observed.routeId === row.route.routeId && state.observed.credentialId === row.route.credentialId;
45224
- }
45225
- function createVaultSelectionState(options) {
45226
- const now = options.now ?? (() => Date.now());
45227
- const attributions = /* @__PURE__ */ new WeakMap();
45228
- const changed = /* @__PURE__ */ new Set();
45229
- let writing = Promise.resolve();
45230
- const mark = (row) => {
45231
- changed.add(row);
45232
- };
45233
- const report = (error) => {
45234
- try {
45235
- options.onError?.(error);
45236
- } catch {
45237
- }
45238
- };
45239
- const writeRow = async (row) => {
45240
- const attribution = attributions.get(row);
45241
- if (attribution === void 0) return;
45242
- changed.delete(row);
45243
- const selection = durableSelectionOf(row);
45244
- try {
45245
- await options.source.commitState(attribution, (current) => ({
45246
- metadata: {
45247
- ...current?.metadata ?? { addedAt: now() },
45248
- ...selection
45249
- },
45250
- ...current?.quota !== void 0 ? { quota: current.quota } : {},
45251
- ...current?.extensions !== void 0 ? { extensions: current.extensions } : {}
45252
- }));
45253
- } catch (error) {
45254
- report(error);
45255
- }
45256
- };
45257
- const flush = () => {
45258
- writing = writing.then(async () => {
45259
- for (const row of [...changed]) await writeRow(row);
45260
- });
45261
- return writing;
45262
- };
45263
- return {
45264
- sink: {
45265
- rateLimit: mark,
45266
- cooldown: mark,
45267
- switched: mark,
45268
- lastUsed: mark,
45269
- usage: mark,
45270
- accessVerdict: mark,
45271
- fingerprint: mark,
45272
- requestSave: () => {
45273
- void flush();
45274
- }
45275
- },
45276
- admitted(row, attribution) {
45277
- attributions.set(row, attribution);
45278
- },
45279
- async hydrate(rows) {
45280
- const file = await options.readState();
45281
- for (const row of rows) {
45282
- const identity = row.route.accountIdentity;
45283
- if (!Object.hasOwn(file.accounts, identity)) continue;
45284
- const state = file.accounts[identity];
45285
- if (state === void 0 || !stateBelongsTo(state, row)) continue;
45286
- if (state.metadata !== void 0)
45287
- applyDurableSelection(row, state.metadata);
45288
- if (state.quota !== void 0) {
45289
- const cached = cachedQuotaOf(state.quota);
45290
- if (cached !== void 0) row.cachedQuota = cached;
45291
- const updatedAt = present4(state.quota.cachedQuotaUpdatedAt);
45292
- if (updatedAt !== void 0) row.cachedQuotaUpdatedAt = updatedAt;
45472
+ inFlight.add(retained.settled);
45473
+ void retained.settled.then(() => inFlight.delete(retained.settled));
45474
+ return redactCredentialResponse(
45475
+ preserveResponseMetadata2(retained.response, response),
45476
+ [...selectedCredentials]
45477
+ );
45478
+ },
45479
+ (error) => {
45480
+ cancellation.release();
45481
+ throw redactCredentialError(error, [...selectedCredentials]);
45293
45482
  }
45294
- }
45483
+ ).catch((error) => {
45484
+ cancellation.release();
45485
+ throw redactCredentialError(error, [...selectedCredentials]);
45486
+ });
45487
+ inFlight.add(pending);
45488
+ void pending.then(
45489
+ () => inFlight.delete(pending),
45490
+ () => inFlight.delete(pending)
45491
+ );
45492
+ return pending;
45295
45493
  },
45296
- flush,
45297
- recordQuota(attribution, quota) {
45298
- return options.source.commitState(attribution, (current) => ({
45299
- ...current?.metadata !== void 0 ? { metadata: current.metadata } : {},
45300
- quota,
45301
- ...current?.extensions !== void 0 ? { extensions: current.extensions } : {}
45302
- }));
45303
- }
45494
+ retire: retire2,
45495
+ dispose
45304
45496
  };
45305
45497
  }
45306
- function createVaultAccountPool(options) {
45307
- const { source, durable } = options;
45308
- const state = durable ? createVaultSelectionState({
45309
- source: durable.source,
45310
- readState: durable.readState,
45311
- ...options.now ? { now: options.now } : {},
45312
- ...durable.onError ? { onError: durable.onError } : {}
45313
- }) : void 0;
45314
- const selector = new AccountSelector({
45315
- ...options.now ? { now: options.now } : {},
45316
- ...state ? { sink: state.sink } : {}
45317
- });
45318
- const credentials = createVaultRequestCredentials(
45319
- source,
45320
- state && durable ? {
45321
- source: durable.source,
45322
- admitted: (row, attribution) => state.admitted(row, attribution)
45323
- } : void 0
45324
- );
45325
- const rowsFromSource = async () => {
45326
- const rows = vaultAccountRows(source.routes());
45327
- if (state) {
45328
- const kept = new Set(selector.rows.map(vaultAccountRowKey));
45329
- const added = rows.filter((row) => !kept.has(vaultAccountRowKey(row)));
45330
- if (added.length > 0) await state.hydrate(added);
45498
+ function extractRateLimitBodyInfo(body) {
45499
+ if (!body || typeof body !== "object") return { retryDelayMs: null };
45500
+ const error = body.error;
45501
+ const message = error && typeof error === "object" ? error.message : void 0;
45502
+ const details = error && typeof error === "object" ? error.details : void 0;
45503
+ let reason;
45504
+ if (Array.isArray(details)) {
45505
+ for (const detail of details) {
45506
+ if (!detail || typeof detail !== "object") continue;
45507
+ const type = detail["@type"];
45508
+ if (typeof type === "string" && type.includes("google.rpc.ErrorInfo")) {
45509
+ const detailReason = detail.reason;
45510
+ if (typeof detailReason === "string") {
45511
+ reason = detailReason;
45512
+ break;
45513
+ }
45514
+ }
45331
45515
  }
45332
- return rows;
45333
- };
45334
- const replace = async () => {
45335
- selector.replaceAccounts(await rowsFromSource(), {
45336
- keyOf: vaultAccountRowKey,
45337
- refresh: refreshVaultAccountRow
45338
- });
45339
- };
45340
- let firstRead = null;
45341
- const readOnce = () => {
45342
- firstRead ??= (async () => {
45343
- await source.refresh();
45344
- selector.resetAccounts(await rowsFromSource());
45345
- })().catch((error) => {
45346
- firstRead = null;
45347
- throw error;
45348
- });
45349
- return firstRead;
45350
- };
45351
- return {
45352
- selector,
45353
- credentials,
45354
- state,
45355
- async sync() {
45356
- const pending = firstRead;
45357
- await readOnce();
45358
- if (pending !== null) await replace();
45359
- },
45360
- async refresh() {
45361
- await readOnce();
45362
- await source.refresh();
45363
- await replace();
45364
- },
45365
- flush: async () => {
45366
- await state?.flush();
45516
+ for (const detail of details) {
45517
+ if (!detail || typeof detail !== "object") continue;
45518
+ const type = detail["@type"];
45519
+ if (typeof type === "string" && type.includes("google.rpc.RetryInfo")) {
45520
+ const retryDelay = detail.retryDelay;
45521
+ if (typeof retryDelay === "string") {
45522
+ const retryDelayMs = parseDurationToMs(retryDelay);
45523
+ if (retryDelayMs !== null) {
45524
+ return { retryDelayMs, message, reason };
45525
+ }
45526
+ }
45527
+ }
45367
45528
  }
45368
- };
45369
- }
45370
- function vaultQuotaState(quota, at) {
45371
- const cachedQuota = {};
45372
- for (const [name, group] of Object.entries(quota.groups)) {
45373
- if (group === void 0) continue;
45374
- cachedQuota[name] = {
45375
- modelCount: group.modelCount,
45376
- ...group.remainingFraction !== void 0 ? { remainingFraction: group.remainingFraction } : {},
45377
- ...group.resetTime !== void 0 ? { resetTime: group.resetTime } : {},
45378
- ...group.windows !== void 0 ? { windows: group.windows.map((window) => ({ ...window })) } : {}
45379
- };
45380
- }
45381
- return { schemaVersion: 1, cachedQuota, cachedQuotaUpdatedAt: at };
45382
- }
45383
- async function checkVaultQuota(input2) {
45384
- const state = input2.pool.state;
45385
- const rows = [...input2.pool.selector.rows];
45386
- const now = input2.now ?? (() => Date.now());
45387
- let checked = 0;
45388
- for (const row of rows) {
45389
- if (input2.signal.aborted || state === void 0) break;
45390
- try {
45391
- const answer = await input2.fetchReading(row.route, input2.signal);
45392
- const attribution = answer.quotaAttribution;
45393
- if (attribution === void 0 || answer.quota.error !== void 0)
45394
- continue;
45395
- const reading = vaultQuotaState(answer.quota, now());
45396
- const result = await state.recordQuota(attribution, reading);
45397
- if (result.status !== "written") continue;
45398
- checked += 1;
45399
- const live = input2.pool.selector.rows.find(
45400
- (candidate) => candidate.route.routeId === attribution.routeId && candidate.route.credentialId === attribution.credentialId && candidate.route.accountIdentity === attribution.accountIdentity
45401
- );
45402
- if (live !== void 0) {
45403
- const cached = cachedQuotaOf(reading);
45404
- if (cached !== void 0) live.cachedQuota = cached;
45405
- live.cachedQuotaUpdatedAt = reading.cachedQuotaUpdatedAt ?? void 0;
45529
+ for (const detail of details) {
45530
+ if (!detail || typeof detail !== "object") continue;
45531
+ const metadata = detail.metadata;
45532
+ if (metadata && typeof metadata === "object") {
45533
+ const quotaResetDelay = metadata.quotaResetDelay;
45534
+ const quotaResetTime = metadata.quotaResetTimeStamp;
45535
+ if (typeof quotaResetDelay === "string") {
45536
+ const quotaResetDelayMs = parseDurationToMs(quotaResetDelay);
45537
+ if (quotaResetDelayMs !== null) {
45538
+ return {
45539
+ retryDelayMs: quotaResetDelayMs,
45540
+ message,
45541
+ quotaResetTime,
45542
+ reason
45543
+ };
45544
+ }
45545
+ }
45406
45546
  }
45407
- } catch (error) {
45408
- try {
45409
- input2.onError?.(error);
45410
- } catch {
45547
+ }
45548
+ }
45549
+ if (message) {
45550
+ const afterMatch = message.match(/reset after\s+([0-9hms.]+)/i);
45551
+ const rawDuration = afterMatch?.[1];
45552
+ if (rawDuration) {
45553
+ const parsed = parseDurationToMs(rawDuration);
45554
+ if (parsed !== null) {
45555
+ return { retryDelayMs: parsed, message, reason };
45411
45556
  }
45412
45557
  }
45413
45558
  }
45414
- return { checked, notChecked: rows.length - checked };
45559
+ return { retryDelayMs: null, message, reason };
45560
+ }
45561
+ function parseDurationToMs(duration) {
45562
+ const simpleMatch = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/i);
45563
+ if (simpleMatch) {
45564
+ const value = parseFloat(simpleMatch[1]);
45565
+ const unit = (simpleMatch[2] || "s").toLowerCase();
45566
+ switch (unit) {
45567
+ case "h":
45568
+ return value * 3600 * 1e3;
45569
+ case "m":
45570
+ return value * 60 * 1e3;
45571
+ case "s":
45572
+ return value * 1e3;
45573
+ case "ms":
45574
+ return value;
45575
+ default:
45576
+ return value * 1e3;
45577
+ }
45578
+ }
45579
+ const compoundRegex = /(\d+(?:\.\d+)?)(h|m(?!s)|s|ms)/gi;
45580
+ let totalMs = 0;
45581
+ let matchFound = false;
45582
+ let match = null;
45583
+ while (true) {
45584
+ match = compoundRegex.exec(duration);
45585
+ if (match === null) break;
45586
+ matchFound = true;
45587
+ const value = parseFloat(match[1]);
45588
+ const unit = match[2]?.toLowerCase();
45589
+ switch (unit) {
45590
+ case "h":
45591
+ totalMs += value * 3600 * 1e3;
45592
+ break;
45593
+ case "m":
45594
+ totalMs += value * 60 * 1e3;
45595
+ break;
45596
+ case "s":
45597
+ totalMs += value * 1e3;
45598
+ break;
45599
+ case "ms":
45600
+ totalMs += value;
45601
+ break;
45602
+ }
45603
+ }
45604
+ return matchFound ? totalMs : null;
45415
45605
  }
45416
45606
 
45417
45607
  // src/plugin/fetch-interceptor.ts
@@ -46514,6 +46704,7 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46514
46704
  await drainSidebarWrites();
46515
46705
  }
46516
46706
  });
46707
+ let vaultQuotaPoller;
46517
46708
  const vaultCustody = createOpenCodeVaultCustody({
46518
46709
  accountFile: resolve11(getStoragePath()),
46519
46710
  readHostSlot: async () => {
@@ -46524,7 +46715,10 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46524
46715
  // The request executor reports failed bearer authorization to Claustrum,
46525
46716
  // which decides whether its served credential needs replacement.
46526
46717
  reporterSource: "direct",
46527
- onAuthorityChanged: () => scheduleSidebarPublication(),
46718
+ onAuthorityChanged: (authority) => {
46719
+ if (authority !== "vault") vaultQuotaPoller?.abortTick();
46720
+ scheduleSidebarPublication();
46721
+ },
46528
46722
  onRosterChanged: () => scheduleSidebarPublication()
46529
46723
  });
46530
46724
  let vaultPool;
@@ -46571,6 +46765,7 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46571
46765
  return manager.getAccounts().map((entry) => ({
46572
46766
  index: entry.index,
46573
46767
  label: entry.label,
46768
+ email: entry.email,
46574
46769
  enabled: entry.enabled,
46575
46770
  current: isAccountCurrent(entry.index, activeByFamily),
46576
46771
  coolingDownUntil: entry.coolingDownUntil,
@@ -46598,6 +46793,7 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46598
46793
  return buildSidebarMachineStateFromAccounts(
46599
46794
  manager?.getAccounts().map((entry) => ({
46600
46795
  index: entry.index,
46796
+ email: entry.email,
46601
46797
  enabled: entry.enabled,
46602
46798
  current: active2 ? isAccountCurrent(entry.index, active2) : false,
46603
46799
  coolingDownUntil: entry.coolingDownUntil,
@@ -46615,23 +46811,13 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46615
46811
  vault: async () => {
46616
46812
  const { pool } = await currentVaultPool();
46617
46813
  await pool.sync();
46618
- const projection = buildSidebarMachineStateFromAccounts(
46619
- pool.selector.rows.map((entry) => ({
46620
- index: entry.index,
46621
- enabled: entry.enabled,
46622
- current: false,
46623
- coolingDownUntil: entry.coolingDownUntil,
46624
- healthScore: pool.selector.healthTracker.getScore(entry.index),
46625
- cachedQuota: entry.cachedQuota,
46626
- cachedQuotaUpdatedAt: entry.cachedQuotaUpdatedAt
46627
- }))
46814
+ const projection = buildVaultSidebarProjection(
46815
+ pool,
46816
+ await vaultCustody.readState(),
46817
+ dependencies.clock.now()
46628
46818
  );
46629
46819
  return {
46630
46820
  ...projection,
46631
- accounts: projection.accounts.map((account) => ({
46632
- ...account,
46633
- id: `vault-${account.id}`
46634
- })),
46635
46821
  ...projection.accounts.length === 0 ? { lastError: VAULT_ACCOUNT_REMEDY } : {}
46636
46822
  };
46637
46823
  },
@@ -46710,6 +46896,21 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46710
46896
  options._onPollerCreated?.(poller);
46711
46897
  poller.start();
46712
46898
  lifecycle.register({ dispose: () => poller.dispose() }, "producer");
46899
+ vaultQuotaPoller = new BackgroundVaultQuotaRefresh({
46900
+ intervalMs: config.background_quota_refresh_interval_minutes * 6e4,
46901
+ sidebarStateFile: getSidebarStateFile(),
46902
+ readAuthority,
46903
+ currentVaultPool,
46904
+ readProviderState: () => vaultCustody.readState(),
46905
+ publishSidebar: () => sidebarPublisher.publish(),
46906
+ transport: dependencies.fetchImpl,
46907
+ logger,
46908
+ now: dependencies.clock.now,
46909
+ random: dependencies.clock.random
46910
+ });
46911
+ options._onVaultPollerCreated?.(vaultQuotaPoller);
46912
+ vaultQuotaPoller.start();
46913
+ lifecycle.register(vaultQuotaPoller, "producer");
46713
46914
  }
46714
46915
  const operatorSettings = createOperatorSettingsController({
46715
46916
  projectConfigPath: join29(directory, ".opencode", "antigravity.json"),
@@ -46833,17 +47034,21 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
46833
47034
  vaultSelectors.set(key, selector);
46834
47035
  return selector;
46835
47036
  },
46836
- (signal) => checkVaultQuota({
46837
- pool,
46838
- signal,
46839
- fetchReading: (ref, check) => fetchVaultAccountQuota({
46840
- source,
46841
- ref,
46842
- signal: check,
46843
- logger,
46844
- transport: dependencies.fetchImpl
46845
- })
46846
- })
47037
+ (signal) => checkAndPublishVaultQuota(
47038
+ {
47039
+ pool,
47040
+ signal,
47041
+ now: dependencies.clock.now,
47042
+ fetchReading: (ref, check) => fetchVaultAccountQuota({
47043
+ source,
47044
+ ref,
47045
+ signal: check,
47046
+ logger,
47047
+ transport: dependencies.fetchImpl
47048
+ })
47049
+ },
47050
+ () => sidebarPublisher.publish()
47051
+ )
46847
47052
  )
46848
47053
  })
46849
47054
  };
@@ -47091,10 +47296,8 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
47091
47296
  apply: async (request) => {
47092
47297
  const current = await currentMenu();
47093
47298
  if (current.kind === "unavailable") throw new Error(current.message);
47094
- const result = await current.menu.apply(
47095
- request,
47096
- menuInvocation(pushNotification, request.sessionId)
47097
- );
47299
+ const invocation = menuInvocation(pushNotification, request.sessionId);
47300
+ const result = await current.menu.apply(request, invocation);
47098
47301
  try {
47099
47302
  if (await readAuthority() === "local")
47100
47303
  await lifecycle.getAccountManager()?.refreshFromRepository();
@@ -47111,6 +47314,11 @@ var createAntigravityPlugin = (providerId, options = {}) => async (input2) => {
47111
47314
  request.sessionId
47112
47315
  );
47113
47316
  }
47317
+ const refreshed = await currentMenu();
47318
+ if (refreshed.kind === "menu" && refreshed.menu !== current.menu) {
47319
+ const payload = await refreshed.menu.open(invocation);
47320
+ return { ...result, menu: payload.menu };
47321
+ }
47114
47322
  return result;
47115
47323
  },
47116
47324
  drain: drainNotifications