agent-usage-all-in-one 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +11 -1
  2. package/README.zh-CN.md +7 -1
  3. package/dist/cli.js +867 -98
  4. package/dist/cli.js.map +1 -1
  5. package/dist/web/_app/immutable/assets/2.BWrFQDDF.css +1 -0
  6. package/dist/web/_app/immutable/chunks/3vuWay1s.js +3 -0
  7. package/dist/web/_app/immutable/chunks/55QxuuK3.js +1 -0
  8. package/dist/web/_app/immutable/chunks/CIxAYfti.js +1 -0
  9. package/dist/web/_app/immutable/entry/app.zHTQ4fOQ.js +2 -0
  10. package/dist/web/_app/immutable/entry/start.gNCaHF0d.js +1 -0
  11. package/dist/web/_app/immutable/nodes/0.f03zL-fK.js +1 -0
  12. package/dist/web/_app/immutable/nodes/1.DjE9OLQ8.js +1 -0
  13. package/dist/web/_app/immutable/nodes/2.CeZkCUZN.js +27 -0
  14. package/dist/web/_app/version.json +1 -1
  15. package/dist/web/brand/agent-usage-showcase.jpg +0 -0
  16. package/dist/web/brands/README.md +4 -4
  17. package/dist/web/index.html +30 -8
  18. package/docs/open-source.md +1 -1
  19. package/package.json +1 -1
  20. package/static/brand/agent-usage-showcase.jpg +0 -0
  21. package/static/brands/README.md +4 -4
  22. package/dist/web/_app/immutable/assets/2.BiUr1b86.css +0 -1
  23. package/dist/web/_app/immutable/chunks/BW9s3L2z.js +0 -3
  24. package/dist/web/_app/immutable/chunks/CwKqhBmI.js +0 -1
  25. package/dist/web/_app/immutable/entry/app.CJoUfLXD.js +0 -2
  26. package/dist/web/_app/immutable/entry/start.DEFna-Tq.js +0 -1
  27. package/dist/web/_app/immutable/nodes/0.DTcMU-UW.js +0 -1
  28. package/dist/web/_app/immutable/nodes/1.BRZSpfvp.js +0 -1
  29. package/dist/web/_app/immutable/nodes/2.BKHXaqdm.js +0 -43
  30. package/dist/web/brand/agent-usage-banner.svg +0 -27
  31. package/static/brand/agent-usage-banner.svg +0 -27
package/dist/cli.js CHANGED
@@ -749,16 +749,15 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
749
749
  ratePerMillion: entry.cacheWriteRatesPerMillion.oneHour
750
750
  }
751
751
  ].filter((item) => item.tokens > 0) : null;
752
- if (cacheWriteTokens > 0 && entry.ratesPerMillion["cache-write"] === null && !exactCacheWriteItems) {
753
- return unavailable("pricing-tier-ambiguous");
754
- }
755
- if (cacheWriteTokens > 0 && !exactCacheWriteItems) {
752
+ const cacheWriteFree = cacheWriteTokens > 0 && !exactCacheWriteItems && entry.ratesPerMillion["cache-write"] === null && !entry.cacheWriteRatesPerMillion;
753
+ const cacheWriteBillable = cacheWriteTokens > 0 && !exactCacheWriteItems && !cacheWriteFree;
754
+ if (cacheWriteBillable) {
756
755
  billableTokens.push({ tokenKind: "cache-write", tokens: cacheWriteTokens });
757
756
  }
758
757
  const nonZero = billableTokens.filter((item) => item.tokens > 0);
759
758
  if (nonZero.some((item) => entry.ratesPerMillion[item.tokenKind] === null)) {
760
759
  return unavailable(
761
- normalized.cacheWriteTokens > 0 ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
760
+ normalized.cacheWriteTokens > 0 && !cacheWriteFree ? "pricing-tier-ambiguous" : "token-kinds-incomplete"
762
761
  );
763
762
  }
764
763
  const lineItems = nonZero.map((item) => {
@@ -777,6 +776,14 @@ function priceObservation(providerId, observation, catalog, calculatedAt) {
777
776
  }))
778
777
  );
779
778
  }
779
+ if (cacheWriteFree) {
780
+ lineItems.push({
781
+ tokenKind: "cache-write",
782
+ tokens: cacheWriteTokens,
783
+ ratePerMillion: 0,
784
+ amount: 0
785
+ });
786
+ }
780
787
  const pricedTokens = lineItems.reduce((total, item) => total + item.tokens, 0);
781
788
  if (pricedTokens !== normalized.recordedTokens) return unavailable("token-kinds-incomplete");
782
789
  const amount = preciseMoney(lineItems.reduce((total, item) => total + item.amount, 0));
@@ -1184,6 +1191,382 @@ var init_retail_pricing = __esm({
1184
1191
  }
1185
1192
  });
1186
1193
 
1194
+ // src/core/plan-pricing.ts
1195
+ function buildBillingPeriod(subscription, summary, comparisonCurrency, rates) {
1196
+ if (!summary) return null;
1197
+ const start = new Date(summary.start).getTime();
1198
+ const end = new Date(summary.end).getTime();
1199
+ const observedThrough = new Date(summary.observedThrough).getTime();
1200
+ const totalDays = (end - start) / MILLISECONDS_PER_DAY;
1201
+ const elapsedDays = Math.max(0, Math.min(observedThrough, end) - start) / MILLISECONDS_PER_DAY;
1202
+ const periodCost = planMoneyAmount(
1203
+ preciseAmount(subscription.amount),
1204
+ subscription.currency,
1205
+ comparisonCurrency,
1206
+ // The period's own rate evidence first, so a short rolling window cannot
1207
+ // leave a cycle amount unconverted.
1208
+ [...summary.retailEquivalent.exchangeRates, ...rates],
1209
+ summary.observedThrough
1210
+ );
1211
+ const retailAmount = summary.retailEquivalent.status === "available" ? summary.retailEquivalent.amount : null;
1212
+ const breakEvenRatio = periodCost.amount !== null && periodCost.amount > 0 && retailAmount !== null ? preciseAmount(retailAmount / periodCost.amount) : null;
1213
+ return {
1214
+ start: summary.start,
1215
+ end: summary.end,
1216
+ elapsedDays: preciseAmount(elapsedDays),
1217
+ totalDays: preciseAmount(totalDays),
1218
+ progress: totalDays > 0 ? preciseAmount(Math.min(1, elapsedDays / totalDays)) : 0,
1219
+ periodCost,
1220
+ recordedTokens: summary.observationCount > 0 ? summary.recordedTokens : null,
1221
+ retailEquivalent: summary.retailEquivalent,
1222
+ breakEvenRatio,
1223
+ ratioBound: ratioBoundFor(breakEvenRatio, summary.retailEquivalent.pricingCoverage)
1224
+ };
1225
+ }
1226
+ function planCatalogEntry(catalog, id) {
1227
+ return catalog.entries.find((entry) => entry.id === id) ?? null;
1228
+ }
1229
+ function planCatalogEntriesForDomain(catalog, providerId, billingDomainId) {
1230
+ return catalog.entries.filter(
1231
+ (entry) => entry.providerId === providerId && entry.billingDomainId === billingDomainId
1232
+ );
1233
+ }
1234
+ function addMonths(date, months) {
1235
+ const day = date.getUTCDate();
1236
+ const shifted = new Date(
1237
+ Date.UTC(
1238
+ date.getUTCFullYear(),
1239
+ date.getUTCMonth() + months,
1240
+ 1,
1241
+ date.getUTCHours(),
1242
+ date.getUTCMinutes(),
1243
+ date.getUTCSeconds(),
1244
+ date.getUTCMilliseconds()
1245
+ )
1246
+ );
1247
+ const lastDayOfMonth = new Date(
1248
+ Date.UTC(shifted.getUTCFullYear(), shifted.getUTCMonth() + 1, 0)
1249
+ ).getUTCDate();
1250
+ shifted.setUTCDate(Math.min(day, lastDayOfMonth));
1251
+ return shifted;
1252
+ }
1253
+ function billingPeriodContaining(anchorDate, billingPeriod, at) {
1254
+ const anchor = new Date(anchorDate.length === 10 ? `${anchorDate}T00:00:00.000Z` : anchorDate);
1255
+ if (Number.isNaN(anchor.getTime())) return null;
1256
+ const step = billingPeriod === "monthly" ? 1 : 12;
1257
+ const monthsApart = (at.getUTCFullYear() - anchor.getUTCFullYear()) * 12 + (at.getUTCMonth() - anchor.getUTCMonth());
1258
+ let index = Math.floor(monthsApart / step);
1259
+ let start = addMonths(anchor, index * step);
1260
+ while (start.getTime() > at.getTime()) {
1261
+ index -= 1;
1262
+ start = addMonths(anchor, index * step);
1263
+ }
1264
+ let end = addMonths(anchor, (index + 1) * step);
1265
+ while (end.getTime() <= at.getTime()) {
1266
+ index += 1;
1267
+ start = end;
1268
+ end = addMonths(anchor, (index + 1) * step);
1269
+ }
1270
+ return { start, end };
1271
+ }
1272
+ function windowDays(start, end) {
1273
+ const span = new Date(end).getTime() - new Date(start).getTime();
1274
+ return span > 0 ? span / MILLISECONDS_PER_DAY : 0;
1275
+ }
1276
+ function proratePlanPrice(amount, billingPeriod, days) {
1277
+ return amount * days / PLAN_PERIOD_DAYS[billingPeriod];
1278
+ }
1279
+ function convertPlanAmount(amount, currency, comparisonCurrency, rates, end) {
1280
+ if (currency.toUpperCase() === comparisonCurrency.toUpperCase()) {
1281
+ return { amount, reason: null, rate: null };
1282
+ }
1283
+ const rate = rates.find(
1284
+ (candidate) => candidate.baseCurrency.toUpperCase() === currency.toUpperCase() && candidate.quoteCurrency.toUpperCase() === comparisonCurrency.toUpperCase()
1285
+ );
1286
+ if (!rate) return { amount: null, reason: "missing-rate", rate: null };
1287
+ if (new Date(end).getTime() - new Date(rate.observedAt).getTime() > STALE_RATE_MILLISECONDS) {
1288
+ return { amount: null, reason: "stale-rate", rate };
1289
+ }
1290
+ return { amount: amount * rate.rate, reason: null, rate };
1291
+ }
1292
+ function preciseAmount(value) {
1293
+ return Number(value.toPrecision(12));
1294
+ }
1295
+ function planMoneyAmount(nativeAmount, nativeCurrency, comparisonCurrency, rates, end) {
1296
+ const converted = convertPlanAmount(nativeAmount, nativeCurrency, comparisonCurrency, rates, end);
1297
+ return {
1298
+ status: converted.amount === null ? "unavailable" : "available",
1299
+ amount: converted.amount === null ? null : preciseAmount(converted.amount),
1300
+ nativeAmount,
1301
+ nativeCurrency,
1302
+ comparisonCurrency,
1303
+ conversionUnavailableReason: converted.reason,
1304
+ exchangeRates: converted.rate ? [converted.rate] : []
1305
+ };
1306
+ }
1307
+ function ratioBoundFor(ratio, pricingCoverage) {
1308
+ if (ratio === null) return "unavailable";
1309
+ return pricingCoverage !== null && pricingCoverage < 0.999 ? "lower" : "exact";
1310
+ }
1311
+ function unitPricePerMillion(amount, tokens) {
1312
+ if (amount === null || tokens <= 0) return null;
1313
+ return preciseAmount(amount / (tokens / 1e6));
1314
+ }
1315
+ function buildWorkbenchPlanValue(options) {
1316
+ const { comparisonCurrency, start, end, rates } = options;
1317
+ const days = windowDays(start, end);
1318
+ const subscriptionByDomain = new Map(
1319
+ options.subscriptions.map((subscription) => [
1320
+ `${subscription.providerId}:${subscription.billingDomainId}`,
1321
+ subscription
1322
+ ])
1323
+ );
1324
+ const entries = [];
1325
+ const metered = [];
1326
+ const unconfigured = [];
1327
+ for (const domain of options.domains) {
1328
+ const subscription = subscriptionByDomain.get(`${domain.providerId}:${domain.billingDomainId}`);
1329
+ if (!subscription) {
1330
+ if (domain.actualCost.records > 0) {
1331
+ metered.push({
1332
+ providerId: domain.providerId,
1333
+ providerDisplayName: domain.providerDisplayName,
1334
+ billingDomainId: domain.billingDomainId,
1335
+ billingDomainDisplayName: domain.billingDomainDisplayName,
1336
+ recordedTokens: domain.observationCount > 0 ? domain.recordedTokens : null,
1337
+ actualCost: domain.actualCost,
1338
+ retailEquivalent: domain.retailEquivalent
1339
+ });
1340
+ continue;
1341
+ }
1342
+ if (domain.observationCount > 0) {
1343
+ unconfigured.push({
1344
+ providerId: domain.providerId,
1345
+ providerDisplayName: domain.providerDisplayName,
1346
+ billingDomainId: domain.billingDomainId,
1347
+ billingDomainDisplayName: domain.billingDomainDisplayName,
1348
+ recordedTokens: domain.recordedTokens
1349
+ });
1350
+ }
1351
+ continue;
1352
+ }
1353
+ const windowPlanCost = planMoneyAmount(
1354
+ preciseAmount(proratePlanPrice(subscription.amount, subscription.billingPeriod, days)),
1355
+ subscription.currency,
1356
+ comparisonCurrency,
1357
+ rates,
1358
+ end
1359
+ );
1360
+ const retail = domain.retailEquivalent;
1361
+ const retailAmount = retail.status === "available" ? retail.amount : null;
1362
+ const valueRatio = windowPlanCost.amount !== null && windowPlanCost.amount > 0 && retailAmount !== null ? preciseAmount(retailAmount / windowPlanCost.amount) : null;
1363
+ const ratioBound = ratioBoundFor(valueRatio, retail.pricingCoverage);
1364
+ const billingPeriod = buildBillingPeriod(
1365
+ subscription,
1366
+ options.billingPeriods?.get(`${domain.providerId}:${domain.billingDomainId}`),
1367
+ comparisonCurrency,
1368
+ rates
1369
+ );
1370
+ entries.push({
1371
+ providerId: domain.providerId,
1372
+ providerDisplayName: domain.providerDisplayName,
1373
+ billingDomainId: domain.billingDomainId,
1374
+ billingDomainDisplayName: domain.billingDomainDisplayName,
1375
+ includedInHeadline: domain.includedInHeadline,
1376
+ plan: {
1377
+ planId: subscription.planId,
1378
+ displayName: subscription.displayName,
1379
+ amount: subscription.amount,
1380
+ currency: subscription.currency,
1381
+ billingPeriod: subscription.billingPeriod,
1382
+ anchorDate: subscription.anchorDate,
1383
+ priceSource: subscription.priceSource,
1384
+ updatedAt: subscription.updatedAt
1385
+ },
1386
+ windowDays: preciseAmount(days),
1387
+ windowPlanCost,
1388
+ billingPeriod,
1389
+ recordedTokens: domain.observationCount > 0 ? domain.recordedTokens : null,
1390
+ retailEquivalent: retail,
1391
+ valueRatio,
1392
+ ratioBound,
1393
+ status: valueRatio === null ? "unavailable" : ratioBound === "lower" ? "partial" : "available",
1394
+ effectiveUnitPrice: unitPricePerMillion(windowPlanCost.amount, domain.recordedTokens),
1395
+ retailUnitPrice: unitPricePerMillion(retailAmount, retail.pricedTokens),
1396
+ pricingCoverage: retail.pricingCoverage,
1397
+ authorities: domain.authorities,
1398
+ lastObservedAt: domain.lastObservedAt
1399
+ });
1400
+ }
1401
+ const rank = (entry) => entry.valueRatio ?? -1;
1402
+ entries.sort(
1403
+ (left, right) => rank(right) - rank(left) || `${left.providerId}:${left.billingDomainId}`.localeCompare(
1404
+ `${right.providerId}:${right.billingDomainId}`
1405
+ )
1406
+ );
1407
+ metered.sort(
1408
+ (left, right) => `${left.providerId}:${left.billingDomainId}`.localeCompare(
1409
+ `${right.providerId}:${right.billingDomainId}`
1410
+ )
1411
+ );
1412
+ unconfigured.sort(
1413
+ (left, right) => right.recordedTokens - left.recordedTokens || `${left.providerId}:${left.billingDomainId}`.localeCompare(
1414
+ `${right.providerId}:${right.billingDomainId}`
1415
+ )
1416
+ );
1417
+ return {
1418
+ windowDays: preciseAmount(days),
1419
+ comparisonCurrency,
1420
+ catalogVersion: SUBSCRIPTION_PLAN_CATALOG.version,
1421
+ entries,
1422
+ meteredDomains: metered,
1423
+ unconfiguredDomains: unconfigured
1424
+ };
1425
+ }
1426
+ var PLAN_PERIOD_DAYS, MILLISECONDS_PER_DAY, STALE_RATE_MILLISECONDS, SUBSCRIPTION_PLAN_CATALOG;
1427
+ var init_plan_pricing = __esm({
1428
+ "src/core/plan-pricing.ts"() {
1429
+ "use strict";
1430
+ PLAN_PERIOD_DAYS = {
1431
+ monthly: 365.25 / 12,
1432
+ annual: 365.25
1433
+ };
1434
+ MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1e3;
1435
+ STALE_RATE_MILLISECONDS = 7 * MILLISECONDS_PER_DAY;
1436
+ SUBSCRIPTION_PLAN_CATALOG = {
1437
+ version: "2026-08-30",
1438
+ entries: [
1439
+ {
1440
+ id: "claude-pro-monthly",
1441
+ providerId: "claude-code",
1442
+ billingDomainId: "subscription",
1443
+ displayName: "Claude Pro",
1444
+ amount: 20,
1445
+ currency: "USD",
1446
+ billingPeriod: "monthly",
1447
+ source: {
1448
+ title: "Claude plans and pricing",
1449
+ url: "https://claude.com/pricing",
1450
+ retrievedAt: "2026-08-30"
1451
+ }
1452
+ },
1453
+ {
1454
+ id: "claude-pro-annual",
1455
+ providerId: "claude-code",
1456
+ billingDomainId: "subscription",
1457
+ displayName: "Claude Pro (annual billing)",
1458
+ amount: 17 * 12,
1459
+ currency: "USD",
1460
+ billingPeriod: "annual",
1461
+ source: {
1462
+ title: "Claude plans and pricing",
1463
+ url: "https://claude.com/pricing",
1464
+ retrievedAt: "2026-08-30"
1465
+ }
1466
+ },
1467
+ {
1468
+ id: "claude-max-5x",
1469
+ providerId: "claude-code",
1470
+ billingDomainId: "subscription",
1471
+ displayName: "Claude Max 5x",
1472
+ amount: 100,
1473
+ currency: "USD",
1474
+ billingPeriod: "monthly",
1475
+ source: {
1476
+ title: "What is the Max plan?",
1477
+ url: "https://support.claude.com/en/articles/11049741-what-is-the-max-plan",
1478
+ retrievedAt: "2026-08-30"
1479
+ }
1480
+ },
1481
+ {
1482
+ id: "claude-max-20x",
1483
+ providerId: "claude-code",
1484
+ billingDomainId: "subscription",
1485
+ displayName: "Claude Max 20x",
1486
+ amount: 200,
1487
+ currency: "USD",
1488
+ billingPeriod: "monthly",
1489
+ source: {
1490
+ title: "What is the Max plan?",
1491
+ url: "https://support.claude.com/en/articles/11049741-what-is-the-max-plan",
1492
+ retrievedAt: "2026-08-30"
1493
+ }
1494
+ },
1495
+ {
1496
+ id: "chatgpt-plus",
1497
+ providerId: "codex",
1498
+ billingDomainId: "subscription",
1499
+ displayName: "ChatGPT Plus",
1500
+ amount: 20,
1501
+ currency: "USD",
1502
+ billingPeriod: "monthly",
1503
+ source: {
1504
+ title: "ChatGPT pricing",
1505
+ url: "https://chatgpt.com/pricing/",
1506
+ retrievedAt: "2026-08-30"
1507
+ }
1508
+ },
1509
+ {
1510
+ id: "chatgpt-pro-100",
1511
+ providerId: "codex",
1512
+ billingDomainId: "subscription",
1513
+ displayName: "ChatGPT Pro (5x)",
1514
+ amount: 100,
1515
+ currency: "USD",
1516
+ billingPeriod: "monthly",
1517
+ source: {
1518
+ title: "About ChatGPT Pro tiers",
1519
+ url: "https://help.openai.com/en/articles/9793128-about-chatgpt-pro-tiers",
1520
+ retrievedAt: "2026-08-30"
1521
+ }
1522
+ },
1523
+ {
1524
+ id: "chatgpt-pro-200",
1525
+ providerId: "codex",
1526
+ billingDomainId: "subscription",
1527
+ displayName: "ChatGPT Pro (20x)",
1528
+ amount: 200,
1529
+ currency: "USD",
1530
+ billingPeriod: "monthly",
1531
+ source: {
1532
+ title: "About ChatGPT Pro tiers",
1533
+ url: "https://help.openai.com/en/articles/9793128-about-chatgpt-pro-tiers",
1534
+ retrievedAt: "2026-08-30"
1535
+ }
1536
+ },
1537
+ {
1538
+ id: "chatgpt-business-seat",
1539
+ providerId: "codex",
1540
+ billingDomainId: "subscription",
1541
+ displayName: "ChatGPT Business (one seat)",
1542
+ amount: 25,
1543
+ currency: "USD",
1544
+ billingPeriod: "monthly",
1545
+ source: {
1546
+ title: "ChatGPT Business pricing",
1547
+ url: "https://openai.com/business/chatgpt-pricing/",
1548
+ retrievedAt: "2026-08-30"
1549
+ }
1550
+ },
1551
+ {
1552
+ id: "opencode-go-monthly",
1553
+ providerId: "opencode-go",
1554
+ billingDomainId: "go-subscription",
1555
+ displayName: "OpenCode Go",
1556
+ amount: 10,
1557
+ currency: "USD",
1558
+ billingPeriod: "monthly",
1559
+ source: {
1560
+ title: "OpenCode Go",
1561
+ url: "https://opencode.ai/go",
1562
+ retrievedAt: "2026-08-30"
1563
+ }
1564
+ }
1565
+ ]
1566
+ };
1567
+ }
1568
+ });
1569
+
1187
1570
  // src/core/usage-application.ts
1188
1571
  function safeConnectorFailure(error) {
1189
1572
  if (error instanceof Error && "code" in error && typeof error.code === "string" && "recovery" in error && typeof error.recovery === "string") {
@@ -1289,6 +1672,12 @@ async function withTimeout(promise, timeoutMs, message) {
1289
1672
  function notificationLevel(value) {
1290
1673
  return value === "5" ? 2 : value === "20" ? 1 : 0;
1291
1674
  }
1675
+ function pickAgentProviderIndex(overview) {
1676
+ return {
1677
+ generatedAt: overview.generatedAt,
1678
+ providers: overview.providers.filter((provider) => provider.id !== "opencode").map(({ id, displayName }) => ({ id, displayName }))
1679
+ };
1680
+ }
1292
1681
  var UsageApplication;
1293
1682
  var init_usage_application = __esm({
1294
1683
  "src/core/usage-application.ts"() {
@@ -1297,6 +1686,7 @@ var init_usage_application = __esm({
1297
1686
  init_redaction();
1298
1687
  init_usage_export();
1299
1688
  init_retail_pricing();
1689
+ init_plan_pricing();
1300
1690
  UsageApplication = class {
1301
1691
  #repository;
1302
1692
  #connectors;
@@ -1310,10 +1700,13 @@ var init_usage_application = __esm({
1310
1700
  #notifier;
1311
1701
  #startAtLoginManager;
1312
1702
  #priceCatalog;
1703
+ #planCatalog;
1313
1704
  #refreshPromise = null;
1314
1705
  #refreshMode = null;
1315
1706
  #backgroundPromise = null;
1316
1707
  #queuedHardRebuildPromise = null;
1708
+ #queuedUserProcessingPromise = null;
1709
+ #userInitiatedProcessing = false;
1317
1710
  #databaseWriteQueue = Promise.resolve();
1318
1711
  #processingStatus;
1319
1712
  constructor(options) {
@@ -1329,6 +1722,7 @@ var init_usage_application = __esm({
1329
1722
  this.#notifier = options.notifier;
1330
1723
  this.#startAtLoginManager = options.startAtLoginManager;
1331
1724
  this.#priceCatalog = options.priceCatalog === void 0 ? OFFICIAL_PRICING_CATALOG : options.priceCatalog;
1725
+ this.#planCatalog = options.planCatalog ?? SUBSCRIPTION_PLAN_CATALOG;
1332
1726
  this.#processingStatus = createProcessingStatus(this.#clock().toISOString(), false);
1333
1727
  }
1334
1728
  async #backfillRetailCosts(mode) {
@@ -1375,40 +1769,50 @@ var init_usage_application = __esm({
1375
1769
  getProcessingStatus() {
1376
1770
  return structuredClone(this.#processingStatus);
1377
1771
  }
1378
- startBackgroundProcessing() {
1379
- return this.#startProcessing("incremental");
1772
+ startBackgroundProcessing(options = {}) {
1773
+ return this.#startProcessing("incremental", options.userInitiated === true);
1380
1774
  }
1381
1775
  startHardRebuild() {
1382
- return this.#startProcessing("hard-rebuild");
1776
+ return this.#startProcessing("hard-rebuild", true);
1383
1777
  }
1384
- #startProcessing(mode) {
1778
+ #startProcessing(mode, userInitiated) {
1385
1779
  if (this.#backgroundPromise) {
1386
1780
  if (mode === "hard-rebuild" && !this.#processingStatus.hardRebuild) {
1387
1781
  this.#queuedHardRebuildPromise ??= this.#backgroundPromise.then(
1388
- () => this.#startProcessing("hard-rebuild"),
1389
- () => this.#startProcessing("hard-rebuild")
1782
+ () => this.#startProcessing("hard-rebuild", true),
1783
+ () => this.#startProcessing("hard-rebuild", true)
1390
1784
  ).finally(() => {
1391
1785
  this.#queuedHardRebuildPromise = null;
1392
1786
  });
1393
1787
  return this.#queuedHardRebuildPromise;
1394
1788
  }
1789
+ if (userInitiated && !this.#userInitiatedProcessing) {
1790
+ this.#queuedUserProcessingPromise ??= this.#backgroundPromise.then(
1791
+ () => this.#startProcessing(mode, true),
1792
+ () => this.#startProcessing(mode, true)
1793
+ ).finally(() => {
1794
+ this.#queuedUserProcessingPromise = null;
1795
+ });
1796
+ return this.#queuedUserProcessingPromise;
1797
+ }
1395
1798
  return this.#backgroundPromise;
1396
1799
  }
1800
+ this.#userInitiatedProcessing = userInitiated || mode === "hard-rebuild";
1397
1801
  this.#processingStatus = createProcessingStatus(
1398
1802
  this.#clock().toISOString(),
1399
1803
  mode === "hard-rebuild"
1400
1804
  );
1401
- this.#backgroundPromise = this.#runProcessing(mode).finally(() => {
1402
- this.#backgroundPromise = null;
1403
- });
1805
+ this.#backgroundPromise = this.#runProcessing(mode, this.#userInitiatedProcessing).finally(
1806
+ () => {
1807
+ this.#backgroundPromise = null;
1808
+ this.#userInitiatedProcessing = false;
1809
+ }
1810
+ );
1404
1811
  return this.#backgroundPromise;
1405
1812
  }
1406
- async #runProcessing(mode) {
1813
+ async #runProcessing(mode, userInitiated) {
1407
1814
  await this.#runModule("discovery", () => this.discoverConnectors().then(() => void 0));
1408
- await this.#runModule(
1409
- "usage",
1410
- () => this.refresh({ userInitiated: mode === "hard-rebuild", mode })
1411
- );
1815
+ await this.#runModule("usage", () => this.refresh({ userInitiated, mode }));
1412
1816
  await this.#runModule(
1413
1817
  "pricing",
1414
1818
  () => this.#queueDatabaseWrite(async () => {
@@ -1619,6 +2023,83 @@ var init_usage_application = __esm({
1619
2023
  connectors: this.#repository.getConnectorRuntimeStates()
1620
2024
  };
1621
2025
  }
2026
+ async getPlanSettings() {
2027
+ return {
2028
+ catalogVersion: this.#planCatalog.version,
2029
+ domains: this.#planEligibleDomains(),
2030
+ subscriptions: this.#repository.getPlanSubscriptions()
2031
+ };
2032
+ }
2033
+ async updatePlanSubscription(input) {
2034
+ const domain = this.#planEligibleDomains().find(
2035
+ (candidate) => candidate.providerId === input.providerId && candidate.billingDomainId === input.billingDomainId
2036
+ );
2037
+ if (!domain) throw new Error("Unknown subscription billing domain");
2038
+ if (input.plan === null) {
2039
+ this.#repository.deletePlanSubscription(input.providerId, input.billingDomainId);
2040
+ return this.getPlanSettings();
2041
+ }
2042
+ const preset = input.plan.planId ? planCatalogEntry(this.#planCatalog, input.plan.planId) : null;
2043
+ if (input.plan.planId && !preset) throw new Error("Unknown plan preset");
2044
+ if (preset && (preset.providerId !== input.providerId || preset.billingDomainId !== input.billingDomainId)) {
2045
+ throw new Error("Plan preset belongs to another billing domain");
2046
+ }
2047
+ const amount = input.plan.amount ?? preset?.amount;
2048
+ if (amount === void 0) throw new Error("A plan price is required");
2049
+ if (!Number.isFinite(amount) || amount <= 0) {
2050
+ throw new Error("A plan price must be a positive amount");
2051
+ }
2052
+ const currency = (input.plan.currency ?? preset?.currency ?? "USD").toUpperCase();
2053
+ if (!/^[A-Z]{3}$/.test(currency)) throw new Error("A plan currency must be a 3-letter code");
2054
+ const billingPeriod = input.plan.billingPeriod ?? preset?.billingPeriod ?? "monthly";
2055
+ const anchorDate = input.plan.anchorDate ?? null;
2056
+ if (anchorDate !== null && Number.isNaN((/* @__PURE__ */ new Date(`${anchorDate}T00:00:00.000Z`)).getTime())) {
2057
+ throw new Error("A renewal date must be a calendar date");
2058
+ }
2059
+ const overridesPreset = preset !== null && (amount !== preset.amount || currency !== preset.currency.toUpperCase() || billingPeriod !== preset.billingPeriod);
2060
+ this.#repository.savePlanSubscription({
2061
+ providerId: input.providerId,
2062
+ billingDomainId: input.billingDomainId,
2063
+ planId: preset?.id ?? null,
2064
+ displayName: preset?.displayName ?? "",
2065
+ amount,
2066
+ currency,
2067
+ billingPeriod,
2068
+ anchorDate,
2069
+ priceSource: preset && !overridesPreset ? "catalog-preset" : "user-entered",
2070
+ updatedAt: this.#clock().toISOString()
2071
+ });
2072
+ return this.getPlanSettings();
2073
+ }
2074
+ /**
2075
+ * A billing domain can carry a plan price when its connector does not report
2076
+ * an actual metered charge. Metered domains keep their own billed amounts and
2077
+ * never receive a declared subscription price.
2078
+ */
2079
+ #planEligibleDomains() {
2080
+ const domains = /* @__PURE__ */ new Map();
2081
+ for (const definition of this.#connectorDefinitions) {
2082
+ if (definition.expectedCoverage?.includes("actual-cost")) continue;
2083
+ const key = `${definition.target.provider.id}:${definition.target.billingDomain.id}`;
2084
+ if (domains.has(key)) continue;
2085
+ domains.set(key, {
2086
+ providerId: definition.target.provider.id,
2087
+ providerDisplayName: definition.target.provider.displayName,
2088
+ billingDomainId: definition.target.billingDomain.id,
2089
+ billingDomainDisplayName: definition.target.billingDomain.displayName,
2090
+ presets: planCatalogEntriesForDomain(
2091
+ this.#planCatalog,
2092
+ definition.target.provider.id,
2093
+ definition.target.billingDomain.id
2094
+ )
2095
+ });
2096
+ }
2097
+ return [...domains.values()].sort(
2098
+ (left, right) => `${left.providerId}:${left.billingDomainId}`.localeCompare(
2099
+ `${right.providerId}:${right.billingDomainId}`
2100
+ )
2101
+ );
2102
+ }
1622
2103
  async #sendNotificationTransitions() {
1623
2104
  if (!this.#notifier) return;
1624
2105
  const overview = this.#repository.getOverview(this.#clock());
@@ -1767,6 +2248,14 @@ var init_usage_application = __esm({
1767
2248
  async getOverview(query = {}) {
1768
2249
  return this.#repository.getOverview(this.#clock(), query);
1769
2250
  }
2251
+ async getAgentProviderIndex() {
2252
+ const now = this.#clock();
2253
+ return this.#repository.getAgentProviderIndex?.(now) ?? pickAgentProviderIndex(this.#repository.getOverview(now));
2254
+ }
2255
+ async getProviderOverview(providerId, query = {}) {
2256
+ const now = this.#clock();
2257
+ return this.#repository.getProviderOverview?.(now, providerId, query) ?? this.#repository.getOverview(now, query).providers.find((provider) => provider.id === providerId) ?? null;
2258
+ }
1770
2259
  async exportUsage(request) {
1771
2260
  const overview = this.#repository.getOverview(this.#clock(), request);
1772
2261
  const accountIdentifiers = request.includeAccountIdentifiers ? this.#repository.getProviderAccountIdentifiers() : {};
@@ -2082,7 +2571,7 @@ var init_claude_code_connector = __esm({
2082
2571
  usage: history.usage,
2083
2572
  ...history.usage.length > 0 && history.complete ? {
2084
2573
  usageReconciliation: {
2085
- authoritativeIdPrefix: "claude-transcript:",
2574
+ authoritativeIdPrefixes: ["claude-transcript:"],
2086
2575
  retiredIdPrefixes: ["claude-otel:"]
2087
2576
  }
2088
2577
  } : {},
@@ -2637,22 +3126,50 @@ var init_claude_usage_screen_client = __esm({
2637
3126
 
2638
3127
  // src/connectors/codex/codex-connector.ts
2639
3128
  import { z as z2 } from "zod";
2640
- function reconcileAccountRemainders(accountUsage, localUsage) {
2641
- return accountUsage.flatMap((accountObservation) => {
2642
- const day = accountObservation.observedAt.slice(0, 10);
2643
- const accountTotal = normalizeTokenObservation(accountObservation).recordedTokens;
2644
- const localTotal = localUsage.filter((observation) => observation.observedAt.slice(0, 10) === day).reduce(
2645
- (total, observation) => total + normalizeTokenObservation(observation).recordedTokens,
2646
- 0
2647
- );
2648
- if (localTotal === 0 || localTotal > accountTotal) return [];
3129
+ function settledAccountDays(response, nowMs) {
3130
+ return (response?.dailyUsageBuckets ?? []).map((bucket) => ({
3131
+ day: bucket.startDate,
3132
+ startMs: Date.parse(`${bucket.startDate}T00:00:00.000Z`),
3133
+ tokens: bucket.tokens
3134
+ })).filter((day) => Number.isFinite(day.startMs) && day.startMs + DAY_MS <= nowMs);
3135
+ }
3136
+ function reconcileAccountDays(days, localUsage) {
3137
+ const reconciledDays = new Set(days.map((day) => day.day));
3138
+ const localTotals = /* @__PURE__ */ new Map();
3139
+ for (const observation of localUsage) {
3140
+ const day = observation.observedAt.slice(0, 10);
3141
+ if (!reconciledDays.has(day)) continue;
3142
+ const recorded = normalizeTokenObservation(observation).recordedTokens;
3143
+ localTotals.set(day, (localTotals.get(day) ?? 0) + recorded);
3144
+ }
3145
+ return days.flatMap((day) => {
3146
+ const localTotal = localTotals.get(day.day) ?? 0;
3147
+ if (localTotal > day.tokens) return [];
3148
+ const observedAt = new Date(day.startMs).toISOString();
3149
+ const accountObservation = {
3150
+ id: `codex:daily:${day.day}`,
3151
+ billingDomainId: "subscription",
3152
+ model: null,
3153
+ observedAt,
3154
+ sourceReportedTotalTokens: day.tokens,
3155
+ inputTokens: 0,
3156
+ outputTokens: 0,
3157
+ cacheReadTokens: 0,
3158
+ cacheWriteTokens: 0,
3159
+ modelAttribution: "unclassified",
3160
+ timePrecision: "day",
3161
+ usageScope: "account-wide",
3162
+ authority: "official-account"
3163
+ };
3164
+ if (localTotal === 0) return [accountObservation];
2649
3165
  return [
3166
+ accountObservation,
2650
3167
  {
2651
- id: `codex-transcript:account-remainder:${day}`,
3168
+ id: `codex-transcript:account-remainder:${day.day}`,
2652
3169
  billingDomainId: "subscription",
2653
3170
  model: null,
2654
- observedAt: accountObservation.observedAt,
2655
- reconciledRemainderTokens: accountTotal - localTotal,
3171
+ observedAt,
3172
+ reconciledRemainderTokens: day.tokens - localTotal,
2656
3173
  inputTokens: 0,
2657
3174
  outputTokens: 0,
2658
3175
  cacheReadTokens: 0,
@@ -2709,23 +3226,6 @@ function mapQuotaBuckets(response) {
2709
3226
  }
2710
3227
  return buckets;
2711
3228
  }
2712
- function mapTokenUsage(response) {
2713
- return (response?.dailyUsageBuckets ?? []).map((bucket) => ({
2714
- id: `codex:daily:${bucket.startDate}`,
2715
- billingDomainId: "subscription",
2716
- model: null,
2717
- observedAt: `${bucket.startDate}T00:00:00.000Z`,
2718
- sourceReportedTotalTokens: bucket.tokens,
2719
- inputTokens: 0,
2720
- outputTokens: 0,
2721
- cacheReadTokens: 0,
2722
- cacheWriteTokens: 0,
2723
- modelAttribution: "unclassified",
2724
- timePrecision: "day",
2725
- usageScope: "account-wide",
2726
- authority: "official-account"
2727
- }));
2728
- }
2729
3229
  function formatWindowLabel(durationMinutes, fallback) {
2730
3230
  if (durationMinutes === 300) return "5 hour";
2731
3231
  if (durationMinutes === 10080) return "Week";
@@ -2734,7 +3234,7 @@ function formatWindowLabel(durationMinutes, fallback) {
2734
3234
  if (durationMinutes % 60 === 0) return `${durationMinutes / 60} hours`;
2735
3235
  return `${durationMinutes} minutes`;
2736
3236
  }
2737
- var nullableNumeric, numeric, rateLimitWindowSchema, rateLimitSnapshotSchema, codexRateLimitsSchema, codexTokenUsageSchema, CodexConnector;
3237
+ var nullableNumeric, numeric, rateLimitWindowSchema, rateLimitSnapshotSchema, codexRateLimitsSchema, codexTokenUsageSchema, CodexConnector, DAY_MS;
2738
3238
  var init_codex_connector = __esm({
2739
3239
  "src/connectors/codex/codex-connector.ts"() {
2740
3240
  "use strict";
@@ -2784,7 +3284,8 @@ var init_codex_connector = __esm({
2784
3284
  this.#historyClient = historyClient;
2785
3285
  }
2786
3286
  async collect(options = { mode: "incremental" }) {
2787
- const observedAt = this.#clock().toISOString();
3287
+ const now = this.#clock();
3288
+ const observedAt = now.toISOString();
2788
3289
  const warnings = [];
2789
3290
  let payload = null;
2790
3291
  try {
@@ -2794,18 +3295,18 @@ var init_codex_connector = __esm({
2794
3295
  }
2795
3296
  const history = this.#historyClient ? await this.#historyClient.readUsage(options) : { usage: [], costs: [], complete: true };
2796
3297
  const hasLocalHistory = history.usage.length > 0;
2797
- const accountUsage = mapTokenUsage(payload?.tokenUsage ?? null);
2798
- const canReconcile = history.complete && accountUsage.length > 0;
2799
- const reconciledRemainders = canReconcile ? reconcileAccountRemainders(accountUsage, history.usage) : [];
3298
+ const settledDays = settledAccountDays(payload?.tokenUsage ?? null, now.getTime());
3299
+ const canReconcile = history.complete && settledDays.length > 0;
3300
+ const accountUsage = canReconcile ? reconcileAccountDays(settledDays, history.usage) : [];
2800
3301
  if (!history.complete) warnings.push(incompleteTranscriptFailure2());
2801
3302
  return {
2802
3303
  provider: { id: "codex", displayName: "Codex" },
2803
3304
  billingDomains: [{ id: "subscription", displayName: "Codex subscription" }],
2804
3305
  quotaBuckets: payload ? mapQuotaBuckets(payload.rateLimits) : [],
2805
- usage: [...accountUsage, ...history.usage, ...reconciledRemainders],
3306
+ usage: [...accountUsage, ...history.usage],
2806
3307
  ...hasLocalHistory && canReconcile ? {
2807
3308
  usageReconciliation: {
2808
- authoritativeIdPrefix: "codex-transcript:",
3309
+ authoritativeIdPrefixes: ["codex-transcript:", "codex:daily:"],
2809
3310
  retiredIdPrefixes: []
2810
3311
  }
2811
3312
  } : {},
@@ -2815,6 +3316,7 @@ var init_codex_connector = __esm({
2815
3316
  };
2816
3317
  }
2817
3318
  };
3319
+ DAY_MS = 24 * 60 * 60 * 1e3;
2818
3320
  }
2819
3321
  });
2820
3322
 
@@ -3128,7 +3630,7 @@ var init_opencode_local_connector = __esm({
3128
3630
  quotaBuckets: [],
3129
3631
  usage: requests.map(mapLocalUsage),
3130
3632
  usageReconciliation: {
3131
- authoritativeIdPrefix: "opencode-local-request:",
3633
+ authoritativeIdPrefixes: ["opencode-local-request:"],
3132
3634
  retiredIdPrefixes: []
3133
3635
  },
3134
3636
  costs: requests.flatMap(
@@ -3554,7 +4056,7 @@ var init_opencode_go_connector = __esm({
3554
4056
  usage: [],
3555
4057
  ...localResult.status === "fulfilled" ? {
3556
4058
  usageReconciliation: {
3557
- authoritativeIdPrefix: "opencode-request:",
4059
+ authoritativeIdPrefixes: ["opencode-request:"],
3558
4060
  retiredIdPrefixes: ["opencode-session:"]
3559
4061
  }
3560
4062
  } : {},
@@ -3703,7 +4205,7 @@ var init_grok_build_connector = __esm({
3703
4205
  usage: history.usage,
3704
4206
  ...history.usage.length > 0 && history.complete ? {
3705
4207
  usageReconciliation: {
3706
- authoritativeIdPrefix: "grok-transcript:",
4208
+ authoritativeIdPrefixes: ["grok-transcript:"],
3707
4209
  retiredIdPrefixes: ["grok-otel:", "grok-headless:"]
3708
4210
  }
3709
4211
  } : {},
@@ -5041,6 +5543,25 @@ async function startLocalServer(options) {
5041
5543
  sendOtlpProtobuf(response, 200);
5042
5544
  return;
5043
5545
  }
5546
+ if (request.method === "GET" && requestUrl.pathname === "/api/overview/providers") {
5547
+ sendJson(response, 200, await options.application.getAgentProviderIndex());
5548
+ return;
5549
+ }
5550
+ if (request.method === "GET" && requestUrl.pathname.startsWith("/api/overview/providers/")) {
5551
+ const providerId = decodeURIComponent(
5552
+ requestUrl.pathname.slice("/api/overview/providers/".length)
5553
+ );
5554
+ const provider = await options.application.getProviderOverview(
5555
+ providerId,
5556
+ parseUsageQuery(requestUrl)
5557
+ );
5558
+ if (!provider) {
5559
+ sendJson(response, 404, { error: "provider-not-found" });
5560
+ return;
5561
+ }
5562
+ sendJson(response, 200, provider);
5563
+ return;
5564
+ }
5044
5565
  if (request.method === "GET" && requestUrl.pathname === "/api/overview") {
5045
5566
  const query = parseUsageQuery(requestUrl);
5046
5567
  sendJson(response, 200, await options.application.getOverview(query));
@@ -5051,6 +5572,13 @@ async function startLocalServer(options) {
5051
5572
  sendJson(response, 403, { error: "invalid-origin" });
5052
5573
  return;
5053
5574
  }
5575
+ if (requestUrl.searchParams.get("background") === "true") {
5576
+ void options.application.startBackgroundProcessing({
5577
+ userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
5578
+ });
5579
+ sendJson(response, 202, { accepted: true });
5580
+ return;
5581
+ }
5054
5582
  await options.application.refresh({
5055
5583
  userInitiated: requestUrl.searchParams.get("mode") !== "automatic"
5056
5584
  });
@@ -5103,6 +5631,19 @@ async function startLocalServer(options) {
5103
5631
  sendJson(response, 200, await options.application.clearData(input));
5104
5632
  return;
5105
5633
  }
5634
+ if (request.method === "GET" && requestUrl.pathname === "/api/plans") {
5635
+ sendJson(response, 200, await options.application.getPlanSettings());
5636
+ return;
5637
+ }
5638
+ if (request.method === "PATCH" && requestUrl.pathname === "/api/plans") {
5639
+ if (!validMutationOrigin(authentication, request, origin)) {
5640
+ sendJson(response, 403, { error: "invalid-origin" });
5641
+ return;
5642
+ }
5643
+ const input = planSubscriptionSchema.parse(await readJsonBody(request));
5644
+ sendJson(response, 200, await options.application.updatePlanSubscription(input));
5645
+ return;
5646
+ }
5106
5647
  if (request.method === "GET" && requestUrl.pathname === "/api/monitoring") {
5107
5648
  sendJson(response, 200, await options.application.getMonitoringStatus());
5108
5649
  return;
@@ -5301,7 +5842,7 @@ function sendHtml(response, status, body) {
5301
5842
  response.writeHead(status, { "content-type": "text/html; charset=utf-8" });
5302
5843
  response.end(body);
5303
5844
  }
5304
- var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, clearDataSchema, hardRebuildSchema;
5845
+ var SESSION_COOKIE, connectorActionSchema, usageQuerySchema, monitoringSettingsSchema, planSubscriptionSchema, clearDataSchema, hardRebuildSchema;
5305
5846
  var init_local_server = __esm({
5306
5847
  "src/server/local-server.ts"() {
5307
5848
  "use strict";
@@ -5324,6 +5865,17 @@ var init_local_server = __esm({
5324
5865
  notificationsEnabled: z9.boolean().optional(),
5325
5866
  startAtLogin: z9.boolean().optional()
5326
5867
  }).refine((value) => Object.keys(value).length > 0, "At least one setting is required");
5868
+ planSubscriptionSchema = z9.object({
5869
+ providerId: z9.string().min(1),
5870
+ billingDomainId: z9.string().min(1),
5871
+ plan: z9.object({
5872
+ planId: z9.string().min(1).nullable(),
5873
+ amount: z9.number().positive().finite().optional(),
5874
+ currency: z9.string().length(3).optional(),
5875
+ billingPeriod: z9.enum(["monthly", "annual"]).optional(),
5876
+ anchorDate: z9.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional()
5877
+ }).nullable()
5878
+ });
5327
5879
  clearDataSchema = z9.object({ deleteProductSecrets: z9.boolean().default(false) });
5328
5880
  hardRebuildSchema = z9.object({ confirmExpensiveOperation: z9.literal(true) });
5329
5881
  }
@@ -5613,8 +6165,9 @@ function isForkedCodexSession(payload) {
5613
6165
  const spawn7 = asObject(subagent?.thread_spawn);
5614
6166
  return string(spawn7?.parent_thread_id) !== null;
5615
6167
  }
5616
- async function listTranscriptFiles(root, cutoff) {
6168
+ async function listTranscriptFiles(roots, cutoff) {
5617
6169
  const files = [];
6170
+ const visited = /* @__PURE__ */ new Set();
5618
6171
  let complete = true;
5619
6172
  const walk = async (directory, isRoot = false) => {
5620
6173
  let entries;
@@ -5629,6 +6182,8 @@ async function listTranscriptFiles(root, cutoff) {
5629
6182
  if (entry.isDirectory()) {
5630
6183
  await walk(path);
5631
6184
  } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
6185
+ if (visited.has(path)) continue;
6186
+ visited.add(path);
5632
6187
  try {
5633
6188
  const metadata = await stat(path);
5634
6189
  if (metadata.mtimeMs >= cutoff) {
@@ -5640,7 +6195,7 @@ async function listTranscriptFiles(root, cutoff) {
5640
6195
  }
5641
6196
  }
5642
6197
  };
5643
- await walk(root, true);
6198
+ for (const root of roots) await walk(root, true);
5644
6199
  return { files, complete };
5645
6200
  }
5646
6201
  function isMissingPath(error) {
@@ -5733,7 +6288,7 @@ var init_local_transcript_usage_client = __esm({
5733
6288
  init_token_normalization();
5734
6289
  LocalTranscriptUsageClient = class {
5735
6290
  #provider;
5736
- #root;
6291
+ #roots;
5737
6292
  #clock;
5738
6293
  #lookbackDays;
5739
6294
  #cachePath;
@@ -5741,7 +6296,7 @@ var init_local_transcript_usage_client = __esm({
5741
6296
  #cacheLoaded = false;
5742
6297
  constructor(options) {
5743
6298
  this.#provider = options.provider;
5744
- this.#root = options.root;
6299
+ this.#roots = options.roots;
5745
6300
  this.#clock = options.clock ?? (() => /* @__PURE__ */ new Date());
5746
6301
  this.#lookbackDays = options.lookbackDays ?? 90;
5747
6302
  this.#cachePath = options.cachePath;
@@ -5750,7 +6305,7 @@ var init_local_transcript_usage_client = __esm({
5750
6305
  await this.#loadCache();
5751
6306
  if (options.mode === "hard-rebuild") this.#fileCache.clear();
5752
6307
  const cutoff = this.#clock().getTime() - this.#lookbackDays * 24 * 60 * 60 * 1e3;
5753
- const discovered = await listTranscriptFiles(this.#root, cutoff);
6308
+ const discovered = await listTranscriptFiles(this.#roots, cutoff);
5754
6309
  const files = discovered.files;
5755
6310
  let complete = discovered.complete;
5756
6311
  const usage = [];
@@ -5943,10 +6498,7 @@ async function runDatabaseWorker(workerData) {
5943
6498
  }
5944
6499
  function additiveUsagePredicate(alias = "") {
5945
6500
  const prefix = alias ? `${alias}.` : "";
5946
- const codexOfficialDays = `(SELECT billing_domain_id, date(observed_at)
5947
- FROM usage_observations
5948
- WHERE provider_id = 'codex' AND id LIKE 'codex:daily:%')`;
5949
- const codexRemainderDays = `(SELECT billing_domain_id, date(observed_at)
6501
+ const codexReconciledRemainderIds = `(SELECT billing_domain_id, id
5950
6502
  FROM usage_observations
5951
6503
  WHERE provider_id = 'codex' AND id LIKE 'codex-transcript:account-remainder:%')`;
5952
6504
  return `NOT (
@@ -5960,18 +6512,11 @@ function additiveUsagePredicate(alias = "") {
5960
6512
  )
5961
6513
  OR (
5962
6514
  ${prefix}provider_id = 'codex'
6515
+ AND ${prefix}id LIKE 'codex:daily:%'
5963
6516
  AND (
5964
- (
5965
- ${prefix}id LIKE 'codex:daily:%'
5966
- AND (${prefix}billing_domain_id, date(${prefix}observed_at)) IN ${codexRemainderDays}
5967
- )
5968
- OR (
5969
- ${prefix}id LIKE 'codex-transcript:%'
5970
- AND ${prefix}id NOT LIKE 'codex-transcript:account-remainder:%'
5971
- AND (${prefix}billing_domain_id, date(${prefix}observed_at)) IN ${codexOfficialDays}
5972
- AND (${prefix}billing_domain_id, date(${prefix}observed_at)) NOT IN ${codexRemainderDays}
5973
- )
5974
- )
6517
+ ${prefix}billing_domain_id,
6518
+ 'codex-transcript:account-remainder:' || substr(${prefix}id, 13)
6519
+ ) IN ${codexReconciledRemainderIds}
5975
6520
  )
5976
6521
  )`;
5977
6522
  }
@@ -6253,7 +6798,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
6253
6798
  tokenEvidence,
6254
6799
  apiRetailEquivalent: {
6255
6800
  status: sawRetailEquivalent ? "available" : "unavailable",
6256
- amount: sawRetailEquivalent ? preciseAmount(retailAmount) : null,
6801
+ amount: sawRetailEquivalent ? preciseAmount2(retailAmount) : null,
6257
6802
  currency: "USD",
6258
6803
  pricingCoverage: tokenEvidence.recordedTokens === 0 ? null : retailPricedTokens / tokenEvidence.recordedTokens
6259
6804
  },
@@ -6263,7 +6808,7 @@ function buildGlobalSummary(providers, riskSummary, now, query) {
6263
6808
  contributions
6264
6809
  };
6265
6810
  }
6266
- function buildTokenMoneyWorkbench(providers, now, query) {
6811
+ function buildTokenMoneyWorkbench(providers, now, query, planSubscriptions, planBillingPeriods) {
6267
6812
  const normalized = normalizeUsageQuery(now, query);
6268
6813
  const allHistories = allDomainHistories(providers);
6269
6814
  const headlineHistories = allHistories.filter(({ includedInHeadline }) => includedInHeadline);
@@ -6454,9 +6999,43 @@ function buildTokenMoneyWorkbench(providers, now, query) {
6454
6999
  observationCount > 0 ? recordedTokens : null,
6455
7000
  retailEquivalent,
6456
7001
  reportedEstimate
6457
- )
7002
+ ),
7003
+ planValue: buildWorkbenchPlanValue({
7004
+ domains: planValueDomains(allHistories, comparisonCurrency),
7005
+ subscriptions: planSubscriptions,
7006
+ comparisonCurrency,
7007
+ start: normalized.start.toISOString(),
7008
+ end: normalized.end.toISOString(),
7009
+ rates: uniqueExchangeRates(allHistories.flatMap(({ history }) => history.exchangeRates)),
7010
+ billingPeriods: planBillingPeriods
7011
+ })
6458
7012
  };
6459
7013
  }
7014
+ function planValueDomains(histories, comparisonCurrency) {
7015
+ return histories.map(({ provider, domain, history, includedInHeadline }) => {
7016
+ const domainTokens = history.tokenEvidence.recordedTokens;
7017
+ const metric = (purpose) => buildWorkbenchMetric(
7018
+ history.costs,
7019
+ purpose,
7020
+ comparisonCurrency,
7021
+ domainTokens,
7022
+ history.exchangeRates
7023
+ );
7024
+ return {
7025
+ providerId: provider.id,
7026
+ providerDisplayName: provider.displayName,
7027
+ billingDomainId: domain.id,
7028
+ billingDomainDisplayName: domain.displayName,
7029
+ includedInHeadline,
7030
+ recordedTokens: domainTokens,
7031
+ observationCount: history.tokenEvidence.observationCount,
7032
+ retailEquivalent: metric("retail-equivalent"),
7033
+ actualCost: metric("actual"),
7034
+ authorities: history.authorities ?? [],
7035
+ lastObservedAt: history.lastObservedAt ?? null
7036
+ };
7037
+ });
7038
+ }
6460
7039
  function allDomainHistories(providers) {
6461
7040
  return providers.flatMap(
6462
7041
  (provider) => provider.billingDomains.map((domain) => ({
@@ -6512,11 +7091,11 @@ function buildWorkbenchMetric(costs, purpose, comparisonCurrency, recordedTokens
6512
7091
  return {
6513
7092
  purpose,
6514
7093
  status,
6515
- amount: status === "available" ? preciseAmount(convertedAmount) : null,
7094
+ amount: status === "available" ? preciseAmount2(convertedAmount) : null,
6516
7095
  comparisonCurrency,
6517
7096
  nativeAmounts: [...native.entries()].map(([currency, amount]) => ({
6518
7097
  currency,
6519
- amount: amount.complete ? preciseAmount(amount.amount) : null,
7098
+ amount: amount.complete ? preciseAmount2(amount.amount) : null,
6520
7099
  records: amount.records,
6521
7100
  knownRecords: amount.knownRecords
6522
7101
  })).sort((left, right) => left.currency.localeCompare(right.currency)),
@@ -7108,7 +7687,7 @@ function round(value) {
7108
7687
  function pricingInputsChanged(existing, observation) {
7109
7688
  return existing.billing_domain_id !== observation.billingDomainId || existing.model !== (observation.model?.trim() || "__unclassified__") || existing.observed_at !== observation.observedAt || Number(existing.total_tokens) !== observation.recordedTokens || Number(existing.input_tokens) !== observation.inputTokens || Number(existing.output_tokens) !== observation.outputTokens || Number(existing.reasoning_tokens) !== observation.reasoningTokens || Number(existing.cache_read_tokens) !== observation.cacheReadTokens || Number(existing.cache_write_tokens) !== observation.cacheWriteTokens || (existing.cache_write_5m_tokens === null ? null : Number(existing.cache_write_5m_tokens)) !== (observation.cacheWriteTokenBreakdown?.fiveMinute ?? null) || (existing.cache_write_1h_tokens === null ? null : Number(existing.cache_write_1h_tokens)) !== (observation.cacheWriteTokenBreakdown?.oneHour ?? null) || Number(existing.unclassified_tokens) !== observation.unclassifiedTokens || existing.reasoning_semantics !== observation.tokenSemantics.reasoning || existing.cache_read_semantics !== observation.tokenSemantics.cacheRead || existing.cache_write_semantics !== observation.tokenSemantics.cacheWrite || existing.model_attribution !== observation.modelAttribution || existing.time_precision !== observation.timePrecision || existing.aggregation_temporality !== observation.aggregationTemporality;
7110
7689
  }
7111
- function preciseAmount(value) {
7690
+ function preciseAmount2(value) {
7112
7691
  return Number(value.toFixed(12));
7113
7692
  }
7114
7693
  function tokenAuthority(authorities) {
@@ -7144,6 +7723,7 @@ var init_sqlite_usage_repository = __esm({
7144
7723
  "src/server/sqlite-usage-repository.ts"() {
7145
7724
  "use strict";
7146
7725
  init_token_normalization();
7726
+ init_plan_pricing();
7147
7727
  FRESHNESS_WINDOW_MS = 15 * 60 * 1e3;
7148
7728
  QUERY_INDEXES_SQL = `
7149
7729
  CREATE INDEX IF NOT EXISTS usage_observed_at_idx
@@ -7321,7 +7901,7 @@ var init_sqlite_usage_repository = __esm({
7321
7901
  const usageReconciliation = snapshot2.usageReconciliation;
7322
7902
  if (usageReconciliation) {
7323
7903
  const prefixes = [
7324
- usageReconciliation.authoritativeIdPrefix,
7904
+ ...usageReconciliation.authoritativeIdPrefixes,
7325
7905
  ...usageReconciliation.retiredIdPrefixes
7326
7906
  ];
7327
7907
  if (prefixes.some((prefix) => prefix.length === 0)) {
@@ -7356,11 +7936,12 @@ var init_sqlite_usage_repository = __esm({
7356
7936
  for (const prefix of usageReconciliation.retiredIdPrefixes) {
7357
7937
  deleteStoredUsage(prefix, /* @__PURE__ */ new Set());
7358
7938
  }
7359
- const authoritativePrefix = usageReconciliation.authoritativeIdPrefix;
7360
- const incomingAuthoritativeIds = new Set(
7361
- snapshot2.usage.map((observation) => observation.id).filter((id) => id.startsWith(authoritativePrefix))
7362
- );
7363
- deleteStoredUsage(authoritativePrefix, incomingAuthoritativeIds);
7939
+ for (const authoritativePrefix of usageReconciliation.authoritativeIdPrefixes) {
7940
+ const incomingAuthoritativeIds = new Set(
7941
+ snapshot2.usage.map((observation) => observation.id).filter((id) => id.startsWith(authoritativePrefix))
7942
+ );
7943
+ deleteStoredUsage(authoritativePrefix, incomingAuthoritativeIds);
7944
+ }
7364
7945
  }
7365
7946
  const usageStatement = this.#database.prepare(
7366
7947
  `INSERT INTO usage_observations (
@@ -7704,14 +8285,38 @@ var init_sqlite_usage_repository = __esm({
7704
8285
  (provider) => this.#getProviderOverview(provider, now, query)
7705
8286
  );
7706
8287
  const riskSummary = buildRiskSummary(overviews);
8288
+ const planSubscriptions = this.getPlanSubscriptions();
7707
8289
  return {
7708
8290
  generatedAt: now.toISOString(),
7709
8291
  globalSummary: buildGlobalSummary(overviews, riskSummary, now, query),
7710
- workbench: buildTokenMoneyWorkbench(overviews, now, query),
8292
+ workbench: buildTokenMoneyWorkbench(
8293
+ overviews,
8294
+ now,
8295
+ query,
8296
+ planSubscriptions,
8297
+ this.#planBillingPeriodSummaries(planSubscriptions, now, query)
8298
+ ),
7711
8299
  providers: overviews,
7712
8300
  riskSummary
7713
8301
  };
7714
8302
  }
8303
+ getAgentProviderIndex(now) {
8304
+ const providers = this.#database.prepare("SELECT id, display_name FROM providers ORDER BY id").all();
8305
+ return {
8306
+ generatedAt: now.toISOString(),
8307
+ providers: providers.filter(
8308
+ (provider) => provider.id !== "opencode" && (!this.#hideDemoProvider || provider.id !== "demo")
8309
+ ).map((provider) => ({ id: provider.id, displayName: provider.display_name }))
8310
+ };
8311
+ }
8312
+ getProviderOverview(now, providerId, query = {}) {
8313
+ if (this.#hideDemoProvider && providerId === "demo") return null;
8314
+ const provider = this.#database.prepare(
8315
+ `SELECT id, display_name, last_success_at, last_error, last_error_code, last_recovery
8316
+ FROM providers WHERE id = ?`
8317
+ ).get(providerId);
8318
+ return provider ? this.#getProviderOverview(provider, now, query) : null;
8319
+ }
7715
8320
  saveExchangeRateSnapshot(snapshot2) {
7716
8321
  this.#database.prepare(
7717
8322
  `INSERT INTO exchange_rate_snapshots (
@@ -7787,6 +8392,57 @@ var init_sqlite_usage_repository = __esm({
7787
8392
  settings.startAtLogin ? 1 : 0
7788
8393
  );
7789
8394
  }
8395
+ getPlanSubscriptions() {
8396
+ const rows = this.#database.prepare(
8397
+ `SELECT provider_id, billing_domain_id, plan_id, display_name, amount, currency,
8398
+ billing_period, anchor_date, price_source, updated_at
8399
+ FROM plan_subscriptions
8400
+ ORDER BY provider_id, billing_domain_id`
8401
+ ).all();
8402
+ return rows.map((row) => ({
8403
+ providerId: row.provider_id,
8404
+ billingDomainId: row.billing_domain_id,
8405
+ planId: row.plan_id,
8406
+ displayName: row.display_name,
8407
+ amount: row.amount,
8408
+ currency: row.currency,
8409
+ billingPeriod: row.billing_period,
8410
+ anchorDate: row.anchor_date,
8411
+ priceSource: row.price_source,
8412
+ updatedAt: row.updated_at
8413
+ }));
8414
+ }
8415
+ savePlanSubscription(subscription) {
8416
+ this.#database.prepare(
8417
+ `INSERT INTO plan_subscriptions (
8418
+ provider_id, billing_domain_id, plan_id, display_name, amount, currency,
8419
+ billing_period, anchor_date, price_source, updated_at
8420
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
8421
+ ON CONFLICT(provider_id, billing_domain_id) DO UPDATE SET
8422
+ plan_id = excluded.plan_id,
8423
+ display_name = excluded.display_name,
8424
+ amount = excluded.amount,
8425
+ currency = excluded.currency,
8426
+ billing_period = excluded.billing_period,
8427
+ anchor_date = excluded.anchor_date,
8428
+ price_source = excluded.price_source,
8429
+ updated_at = excluded.updated_at`
8430
+ ).run(
8431
+ subscription.providerId,
8432
+ subscription.billingDomainId,
8433
+ subscription.planId,
8434
+ subscription.displayName,
8435
+ subscription.amount,
8436
+ subscription.currency,
8437
+ subscription.billingPeriod,
8438
+ subscription.anchorDate,
8439
+ subscription.priceSource,
8440
+ subscription.updatedAt
8441
+ );
8442
+ }
8443
+ deletePlanSubscription(providerId, billingDomainId) {
8444
+ this.#database.prepare("DELETE FROM plan_subscriptions WHERE provider_id = ? AND billing_domain_id = ?").run(providerId, billingDomainId);
8445
+ }
7790
8446
  getNotificationState(key) {
7791
8447
  const row = this.#database.prepare("SELECT value FROM notification_state WHERE key = ?").get(key);
7792
8448
  return row?.value ?? null;
@@ -8245,6 +8901,101 @@ var init_sqlite_usage_repository = __esm({
8245
8901
  coverageByDomain
8246
8902
  };
8247
8903
  }
8904
+ /**
8905
+ * Cycle-to-date evidence for every subscription that declares a renewal date.
8906
+ * Each Provider is measured over its own billing period, which is why this
8907
+ * cannot reuse the one rolling window the rest of the workbench shares.
8908
+ */
8909
+ #planBillingPeriodSummaries(subscriptions, now, query) {
8910
+ const normalized = normalizeUsageQuery(now, query);
8911
+ const summaries = /* @__PURE__ */ new Map();
8912
+ for (const subscription of subscriptions) {
8913
+ if (!subscription.anchorDate) continue;
8914
+ const period = billingPeriodContaining(
8915
+ subscription.anchorDate,
8916
+ subscription.billingPeriod,
8917
+ now
8918
+ );
8919
+ if (!period) continue;
8920
+ const observedThrough = new Date(Math.min(now.getTime(), period.end.getTime()));
8921
+ summaries.set(`${subscription.providerId}:${subscription.billingDomainId}`, {
8922
+ start: period.start.toISOString(),
8923
+ end: period.end.toISOString(),
8924
+ observedThrough: observedThrough.toISOString(),
8925
+ ...this.#rangeUsageSummary(
8926
+ subscription.providerId,
8927
+ subscription.billingDomainId,
8928
+ period.start,
8929
+ observedThrough,
8930
+ normalized.comparisonCurrency
8931
+ )
8932
+ });
8933
+ }
8934
+ return summaries;
8935
+ }
8936
+ #rangeUsageSummary(providerId, billingDomainId, start, end, comparisonCurrency) {
8937
+ const startIso = start.toISOString();
8938
+ const endIso = end.toISOString();
8939
+ const usage = this.#database.prepare(
8940
+ `SELECT id, model, observed_at, authority, total_tokens, input_tokens, output_tokens,
8941
+ reasoning_tokens, cache_read_tokens, cache_write_tokens, cache_write_5m_tokens,
8942
+ cache_write_1h_tokens, source_reported_total_tokens,
8943
+ unclassified_tokens, total_derivation, model_attribution, time_precision,
8944
+ usage_scope, aggregation_temporality, reasoning_semantics,
8945
+ cache_read_semantics, cache_write_semantics
8946
+ FROM usage_observations
8947
+ WHERE provider_id = ? AND billing_domain_id = ?
8948
+ AND observed_at >= ? AND observed_at < ?
8949
+ AND ${additiveUsagePredicate()}
8950
+ ORDER BY observed_at, id`
8951
+ ).all(providerId, billingDomainId, startIso, endIso);
8952
+ const costs = this.#database.prepare(
8953
+ `SELECT id, source_id, billing_domain_id, observed_at, kind, amount, currency, authority,
8954
+ price_snapshot_id, price_snapshot_version, price_snapshot_source,
8955
+ price_snapshot_canonical_model, price_snapshot_effective_at,
8956
+ price_snapshot_effective_until, price_snapshot_currency,
8957
+ price_snapshot_rates_json, price_snapshot_source_url,
8958
+ price_snapshot_context_tier, model, usage_observation_id, priced_tokens,
8959
+ line_items_json, calculated_at
8960
+ FROM cost_records
8961
+ WHERE provider_id = ? AND billing_domain_id = ?
8962
+ AND observed_at >= ? AND observed_at < ?
8963
+ ORDER BY observed_at, id`
8964
+ ).all(providerId, billingDomainId, startIso, endIso);
8965
+ const rateRows = this.#database.prepare(
8966
+ `SELECT id, base_currency, quote_currency, rate, observed_at, source
8967
+ FROM exchange_rate_snapshots WHERE observed_at < ? ORDER BY observed_at DESC, id`
8968
+ ).all(endIso);
8969
+ const rateByCurrency = /* @__PURE__ */ new Map();
8970
+ for (const row of rateRows) {
8971
+ if (row.quote_currency === comparisonCurrency && !rateByCurrency.has(row.base_currency)) {
8972
+ rateByCurrency.set(row.base_currency, row);
8973
+ }
8974
+ }
8975
+ const evidence = emptyTokenEvidence();
8976
+ for (const row of usage) addTokenEvidence(evidence, row);
8977
+ const finished = finishTokenEvidence(evidence);
8978
+ const usedRates = /* @__PURE__ */ new Map();
8979
+ const historyCosts = summarizeHistoryCosts(
8980
+ costs,
8981
+ finished.recordedTokens,
8982
+ rateByCurrency,
8983
+ comparisonCurrency,
8984
+ end,
8985
+ usedRates
8986
+ );
8987
+ return {
8988
+ recordedTokens: finished.recordedTokens,
8989
+ observationCount: finished.observationCount,
8990
+ retailEquivalent: buildWorkbenchMetric(
8991
+ historyCosts,
8992
+ "retail-equivalent",
8993
+ comparisonCurrency,
8994
+ finished.recordedTokens,
8995
+ [...usedRates.values()]
8996
+ )
8997
+ };
8998
+ }
8248
8999
  #getBillingHistory(providerId, billingDomainId, now, query) {
8249
9000
  const normalized = normalizeUsageQuery(now, query);
8250
9001
  const start = normalized.start.toISOString();
@@ -8653,6 +9404,19 @@ var init_sqlite_usage_repository = __esm({
8653
9404
  key TEXT PRIMARY KEY,
8654
9405
  value TEXT NOT NULL
8655
9406
  );
9407
+ CREATE TABLE IF NOT EXISTS plan_subscriptions (
9408
+ provider_id TEXT NOT NULL,
9409
+ billing_domain_id TEXT NOT NULL,
9410
+ plan_id TEXT,
9411
+ display_name TEXT NOT NULL,
9412
+ amount REAL NOT NULL,
9413
+ currency TEXT NOT NULL,
9414
+ billing_period TEXT NOT NULL,
9415
+ anchor_date TEXT,
9416
+ price_source TEXT NOT NULL,
9417
+ updated_at TEXT NOT NULL,
9418
+ PRIMARY KEY (provider_id, billing_domain_id)
9419
+ );
8656
9420
  `);
8657
9421
  const usageColumns = this.#database.prepare("PRAGMA table_info(usage_observations)").all();
8658
9422
  if (!usageColumns.some((column) => column.name === "total_tokens")) {
@@ -8821,6 +9585,10 @@ var init_sqlite_usage_repository = __esm({
8821
9585
  this.#database.exec(`ALTER TABLE quota_buckets ADD COLUMN ${name} ${type}`);
8822
9586
  }
8823
9587
  }
9588
+ const planColumns = this.#database.prepare("PRAGMA table_info(plan_subscriptions)").all();
9589
+ if (planColumns.length > 0 && !planColumns.some((column) => column.name === "anchor_date")) {
9590
+ this.#database.exec("ALTER TABLE plan_subscriptions ADD COLUMN anchor_date TEXT");
9591
+ }
8824
9592
  }
8825
9593
  };
8826
9594
  }
@@ -8957,18 +9725,19 @@ async function runDaemon(home) {
8957
9725
  function localTranscriptClient(provider, applicationHome) {
8958
9726
  return new LocalTranscriptUsageClient({
8959
9727
  provider,
8960
- root: localTranscriptRoot(provider),
9728
+ roots: localTranscriptRoots(provider),
8961
9729
  cachePath: join8(applicationHome, "cache", `${provider}-transcripts.json`)
8962
9730
  });
8963
9731
  }
8964
- function localTranscriptRoot(provider) {
9732
+ function localTranscriptRoots(provider) {
8965
9733
  if (provider === "codex") {
8966
- return join8(resolveHomeOverride(process.env.CODEX_HOME, ".codex"), "sessions");
9734
+ const home = resolveHomeOverride(process.env.CODEX_HOME, ".codex");
9735
+ return [join8(home, "sessions"), join8(home, "archived_sessions")];
8967
9736
  }
8968
9737
  if (provider === "claude-code") {
8969
- return join8(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects");
9738
+ return [join8(resolveHomeOverride(process.env.CLAUDE_CONFIG_DIR, ".claude"), "projects")];
8970
9739
  }
8971
- return join8(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions");
9740
+ return [join8(resolveHomeOverride(process.env.GROK_HOME, ".grok"), "sessions")];
8972
9741
  }
8973
9742
  function resolveHomeOverride(value, fallback) {
8974
9743
  const configured = value?.trim();