agent-usage-all-in-one 0.5.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -159,7 +159,7 @@ function buildUsageExport(overview, request, accountIdentifiers = {}) {
159
159
  calculatedAt: null
160
160
  };
161
161
  const modelObservations = history.models.flatMap(
162
- (model) => model.observations.map((observation) => ({
162
+ (model) => (model.observations ?? []).map((observation) => ({
163
163
  model: model.model,
164
164
  observation
165
165
  }))
@@ -210,14 +210,14 @@ function buildUsageExport(overview, request, accountIdentifiers = {}) {
210
210
  });
211
211
  const observationsById = new Map([
212
212
  ...history.models.flatMap(
213
- (model) => model.observations.map((observation) => [observation.id, observation])
213
+ (model) => (model.observations ?? []).map((observation) => [observation.id, observation])
214
214
  ),
215
215
  ...(history.unclassified.observations ?? []).map(
216
216
  (observation) => [observation.id, observation]
217
217
  )
218
218
  ]);
219
219
  const costRows = history.costs.map((cost) => {
220
- const authorities = domain.costs.filter(
220
+ const authorities = (domain.costs ?? []).filter(
221
221
  (candidate) => candidate.kind === cost.kind && candidate.currency.toUpperCase() === cost.currency.toUpperCase() && candidate.observedAt >= history.start && candidate.observedAt < history.end
222
222
  ).map((candidate) => candidate.authority);
223
223
  return {
@@ -268,7 +268,7 @@ function buildUsageExport(overview, request, accountIdentifiers = {}) {
268
268
  calculatedAt: null
269
269
  };
270
270
  });
271
- const costObservationRows = domain.costs.filter((cost) => cost.observedAt >= history.start && cost.observedAt < history.end).map((cost) => {
271
+ const costObservationRows = (domain.costs ?? []).filter((cost) => cost.observedAt >= history.start && cost.observedAt < history.end).map((cost) => {
272
272
  const snapshot2 = cost.priceSnapshot ?? null;
273
273
  const observation = cost.usageObservationId ? observationsById.get(cost.usageObservationId) : null;
274
274
  return {
@@ -557,10 +557,34 @@ function anthropicEntry(options) {
557
557
  source: {
558
558
  title: "Anthropic Claude API pricing",
559
559
  url: options.sourceUrl,
560
- retrievedAt: "2026-08-28"
560
+ retrievedAt: options.retrievedAt ?? "2026-08-28"
561
561
  }
562
562
  };
563
563
  }
564
+ function antigravityEntry(options) {
565
+ const versionDate = "2026-09-02";
566
+ return {
567
+ id: options.id,
568
+ priceVersion: `antigravity-${versionDate}`,
569
+ providerId: "antigravity",
570
+ billingDomainId: "code-assist-subscription",
571
+ canonicalModel: options.model,
572
+ aliases: options.aliases,
573
+ currency: "USD",
574
+ effectiveFrom: GOOGLE_ANTIGRAVITY_EFFECTIVE_FROM,
575
+ effectiveUntil: null,
576
+ contextTier: "standard-api",
577
+ contextRule: { kind: "fixed" },
578
+ ratesPerMillion: {
579
+ input: options.input,
580
+ output: options.output,
581
+ reasoning: options.output,
582
+ "cache-read": options.cacheRead ?? null,
583
+ "cache-write": null
584
+ },
585
+ source: options.source ?? GOOGLE_ANTIGRAVITY_SOURCE
586
+ };
587
+ }
564
588
  function openAiGpt56Entries(model, periods) {
565
589
  return periods.flatMap(([effectiveFrom, effectiveUntil, input, output]) => {
566
590
  const versionDate = effectiveFrom.slice(0, 10);
@@ -705,8 +729,48 @@ function openCodeLocalModelPrefix(entry) {
705
729
  if (entry.providerId === "opencode-go" && entry.billingDomainId === "go-subscription") {
706
730
  return "opencode-go";
707
731
  }
732
+ if (entry.providerId === "antigravity" && entry.billingDomainId === "code-assist-subscription") {
733
+ return "google";
734
+ }
708
735
  return null;
709
736
  }
737
+ function customModelRateToCatalogEntry(rate) {
738
+ const versionDate = rate.updatedAt.slice(0, 10);
739
+ return {
740
+ id: `custom-rate-${rate.id}`,
741
+ priceVersion: `custom-${rate.id}-${rate.updatedAt}`,
742
+ providerId: rate.providerId,
743
+ billingDomainId: rate.billingDomainId ?? "*",
744
+ canonicalModel: rate.model,
745
+ aliases: [],
746
+ currency: "USD",
747
+ effectiveFrom: "2020-01-01T00:00:00.000Z",
748
+ effectiveUntil: null,
749
+ contextTier: "custom-rate",
750
+ contextRule: { kind: "fixed" },
751
+ ratesPerMillion: {
752
+ input: rate.ratesPerMillion.input,
753
+ output: rate.ratesPerMillion.output,
754
+ reasoning: rate.ratesPerMillion.output,
755
+ "cache-read": rate.ratesPerMillion.cacheRead,
756
+ "cache-write": null
757
+ },
758
+ source: {
759
+ title: "User-configured custom model rate",
760
+ url: "local://settings/rates",
761
+ retrievedAt: versionDate
762
+ }
763
+ };
764
+ }
765
+ function mergeCatalogWithCustomRates(catalog, customRates) {
766
+ if (!customRates || customRates.length === 0) return catalog;
767
+ const customEntries = customRates.map(customModelRateToCatalogEntry);
768
+ const rateFingerprints = customRates.map((r) => `${r.id}:${r.updatedAt}`).sort().join(";");
769
+ return {
770
+ version: `${catalog.version}+custom:${rateFingerprints}`,
771
+ entries: [...customEntries, ...catalog.entries]
772
+ };
773
+ }
710
774
  function deriveRetailEquivalentCosts(snapshot2, catalog = ANTHROPIC_PRICING_CATALOG, calculatedAt = snapshot2.observedAt) {
711
775
  const costs = [];
712
776
  const decisions = [];
@@ -732,13 +796,31 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
732
796
  return unavailable("model-unclassified");
733
797
  }
734
798
  const normalizedModel = normalized.model.trim().toLowerCase();
735
- const modelEntries = catalog.entries.filter(
736
- (entry2) => entry2.providerId === providerId && entry2.billingDomainId === observation.billingDomainId && [entry2.canonicalModel, ...entry2.aliases].some(
799
+ const matchingCandidates = catalog.entries.filter(
800
+ (entry2) => entry2.providerId === providerId && (entry2.billingDomainId === observation.billingDomainId || entry2.billingDomainId === "*") && [entry2.canonicalModel, ...entry2.aliases].some(
737
801
  (model) => model.trim().toLowerCase() === normalizedModel
738
802
  )
739
803
  );
740
- if (modelEntries.length === 0) return unavailable("model-unrecognized");
741
- const timeActiveEntries = modelEntries.filter(
804
+ let selectedEntries;
805
+ const customExact = matchingCandidates.filter(
806
+ (e) => e.contextTier === "custom-rate" && e.billingDomainId === observation.billingDomainId
807
+ );
808
+ if (customExact.length > 0) {
809
+ selectedEntries = customExact;
810
+ } else {
811
+ const customWildcard = matchingCandidates.filter(
812
+ (e) => e.contextTier === "custom-rate" && e.billingDomainId === "*"
813
+ );
814
+ if (customWildcard.length > 0) {
815
+ selectedEntries = customWildcard;
816
+ } else {
817
+ selectedEntries = matchingCandidates.filter(
818
+ (e) => e.contextTier !== "custom-rate" && e.billingDomainId === observation.billingDomainId
819
+ );
820
+ }
821
+ }
822
+ if (selectedEntries.length === 0) return unavailable("model-unrecognized");
823
+ const timeActiveEntries = selectedEntries.filter(
742
824
  (entry2) => observation.observedAt >= entry2.effectiveFrom && (!entry2.effectiveUntil || observation.observedAt < entry2.effectiveUntil)
743
825
  );
744
826
  if (timeActiveEntries.length === 0) return unavailable("price-not-effective");
@@ -877,7 +959,7 @@ function matchContext(rule, observation) {
877
959
  function preciseMoney(value) {
878
960
  return Number(value.toFixed(12));
879
961
  }
880
- var ANTHROPIC_PRICING_CATALOG, OPENCODE_GO_SOURCE, XAI_PRICING_SOURCE, OPENAI_GPT_56_SOURCE, OPEN_CODE_GO_SOURCE_PATH, openCodeHistory, OPEN_CODE_BASE_HISTORY, OPEN_CODE_PRICE_HISTORY, OPEN_CODE_FLAT_MODELS, OPEN_CODE_CONTEXT_MODELS, DEEPSEEK_GO_MODELS, WEEKDAY_PEAK_RANGES, DEEPSEEK_OFFICIAL_SOURCE, DEEPSEEK_OFFICIAL_MODELS, DEEPSEEK_OFFICIAL_EFFECTIVE_FROM, DIRECT_OFFICIAL_PRICING_CATALOG, OFFICIAL_PRICING_CATALOG;
962
+ var ANTHROPIC_PRICING_CATALOG, OPENCODE_GO_SOURCE, XAI_PRICING_SOURCE, OPENAI_GPT_56_SOURCE, OPEN_CODE_GO_SOURCE_PATH, openCodeHistory, OPEN_CODE_BASE_HISTORY, OPEN_CODE_PRICE_HISTORY, OPEN_CODE_FLAT_MODELS, OPEN_CODE_CONTEXT_MODELS, DEEPSEEK_GO_MODELS, WEEKDAY_PEAK_RANGES, DEEPSEEK_OFFICIAL_SOURCE, DEEPSEEK_OFFICIAL_MODELS, DEEPSEEK_OFFICIAL_EFFECTIVE_FROM, GOOGLE_ANTIGRAVITY_SOURCE, GOOGLE_ANTIGRAVITY_EFFECTIVE_FROM, DIRECT_OFFICIAL_PRICING_CATALOG, OFFICIAL_PRICING_CATALOG;
881
963
  var init_retail_pricing = __esm({
882
964
  "src/core/retail-pricing.ts"() {
883
965
  "use strict";
@@ -885,6 +967,25 @@ var init_retail_pricing = __esm({
885
967
  ANTHROPIC_PRICING_CATALOG = {
886
968
  version: "2026-08-28",
887
969
  entries: [
970
+ anthropicEntry({
971
+ id: "anthropic-fable-5-1-2026-09-01",
972
+ priceVersion: "anthropic-2026-09-01",
973
+ model: "claude-fable-5-1",
974
+ aliases: [
975
+ "Claude Fable 5.1",
976
+ "claude-fable-5.1",
977
+ "claude-fable-5-1-20260901",
978
+ "claude-fable-5.1-20260901",
979
+ "fable-5.1",
980
+ "fable-5-1"
981
+ ],
982
+ effectiveFrom: "2026-09-01T00:00:00.000Z",
983
+ input: 10,
984
+ output: 50,
985
+ cacheRead: 0.25,
986
+ sourceUrl: "https://platform.claude.com/docs/en/about-claude/pricing",
987
+ retrievedAt: "2026-09-03"
988
+ }),
888
989
  {
889
990
  id: "anthropic-fable-5-2026-06-09",
890
991
  priceVersion: "anthropic-2026-06-09",
@@ -1086,6 +1187,12 @@ var init_retail_pricing = __esm({
1086
1187
  ["deepseek-v4-flash-vision-exp", 0.22, 0.66, 7e-3]
1087
1188
  ];
1088
1189
  DEEPSEEK_OFFICIAL_EFFECTIVE_FROM = "2026-08-17T00:00:00.000Z";
1190
+ GOOGLE_ANTIGRAVITY_SOURCE = {
1191
+ title: "Google Gemini API pricing",
1192
+ url: "https://ai.google.dev/pricing",
1193
+ retrievedAt: "2026-09-02"
1194
+ };
1195
+ GOOGLE_ANTIGRAVITY_EFFECTIVE_FROM = "2026-01-01T00:00:00.000Z";
1089
1196
  DIRECT_OFFICIAL_PRICING_CATALOG = {
1090
1197
  version: "2026-08-28-grok-4.6-build",
1091
1198
  entries: [
@@ -1222,11 +1329,91 @@ var init_retail_pricing = __esm({
1222
1329
  output: 12,
1223
1330
  cacheRead: 1,
1224
1331
  contextRule: { kind: "prompt-tokens", minimumExclusive: 2e5 }
1332
+ }),
1333
+ antigravityEntry({
1334
+ id: "antigravity-gemini-3.7-flash-2026-09-02",
1335
+ model: "gemini-3.7-flash",
1336
+ aliases: [
1337
+ "gemini-3.7-flash-high",
1338
+ "gemini-3.7-flash-medium",
1339
+ "gemini-3.7-flash-low",
1340
+ "gemini-3p7-flash-exp-d",
1341
+ "gemini-3.7-flash-exp",
1342
+ "Gemini 3.7 Flash (High)",
1343
+ "Gemini 3.7 Flash (Medium)",
1344
+ "Gemini 3.7 Flash (Low)"
1345
+ ],
1346
+ input: 0.1,
1347
+ output: 0.4,
1348
+ cacheRead: 0.025
1349
+ }),
1350
+ antigravityEntry({
1351
+ id: "antigravity-gemini-3.6-flash-2026-09-02",
1352
+ model: "gemini-3.6-flash",
1353
+ aliases: [
1354
+ "gemini-3.6-flash-high",
1355
+ "gemini-3.6-flash-medium",
1356
+ "gemini-3.6-flash-low",
1357
+ "Gemini 3.6 Flash (High)",
1358
+ "Gemini 3.6 Flash (Medium)",
1359
+ "Gemini 3.6 Flash (Low)"
1360
+ ],
1361
+ input: 0.1,
1362
+ output: 0.4,
1363
+ cacheRead: 0.025
1364
+ }),
1365
+ antigravityEntry({
1366
+ id: "antigravity-gemini-3.1-pro-2026-09-02",
1367
+ model: "gemini-3.1-pro",
1368
+ aliases: [
1369
+ "gemini-3.1-pro-high",
1370
+ "gemini-3.1-pro-low",
1371
+ "gemini-3p1-pro-exp",
1372
+ "Gemini 3.1 Pro (High)",
1373
+ "Gemini 3.1 Pro (Low)"
1374
+ ],
1375
+ input: 1.25,
1376
+ output: 5,
1377
+ cacheRead: 0.3125
1378
+ }),
1379
+ antigravityEntry({
1380
+ id: "antigravity-claude-sonnet-4-6-2026-09-02",
1381
+ model: "claude-sonnet-4-6",
1382
+ aliases: ["claude-sonnet-4-6-thinking", "Claude Sonnet 4.6 (Thinking)"],
1383
+ input: 3,
1384
+ output: 15,
1385
+ cacheRead: 0.3,
1386
+ source: {
1387
+ title: "Anthropic Claude API pricing",
1388
+ url: "https://platform.claude.com/docs/en/about-claude/pricing",
1389
+ retrievedAt: "2026-09-02"
1390
+ }
1391
+ }),
1392
+ antigravityEntry({
1393
+ id: "antigravity-claude-opus-4-6-2026-09-02",
1394
+ model: "claude-opus-4-6-thinking",
1395
+ aliases: ["claude-opus-4-6", "Claude Opus 4.6 (Thinking)"],
1396
+ input: 15,
1397
+ output: 75,
1398
+ cacheRead: 1.5,
1399
+ source: {
1400
+ title: "Anthropic Claude API pricing",
1401
+ url: "https://platform.claude.com/docs/en/about-claude/pricing",
1402
+ retrievedAt: "2026-09-02"
1403
+ }
1404
+ }),
1405
+ antigravityEntry({
1406
+ id: "antigravity-gpt-oss-120b-2026-09-02",
1407
+ model: "gpt-oss-120b-medium",
1408
+ aliases: ["gpt-oss-120b", "GPT-OSS 120B (Medium)"],
1409
+ input: 0.15,
1410
+ output: 0.6,
1411
+ cacheRead: 0.05
1225
1412
  })
1226
1413
  ]
1227
1414
  };
1228
1415
  OFFICIAL_PRICING_CATALOG = {
1229
- version: "2026-09-01-dsh-deepseek-official",
1416
+ version: "2026-09-03-fable-5.1",
1230
1417
  entries: [
1231
1418
  ...DIRECT_OFFICIAL_PRICING_CATALOG.entries,
1232
1419
  ...openCodeLocalHistoryEntries(DIRECT_OFFICIAL_PRICING_CATALOG.entries)
@@ -1612,6 +1799,7 @@ var init_plan_pricing = __esm({
1612
1799
  });
1613
1800
 
1614
1801
  // src/core/usage-application.ts
1802
+ import { randomUUID } from "crypto";
1615
1803
  function safeConnectorFailure(error) {
1616
1804
  if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
1617
1805
  return { code: error.code, message: error.message, recovery: error.recovery };
@@ -1719,7 +1907,7 @@ function notificationLevel(value) {
1719
1907
  function pickAgentProviderIndex(overview) {
1720
1908
  return {
1721
1909
  generatedAt: overview.generatedAt,
1722
- providers: overview.providers.filter((provider) => provider.id !== "opencode").map(({ id, displayName }) => ({ id, displayName }))
1910
+ providers: overview.providers.filter((provider) => provider.id !== "opencode" && provider.id !== "dsh").map(({ id, displayName }) => ({ id, displayName }))
1723
1911
  };
1724
1912
  }
1725
1913
  var UsageApplication;
@@ -1743,6 +1931,7 @@ var init_usage_application = __esm({
1743
1931
  #connectorPolicies;
1744
1932
  #notifier;
1745
1933
  #startAtLoginManager;
1934
+ #basePriceCatalog;
1746
1935
  #priceCatalog;
1747
1936
  #planCatalog;
1748
1937
  #refreshPromise = null;
@@ -1765,10 +1954,16 @@ var init_usage_application = __esm({
1765
1954
  this.#connectorPolicies = options.connectorPolicies ?? {};
1766
1955
  this.#notifier = options.notifier;
1767
1956
  this.#startAtLoginManager = options.startAtLoginManager;
1768
- this.#priceCatalog = options.priceCatalog === void 0 ? OFFICIAL_PRICING_CATALOG : options.priceCatalog;
1957
+ this.#basePriceCatalog = options.priceCatalog === void 0 ? OFFICIAL_PRICING_CATALOG : options.priceCatalog;
1958
+ this.#priceCatalog = this.#resolvePriceCatalog();
1769
1959
  this.#planCatalog = options.planCatalog ?? SUBSCRIPTION_PLAN_CATALOG;
1770
1960
  this.#processingStatus = createProcessingStatus(this.#clock().toISOString(), false);
1771
1961
  }
1962
+ #resolvePriceCatalog() {
1963
+ if (!this.#basePriceCatalog) return null;
1964
+ const customRates = this.#repository.getCustomModelRates?.() ?? [];
1965
+ return mergeCatalogWithCustomRates(this.#basePriceCatalog, customRates);
1966
+ }
1772
1967
  async #backfillRetailCosts(mode) {
1773
1968
  if (!this.#priceCatalog || !this.#repository.getRetailPricingBackfillPage && !this.#repository.getRetailPricingBackfillSnapshots || !this.#repository.saveDerivedCosts) {
1774
1969
  return;
@@ -2115,6 +2310,57 @@ var init_usage_application = __esm({
2115
2310
  });
2116
2311
  return this.getPlanSettings();
2117
2312
  }
2313
+ async getCustomModelRates() {
2314
+ return this.#repository.getCustomModelRates?.() ?? [];
2315
+ }
2316
+ async getCustomModelRate(id) {
2317
+ return this.#repository.getCustomModelRate?.(id) ?? null;
2318
+ }
2319
+ async setCustomModelRate(input) {
2320
+ const providerId = input.providerId?.trim();
2321
+ if (!providerId) throw new Error("Provider ID is required");
2322
+ const model = input.model?.trim();
2323
+ if (!model) throw new Error("Model name is required");
2324
+ if (!Number.isFinite(input.inputRate) || input.inputRate < 0) {
2325
+ throw new Error("Input rate must be a non-negative number");
2326
+ }
2327
+ if (!Number.isFinite(input.outputRate) || input.outputRate < 0) {
2328
+ throw new Error("Output rate must be a non-negative number");
2329
+ }
2330
+ const cacheReadRate = input.cacheReadRate ?? 0;
2331
+ if (!Number.isFinite(cacheReadRate) || cacheReadRate < 0) {
2332
+ throw new Error("Cache read rate must be a non-negative number");
2333
+ }
2334
+ const rate = {
2335
+ id: input.id || `rate-${randomUUID()}`,
2336
+ providerId,
2337
+ billingDomainId: input.billingDomainId?.trim() || null,
2338
+ model,
2339
+ ratesPerMillion: {
2340
+ input: input.inputRate,
2341
+ output: input.outputRate,
2342
+ cacheRead: cacheReadRate
2343
+ },
2344
+ updatedAt: this.#clock().toISOString()
2345
+ };
2346
+ await this.#queueDatabaseWrite(() => {
2347
+ this.#repository.saveCustomModelRate?.(rate);
2348
+ });
2349
+ this.#priceCatalog = this.#resolvePriceCatalog();
2350
+ await this.#backfillRetailCosts("hard-rebuild");
2351
+ return rate;
2352
+ }
2353
+ async deleteCustomModelRate(id) {
2354
+ let deleted = false;
2355
+ await this.#queueDatabaseWrite(() => {
2356
+ deleted = this.#repository.deleteCustomModelRate?.(id) ?? false;
2357
+ });
2358
+ if (deleted) {
2359
+ this.#priceCatalog = this.#resolvePriceCatalog();
2360
+ await this.#backfillRetailCosts("hard-rebuild");
2361
+ }
2362
+ return deleted;
2363
+ }
2118
2364
  /**
2119
2365
  * A billing domain can carry a plan price when its connector does not report
2120
2366
  * an actual metered charge. Metered domains keep their own billed amounts and
@@ -2301,7 +2547,10 @@ var init_usage_application = __esm({
2301
2547
  return this.#repository.getProviderOverview?.(now, providerId, query) ?? this.#repository.getOverview(now, query).providers.find((provider) => provider.id === providerId) ?? null;
2302
2548
  }
2303
2549
  async exportUsage(request) {
2304
- const overview = this.#repository.getOverview(this.#clock(), request);
2550
+ const overview = this.#repository.getOverview(this.#clock(), {
2551
+ ...request,
2552
+ auditEvidence: true
2553
+ });
2305
2554
  const accountIdentifiers = request.includeAccountIdentifiers ? this.#repository.getProviderAccountIdentifiers() : {};
2306
2555
  return buildUsageExport(overview, request, accountIdentifiers);
2307
2556
  }
@@ -2560,6 +2809,20 @@ var init_catalog = __esm({
2560
2809
  billingDomain: { id: "deepseek-official", displayName: "DeepSeek API" }
2561
2810
  },
2562
2811
  officialCredentialPaths: [".dsh"]
2812
+ },
2813
+ {
2814
+ id: "antigravity",
2815
+ displayName: "Antigravity",
2816
+ command: "agy",
2817
+ permissionDescription: "Read local Antigravity conversation session databases for token history. No credential file is opened.",
2818
+ credentialOwner: "official-client",
2819
+ experimental: true,
2820
+ expectedCoverage: ["quota", "tokens", "history"],
2821
+ target: {
2822
+ provider: { id: "antigravity", displayName: "Antigravity" },
2823
+ billingDomain: { id: "code-assist-subscription", displayName: "Gemini Code Assist" }
2824
+ },
2825
+ officialCredentialPaths: [".gemini/antigravity-cli", ".gemini/antigravity"]
2563
2826
  }
2564
2827
  ];
2565
2828
  }
@@ -4127,6 +4390,186 @@ var init_opencode_go_connector = __esm({
4127
4390
  }
4128
4391
  });
4129
4392
 
4393
+ // src/connectors/grok-build/grok-build-connector.ts
4394
+ import { z as z6 } from "zod";
4395
+ function incompleteTranscriptFailure3() {
4396
+ return {
4397
+ code: "local-transcript-scan-incomplete",
4398
+ message: "Some local Grok history could not be read.",
4399
+ recovery: "Agent Usage will retry automatically without removing stored history."
4400
+ };
4401
+ }
4402
+ function grokBuildBillingDomain() {
4403
+ return {
4404
+ id: "grok-build-subscription",
4405
+ displayName: "Grok Build / SuperGrok shared pool"
4406
+ };
4407
+ }
4408
+ function isGrokOfficialModel(model) {
4409
+ if (!model) return false;
4410
+ const normalized = model.trim().toLowerCase();
4411
+ if (normalized === "grok") return true;
4412
+ if (normalized.startsWith("grok-") || normalized.startsWith("grok/")) {
4413
+ return true;
4414
+ }
4415
+ return false;
4416
+ }
4417
+ function resolveGrokBillingDomain(model, customEndpoints) {
4418
+ if (!model) return "grok-build-subscription";
4419
+ const normalized = model.trim().toLowerCase();
4420
+ if (customEndpoints?.has(normalized)) {
4421
+ return customEndpoints.get(normalized);
4422
+ }
4423
+ if (isGrokOfficialModel(model)) {
4424
+ return "grok-build-subscription";
4425
+ }
4426
+ return "custom";
4427
+ }
4428
+ function grokBuildBillingDomains(usage = []) {
4429
+ const primary = grokBuildBillingDomain();
4430
+ const observed = [...new Set(usage.map((o) => o.billingDomainId))].filter((id) => id !== primary.id).sort();
4431
+ return [
4432
+ primary,
4433
+ ...observed.map((id) => ({
4434
+ id,
4435
+ displayName: id === "custom" ? "Custom endpoints" : id
4436
+ }))
4437
+ ];
4438
+ }
4439
+ function mapBillingQuota(billing) {
4440
+ const config = billing.config;
4441
+ if (!config) return [];
4442
+ const usedPercent = config.creditUsagePercent ?? derivePercent(config.used?.val, config.monthlyLimit?.val);
4443
+ if (usedPercent === null) return [];
4444
+ const periodType = config.currentPeriod?.type;
4445
+ const period = nativePeriod(periodType, config.currentPeriod ? void 0 : "monthly");
4446
+ const resetsAt = config.currentPeriod?.end ?? config.billingPeriodEnd ?? null;
4447
+ return [
4448
+ {
4449
+ id: `grok-build:${period.id}`,
4450
+ billingDomainId: "grok-build-subscription",
4451
+ label: period.label,
4452
+ usedPercent,
4453
+ windowDurationMinutes: nativeWindowDurationMinutes(
4454
+ config.currentPeriod?.start ?? config.billingPeriodStart,
4455
+ resetsAt,
4456
+ period.id
4457
+ ),
4458
+ resetsAt,
4459
+ authority: "official-client",
4460
+ scope: "account-wide",
4461
+ status: billing.subscriptionTier ?? null
4462
+ }
4463
+ ];
4464
+ }
4465
+ function nativeWindowDurationMinutes(startsAt, resetsAt, period) {
4466
+ const start = startsAt ? Date.parse(startsAt) : Number.NaN;
4467
+ const end = resetsAt ? Date.parse(resetsAt) : Number.NaN;
4468
+ if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
4469
+ return Math.round((end - start) / 6e4);
4470
+ }
4471
+ if (period === "weekly") return 10080;
4472
+ if (period === "monthly") return 43200;
4473
+ return null;
4474
+ }
4475
+ function nativePeriod(type, fallback) {
4476
+ if (type === "USAGE_PERIOD_TYPE_WEEKLY") return { id: "weekly", label: "Weekly limit" };
4477
+ if (type === "USAGE_PERIOD_TYPE_MONTHLY" || fallback === "monthly") {
4478
+ return { id: "monthly", label: "Monthly limit" };
4479
+ }
4480
+ return { id: "usage", label: "Usage" };
4481
+ }
4482
+ function derivePercent(used, limit) {
4483
+ if (used === void 0 || limit === void 0 || limit <= 0) return null;
4484
+ return Math.min(100, Math.max(0, used / limit * 100));
4485
+ }
4486
+ function safeFailure5(error) {
4487
+ if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
4488
+ return { code: error.code, message: error.message, recovery: error.recovery };
4489
+ }
4490
+ return {
4491
+ code: "grok-billing-adapter-failed",
4492
+ message: "Grok Build subscription quota is unavailable.",
4493
+ recovery: "Open Grok Build and run /usage, then update Grok Build before retrying."
4494
+ };
4495
+ }
4496
+ var centSchema, usagePeriodSchema, billingConfigSchema, grokBillingResponseSchema, GrokBuildConnector;
4497
+ var init_grok_build_connector = __esm({
4498
+ "src/connectors/grok-build/grok-build-connector.ts"() {
4499
+ "use strict";
4500
+ centSchema = z6.object({ val: z6.number().default(0) }).passthrough();
4501
+ usagePeriodSchema = z6.object({
4502
+ type: z6.string().optional(),
4503
+ start: z6.string().optional(),
4504
+ end: z6.string().optional()
4505
+ }).passthrough();
4506
+ billingConfigSchema = z6.object({
4507
+ creditUsagePercent: z6.number().min(0).max(100).optional(),
4508
+ currentPeriod: usagePeriodSchema.optional(),
4509
+ monthlyLimit: centSchema.optional(),
4510
+ used: centSchema.optional(),
4511
+ billingPeriodStart: z6.string().optional(),
4512
+ billingPeriodEnd: z6.string().optional(),
4513
+ isUnifiedBillingUser: z6.boolean().optional()
4514
+ }).passthrough();
4515
+ grokBillingResponseSchema = z6.object({
4516
+ config: billingConfigSchema.nullable(),
4517
+ onDemandEnabled: z6.boolean().nullable().optional(),
4518
+ subscriptionTier: z6.string().optional(),
4519
+ sourceObservedAt: z6.string().datetime({ offset: true }).optional()
4520
+ }).passthrough();
4521
+ GrokBuildConnector = class {
4522
+ id = "grok";
4523
+ displayName = "Grok";
4524
+ consentId = "grok";
4525
+ #billingClient;
4526
+ #historyClient;
4527
+ #clock;
4528
+ constructor(options) {
4529
+ this.#billingClient = options.billingClient;
4530
+ this.#historyClient = options.historyClient;
4531
+ this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
4532
+ }
4533
+ async collect(options = { mode: "incremental" }) {
4534
+ const warnings = [];
4535
+ let quotaBuckets = [];
4536
+ let observedAt = this.#clock().toISOString();
4537
+ try {
4538
+ const billing = await this.#billingClient.readBilling();
4539
+ observedAt = billing.sourceObservedAt ?? observedAt;
4540
+ quotaBuckets = mapBillingQuota(billing);
4541
+ if (quotaBuckets.length === 0) {
4542
+ warnings.push({
4543
+ code: "grok-subscription-quota-unavailable",
4544
+ message: "Grok Build subscription quota is unavailable.",
4545
+ recovery: "Open Grok Build and run /usage, then retry refresh."
4546
+ });
4547
+ }
4548
+ } catch (error) {
4549
+ warnings.push(safeFailure5(error));
4550
+ }
4551
+ const history = this.#historyClient ? await this.#historyClient.readUsage(options) : { usage: [], costs: [], complete: true };
4552
+ if (!history.complete) warnings.push(incompleteTranscriptFailure3());
4553
+ return {
4554
+ provider: { id: this.id, displayName: this.displayName },
4555
+ billingDomains: grokBuildBillingDomains(history.usage),
4556
+ quotaBuckets,
4557
+ usage: history.usage,
4558
+ ...history.usage.length > 0 && history.complete ? {
4559
+ usageReconciliation: {
4560
+ authoritativeIdPrefixes: ["grok-transcript:"],
4561
+ retiredIdPrefixes: ["grok-otel:", "grok-headless:"]
4562
+ }
4563
+ } : {},
4564
+ costs: history.costs,
4565
+ warnings,
4566
+ observedAt
4567
+ };
4568
+ }
4569
+ };
4570
+ }
4571
+ });
4572
+
4130
4573
  // src/server/zstd-frames.ts
4131
4574
  import { zstdDecompressSync } from "zlib";
4132
4575
  async function readZstdFramedText(source) {
@@ -4220,6 +4663,34 @@ import { createReadStream } from "fs";
4220
4663
  import { mkdir as mkdir2, opendir, readFile as readFile3, rename as rename2, stat, writeFile as writeFile2 } from "fs/promises";
4221
4664
  import { dirname, join as join3 } from "path";
4222
4665
  import { createInterface } from "readline";
4666
+ function loadGrokConfigCustomModels(configContent) {
4667
+ const customModels = /* @__PURE__ */ new Map();
4668
+ const modelSections = [
4669
+ ...configContent.matchAll(/\[model\.(?:"([^"]+)"|([a-zA-Z0-9_-]+))\]([\s\S]*?)(?=\n\[|$)/g)
4670
+ ];
4671
+ for (const m of modelSections) {
4672
+ const key = m[1] || m[2];
4673
+ const body = m[3];
4674
+ const modelMatch = body.match(/model\s*=\s*"([^"]+)"/);
4675
+ const baseUrlMatch = body.match(/base_url\s*=\s*"([^"]+)"/);
4676
+ const providerMatch = body.match(/model_provider\s*=\s*"([^"]+)"/);
4677
+ if (baseUrlMatch || providerMatch) {
4678
+ const domain = providerMatch ? providerMatch[1].trim() : "custom";
4679
+ if (key) {
4680
+ customModels.set(key.trim().toLowerCase(), domain);
4681
+ const stripped = key.trim().toLowerCase().replace(/-(high|medium|low)$/, "");
4682
+ if (stripped) customModels.set(stripped, domain);
4683
+ }
4684
+ if (modelMatch) {
4685
+ const mName = modelMatch[1].trim().toLowerCase();
4686
+ customModels.set(mName, domain);
4687
+ const stripped = mName.replace(/-(high|medium|low)$/, "");
4688
+ if (stripped) customModels.set(stripped, domain);
4689
+ }
4690
+ }
4691
+ }
4692
+ return customModels;
4693
+ }
4223
4694
  function isCachedTranscriptFile(value) {
4224
4695
  const record = asObject(value);
4225
4696
  if (!record || !Array.isArray(record.records)) return false;
@@ -4261,7 +4732,7 @@ function isParsedTranscriptRecord(value) {
4261
4732
  function nonNegativeSafeInteger(value) {
4262
4733
  return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
4263
4734
  }
4264
- function parseGrokTranscriptLine(line) {
4735
+ function parseGrokTranscriptLine(line, grokCustomModels) {
4265
4736
  const record = parseObject(line);
4266
4737
  const params = asObject(record?.params);
4267
4738
  const update = asObject(params?.update);
@@ -4305,11 +4776,12 @@ function parseGrokTranscriptLine(line) {
4305
4776
  const dedupeKey = `${sessionId}:${promptId ?? stableId(line)}:${model}`;
4306
4777
  const directCost = grokCostUsd(totals.costUsdTicks);
4307
4778
  const reportedCostUsd = directCost ?? (remainingCost !== null && untickedTokens > 0 ? remainingCost * (grokRecordedTokens(totals) / untickedTokens) : null);
4779
+ const billingDomainId = resolveGrokBillingDomain(model, grokCustomModels);
4308
4780
  return {
4309
4781
  dedupeKey,
4310
4782
  observation: {
4311
4783
  id: `grok-transcript:${stableId(dedupeKey)}`,
4312
- billingDomainId: "grok-build-subscription",
4784
+ billingDomainId,
4313
4785
  model,
4314
4786
  sessionId,
4315
4787
  observedAt,
@@ -4634,6 +5106,7 @@ var init_local_transcript_usage_client = __esm({
4634
5106
  "src/server/local-transcript-usage-client.ts"() {
4635
5107
  "use strict";
4636
5108
  init_token_normalization();
5109
+ init_grok_build_connector();
4637
5110
  init_zstd_frames();
4638
5111
  DSH_PRIMARY_BILLING_DOMAIN_ID = "deepseek-official";
4639
5112
  DSH_SESSION_FORMAT_VERSION = 0;
@@ -4647,6 +5120,8 @@ var init_local_transcript_usage_client = __esm({
4647
5120
  #cachePath;
4648
5121
  #fileCache = /* @__PURE__ */ new Map();
4649
5122
  #cacheLoaded = false;
5123
+ #grokCustomModels = /* @__PURE__ */ new Map();
5124
+ #grokCustomModelsLoaded = false;
4650
5125
  constructor(options) {
4651
5126
  this.#provider = options.provider;
4652
5127
  this.#roots = options.roots;
@@ -4655,6 +5130,19 @@ var init_local_transcript_usage_client = __esm({
4655
5130
  this.#cachePath = options.cachePath;
4656
5131
  }
4657
5132
  async readUsage(options = { mode: "incremental" }) {
5133
+ if (this.#provider === "grok" && !this.#grokCustomModelsLoaded) {
5134
+ this.#grokCustomModelsLoaded = true;
5135
+ for (const root of this.#roots) {
5136
+ try {
5137
+ const configPath = join3(dirname(root), "config.toml");
5138
+ const content = await readFile3(configPath, "utf8");
5139
+ for (const [k, v] of loadGrokConfigCustomModels(content)) {
5140
+ this.#grokCustomModels.set(k, v);
5141
+ }
5142
+ } catch {
5143
+ }
5144
+ }
5145
+ }
4658
5146
  await this.#loadCache();
4659
5147
  if (options.mode === "hard-rebuild") this.#fileCache.clear();
4660
5148
  const cutoff = this.#clock().getTime() - this.#lookbackDays * 24 * 60 * 60 * 1e3;
@@ -4702,6 +5190,22 @@ var init_local_transcript_usage_client = __esm({
4702
5190
  const cacheKey = stableId(file.path);
4703
5191
  const cached = this.#fileCache.get(cacheKey);
4704
5192
  if (cached && cached.size === file.size && cached.mtimeMs === file.mtimeMs) {
5193
+ if (this.#provider === "grok") {
5194
+ const remapped = cached.records.map((r) => {
5195
+ const expectedDomain = resolveGrokBillingDomain(
5196
+ r.observation.model,
5197
+ this.#grokCustomModels
5198
+ );
5199
+ if (r.observation.billingDomainId !== expectedDomain) {
5200
+ return {
5201
+ ...r,
5202
+ observation: { ...r.observation, billingDomainId: expectedDomain }
5203
+ };
5204
+ }
5205
+ return r;
5206
+ });
5207
+ return { records: remapped, complete: true, unsupportedFormat: false };
5208
+ }
4705
5209
  return { records: cached.records, complete: true, unsupportedFormat: false };
4706
5210
  }
4707
5211
  const records = [];
@@ -4745,7 +5249,7 @@ var init_local_transcript_usage_client = __esm({
4745
5249
  if (this.#provider === "dsh") {
4746
5250
  return DSH_LINE_HINTS.some((hint) => line.includes(hint)) ? parseDshTranscriptLine(line, dshState) : [];
4747
5251
  }
4748
- return line.includes('"turn_completed"') ? parseGrokTranscriptLine(line) : [];
5252
+ return line.includes('"turn_completed"') ? parseGrokTranscriptLine(line, this.#grokCustomModels) : [];
4749
5253
  }
4750
5254
  async #loadCache() {
4751
5255
  if (this.#cacheLoaded) return;
@@ -4800,7 +5304,7 @@ function unsupportedFormatFailure() {
4800
5304
  recovery: "Update Agent Usage; stored dsh history from earlier scans is retained."
4801
5305
  };
4802
5306
  }
4803
- function safeFailure5(error) {
5307
+ function safeFailure6(error) {
4804
5308
  if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
4805
5309
  return { code: error.code, message: error.message, recovery: error.recovery };
4806
5310
  }
@@ -4832,7 +5336,7 @@ var init_dsh_connector = __esm({
4832
5336
  try {
4833
5337
  history = await this.#historyClient.readUsage(options);
4834
5338
  } catch (error) {
4835
- warnings.push(safeFailure5(error));
5339
+ warnings.push(safeFailure6(error));
4836
5340
  }
4837
5341
  if (history?.unsupportedFormat) warnings.push(unsupportedFormatFailure());
4838
5342
  else if (history && !history.complete) warnings.push(incompleteSessionLogFailure());
@@ -4857,151 +5361,696 @@ var init_dsh_connector = __esm({
4857
5361
  }
4858
5362
  });
4859
5363
 
4860
- // src/connectors/grok-build/grok-build-connector.ts
4861
- import { z as z6 } from "zod";
4862
- function incompleteTranscriptFailure3() {
4863
- return {
4864
- code: "local-transcript-scan-incomplete",
4865
- message: "Some local Grok history could not be read.",
4866
- recovery: "Agent Usage will retry automatically without removing stored history."
4867
- };
4868
- }
4869
- function grokBuildBillingDomain() {
4870
- return {
4871
- id: "grok-build-subscription",
4872
- displayName: "Grok Build / SuperGrok shared pool"
4873
- };
5364
+ // src/server/antigravity-quota-client.ts
5365
+ import { readdirSync, readFileSync } from "fs";
5366
+ import { homedir as homedir2 } from "os";
5367
+ import { join as join4 } from "path";
5368
+ function mapQuotaGroups(groups) {
5369
+ const buckets = [];
5370
+ for (const group of groups) {
5371
+ const isGemini = /gemini/i.test(group.displayName ?? "");
5372
+ const isClaudeOrGpt = /claude|gpt/i.test(group.displayName ?? "");
5373
+ for (const rawBucket of group.buckets ?? []) {
5374
+ const windowStr = (rawBucket.window ?? "").toLowerCase();
5375
+ const displayStr = (rawBucket.displayName ?? "").toLowerCase();
5376
+ const is5h = windowStr === "5h" || /5[- ]?hour/i.test(displayStr);
5377
+ const isWeekly = windowStr === "weekly" || /week/i.test(displayStr);
5378
+ let durationMinutes;
5379
+ let windowName;
5380
+ if (is5h) {
5381
+ durationMinutes = 300;
5382
+ windowName = "5 hour";
5383
+ } else if (isWeekly) {
5384
+ durationMinutes = 10080;
5385
+ windowName = "Week";
5386
+ } else {
5387
+ continue;
5388
+ }
5389
+ let label = windowName;
5390
+ if (!isGemini) {
5391
+ if (isClaudeOrGpt) {
5392
+ label = `Claude / GPT \xB7 ${windowName}`;
5393
+ } else if (group.displayName) {
5394
+ label = `${group.displayName} \xB7 ${windowName}`;
5395
+ }
5396
+ }
5397
+ const usedPercent = typeof rawBucket.remainingFraction === "number" ? Math.max(0, Math.min(100, Math.round((1 - rawBucket.remainingFraction) * 100))) : null;
5398
+ buckets.push({
5399
+ id: rawBucket.bucketId || `${isGemini ? "gemini" : "3p"}-${windowStr}`,
5400
+ billingDomainId: ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID,
5401
+ label,
5402
+ usedPercent,
5403
+ windowDurationMinutes: durationMinutes,
5404
+ resetsAt: rawBucket.resetTime ?? null,
5405
+ authority: "official-client",
5406
+ scope: "account-wide"
5407
+ });
5408
+ }
5409
+ }
5410
+ return buckets;
4874
5411
  }
4875
- function mapBillingQuota(billing) {
4876
- const config = billing.config;
4877
- if (!config) return [];
4878
- const usedPercent = config.creditUsagePercent ?? derivePercent(config.used?.val, config.monthlyLimit?.val);
4879
- if (usedPercent === null) return [];
4880
- const periodType = config.currentPeriod?.type;
4881
- const period = nativePeriod(periodType, config.currentPeriod ? void 0 : "monthly");
4882
- const resetsAt = config.currentPeriod?.end ?? config.billingPeriodEnd ?? null;
5412
+ var AntigravityQuotaClient;
5413
+ var init_antigravity_quota_client = __esm({
5414
+ "src/server/antigravity-quota-client.ts"() {
5415
+ "use strict";
5416
+ init_antigravity_connector();
5417
+ AntigravityQuotaClient = class {
5418
+ #logRoots;
5419
+ #ports;
5420
+ #fetchFn;
5421
+ #timeoutMs;
5422
+ constructor(options = {}) {
5423
+ this.#logRoots = options.logRoots ?? [
5424
+ join4(homedir2(), "Library/Logs/Antigravity"),
5425
+ join4(homedir2(), ".gemini/antigravity-cli/log"),
5426
+ join4(homedir2(), ".gemini/antigravity/log")
5427
+ ];
5428
+ this.#ports = options.ports;
5429
+ this.#fetchFn = options.fetchFn ?? globalThis.fetch;
5430
+ this.#timeoutMs = options.timeoutMs ?? 1500;
5431
+ }
5432
+ async readQuota() {
5433
+ const { ports, csrfToken } = this.discoverCandidates();
5434
+ const candidatePorts = this.#ports ?? ports;
5435
+ if (candidatePorts.length === 0) {
5436
+ return null;
5437
+ }
5438
+ for (const port of candidatePorts) {
5439
+ const quota = await this.queryPort(port, csrfToken);
5440
+ if (quota) {
5441
+ return quota;
5442
+ }
5443
+ }
5444
+ return null;
5445
+ }
5446
+ discoverCandidates() {
5447
+ let csrfToken = process.env.ANTIGRAVITY_CSRF_TOKEN ?? null;
5448
+ const ports = [];
5449
+ const httpPortRegex = /(?:Language server listening on random port at|Reloading all windows with URL: https?:\/\/127\.0\.0\.1:|Local:\s+https?:\/\/127\.0\.0\.1:|Host bridge server listening on http:\/\/127\.0\.0\.1:)\s*(\d+)/g;
5450
+ const csrfRegex = /--csrf_token[= ]([a-f0-9-]+)/;
5451
+ for (const root of this.#logRoots) {
5452
+ try {
5453
+ const files = readdirSync(root).filter((file) => file.endsWith(".log")).sort().reverse().slice(0, 10);
5454
+ for (const file of files) {
5455
+ const filePath = join4(root, file);
5456
+ try {
5457
+ const content = readFileSync(filePath, "utf8");
5458
+ if (!csrfToken) {
5459
+ const csrfMatch = csrfRegex.exec(content);
5460
+ if (csrfMatch) csrfToken = csrfMatch[1];
5461
+ }
5462
+ let match;
5463
+ while ((match = httpPortRegex.exec(content)) !== null) {
5464
+ const basePort = Number(match[1]);
5465
+ ports.push(basePort);
5466
+ ports.push(basePort + 1);
5467
+ ports.push(basePort + 2);
5468
+ }
5469
+ } catch {
5470
+ }
5471
+ }
5472
+ } catch {
5473
+ }
5474
+ }
5475
+ return {
5476
+ ports: [...new Set(ports.reverse())],
5477
+ csrfToken
5478
+ };
5479
+ }
5480
+ discoverCandidatePorts() {
5481
+ return this.discoverCandidates().ports;
5482
+ }
5483
+ async queryPort(port, csrfToken = null) {
5484
+ try {
5485
+ const url = `http://127.0.0.1:${port}/exa.language_server_pb.LanguageServerService/RetrieveUserQuotaSummary`;
5486
+ const response = await this.#fetchFn(url, {
5487
+ method: "POST",
5488
+ headers: {
5489
+ "Content-Type": "application/json",
5490
+ "Connect-Protocol-Version": "1",
5491
+ ...csrfToken ? { "x-codeium-csrf-token": csrfToken } : {}
5492
+ },
5493
+ body: JSON.stringify({}),
5494
+ signal: AbortSignal.timeout(this.#timeoutMs)
5495
+ });
5496
+ if (!response.ok) {
5497
+ return null;
5498
+ }
5499
+ const body = await response.json();
5500
+ const groups = body.response?.groups;
5501
+ if (!Array.isArray(groups) || groups.length === 0) {
5502
+ return null;
5503
+ }
5504
+ return mapQuotaGroups(groups);
5505
+ } catch {
5506
+ return null;
5507
+ }
5508
+ }
5509
+ };
5510
+ }
5511
+ });
5512
+
5513
+ // src/connectors/antigravity/antigravity-connector.ts
5514
+ function buildAntigravityQuotaBuckets(usage, nowMs, limits = DEFAULT_ANTIGRAVITY_QUOTA_LIMITS) {
5515
+ const fiveHoursMs = 300 * 60 * 1e3;
5516
+ const sevenDaysMs = 7 * 24 * 60 * 60 * 1e3;
5517
+ const cutoff5h = nowMs - fiveHoursMs;
5518
+ const cutoff7d = nowMs - sevenDaysMs;
5519
+ const obs5h = [];
5520
+ const obs7d = [];
5521
+ for (const obs of usage) {
5522
+ const t = new Date(obs.observedAt).getTime();
5523
+ if (t >= cutoff5h) obs5h.push(obs);
5524
+ if (t >= cutoff7d) obs7d.push(obs);
5525
+ }
5526
+ const tokens5h = obs5h.reduce(
5527
+ (acc, obs) => acc + obs.inputTokens + obs.outputTokens + obs.cacheReadTokens,
5528
+ 0
5529
+ );
5530
+ const usedPercent5h = Math.min(100, Math.round(tokens5h / limits.rolling5hTokens * 100));
5531
+ let resetsAt5h;
5532
+ if (obs5h.length > 0) {
5533
+ const oldestMs = Math.min(...obs5h.map((o) => new Date(o.observedAt).getTime()));
5534
+ resetsAt5h = new Date(oldestMs + fiveHoursMs).toISOString();
5535
+ } else {
5536
+ resetsAt5h = new Date(nowMs + fiveHoursMs).toISOString();
5537
+ }
5538
+ const tokens7d = obs7d.reduce(
5539
+ (acc, obs) => acc + obs.inputTokens + obs.outputTokens + obs.cacheReadTokens,
5540
+ 0
5541
+ );
5542
+ const usedPercent7d = Math.min(100, Math.round(tokens7d / limits.weeklyTokens * 100));
5543
+ let resetsAt7d;
5544
+ if (obs7d.length > 0) {
5545
+ const oldestMs = Math.min(...obs7d.map((o) => new Date(o.observedAt).getTime()));
5546
+ resetsAt7d = new Date(oldestMs + sevenDaysMs).toISOString();
5547
+ } else {
5548
+ resetsAt7d = new Date(nowMs + sevenDaysMs).toISOString();
5549
+ }
4883
5550
  return [
4884
5551
  {
4885
- id: `grok-build:${period.id}`,
4886
- billingDomainId: "grok-build-subscription",
4887
- label: period.label,
4888
- usedPercent,
4889
- windowDurationMinutes: nativeWindowDurationMinutes(
4890
- config.currentPeriod?.start ?? config.billingPeriodStart,
4891
- resetsAt,
4892
- period.id
4893
- ),
4894
- resetsAt,
4895
- authority: "official-client",
4896
- scope: "account-wide",
4897
- status: billing.subscriptionTier ?? null
5552
+ id: "gemini-5h",
5553
+ billingDomainId: ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID,
5554
+ label: "5 hour",
5555
+ usedPercent: usedPercent5h,
5556
+ windowDurationMinutes: 300,
5557
+ resetsAt: resetsAt5h,
5558
+ authority: "local-observation",
5559
+ scope: "local-only"
5560
+ },
5561
+ {
5562
+ id: "gemini-weekly",
5563
+ billingDomainId: ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID,
5564
+ label: "Week",
5565
+ usedPercent: usedPercent7d,
5566
+ windowDurationMinutes: 10080,
5567
+ resetsAt: resetsAt7d,
5568
+ authority: "local-observation",
5569
+ scope: "local-only"
4898
5570
  }
4899
5571
  ];
4900
5572
  }
4901
- function nativeWindowDurationMinutes(startsAt, resetsAt, period) {
4902
- const start = startsAt ? Date.parse(startsAt) : Number.NaN;
4903
- const end = resetsAt ? Date.parse(resetsAt) : Number.NaN;
4904
- if (Number.isFinite(start) && Number.isFinite(end) && end > start) {
4905
- return Math.round((end - start) / 6e4);
4906
- }
4907
- if (period === "weekly") return 10080;
4908
- if (period === "monthly") return 43200;
4909
- return null;
4910
- }
4911
- function nativePeriod(type, fallback) {
4912
- if (type === "USAGE_PERIOD_TYPE_WEEKLY") return { id: "weekly", label: "Weekly limit" };
4913
- if (type === "USAGE_PERIOD_TYPE_MONTHLY" || fallback === "monthly") {
4914
- return { id: "monthly", label: "Monthly limit" };
4915
- }
4916
- return { id: "usage", label: "Usage" };
5573
+ function billingDomains2(usage) {
5574
+ const primary = {
5575
+ id: ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID,
5576
+ displayName: "Gemini Code Assist"
5577
+ };
5578
+ const observedRoutes = [...new Set(usage.map((obs) => obs.billingDomainId))].filter((id) => id !== primary.id).sort();
5579
+ return [primary, ...observedRoutes.map((id) => ({ id, displayName: id }))];
4917
5580
  }
4918
- function derivePercent(used, limit) {
4919
- if (used === void 0 || limit === void 0 || limit <= 0) return null;
4920
- return Math.min(100, Math.max(0, used / limit * 100));
5581
+ function incompleteSessionScanFailure() {
5582
+ return {
5583
+ code: "antigravity-session-scan-incomplete",
5584
+ message: "Some local Antigravity conversation databases could not be read.",
5585
+ recovery: "Agent Usage will retry automatically on the next scan without removing stored history."
5586
+ };
4921
5587
  }
4922
- function safeFailure6(error) {
4923
- if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
4924
- return { code: error.code, message: error.message, recovery: error.recovery };
4925
- }
5588
+ function safeFailure7() {
4926
5589
  return {
4927
- code: "grok-billing-adapter-failed",
4928
- message: "Grok Build subscription quota is unavailable.",
4929
- recovery: "Open Grok Build and run /usage, then update Grok Build before retrying."
5590
+ code: "antigravity-sqlite-read-failed",
5591
+ message: "Failed to read local Antigravity conversation stores.",
5592
+ recovery: "Check read permissions for ~/.gemini/antigravity-cli and ~/.gemini/antigravity directories."
4930
5593
  };
4931
5594
  }
4932
- var centSchema, usagePeriodSchema, billingConfigSchema, grokBillingResponseSchema, GrokBuildConnector;
4933
- var init_grok_build_connector = __esm({
4934
- "src/connectors/grok-build/grok-build-connector.ts"() {
5595
+ var ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID, DEFAULT_ANTIGRAVITY_QUOTA_LIMITS, AntigravityConnector;
5596
+ var init_antigravity_connector = __esm({
5597
+ "src/connectors/antigravity/antigravity-connector.ts"() {
4935
5598
  "use strict";
4936
- centSchema = z6.object({ val: z6.number().default(0) }).passthrough();
4937
- usagePeriodSchema = z6.object({
4938
- type: z6.string().optional(),
4939
- start: z6.string().optional(),
4940
- end: z6.string().optional()
4941
- }).passthrough();
4942
- billingConfigSchema = z6.object({
4943
- creditUsagePercent: z6.number().min(0).max(100).optional(),
4944
- currentPeriod: usagePeriodSchema.optional(),
4945
- monthlyLimit: centSchema.optional(),
4946
- used: centSchema.optional(),
4947
- billingPeriodStart: z6.string().optional(),
4948
- billingPeriodEnd: z6.string().optional(),
4949
- isUnifiedBillingUser: z6.boolean().optional()
4950
- }).passthrough();
4951
- grokBillingResponseSchema = z6.object({
4952
- config: billingConfigSchema.nullable(),
4953
- onDemandEnabled: z6.boolean().nullable().optional(),
4954
- subscriptionTier: z6.string().optional(),
4955
- sourceObservedAt: z6.string().datetime({ offset: true }).optional()
4956
- }).passthrough();
4957
- GrokBuildConnector = class {
4958
- id = "grok";
4959
- displayName = "Grok";
4960
- consentId = "grok";
4961
- #billingClient;
5599
+ init_antigravity_quota_client();
5600
+ ANTIGRAVITY_PRIMARY_BILLING_DOMAIN_ID = "code-assist-subscription";
5601
+ DEFAULT_ANTIGRAVITY_QUOTA_LIMITS = {
5602
+ rolling5hTokens: 15e6,
5603
+ weeklyTokens: 1e8
5604
+ };
5605
+ AntigravityConnector = class {
5606
+ id = "antigravity";
5607
+ displayName = "Antigravity";
5608
+ consentId = "antigravity";
4962
5609
  #historyClient;
5610
+ #quotaClient;
4963
5611
  #clock;
5612
+ #quotaLimits;
4964
5613
  constructor(options) {
4965
- this.#billingClient = options.billingClient;
4966
5614
  this.#historyClient = options.historyClient;
5615
+ this.#quotaClient = options.quotaClient ?? new AntigravityQuotaClient();
4967
5616
  this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
5617
+ this.#quotaLimits = {
5618
+ rolling5hTokens: options.quotaLimits?.rolling5hTokens ?? DEFAULT_ANTIGRAVITY_QUOTA_LIMITS.rolling5hTokens,
5619
+ weeklyTokens: options.quotaLimits?.weeklyTokens ?? DEFAULT_ANTIGRAVITY_QUOTA_LIMITS.weeklyTokens
5620
+ };
4968
5621
  }
4969
5622
  async collect(options = { mode: "incremental" }) {
5623
+ const observedAt = this.#clock().toISOString();
5624
+ const nowMs = this.#clock().getTime();
4970
5625
  const warnings = [];
5626
+ let history = null;
5627
+ try {
5628
+ history = await this.#historyClient.readUsage(options);
5629
+ } catch {
5630
+ warnings.push(safeFailure7());
5631
+ }
5632
+ if (history && !history.complete) {
5633
+ warnings.push(incompleteSessionScanFailure());
5634
+ }
5635
+ const usage = history?.usage ?? [];
4971
5636
  let quotaBuckets = [];
4972
- let observedAt = this.#clock().toISOString();
4973
5637
  try {
4974
- const billing = await this.#billingClient.readBilling();
4975
- observedAt = billing.sourceObservedAt ?? observedAt;
4976
- quotaBuckets = mapBillingQuota(billing);
4977
- if (quotaBuckets.length === 0) {
4978
- warnings.push({
4979
- code: "grok-subscription-quota-unavailable",
4980
- message: "Grok Build subscription quota is unavailable.",
4981
- recovery: "Open Grok Build and run /usage, then retry refresh."
4982
- });
5638
+ const liveQuota = await this.#quotaClient.readQuota();
5639
+ if (liveQuota && liveQuota.length > 0) {
5640
+ quotaBuckets = [...liveQuota];
5641
+ }
5642
+ } catch {
5643
+ }
5644
+ const localBuckets = buildAntigravityQuotaBuckets(usage, nowMs, this.#quotaLimits);
5645
+ const has5h = quotaBuckets.some((b) => b.label === "5 hour" || b.id === "gemini-5h");
5646
+ const hasWeekly = quotaBuckets.some((b) => b.label === "Week" || b.id === "gemini-weekly");
5647
+ if (!has5h || !hasWeekly) {
5648
+ for (const localBucket of localBuckets) {
5649
+ if (!quotaBuckets.some((b) => b.id === localBucket.id || b.label === localBucket.label)) {
5650
+ quotaBuckets.push(localBucket);
5651
+ }
5652
+ }
5653
+ }
5654
+ return {
5655
+ provider: { id: this.id, displayName: this.displayName },
5656
+ billingDomains: billingDomains2(usage),
5657
+ quotaBuckets,
5658
+ usage,
5659
+ ...usage.length > 0 && history?.complete ? {
5660
+ usageReconciliation: {
5661
+ authoritativeIdPrefixes: ["antigravity:"],
5662
+ retiredIdPrefixes: []
5663
+ }
5664
+ } : {},
5665
+ costs: history?.costs ?? [],
5666
+ warnings,
5667
+ observedAt
5668
+ };
5669
+ }
5670
+ };
5671
+ }
5672
+ });
5673
+
5674
+ // src/server/antigravity-sqlite-usage-client.ts
5675
+ import { createHash as createHash3 } from "crypto";
5676
+ import { readdir, readFile as readFile4, rename as rename3, stat as stat2, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
5677
+ import { createRequire as createRequire2 } from "module";
5678
+ import { dirname as dirname2, join as join5 } from "path";
5679
+ import protobuf from "protobufjs";
5680
+ function parseAntigravityDatabase(dbPath, conversationId, fallbackMtimeMs) {
5681
+ const db = new DatabaseSync2(dbPath, { readOnly: true });
5682
+ try {
5683
+ const tableExists = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='gen_metadata'").get();
5684
+ if (!tableExists) return [];
5685
+ const stepTimestamps = /* @__PURE__ */ new Map();
5686
+ try {
5687
+ const stepRows = db.prepare("SELECT idx, metadata FROM steps WHERE length(metadata) > 0").all();
5688
+ for (const row of stepRows) {
5689
+ const ts = extractTimestampFromStepMetadata(row.metadata);
5690
+ if (ts !== null) stepTimestamps.set(row.idx, ts);
5691
+ }
5692
+ } catch {
5693
+ }
5694
+ const genRows = db.prepare("SELECT idx, data FROM gen_metadata ORDER BY idx ASC").all();
5695
+ const records = [];
5696
+ for (const row of genRows) {
5697
+ if (!row.data || row.data.length === 0) continue;
5698
+ const parsed = decodeGenMetadata(row.data);
5699
+ if (parsed.inputTokens === 0 && parsed.outputTokens === 0 && parsed.cacheReadTokens === 0) {
5700
+ continue;
5701
+ }
5702
+ const timestampMs = stepTimestamps.get(row.idx) ?? fallbackMtimeMs;
5703
+ const observedAt = new Date(timestampMs).toISOString();
5704
+ const dedupeKey = `antigravity:${conversationId}:${row.idx}`;
5705
+ const observation = {
5706
+ id: dedupeKey,
5707
+ billingDomainId: "code-assist-subscription",
5708
+ model: parsed.canonicalModel,
5709
+ observedAt,
5710
+ inputTokens: parsed.inputTokens,
5711
+ outputTokens: parsed.outputTokens,
5712
+ reasoningTokens: 0,
5713
+ cacheReadTokens: parsed.cacheReadTokens,
5714
+ cacheWriteTokens: 0,
5715
+ tokenSemantics: {
5716
+ reasoning: "included-in-output",
5717
+ cacheRead: "separate",
5718
+ cacheWrite: "separate"
5719
+ },
5720
+ modelAttribution: "known",
5721
+ timePrecision: "event",
5722
+ usageScope: "this-mac",
5723
+ aggregationTemporality: "delta",
5724
+ authority: "local-observation"
5725
+ };
5726
+ normalizeTokenObservation(observation);
5727
+ records.push({ dedupeKey, observation });
5728
+ }
5729
+ return records;
5730
+ } finally {
5731
+ db.close();
5732
+ }
5733
+ }
5734
+ function decodeGenMetadata(buf) {
5735
+ let rawModel = "unknown";
5736
+ let inputTokens = 0;
5737
+ const outputTokens = 0;
5738
+ let cacheReadTokens = 0;
5739
+ try {
5740
+ const r = protobuf.Reader.create(buf);
5741
+ while (r.pos < r.len) {
5742
+ const tag = r.uint32();
5743
+ const fn = tag >>> 3;
5744
+ const wt = tag & 7;
5745
+ if (fn === 1 && wt === 2) {
5746
+ const sub1 = r.bytes();
5747
+ const rSub = protobuf.Reader.create(sub1);
5748
+ while (rSub.pos < rSub.len) {
5749
+ const sTag = rSub.uint32();
5750
+ const sFn = sTag >>> 3;
5751
+ const sWt = sTag & 7;
5752
+ if (sFn === 19 && sWt === 2) {
5753
+ rawModel = Buffer.from(rSub.bytes()).toString("utf8");
5754
+ } else if (sFn === 9 && sWt === 2) {
5755
+ const sub9 = rSub.bytes();
5756
+ const r9 = protobuf.Reader.create(sub9);
5757
+ while (r9.pos < r9.len) {
5758
+ const t9 = r9.uint32();
5759
+ const fn9 = t9 >>> 3;
5760
+ const wt9 = t9 & 7;
5761
+ if (fn9 === 10 && wt9 === 2) {
5762
+ const sub10 = r9.bytes();
5763
+ const r10 = protobuf.Reader.create(sub10);
5764
+ while (r10.pos < r10.len) {
5765
+ const t10 = r10.uint32();
5766
+ const fn10 = t10 >>> 3;
5767
+ const wt10 = t10 & 7;
5768
+ if (fn10 === 1 && wt10 === 0) {
5769
+ inputTokens = toNumeric(r10.uint64());
5770
+ } else if (fn10 === 3 && wt10 === 2) {
5771
+ const sub3 = r10.bytes();
5772
+ const r3 = protobuf.Reader.create(sub3);
5773
+ while (r3.pos < r3.len) {
5774
+ const t3 = r3.uint32();
5775
+ const fn3 = t3 >>> 3;
5776
+ const wt3 = t3 & 7;
5777
+ if (fn3 === 2 && wt3 === 0) {
5778
+ cacheReadTokens = toNumeric(r3.uint64());
5779
+ } else {
5780
+ r3.skipType(wt3);
5781
+ }
5782
+ }
5783
+ } else {
5784
+ r10.skipType(wt10);
5785
+ }
5786
+ }
5787
+ } else {
5788
+ r9.skipType(wt9);
5789
+ }
5790
+ }
5791
+ } else {
5792
+ rSub.skipType(sWt);
5793
+ }
5794
+ }
5795
+ } else {
5796
+ r.skipType(wt);
5797
+ }
5798
+ }
5799
+ } catch {
5800
+ }
5801
+ return {
5802
+ rawModel,
5803
+ canonicalModel: canonicalizeAntigravityModel(rawModel),
5804
+ inputTokens,
5805
+ outputTokens,
5806
+ cacheReadTokens
5807
+ };
5808
+ }
5809
+ function canonicalizeAntigravityModel(raw) {
5810
+ const normalized = raw.trim().toLowerCase().replace(/[\s_]+/g, "-");
5811
+ if (normalized.startsWith("gemini-3p7") || normalized.startsWith("gemini-3.7")) {
5812
+ return "gemini-3.7-flash";
5813
+ }
5814
+ if (normalized.startsWith("gemini-3p6") || normalized.startsWith("gemini-3.6")) {
5815
+ return "gemini-3.6-flash";
5816
+ }
5817
+ if (normalized.startsWith("gemini-3p1") || normalized.startsWith("gemini-3.1")) {
5818
+ return "gemini-3.1-pro";
5819
+ }
5820
+ if (normalized.includes("claude-sonnet")) {
5821
+ return "claude-sonnet-4-6";
5822
+ }
5823
+ if (normalized.includes("claude-opus")) {
5824
+ return "claude-opus-4-6-thinking";
5825
+ }
5826
+ if (normalized.includes("gpt-oss")) {
5827
+ return "gpt-oss-120b-medium";
5828
+ }
5829
+ return raw.trim() || "unknown";
5830
+ }
5831
+ function extractTimestampFromStepMetadata(buf) {
5832
+ try {
5833
+ const r = protobuf.Reader.create(buf);
5834
+ while (r.pos < r.len) {
5835
+ const tag = r.uint32();
5836
+ const fn = tag >>> 3;
5837
+ const wt = tag & 7;
5838
+ if (fn === 1 && wt === 2) {
5839
+ const tsBytes = r.bytes();
5840
+ const rTs = protobuf.Reader.create(tsBytes);
5841
+ let seconds = 0;
5842
+ let nanos = 0;
5843
+ while (rTs.pos < rTs.len) {
5844
+ const t = rTs.uint32();
5845
+ const tFn = t >>> 3;
5846
+ const tWt = t & 7;
5847
+ if (tFn === 1 && tWt === 0) {
5848
+ seconds = toNumeric(rTs.uint64());
5849
+ } else if (tFn === 2 && tWt === 0) {
5850
+ nanos = toNumeric(rTs.uint64());
5851
+ } else {
5852
+ rTs.skipType(tWt);
5853
+ }
5854
+ }
5855
+ if (seconds > 0) {
5856
+ return seconds * 1e3 + Math.floor(nanos / 1e6);
5857
+ }
5858
+ } else {
5859
+ r.skipType(wt);
5860
+ }
5861
+ }
5862
+ } catch {
5863
+ }
5864
+ return null;
5865
+ }
5866
+ function toNumeric(val) {
5867
+ return Number(val?.toString?.() ?? 0);
5868
+ }
5869
+ function stableId2(value) {
5870
+ return createHash3("sha256").update(value).digest("hex").slice(0, 16);
5871
+ }
5872
+ function isCachedDatabaseFile(value) {
5873
+ if (typeof value !== "object" || value === null) return false;
5874
+ const rec = value;
5875
+ return typeof rec.path === "string" && typeof rec.size === "number" && typeof rec.mtimeMs === "number" && Array.isArray(rec.records) && typeof rec.recordsDigest === "string";
5876
+ }
5877
+ var DatabaseSync2, AntigravitySqliteUsageClient;
5878
+ var init_antigravity_sqlite_usage_client = __esm({
5879
+ "src/server/antigravity-sqlite-usage-client.ts"() {
5880
+ "use strict";
5881
+ init_token_normalization();
5882
+ init_retail_pricing();
5883
+ ({ DatabaseSync: DatabaseSync2 } = createRequire2(import.meta.url)(
5884
+ "node:sqlite"
5885
+ ));
5886
+ AntigravitySqliteUsageClient = class {
5887
+ #roots;
5888
+ #clock;
5889
+ #lookbackDays;
5890
+ #cachePath;
5891
+ #priceCatalog;
5892
+ #fileCache = /* @__PURE__ */ new Map();
5893
+ #cacheLoaded = false;
5894
+ constructor(options) {
5895
+ this.#roots = options.roots;
5896
+ this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
5897
+ this.#lookbackDays = options.lookbackDays ?? 90;
5898
+ this.#cachePath = options.cachePath;
5899
+ this.#priceCatalog = options.priceCatalog ?? OFFICIAL_PRICING_CATALOG;
5900
+ }
5901
+ async readUsage(options = { mode: "incremental" }) {
5902
+ await this.#loadCache();
5903
+ if (options.mode === "hard-rebuild") this.#fileCache.clear();
5904
+ const cutoff = this.#clock().getTime() - this.#lookbackDays * 24 * 60 * 60 * 1e3;
5905
+ const discoveredFiles = await this.#discoverDatabases(cutoff);
5906
+ const livePaths = new Set(discoveredFiles.map((file) => stableId2(file.path)));
5907
+ for (const path of this.#fileCache.keys()) {
5908
+ if (!livePaths.has(path)) this.#fileCache.delete(path);
5909
+ }
5910
+ const usage = [];
5911
+ const seenDedupeKeys = /* @__PURE__ */ new Set();
5912
+ let allComplete = true;
5913
+ for (const file of discoveredFiles) {
5914
+ const parsed = await this.#parseDatabaseFile(file);
5915
+ allComplete &&= parsed.complete;
5916
+ for (const record of parsed.records) {
5917
+ if (new Date(record.observation.observedAt).getTime() < cutoff) continue;
5918
+ if (seenDedupeKeys.has(record.dedupeKey)) continue;
5919
+ seenDedupeKeys.add(record.dedupeKey);
5920
+ usage.push(record.observation);
4983
5921
  }
4984
- } catch (error) {
4985
- warnings.push(safeFailure6(error));
4986
5922
  }
4987
- const history = this.#historyClient ? await this.#historyClient.readUsage(options) : { usage: [], costs: [], complete: true };
4988
- if (!history.complete) warnings.push(incompleteTranscriptFailure3());
5923
+ usage.sort((a, b) => a.observedAt.localeCompare(b.observedAt) || a.id.localeCompare(b.id));
5924
+ const { costs } = deriveRetailEquivalentCosts(
5925
+ {
5926
+ provider: { id: "antigravity", displayName: "Antigravity" },
5927
+ billingDomains: [{ id: "code-assist-subscription", displayName: "Gemini Code Assist" }],
5928
+ quotaBuckets: [],
5929
+ usage,
5930
+ costs: [],
5931
+ observedAt: this.#clock().toISOString()
5932
+ },
5933
+ this.#priceCatalog
5934
+ );
5935
+ await this.#persistCache();
4989
5936
  return {
4990
- provider: { id: this.id, displayName: this.displayName },
4991
- billingDomains: [grokBuildBillingDomain()],
4992
- quotaBuckets,
4993
- usage: history.usage,
4994
- ...history.usage.length > 0 && history.complete ? {
4995
- usageReconciliation: {
4996
- authoritativeIdPrefixes: ["grok-transcript:"],
4997
- retiredIdPrefixes: ["grok-otel:", "grok-headless:"]
4998
- }
4999
- } : {},
5000
- costs: history.costs,
5001
- warnings,
5002
- observedAt
5937
+ usage,
5938
+ costs,
5939
+ complete: allComplete
5003
5940
  };
5004
5941
  }
5942
+ async #discoverDatabases(cutoff) {
5943
+ const results = [];
5944
+ const seenDbPaths = /* @__PURE__ */ new Set();
5945
+ for (const root of this.#roots) {
5946
+ try {
5947
+ const rootStat = await stat2(root);
5948
+ if (!rootStat.isDirectory()) continue;
5949
+ } catch {
5950
+ continue;
5951
+ }
5952
+ const summariesPath = join5(root, "conversation_summaries.db");
5953
+ const candidateIds = /* @__PURE__ */ new Set();
5954
+ let summariesLoaded = false;
5955
+ try {
5956
+ const summariesStat = await stat2(summariesPath);
5957
+ if (summariesStat.isFile()) {
5958
+ const db = new DatabaseSync2(summariesPath, { readOnly: true });
5959
+ try {
5960
+ const rows = db.prepare("SELECT conversation_id, last_modified_time FROM conversation_summaries").all();
5961
+ for (const row of rows) {
5962
+ if (!row.conversation_id) continue;
5963
+ const modifiedMs = row.last_modified_time ? new Date(row.last_modified_time).getTime() : 0;
5964
+ if (modifiedMs >= cutoff || modifiedMs === 0) {
5965
+ candidateIds.add(row.conversation_id);
5966
+ }
5967
+ }
5968
+ summariesLoaded = true;
5969
+ } finally {
5970
+ db.close();
5971
+ }
5972
+ }
5973
+ } catch {
5974
+ }
5975
+ const convDir = join5(root, "conversations");
5976
+ try {
5977
+ const entries = await readdir(convDir);
5978
+ for (const entry of entries) {
5979
+ if (!entry.endsWith(".db")) continue;
5980
+ const convId = entry.slice(0, -3);
5981
+ if (summariesLoaded && candidateIds.size > 0 && !candidateIds.has(convId)) {
5982
+ continue;
5983
+ }
5984
+ const fullPath = join5(convDir, entry);
5985
+ if (seenDbPaths.has(fullPath)) continue;
5986
+ seenDbPaths.add(fullPath);
5987
+ try {
5988
+ const fileStat = await stat2(fullPath);
5989
+ if (fileStat.mtimeMs < cutoff && summariesLoaded) continue;
5990
+ results.push({
5991
+ path: fullPath,
5992
+ size: fileStat.size,
5993
+ mtimeMs: fileStat.mtimeMs,
5994
+ conversationId: convId
5995
+ });
5996
+ } catch {
5997
+ }
5998
+ }
5999
+ } catch {
6000
+ }
6001
+ }
6002
+ return results;
6003
+ }
6004
+ async #parseDatabaseFile(file) {
6005
+ const cached = this.#fileCache.get(stableId2(file.path));
6006
+ if (cached && cached.size === file.size && cached.mtimeMs === file.mtimeMs) {
6007
+ return { records: cached.records, complete: true };
6008
+ }
6009
+ try {
6010
+ const records = parseAntigravityDatabase(file.path, file.conversationId, file.mtimeMs);
6011
+ const digest = stableId2(JSON.stringify(records));
6012
+ this.#fileCache.set(stableId2(file.path), {
6013
+ path: file.path,
6014
+ size: file.size,
6015
+ mtimeMs: file.mtimeMs,
6016
+ records,
6017
+ recordsDigest: digest
6018
+ });
6019
+ return { records, complete: true };
6020
+ } catch {
6021
+ return { records: [], complete: false };
6022
+ }
6023
+ }
6024
+ async #loadCache() {
6025
+ if (this.#cacheLoaded) return;
6026
+ this.#cacheLoaded = true;
6027
+ if (!this.#cachePath) return;
6028
+ try {
6029
+ const content = await readFile4(this.#cachePath, "utf8");
6030
+ const stored = JSON.parse(content);
6031
+ if (stored.version !== 1 || !Array.isArray(stored.files)) return;
6032
+ for (const item of stored.files) {
6033
+ if (!isCachedDatabaseFile(item)) continue;
6034
+ if (item.recordsDigest !== stableId2(JSON.stringify(item.records))) continue;
6035
+ this.#fileCache.set(stableId2(item.path), item);
6036
+ }
6037
+ } catch {
6038
+ }
6039
+ }
6040
+ async #persistCache() {
6041
+ if (!this.#cachePath) return;
6042
+ try {
6043
+ await mkdir3(dirname2(this.#cachePath), { recursive: true, mode: 448 });
6044
+ const temporary = `${this.#cachePath}.${process.pid}.tmp`;
6045
+ await writeFile3(
6046
+ temporary,
6047
+ JSON.stringify({ version: 1, files: [...this.#fileCache.values()] }),
6048
+ { mode: 384 }
6049
+ );
6050
+ await rename3(temporary, this.#cachePath);
6051
+ } catch {
6052
+ }
6053
+ }
5005
6054
  };
5006
6055
  }
5007
6056
  });
@@ -5066,7 +6115,7 @@ function parseGrokOtlpMetrics(payload, receivedAt) {
5066
6115
  return snapshot(
5067
6116
  [...usage.entries()].map(([key, item]) => ({
5068
6117
  id: `grok-otel:${key}`,
5069
- billingDomainId: "grok-build-subscription",
6118
+ billingDomainId: resolveGrokBillingDomain(item.model),
5070
6119
  model: item.model,
5071
6120
  sessionId: item.sessionId,
5072
6121
  observedAt: item.timestamp,
@@ -5093,7 +6142,7 @@ function parseGrokOtlpMetrics(payload, receivedAt) {
5093
6142
  function snapshot(usage, warnings, receivedAt) {
5094
6143
  return {
5095
6144
  provider: { id: "grok", displayName: "Grok" },
5096
- billingDomains: [grokBuildBillingDomain()],
6145
+ billingDomains: grokBuildBillingDomains(usage),
5097
6146
  quotaBuckets: [],
5098
6147
  usage,
5099
6148
  costs: [],
@@ -5123,8 +6172,8 @@ var init_grok_telemetry = __esm({
5123
6172
  // src/connectors/grok-build/stdio-grok-billing-client.ts
5124
6173
  import { spawn as spawn3 } from "child_process";
5125
6174
  import { open } from "fs/promises";
5126
- import { homedir as homedir2 } from "os";
5127
- import { join as join4 } from "path";
6175
+ import { homedir as homedir3 } from "os";
6176
+ import { join as join6 } from "path";
5128
6177
  import { z as z7 } from "zod";
5129
6178
  function billingIsFresh(billing, now) {
5130
6179
  if (!billing.sourceObservedAt) return false;
@@ -5249,7 +6298,7 @@ var init_stdio_grok_billing_client = __esm({
5249
6298
  stdio: ["pipe", "pipe", "pipe"]
5250
6299
  }));
5251
6300
  this.#readUnifiedLog = options.readUnifiedLog ?? (() => readLogTail(
5252
- join4(process.env.GROK_HOME ?? join4(homedir2(), ".grok"), "logs", "unified.jsonl")
6301
+ join6(process.env.GROK_HOME ?? join6(homedir3(), ".grok"), "logs", "unified.jsonl")
5253
6302
  ));
5254
6303
  this.#refreshBillingLog = options.refreshBillingLog ?? (options.readUnifiedLog ? async () => void 0 : () => refreshBillingLogViaDashboard(this.#command, this.#timeoutMs));
5255
6304
  this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
@@ -5620,7 +6669,7 @@ function endpointFailure(error, label) {
5620
6669
  recovery: "Check network access and xAI status, then retry."
5621
6670
  };
5622
6671
  }
5623
- function safeFailure7(error) {
6672
+ function safeFailure8(error) {
5624
6673
  if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
5625
6674
  return { code: error.code, message: error.message, recovery: error.recovery };
5626
6675
  }
@@ -5875,7 +6924,7 @@ var init_xai_api_connector = __esm({
5875
6924
  costs: [],
5876
6925
  balances: [],
5877
6926
  invoices: [],
5878
- warnings: [safeFailure7(error)],
6927
+ warnings: [safeFailure8(error)],
5879
6928
  observedAt
5880
6929
  };
5881
6930
  }
@@ -6074,8 +7123,8 @@ var init_macos_notifier = __esm({
6074
7123
  });
6075
7124
 
6076
7125
  // src/server/macos-start-at-login-manager.ts
6077
- import { mkdir as mkdir3, rm, writeFile as writeFile3 } from "fs/promises";
6078
- import { dirname as dirname2, isAbsolute, join as join5 } from "path";
7126
+ import { mkdir as mkdir4, rm, writeFile as writeFile4 } from "fs/promises";
7127
+ import { dirname as dirname3, isAbsolute, join as join7 } from "path";
6079
7128
  function launchAgentPlist(options) {
6080
7129
  const label = options.label ?? LABEL;
6081
7130
  const arguments_ = [
@@ -6127,15 +7176,15 @@ var init_macos_start_at_login_manager = __esm({
6127
7176
  const label = validateLaunchAgentLabel(options.label ?? LABEL);
6128
7177
  if (options.nodeImport) validateNodeImport(options.nodeImport);
6129
7178
  this.#options = { ...options, label };
6130
- this.#path = join5(options.userHome, "Library", "LaunchAgents", `${label}.plist`);
7179
+ this.#path = join7(options.userHome, "Library", "LaunchAgents", `${label}.plist`);
6131
7180
  }
6132
7181
  async setEnabled(enabled) {
6133
7182
  if (!enabled) {
6134
7183
  await rm(this.#path, { force: true });
6135
7184
  return;
6136
7185
  }
6137
- await mkdir3(dirname2(this.#path), { recursive: true, mode: 448 });
6138
- await writeFile3(this.#path, launchAgentPlist(this.#options), { mode: 384 });
7186
+ await mkdir4(dirname3(this.#path), { recursive: true, mode: 448 });
7187
+ await writeFile4(this.#path, launchAgentPlist(this.#options), { mode: 384 });
6139
7188
  }
6140
7189
  async isEnabled() {
6141
7190
  try {
@@ -6252,7 +7301,7 @@ var init_otlp_protobuf = __esm({
6252
7301
  // src/server/local-server.ts
6253
7302
  import { createReadStream as createReadStream2, existsSync, statSync } from "fs";
6254
7303
  import { createServer } from "http";
6255
- import { extname, join as join6, normalize, resolve } from "path";
7304
+ import { extname, join as join8, normalize, resolve } from "path";
6256
7305
  import { randomBytes } from "crypto";
6257
7306
  import { z as z9 } from "zod";
6258
7307
  async function startLocalServer(options) {
@@ -6432,6 +7481,53 @@ async function startLocalServer(options) {
6432
7481
  sendJson(response, 200, await options.application.updatePlanSubscription(input));
6433
7482
  return;
6434
7483
  }
7484
+ if (request.method === "GET" && requestUrl.pathname === "/api/custom-rates") {
7485
+ const rates = await options.application.getCustomModelRates();
7486
+ sendJson(response, 200, { rates });
7487
+ return;
7488
+ }
7489
+ if (request.method === "GET" && requestUrl.pathname.startsWith("/api/custom-rates/")) {
7490
+ const id = decodeURIComponent(requestUrl.pathname.slice("/api/custom-rates/".length));
7491
+ const rate = await options.application.getCustomModelRate(id);
7492
+ if (!rate) {
7493
+ sendJson(response, 404, { error: "rate-not-found" });
7494
+ return;
7495
+ }
7496
+ sendJson(response, 200, { rate });
7497
+ return;
7498
+ }
7499
+ if (request.method === "POST" && requestUrl.pathname === "/api/custom-rates") {
7500
+ if (!validMutationOrigin(authentication, request, origin)) {
7501
+ sendJson(response, 403, { error: "invalid-origin" });
7502
+ return;
7503
+ }
7504
+ const input = customModelRateSchema.parse(await readJsonBody(request));
7505
+ const rate = await options.application.setCustomModelRate(input);
7506
+ sendJson(response, 200, { rate });
7507
+ return;
7508
+ }
7509
+ if ((request.method === "PUT" || request.method === "PATCH") && requestUrl.pathname.startsWith("/api/custom-rates/")) {
7510
+ if (!validMutationOrigin(authentication, request, origin)) {
7511
+ sendJson(response, 403, { error: "invalid-origin" });
7512
+ return;
7513
+ }
7514
+ const id = decodeURIComponent(requestUrl.pathname.slice("/api/custom-rates/".length));
7515
+ const body = await readJsonBody(request);
7516
+ const input = customModelRateSchema.parse({ ...body, id });
7517
+ const rate = await options.application.setCustomModelRate(input);
7518
+ sendJson(response, 200, { rate });
7519
+ return;
7520
+ }
7521
+ if (request.method === "DELETE" && requestUrl.pathname.startsWith("/api/custom-rates/")) {
7522
+ if (!validMutationOrigin(authentication, request, origin)) {
7523
+ sendJson(response, 403, { error: "invalid-origin" });
7524
+ return;
7525
+ }
7526
+ const id = decodeURIComponent(requestUrl.pathname.slice("/api/custom-rates/".length));
7527
+ const deleted = await options.application.deleteCustomModelRate(id);
7528
+ sendJson(response, 200, { deleted });
7529
+ return;
7530
+ }
6435
7531
  if (request.method === "GET" && requestUrl.pathname === "/api/monitoring") {
6436
7532
  sendJson(response, 200, await options.application.getMonitoringStatus());
6437
7533
  return;
@@ -6488,8 +7584,7 @@ async function startLocalServer(options) {
6488
7584
  }
6489
7585
  sendJson(response, 404, { error: "not-found" });
6490
7586
  } catch (error) {
6491
- const message = publicErrorMessage(error);
6492
- sendJson(response, 500, { error: "internal-error", message });
7587
+ respondWithFailure(response, error);
6493
7588
  }
6494
7589
  });
6495
7590
  await new Promise((resolveListening, reject) => {
@@ -6575,8 +7670,8 @@ async function serveStatic(response, pathname, staticDirectory) {
6575
7670
  const root = resolve(staticDirectory);
6576
7671
  const requested = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
6577
7672
  const normalized = normalize(requested);
6578
- const candidate = resolve(join6(root, normalized));
6579
- const indexFile = join6(root, "index.html");
7673
+ const candidate = resolve(join8(root, normalized));
7674
+ const indexFile = join8(root, "index.html");
6580
7675
  const file = candidate.startsWith(`${root}/`) && existsSync(candidate) && statSync(candidate).isFile() ? candidate : indexFile;
6581
7676
  if (!existsSync(file)) {
6582
7677
  sendText(response, 404, "Dashboard assets are not built");
@@ -6609,11 +7704,28 @@ function secureToken() {
6609
7704
  return randomBytes(32).toString("base64url");
6610
7705
  }
6611
7706
  function sendJson(response, status, body) {
7707
+ const payload = JSON.stringify(body);
6612
7708
  response.writeHead(status, {
6613
7709
  "content-type": "application/json; charset=utf-8",
6614
7710
  "cache-control": "no-store"
6615
7711
  });
6616
- response.end(JSON.stringify(body));
7712
+ response.end(payload);
7713
+ }
7714
+ function respondWithFailure(response, error) {
7715
+ const message = publicErrorMessage(error);
7716
+ if (response.headersSent || response.writableEnded) {
7717
+ process.stderr.write(`Agent Usage: a started response failed to finish: ${message}
7718
+ `);
7719
+ response.destroy();
7720
+ return;
7721
+ }
7722
+ try {
7723
+ sendJson(response, 500, { error: "internal-error", message });
7724
+ } catch {
7725
+ process.stderr.write(`Agent Usage: a request failed without a reportable reason.
7726
+ `);
7727
+ response.destroy();
7728
+ }
6617
7729
  }
6618
7730
  function sendOtlpProtobuf(response, status) {
6619
7731
  response.writeHead(status, {
@@ -6630,7 +7742,7 @@ function sendHtml(response, status, body) {
6630
7742
  response.writeHead(status, { "content-type": "text/html; charset=utf-8" });
6631
7743
  response.end(body);
6632
7744
  }
6633
- var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, planSubscriptionSchema, clearDataSchema, hardRebuildSchema;
7745
+ var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, planSubscriptionSchema, customModelRateSchema, clearDataSchema, hardRebuildSchema;
6634
7746
  var init_local_server = __esm({
6635
7747
  "src/server/local-server.ts"() {
6636
7748
  "use strict";
@@ -6664,6 +7776,15 @@ var init_local_server = __esm({
6664
7776
  anchorDate: z9.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
6665
7777
  }).nullable()
6666
7778
  });
7779
+ customModelRateSchema = z9.object({
7780
+ id: z9.string().optional(),
7781
+ providerId: z9.string().min(1),
7782
+ billingDomainId: z9.string().nullable().optional(),
7783
+ model: z9.string().min(1),
7784
+ inputRate: z9.number().nonnegative(),
7785
+ outputRate: z9.number().nonnegative(),
7786
+ cacheReadRate: z9.number().nonnegative().optional()
7787
+ });
6667
7788
  clearDataSchema = z9.object({ deleteProductSecrets: z9.boolean().default(false) });
6668
7789
  hardRebuildSchema = z9.object({ confirmExpensiveOperation: z9.literal(true) });
6669
7790
  }
@@ -6672,7 +7793,7 @@ var init_local_server = __esm({
6672
7793
  // src/server/path-discovery-probe.ts
6673
7794
  import { constants } from "fs";
6674
7795
  import { access } from "fs/promises";
6675
- import { delimiter, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "path";
7796
+ import { delimiter, isAbsolute as isAbsolute2, join as join9, resolve as resolve2 } from "path";
6676
7797
  async function isExecutable(path) {
6677
7798
  try {
6678
7799
  await access(path, constants.X_OK);
@@ -6718,7 +7839,7 @@ var init_path_discovery_probe = __esm({
6718
7839
  async #findExecutable(command) {
6719
7840
  if (command.includes("/")) return await isExecutable(command) ? resolve2(command) : null;
6720
7841
  for (const directory of this.#path.split(delimiter).filter(Boolean)) {
6721
- const candidate = join7(directory, command);
7842
+ const candidate = join9(directory, command);
6722
7843
  if (await isExecutable(candidate)) return candidate;
6723
7844
  }
6724
7845
  return null;
@@ -6729,8 +7850,8 @@ var init_path_discovery_probe = __esm({
6729
7850
 
6730
7851
  // src/server/sqlite-usage-repository.ts
6731
7852
  import { mkdirSync } from "fs";
6732
- import { createRequire as createRequire2 } from "module";
6733
- import { dirname as dirname3 } from "path";
7853
+ import { createRequire as createRequire3 } from "module";
7854
+ import { dirname as dirname4 } from "path";
6734
7855
  import { Worker } from "worker_threads";
6735
7856
  async function runDatabaseWorker(workerData) {
6736
7857
  return new Promise((resolve5, reject) => {
@@ -7096,7 +8217,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
7096
8217
  for (const observedAt of [
7097
8218
  domain.history.lastObservedAt,
7098
8219
  ...domain.quotaBuckets.map((bucket) => bucket.observedAt ?? null),
7099
- ...domain.costs.map((cost) => cost.observedAt),
8220
+ domain.lastCostObservedAt,
7100
8221
  ...domain.balances.map((balance) => balance.observedAt),
7101
8222
  ...domain.invoices.map((invoice) => invoice.createdAt)
7102
8223
  ]) {
@@ -7358,7 +8479,7 @@ function allDomainHistories(providers) {
7358
8479
  provider,
7359
8480
  domain,
7360
8481
  history: domain.history,
7361
- includedInHeadline: domain.id === provider.summaryBillingDomainId
8482
+ includedInHeadline: provider.id === "dsh" || provider.id === "grok" && domain.id !== "xai-api" ? true : domain.id === provider.summaryBillingDomainId
7362
8483
  }))
7363
8484
  );
7364
8485
  }
@@ -7436,25 +8557,28 @@ function buildWorkbenchModelRanking(histories, buckets, comparisonCurrency, reco
7436
8557
  const entries = histories.flatMap(
7437
8558
  ({ provider, domain, history, includedInHeadline }) => history.models.map((model) => {
7438
8559
  const retailEquivalent = buildWorkbenchMetric(
7439
- model.priceEvidence,
8560
+ model.priceEvidence ?? [],
7440
8561
  "retail-equivalent",
7441
8562
  comparisonCurrency,
7442
8563
  model.tokenEvidence.recordedTokens,
7443
8564
  history.exchangeRates
7444
8565
  );
7445
8566
  const reportedEstimate = buildWorkbenchMetric(
7446
- model.priceEvidence,
8567
+ model.priceEvidence ?? [],
7447
8568
  "reported-estimate",
7448
8569
  comparisonCurrency,
7449
8570
  model.tokenEvidence.recordedTokens,
7450
8571
  history.exchangeRates
7451
8572
  );
8573
+ const observations = model.observations ?? [];
8574
+ const priceEvidence = model.priceEvidence ?? [];
7452
8575
  const authorities = [
7453
- ...new Set(model.observations.map((observation) => observation.authority))
8576
+ ...new Set(observations.map((observation) => observation.authority))
7454
8577
  ].sort();
7455
- const lastObservedAt = [...model.observations].sort(
7456
- (left, right) => right.observedAt.localeCompare(left.observedAt)
7457
- )[0]?.observedAt ?? null;
8578
+ const lastObservedAt = observations.reduce(
8579
+ (latest, observation) => latest === null || observation.observedAt > latest ? observation.observedAt : latest,
8580
+ null
8581
+ ) ?? null;
7458
8582
  return {
7459
8583
  id: modelRankingId(provider.id, domain.id, model.model),
7460
8584
  providerId: provider.id,
@@ -7472,28 +8596,28 @@ function buildWorkbenchModelRanking(histories, buckets, comparisonCurrency, reco
7472
8596
  reportedShare: includedInHeadline && reportedEstimate.status === "available" && reportedEstimate.amount !== null && totalReportedEstimate.status === "available" && totalReportedEstimate.amount !== null && totalReportedEstimate.amount !== 0 ? reportedEstimate.amount / totalReportedEstimate.amount : null,
7473
8597
  authorities,
7474
8598
  lastObservedAt,
7475
- observations: model.observations,
7476
- priceEvidence: model.priceEvidence,
8599
+ composition: nonOverlappingComposition(observations, model.tokenTotals),
8600
+ priceSnapshots: distinctPriceSnapshots(priceEvidence),
7477
8601
  trend: buckets.map((bucket) => {
7478
- const observations = model.observations.filter(
8602
+ const bucketObservations = observations.filter(
7479
8603
  (observation) => observation.observedAt >= bucket.start && observation.observedAt < bucket.end
7480
8604
  );
7481
- const priceEvidence = model.priceEvidence.filter(
8605
+ const bucketPriceEvidence = priceEvidence.filter(
7482
8606
  (cost) => (cost.observedAt ?? "") >= bucket.start && (cost.observedAt ?? "") < bucket.end
7483
8607
  );
7484
- const tokenTotals = observations.reduce(
8608
+ const tokenTotals = bucketObservations.reduce(
7485
8609
  (total, observation) => addTokenTotals(total, observation.tokenTotals),
7486
8610
  zeroTokenTotals()
7487
8611
  );
7488
8612
  const intervalRetail = buildWorkbenchMetric(
7489
- priceEvidence,
8613
+ bucketPriceEvidence,
7490
8614
  "retail-equivalent",
7491
8615
  comparisonCurrency,
7492
8616
  tokenTotals.total,
7493
8617
  history.exchangeRates
7494
8618
  );
7495
8619
  const intervalReported = buildWorkbenchMetric(
7496
- priceEvidence,
8620
+ bucketPriceEvidence,
7497
8621
  "reported-estimate",
7498
8622
  comparisonCurrency,
7499
8623
  tokenTotals.total,
@@ -7503,14 +8627,19 @@ function buildWorkbenchModelRanking(histories, buckets, comparisonCurrency, reco
7503
8627
  start: bucket.start,
7504
8628
  end: bucket.end,
7505
8629
  label: bucket.label,
7506
- gap: observations.length === 0,
8630
+ gap: bucketObservations.length === 0,
7507
8631
  tokenTotals,
8632
+ recordedTokens: bucketObservations.reduce(
8633
+ (total, observation) => total + observation.recordedTokens,
8634
+ 0
8635
+ ),
7508
8636
  authorities: [
7509
- ...new Set(observations.map((observation) => observation.authority))
8637
+ ...new Set(bucketObservations.map((observation) => observation.authority))
7510
8638
  ].sort(),
7511
- lastObservedAt: [...observations].sort(
7512
- (left, right) => right.observedAt.localeCompare(left.observedAt)
7513
- )[0]?.observedAt ?? null,
8639
+ lastObservedAt: bucketObservations.reduce(
8640
+ (latest, observation) => latest === null || observation.observedAt > latest ? observation.observedAt : latest,
8641
+ null
8642
+ ) ?? null,
7514
8643
  retailEquivalent: {
7515
8644
  status: intervalRetail.status,
7516
8645
  amount: intervalRetail.amount,
@@ -7587,6 +8716,49 @@ function buildWorkbenchModelRanking(histories, buckets, comparisonCurrency, reco
7587
8716
  unclassified
7588
8717
  };
7589
8718
  }
8719
+ function dropAuditEvidence(providers) {
8720
+ for (const provider of providers) {
8721
+ for (const domain of provider.billingDomains) {
8722
+ for (const model of domain.history.models) {
8723
+ delete model.observations;
8724
+ delete model.priceEvidence;
8725
+ }
8726
+ delete domain.history.unclassified.observations;
8727
+ }
8728
+ }
8729
+ }
8730
+ function nonOverlappingComposition(observations, fallback) {
8731
+ if (observations.length === 0) {
8732
+ return {
8733
+ ...fallback,
8734
+ total: fallback.input + fallback.output + fallback.cacheRead + fallback.cacheWrite
8735
+ };
8736
+ }
8737
+ const totals = zeroTokenTotals();
8738
+ for (const observation of observations) {
8739
+ totals.input += observation.tokenTotals.input;
8740
+ totals.output += observation.tokenTotals.output;
8741
+ if (observation.tokenSemantics.reasoning === "separate") {
8742
+ totals.reasoning += observation.tokenTotals.reasoning;
8743
+ }
8744
+ if (observation.tokenSemantics.cacheRead === "separate") {
8745
+ totals.cacheRead += observation.tokenTotals.cacheRead;
8746
+ }
8747
+ if (observation.tokenSemantics.cacheWrite === "separate") {
8748
+ totals.cacheWrite += observation.tokenTotals.cacheWrite;
8749
+ }
8750
+ }
8751
+ totals.total = totals.input + totals.output + totals.reasoning + totals.cacheRead + totals.cacheWrite;
8752
+ return totals;
8753
+ }
8754
+ function distinctPriceSnapshots(priceEvidence) {
8755
+ const snapshots = /* @__PURE__ */ new Map();
8756
+ for (const evidence of priceEvidence) {
8757
+ const snapshot2 = evidence.priceSnapshot;
8758
+ if (snapshot2 && !snapshots.has(snapshot2.id)) snapshots.set(snapshot2.id, snapshot2);
8759
+ }
8760
+ return [...snapshots.values()];
8761
+ }
7590
8762
  function modelRankingId(providerId, billingDomainId, model) {
7591
8763
  return `${providerId}::${billingDomainId}::${model}`;
7592
8764
  }
@@ -8034,7 +9206,7 @@ function freshnessStatus(lastSuccessAt, now) {
8034
9206
  if (!lastSuccessAt) return "unavailable";
8035
9207
  return now.getTime() - new Date(lastSuccessAt).getTime() <= FRESHNESS_WINDOW_MS ? "fresh" : "stale";
8036
9208
  }
8037
- var FRESHNESS_WINDOW_MS, QUERY_INDEXES_SQL, DELETE_DERIVED_RETAIL_COSTS_SQL, DELETE_DEMO_PROVIDER_DATA_SQL, DatabaseSync2, RETENTION_AGGREGATE_SQL, SqliteUsageRepository;
9209
+ var FRESHNESS_WINDOW_MS, QUERY_INDEXES_SQL, DELETE_DERIVED_RETAIL_COSTS_SQL, DELETE_DEMO_PROVIDER_DATA_SQL, DatabaseSync3, RETENTION_AGGREGATE_SQL, SqliteUsageRepository;
8038
9210
  var init_sqlite_usage_repository = __esm({
8039
9211
  "src/server/sqlite-usage-repository.ts"() {
8040
9212
  "use strict";
@@ -8075,7 +9247,7 @@ var init_sqlite_usage_repository = __esm({
8075
9247
  DELETE FROM connector_settings WHERE id = 'demo';
8076
9248
  DELETE FROM providers WHERE id = 'demo';
8077
9249
  `;
8078
- ({ DatabaseSync: DatabaseSync2 } = createRequire2(import.meta.url)(
9250
+ ({ DatabaseSync: DatabaseSync3 } = createRequire3(import.meta.url)(
8079
9251
  "node:sqlite"
8080
9252
  ));
8081
9253
  RETENTION_AGGREGATE_SQL = `
@@ -8106,8 +9278,8 @@ var init_sqlite_usage_repository = __esm({
8106
9278
  constructor(databasePath, options = {}) {
8107
9279
  this.#databasePath = databasePath;
8108
9280
  this.#hideDemoProvider = options.hideDemoProvider ?? false;
8109
- mkdirSync(dirname3(databasePath), { recursive: true });
8110
- this.#database = new DatabaseSync2(databasePath);
9281
+ mkdirSync(dirname4(databasePath), { recursive: true });
9282
+ this.#database = new DatabaseSync3(databasePath);
8111
9283
  this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA foreign_keys = ON;");
8112
9284
  this.#migrate();
8113
9285
  }
@@ -8171,6 +9343,22 @@ var init_sqlite_usage_repository = __esm({
8171
9343
  limit_currency = excluded.limit_currency,
8172
9344
  fallback_status = excluded.fallback_status`
8173
9345
  );
9346
+ const reportedQuotaDomains = [
9347
+ ...new Set(snapshot2.quotaBuckets.map((bucket) => bucket.billingDomainId))
9348
+ ];
9349
+ if (reportedQuotaDomains.length > 0) {
9350
+ const retiredQuotaStatement = this.#database.prepare(
9351
+ `DELETE FROM quota_buckets
9352
+ WHERE provider_id = ? AND billing_domain_id = ? AND id NOT IN (${snapshot2.quotaBuckets.map(() => "?").join(", ")})`
9353
+ );
9354
+ for (const billingDomainId of reportedQuotaDomains) {
9355
+ retiredQuotaStatement.run(
9356
+ snapshot2.provider.id,
9357
+ billingDomainId,
9358
+ ...snapshot2.quotaBuckets.map((bucket) => bucket.id)
9359
+ );
9360
+ }
9361
+ }
8174
9362
  for (const bucket of snapshot2.quotaBuckets) {
8175
9363
  quotaStatement.run(
8176
9364
  snapshot2.provider.id,
@@ -8532,7 +9720,12 @@ var init_sqlite_usage_repository = __esm({
8532
9720
  price_snapshot_source_url, price_snapshot_context_tier,
8533
9721
  model, usage_observation_id, priced_tokens, line_items_json, calculated_at
8534
9722
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8535
- ON CONFLICT(provider_id, id) DO NOTHING`
9723
+ ON CONFLICT(provider_id, id) DO UPDATE SET
9724
+ billing_domain_id = excluded.billing_domain_id,
9725
+ amount = excluded.amount,
9726
+ price_snapshot_rates_json = excluded.price_snapshot_rates_json,
9727
+ line_items_json = excluded.line_items_json,
9728
+ calculated_at = excluded.calculated_at`
8536
9729
  );
8537
9730
  this.#database.exec("BEGIN IMMEDIATE");
8538
9731
  try {
@@ -8602,7 +9795,7 @@ var init_sqlite_usage_repository = __esm({
8602
9795
  );
8603
9796
  const riskSummary = buildRiskSummary(overviews);
8604
9797
  const planSubscriptions = this.getPlanSubscriptions();
8605
- return {
9798
+ const overview = {
8606
9799
  generatedAt: now.toISOString(),
8607
9800
  globalSummary: buildGlobalSummary(overviews, riskSummary, now, query),
8608
9801
  workbench: buildTokenMoneyWorkbench(
@@ -8615,13 +9808,15 @@ var init_sqlite_usage_repository = __esm({
8615
9808
  providers: overviews,
8616
9809
  riskSummary
8617
9810
  };
9811
+ if (!query.auditEvidence) dropAuditEvidence(overviews);
9812
+ return overview;
8618
9813
  }
8619
9814
  getAgentProviderIndex(now) {
8620
9815
  const providers = this.#database.prepare("SELECT id, display_name FROM providers ORDER BY id").all();
8621
9816
  return {
8622
9817
  generatedAt: now.toISOString(),
8623
9818
  providers: providers.filter(
8624
- (provider) => provider.id !== "opencode" && (!this.#hideDemoProvider || provider.id !== "demo")
9819
+ (provider) => provider.id !== "opencode" && provider.id !== "dsh" && (!this.#hideDemoProvider || provider.id !== "demo")
8625
9820
  ).map((provider) => ({ id: provider.id, displayName: provider.display_name }))
8626
9821
  };
8627
9822
  }
@@ -8631,7 +9826,10 @@ var init_sqlite_usage_repository = __esm({
8631
9826
  `SELECT id, display_name, last_success_at, last_error, last_error_code, last_recovery
8632
9827
  FROM providers WHERE id = ?`
8633
9828
  ).get(providerId);
8634
- return provider ? this.#getProviderOverview(provider, now, query) : null;
9829
+ if (!provider) return null;
9830
+ const overview = this.#getProviderOverview(provider, now, query);
9831
+ if (!query.auditEvidence) dropAuditEvidence([overview]);
9832
+ return overview;
8635
9833
  }
8636
9834
  saveExchangeRateSnapshot(snapshot2) {
8637
9835
  this.#database.prepare(
@@ -8759,6 +9957,95 @@ var init_sqlite_usage_repository = __esm({
8759
9957
  deletePlanSubscription(providerId, billingDomainId) {
8760
9958
  this.#database.prepare("DELETE FROM plan_subscriptions WHERE provider_id = ? AND billing_domain_id = ?").run(providerId, billingDomainId);
8761
9959
  }
9960
+ getCustomModelRates() {
9961
+ const rows = this.#database.prepare(
9962
+ `SELECT id, provider_id, billing_domain_id, model,
9963
+ input_rate, output_rate, cache_read_rate, updated_at
9964
+ FROM custom_model_rates
9965
+ ORDER BY updated_at DESC`
9966
+ ).all();
9967
+ return rows.map((row) => ({
9968
+ id: row.id,
9969
+ providerId: row.provider_id,
9970
+ billingDomainId: row.billing_domain_id ?? null,
9971
+ model: row.model,
9972
+ ratesPerMillion: {
9973
+ input: row.input_rate,
9974
+ output: row.output_rate,
9975
+ cacheRead: row.cache_read_rate
9976
+ },
9977
+ updatedAt: row.updated_at
9978
+ }));
9979
+ }
9980
+ getCustomModelRate(id) {
9981
+ const row = this.#database.prepare(
9982
+ `SELECT id, provider_id, billing_domain_id, model,
9983
+ input_rate, output_rate, cache_read_rate, updated_at
9984
+ FROM custom_model_rates
9985
+ WHERE id = ?`
9986
+ ).get(id);
9987
+ if (!row) return null;
9988
+ return {
9989
+ id: row.id,
9990
+ providerId: row.provider_id,
9991
+ billingDomainId: row.billing_domain_id ?? null,
9992
+ model: row.model,
9993
+ ratesPerMillion: {
9994
+ input: row.input_rate,
9995
+ output: row.output_rate,
9996
+ cacheRead: row.cache_read_rate
9997
+ },
9998
+ updatedAt: row.updated_at
9999
+ };
10000
+ }
10001
+ saveCustomModelRate(rate) {
10002
+ const byId = this.#database.prepare("SELECT id FROM custom_model_rates WHERE id = ?").get(rate.id);
10003
+ const byKey = this.#database.prepare(
10004
+ `SELECT id FROM custom_model_rates
10005
+ WHERE provider_id = ? AND COALESCE(billing_domain_id, '') = COALESCE(?, '') AND lower(model) = lower(?)`
10006
+ ).get(rate.providerId, rate.billingDomainId, rate.model);
10007
+ if (byId && byKey && byId.id !== byKey.id) {
10008
+ this.#database.prepare("DELETE FROM custom_model_rates WHERE id = ?").run(byKey.id);
10009
+ }
10010
+ const existingId = byId?.id ?? byKey?.id;
10011
+ if (existingId) {
10012
+ this.#database.prepare(
10013
+ `UPDATE custom_model_rates SET
10014
+ provider_id = ?, billing_domain_id = ?, model = ?,
10015
+ input_rate = ?, output_rate = ?, cache_read_rate = ?, updated_at = ?
10016
+ WHERE id = ?`
10017
+ ).run(
10018
+ rate.providerId,
10019
+ rate.billingDomainId,
10020
+ rate.model,
10021
+ rate.ratesPerMillion.input,
10022
+ rate.ratesPerMillion.output,
10023
+ rate.ratesPerMillion.cacheRead,
10024
+ rate.updatedAt,
10025
+ existingId
10026
+ );
10027
+ } else {
10028
+ this.#database.prepare(
10029
+ `INSERT INTO custom_model_rates (
10030
+ id, provider_id, billing_domain_id, model,
10031
+ input_rate, output_rate, cache_read_rate, updated_at
10032
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
10033
+ ).run(
10034
+ rate.id,
10035
+ rate.providerId,
10036
+ rate.billingDomainId,
10037
+ rate.model,
10038
+ rate.ratesPerMillion.input,
10039
+ rate.ratesPerMillion.output,
10040
+ rate.ratesPerMillion.cacheRead,
10041
+ rate.updatedAt
10042
+ );
10043
+ }
10044
+ }
10045
+ deleteCustomModelRate(id) {
10046
+ const result = this.#database.prepare("DELETE FROM custom_model_rates WHERE id = ?").run(id);
10047
+ return result.changes > 0;
10048
+ }
8762
10049
  getNotificationState(key) {
8763
10050
  const row = this.#database.prepare("SELECT value FROM notification_state WHERE key = ?").get(key);
8764
10051
  return row?.value ?? null;
@@ -8976,7 +10263,7 @@ var init_sqlite_usage_repository = __esm({
8976
10263
  FROM billing_domains WHERE provider_id = ? ORDER BY id`
8977
10264
  ).all(provider.id);
8978
10265
  const summaryBillingDomainId = selectSummaryBillingDomainId(provider.id, domainRows);
8979
- const billingDomains2 = domainRows.map(
10266
+ const billingDomains3 = domainRows.map(
8980
10267
  (domain) => this.#getBillingDomainOverview(
8981
10268
  provider.id,
8982
10269
  domain,
@@ -8988,7 +10275,7 @@ var init_sqlite_usage_repository = __esm({
8988
10275
  domainRows.length === 1 ? provider : null
8989
10276
  )
8990
10277
  );
8991
- const summaryDomain = billingDomains2.find((domain) => domain.id === summaryBillingDomainId);
10278
+ const summaryDomain = billingDomains3.find((domain) => domain.id === summaryBillingDomainId);
8992
10279
  const emptyTotals = zeroTokenTotals();
8993
10280
  const emptyEvidence = finishTokenEvidence(emptyTokenEvidence());
8994
10281
  const emptyCoverage = {
@@ -9003,6 +10290,24 @@ var init_sqlite_usage_repository = __esm({
9003
10290
  message: provider.last_error,
9004
10291
  recovery: provider.last_recovery
9005
10292
  } : { status: "healthy", errorCode: null, message: null, recovery: null };
10293
+ let providerTokenTotals = summaryDomain?.tokenTotals ?? emptyTotals;
10294
+ let providerTokenEvidence = summaryDomain?.tokenEvidence ?? emptyEvidence;
10295
+ if ((provider.id === "dsh" || provider.id === "grok") && billingDomains3.length > 1) {
10296
+ const combinedTotals = zeroTokenTotals();
10297
+ const combinedEvidence = emptyTokenEvidence();
10298
+ for (const d of billingDomains3) {
10299
+ if (provider.id === "grok" && d.id === "xai-api") continue;
10300
+ combinedTotals.total += d.tokenTotals.total;
10301
+ combinedTotals.input += d.tokenTotals.input;
10302
+ combinedTotals.output += d.tokenTotals.output;
10303
+ combinedTotals.reasoning += d.tokenTotals.reasoning;
10304
+ combinedTotals.cacheRead += d.tokenTotals.cacheRead;
10305
+ combinedTotals.cacheWrite += d.tokenTotals.cacheWrite;
10306
+ addAggregatedTokenEvidence(combinedEvidence, d.tokenEvidence);
10307
+ }
10308
+ providerTokenTotals = combinedTotals;
10309
+ providerTokenEvidence = finishTokenEvidence(combinedEvidence);
10310
+ }
9006
10311
  return {
9007
10312
  id: provider.id,
9008
10313
  displayName: provider.display_name,
@@ -9014,10 +10319,10 @@ var init_sqlite_usage_repository = __esm({
9014
10319
  health: summaryDomain?.health ?? providerFallbackHealth,
9015
10320
  coverage: summaryDomain?.coverage ?? emptyCoverage,
9016
10321
  quotaBuckets: summaryDomain?.quotaBuckets ?? [],
9017
- tokenTotals: summaryDomain?.tokenTotals ?? emptyTotals,
9018
- tokenEvidence: summaryDomain?.tokenEvidence ?? emptyEvidence,
10322
+ tokenTotals: providerTokenTotals,
10323
+ tokenEvidence: providerTokenEvidence,
9019
10324
  tokenAuthority: summaryDomain?.tokenAuthority ?? null,
9020
- billingDomains: billingDomains2,
10325
+ billingDomains: billingDomains3,
9021
10326
  forecasts: summaryDomain?.forecasts ?? [],
9022
10327
  forecastCoverage: summaryDomain?.forecastCoverage ?? "insufficient"
9023
10328
  };
@@ -9049,7 +10354,7 @@ var init_sqlite_usage_repository = __esm({
9049
10354
  FROM usage_observations
9050
10355
  WHERE provider_id = ? AND billing_domain_id = ? AND ${additiveUsagePredicate()}`
9051
10356
  ).get(providerId, domain.id);
9052
- const costs = this.#database.prepare(
10357
+ const costs = query.auditEvidence ? this.#database.prepare(
9053
10358
  `SELECT id, source_id, billing_domain_id, observed_at, kind, amount, currency, authority,
9054
10359
  price_snapshot_id, price_snapshot_version, price_snapshot_source,
9055
10360
  price_snapshot_canonical_model, price_snapshot_effective_at,
@@ -9058,7 +10363,11 @@ var init_sqlite_usage_repository = __esm({
9058
10363
  price_snapshot_context_tier, model, usage_observation_id, priced_tokens,
9059
10364
  line_items_json, calculated_at
9060
10365
  FROM cost_records WHERE provider_id = ? AND billing_domain_id = ? ORDER BY observed_at DESC, id`
9061
- ).all(providerId, domain.id);
10366
+ ).all(providerId, domain.id) : null;
10367
+ const lastCostObservedAt = this.#database.prepare(
10368
+ `SELECT MAX(observed_at) AS observed_at FROM cost_records
10369
+ WHERE provider_id = ? AND billing_domain_id = ?`
10370
+ ).get(providerId, domain.id).observed_at ?? null;
9062
10371
  const balances = this.#database.prepare(
9063
10372
  `SELECT id, source_id, billing_domain_id, observed_at, kind, amount, currency, authority
9064
10373
  FROM balance_records WHERE provider_id = ? AND billing_domain_id = ? ORDER BY observed_at DESC, id`
@@ -9067,7 +10376,12 @@ var init_sqlite_usage_repository = __esm({
9067
10376
  `SELECT id, billing_domain_id, created_at, number, status, amount, currency, authority
9068
10377
  FROM invoice_records WHERE provider_id = ? AND billing_domain_id = ? ORDER BY created_at DESC, id`
9069
10378
  ).all(providerId, domain.id);
9070
- const actualCostCount = costs.filter((cost) => cost.kind === "actual").length;
10379
+ const actualCostCount = Number(
10380
+ this.#database.prepare(
10381
+ `SELECT COUNT(*) AS count FROM cost_records
10382
+ WHERE provider_id = ? AND billing_domain_id = ? AND kind = 'actual'`
10383
+ ).get(providerId, domain.id).count
10384
+ );
9071
10385
  const nonAdditiveTokenCount = this.#database.prepare(
9072
10386
  `SELECT COUNT(*) AS count FROM usage_observations
9073
10387
  WHERE provider_id = ? AND billing_domain_id = ?
@@ -9097,22 +10411,25 @@ var init_sqlite_usage_repository = __esm({
9097
10411
  tokenTotals: mapTokenTotals(tokens),
9098
10412
  tokenEvidence: mapTokenEvidence(tokens),
9099
10413
  tokenAuthority: tokenAuthority(tokens.authorities),
9100
- costs: costs.map((row) => ({
9101
- id: row.id,
9102
- sourceId: row.source_id,
9103
- billingDomainId: row.billing_domain_id,
9104
- observedAt: row.observed_at,
9105
- kind: row.kind,
9106
- amount: row.amount,
9107
- currency: row.currency,
9108
- authority: row.authority,
9109
- priceSnapshot: priceSnapshotFromRow(row),
9110
- model: row.model,
9111
- usageObservationId: row.usage_observation_id,
9112
- pricedTokens: row.priced_tokens,
9113
- lineItems: parseLineItems(row.line_items_json),
9114
- calculatedAt: row.calculated_at
9115
- })),
10414
+ ...costs ? {
10415
+ costs: costs.map((row) => ({
10416
+ id: row.id,
10417
+ sourceId: row.source_id,
10418
+ billingDomainId: row.billing_domain_id,
10419
+ observedAt: row.observed_at,
10420
+ kind: row.kind,
10421
+ amount: row.amount,
10422
+ currency: row.currency,
10423
+ authority: row.authority,
10424
+ priceSnapshot: priceSnapshotFromRow(row),
10425
+ model: row.model,
10426
+ usageObservationId: row.usage_observation_id,
10427
+ pricedTokens: row.priced_tokens,
10428
+ lineItems: parseLineItems(row.line_items_json),
10429
+ calculatedAt: row.calculated_at
10430
+ }))
10431
+ } : {},
10432
+ lastCostObservedAt,
9116
10433
  balances: balances.map((row) => ({
9117
10434
  id: row.id,
9118
10435
  sourceId: row.source_id,
@@ -9733,6 +11050,18 @@ var init_sqlite_usage_repository = __esm({
9733
11050
  updated_at TEXT NOT NULL,
9734
11051
  PRIMARY KEY (provider_id, billing_domain_id)
9735
11052
  );
11053
+ CREATE TABLE IF NOT EXISTS custom_model_rates (
11054
+ id TEXT PRIMARY KEY,
11055
+ provider_id TEXT NOT NULL,
11056
+ billing_domain_id TEXT,
11057
+ model TEXT NOT NULL,
11058
+ input_rate REAL NOT NULL,
11059
+ output_rate REAL NOT NULL,
11060
+ cache_read_rate REAL NOT NULL,
11061
+ updated_at TEXT NOT NULL
11062
+ );
11063
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_custom_model_rates_key
11064
+ ON custom_model_rates(provider_id, COALESCE(billing_domain_id, ''), lower(model));
9736
11065
  `);
9737
11066
  const usageColumns = this.#database.prepare("PRAGMA table_info(usage_observations)").all();
9738
11067
  if (!usageColumns.some((column) => column.name === "total_tokens")) {
@@ -9890,9 +11219,9 @@ var init_sqlite_usage_repository = __esm({
9890
11219
  `);
9891
11220
  const quotaColumns = this.#database.prepare("PRAGMA table_info(quota_buckets)").all();
9892
11221
  for (const [name, type] of [
11222
+ ["window_duration_minutes", "INTEGER"],
9893
11223
  ["scope", "TEXT"],
9894
11224
  ["status", "TEXT"],
9895
- ["window_duration_minutes", "INTEGER"],
9896
11225
  ["limit_amount", "REAL"],
9897
11226
  ["limit_currency", "TEXT"],
9898
11227
  ["fallback_status", "TEXT"]
@@ -9916,14 +11245,14 @@ __export(runtime_exports, {
9916
11245
  runDaemon: () => runDaemon
9917
11246
  });
9918
11247
  import { existsSync as existsSync2 } from "fs";
9919
- import { mkdir as mkdir4, rm as rm2 } from "fs/promises";
9920
- import { homedir as homedir3 } from "os";
9921
- import { isAbsolute as isAbsolute3, join as join8, resolve as resolve3 } from "path";
11248
+ import { mkdir as mkdir5, rm as rm2 } from "fs/promises";
11249
+ import { homedir as homedir4 } from "os";
11250
+ import { isAbsolute as isAbsolute3, join as join10, resolve as resolve3 } from "path";
9922
11251
  import { fileURLToPath } from "url";
9923
11252
  async function runDaemon(home) {
9924
- await mkdir4(home, { recursive: true, mode: 448 });
11253
+ await mkdir5(home, { recursive: true, mode: 448 });
9925
11254
  const demoEnabled = process.env.AGENT_USAGE_DEMO === "1";
9926
- const repository = new SqliteUsageRepository(join8(home, "usage.sqlite"), {
11255
+ const repository = new SqliteUsageRepository(join10(home, "usage.sqlite"), {
9927
11256
  hideDemoProvider: !demoEnabled
9928
11257
  });
9929
11258
  const keychainService = process.env.AGENT_USAGE_KEYCHAIN_SERVICE;
@@ -9958,7 +11287,16 @@ async function runDaemon(home) {
9958
11287
  historyClient: localTranscriptClient("grok", home)
9959
11288
  }),
9960
11289
  new XaiApiConnector({ accountClient: new XaiManagementApiClient({ secretStore }) }),
9961
- new DshConnector({ historyClient: localTranscriptClient("dsh", home) })
11290
+ new DshConnector({ historyClient: localTranscriptClient("dsh", home) }),
11291
+ new AntigravityConnector({
11292
+ historyClient: new AntigravitySqliteUsageClient({
11293
+ roots: [
11294
+ join10(homedir4(), ".gemini/antigravity-cli"),
11295
+ join10(homedir4(), ".gemini/antigravity")
11296
+ ],
11297
+ cachePath: join10(home, "antigravity-usage-cache.json")
11298
+ })
11299
+ })
9962
11300
  ],
9963
11301
  connectorDefinitions: defaultConnectorDefinitions,
9964
11302
  discoveryProbe: new PathDiscoveryProbe(),
@@ -9966,7 +11304,7 @@ async function runDaemon(home) {
9966
11304
  exchangeRateProvider: new EcbExchangeRateProvider(),
9967
11305
  notifier: new MacOsNotifier(),
9968
11306
  startAtLoginManager: new MacOsStartAtLoginManager({
9969
- userHome: homedir3(),
11307
+ userHome: homedir4(),
9970
11308
  executable: process.execPath,
9971
11309
  cliPath: process.argv[1],
9972
11310
  applicationHome: home,
@@ -9981,7 +11319,16 @@ async function runDaemon(home) {
9981
11319
  }
9982
11320
  }),
9983
11321
  connectorPolicies: Object.fromEntries(
9984
- ["codex", "claude-code", "opencode-go", "opencode", "grok", "xai-api", "dsh"].map((id) => [
11322
+ [
11323
+ "codex",
11324
+ "claude-code",
11325
+ "opencode-go",
11326
+ "opencode",
11327
+ "grok",
11328
+ "xai-api",
11329
+ "dsh",
11330
+ "antigravity"
11331
+ ].map((id) => [
9985
11332
  id,
9986
11333
  { minimumIntervalMs: 5 * 60 * 1e3, timeoutMs: id === "claude-code" ? 25e3 : 2e4 }
9987
11334
  ])
@@ -10032,7 +11379,7 @@ async function runDaemon(home) {
10032
11379
  scheduler.stop();
10033
11380
  await server.close();
10034
11381
  repository.close();
10035
- await rm2(join8(home, "daemon.json"), { force: true });
11382
+ await rm2(join10(home, "daemon.json"), { force: true });
10036
11383
  resolveStop();
10037
11384
  };
10038
11385
  process.once("SIGTERM", () => void stop());
@@ -10043,27 +11390,27 @@ function localTranscriptClient(provider, applicationHome) {
10043
11390
  return new LocalTranscriptUsageClient({
10044
11391
  provider,
10045
11392
  roots: localTranscriptRoots(provider),
10046
- cachePath: join8(applicationHome, "cache", `${provider}-transcripts.json`)
11393
+ cachePath: join10(applicationHome, "cache", `${provider}-transcripts.json`)
10047
11394
  });
10048
11395
  }
10049
11396
  function localTranscriptRoots(provider) {
10050
11397
  if (provider === "codex") {
10051
11398
  const home = resolveHomeOverride(process.env.CODEX_HOME, ".codex");
10052
- return [join8(home, "sessions"), join8(home, "archived_sessions")];
11399
+ return [join10(home, "sessions"), join10(home, "archived_sessions")];
10053
11400
  }
10054
11401
  if (provider === "claude-code") {
10055
- return [join8(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
11402
+ return [join10(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
10056
11403
  }
10057
11404
  if (provider === "dsh") {
10058
- return [join8(resolveHomeOverride(process.env.DSH_HOME, ".dsh"), "sessions")];
11405
+ return [join10(resolveHomeOverride(process.env.DSH_HOME, ".dsh"), "sessions")];
10059
11406
  }
10060
- return [join8(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
11407
+ return [join10(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
10061
11408
  }
10062
11409
  function resolveHomeOverride(value, fallback) {
10063
11410
  const configured = value?.trim();
10064
- if (!configured) return join8(homedir3(), fallback);
10065
- if (configured === "~") return homedir3();
10066
- if (configured.startsWith("~/")) return join8(homedir3(), configured.slice(2));
11411
+ if (!configured) return join10(homedir4(), fallback);
11412
+ if (configured === "~") return homedir4();
11413
+ if (configured.startsWith("~/")) return join10(homedir4(), configured.slice(2));
10067
11414
  return isAbsolute3(configured) ? configured : resolve3(configured);
10068
11415
  }
10069
11416
  function createDemoConnector() {
@@ -10109,7 +11456,7 @@ function locateStaticDirectory() {
10109
11456
  fileURLToPath(new URL("./web", import.meta.url)),
10110
11457
  fileURLToPath(new URL("../../dist/web", import.meta.url))
10111
11458
  ];
10112
- return candidates.find((candidate) => existsSync2(join8(candidate, "index.html")));
11459
+ return candidates.find((candidate) => existsSync2(join10(candidate, "index.html")));
10113
11460
  }
10114
11461
  var init_runtime = __esm({
10115
11462
  "src/server/runtime.ts"() {
@@ -10127,6 +11474,8 @@ var init_runtime = __esm({
10127
11474
  init_opencode_auth_reader();
10128
11475
  init_opencode_go_connector();
10129
11476
  init_dsh_connector();
11477
+ init_antigravity_connector();
11478
+ init_antigravity_sqlite_usage_client();
10130
11479
  init_grok_build_connector();
10131
11480
  init_grok_telemetry();
10132
11481
  init_stdio_grok_billing_client();
@@ -10149,8 +11498,8 @@ init_redaction();
10149
11498
  init_daemon_state();
10150
11499
  import { spawn as spawn6 } from "child_process";
10151
11500
  import { connect as tcpConnect } from "net";
10152
- import { homedir as homedir4 } from "os";
10153
- import { join as join9, resolve as resolve4 } from "path";
11501
+ import { homedir as homedir5 } from "os";
11502
+ import { join as join11, resolve as resolve4 } from "path";
10154
11503
  import { Command } from "commander";
10155
11504
  import open2 from "open";
10156
11505
  var program = new Command();
@@ -10332,6 +11681,97 @@ program.command("start-at-login <action>").description("enable, disable, or show
10332
11681
  process.stdout.write(`${status.settings.startAtLogin ? "enabled" : "disabled"}
10333
11682
  `);
10334
11683
  });
11684
+ var ratesCommand = program.command("rates").description("view and manage custom model rates for custom endpoints");
11685
+ ratesCommand.command("list").description("list configured custom model rates").option("--json", "emit machine-readable JSON").action(async (options) => {
11686
+ const home = resolve4(program.opts().home);
11687
+ const state = await requireDaemonState(home);
11688
+ const response = await fetch(`${state.origin}/api/custom-rates`, {
11689
+ headers: { authorization: `Bearer ${state.apiToken}` }
11690
+ });
11691
+ if (!response.ok) throw new Error(`Daemon returned HTTP ${response.status}`);
11692
+ const data = await response.json();
11693
+ if (options.json) {
11694
+ process.stdout.write(`${JSON.stringify(data.rates, null, 2)}
11695
+ `);
11696
+ return;
11697
+ }
11698
+ if (data.rates.length === 0) {
11699
+ process.stdout.write("No custom model rates configured.\n");
11700
+ return;
11701
+ }
11702
+ for (const rate of data.rates) {
11703
+ const domain = rate.billingDomainId ?? "*";
11704
+ process.stdout.write(
11705
+ `${rate.id} | ${rate.providerId} | domain: ${domain} | model: ${rate.model} | in: $${rate.ratesPerMillion.input}/M | out: $${rate.ratesPerMillion.output}/M | cache: $${rate.ratesPerMillion.cacheRead}/M
11706
+ `
11707
+ );
11708
+ }
11709
+ });
11710
+ ratesCommand.command("set <model>").description("set custom model rate").requiredOption("--provider <provider>", "provider id (e.g. dsh)").option("--domain <domain>", "billing domain id (omit for wildcard across all routes)").requiredOption("--input <rate>", "input rate in USD per million tokens", parseFloat).requiredOption("--output <rate>", "output rate in USD per million tokens", parseFloat).option("--cache-read <rate>", "cache read rate in USD per million tokens", parseFloat, 0).option("--id <id>", "custom model rate ID to update").action(
11711
+ async (model, options) => {
11712
+ const home = resolve4(program.opts().home);
11713
+ const state = await requireDaemonState(home);
11714
+ const response = await fetch(`${state.origin}/api/custom-rates`, {
11715
+ method: "POST",
11716
+ headers: {
11717
+ authorization: `Bearer ${state.apiToken}`,
11718
+ "content-type": "application/json",
11719
+ origin: state.origin
11720
+ },
11721
+ body: JSON.stringify({
11722
+ ...options.id ? { id: options.id } : {},
11723
+ providerId: options.provider,
11724
+ billingDomainId: options.domain || null,
11725
+ model,
11726
+ inputRate: options.input,
11727
+ outputRate: options.output,
11728
+ cacheReadRate: options.cacheRead
11729
+ })
11730
+ });
11731
+ if (!response.ok) throw new Error(`Daemon returned HTTP ${response.status}`);
11732
+ const data = await response.json();
11733
+ process.stdout.write(
11734
+ `Custom model rate configured for ${data.rate.model} (ID: ${data.rate.id})
11735
+ `
11736
+ );
11737
+ }
11738
+ );
11739
+ ratesCommand.command("get <id>").description("get details of a custom model rate by ID").option("--json", "emit machine-readable JSON").action(async (id, options) => {
11740
+ const home = resolve4(program.opts().home);
11741
+ const state = await requireDaemonState(home);
11742
+ const response = await fetch(`${state.origin}/api/custom-rates/${encodeURIComponent(id)}`, {
11743
+ headers: { authorization: `Bearer ${state.apiToken}` }
11744
+ });
11745
+ if (!response.ok) {
11746
+ if (response.status === 404) throw new Error(`Custom model rate not found: ${id}`);
11747
+ throw new Error(`Daemon returned HTTP ${response.status}`);
11748
+ }
11749
+ const data = await response.json();
11750
+ if (options.json) {
11751
+ process.stdout.write(`${JSON.stringify(data.rate, null, 2)}
11752
+ `);
11753
+ return;
11754
+ }
11755
+ const domain = data.rate.billingDomainId ?? "*";
11756
+ process.stdout.write(
11757
+ `${data.rate.id} | ${data.rate.providerId} | domain: ${domain} | model: ${data.rate.model} | in: $${data.rate.ratesPerMillion.input}/M | out: $${data.rate.ratesPerMillion.output}/M | cache: $${data.rate.ratesPerMillion.cacheRead}/M | updated: ${data.rate.updatedAt}
11758
+ `
11759
+ );
11760
+ });
11761
+ ratesCommand.command("delete <id>").description("delete custom model rate by ID").action(async (id) => {
11762
+ const home = resolve4(program.opts().home);
11763
+ const state = await requireDaemonState(home);
11764
+ const response = await fetch(`${state.origin}/api/custom-rates/${encodeURIComponent(id)}`, {
11765
+ method: "DELETE",
11766
+ headers: {
11767
+ authorization: `Bearer ${state.apiToken}`,
11768
+ origin: state.origin
11769
+ }
11770
+ });
11771
+ if (!response.ok) throw new Error(`Daemon returned HTTP ${response.status}`);
11772
+ process.stdout.write(`Deleted custom model rate ${id}
11773
+ `);
11774
+ });
10335
11775
  program.command("serve").description("run the internal local daemon").action(async () => {
10336
11776
  const home = resolve4(program.opts().home);
10337
11777
  const { runDaemon: runDaemon2 } = await Promise.resolve().then(() => (init_runtime(), runtime_exports));
@@ -10515,6 +11955,6 @@ function formatDoctor(report) {
10515
11955
  ].join("\n");
10516
11956
  }
10517
11957
  function defaultHome() {
10518
- return join9(homedir4(), "Library", "Application Support", "Agent Usage");
11958
+ return join11(homedir5(), "Library", "Application Support", "Agent Usage");
10519
11959
  }
10520
11960
  //# sourceMappingURL=cli.js.map