tokentracker-cli 0.77.1 → 0.77.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/README.de.md +3 -3
  2. package/README.ja.md +3 -3
  3. package/README.ko.md +3 -3
  4. package/README.md +3 -3
  5. package/README.zh-CN.md +3 -3
  6. package/dashboard/dist/assets/{ActivityHeatmap-DTZh551Z.js → ActivityHeatmap-CH6m_ZJM.js} +1 -1
  7. package/dashboard/dist/assets/{Card-DP2HT77t.js → Card-D_aKGDr7.js} +1 -1
  8. package/dashboard/dist/assets/{ClawdAnimated-ScYRJSG0.js → ClawdAnimated-vRiBiNhb.js} +1 -1
  9. package/dashboard/dist/assets/{CommunityStatsModal-CU46JMkg.js → CommunityStatsModal-CjetJZ-5.js} +1 -1
  10. package/dashboard/dist/assets/{ConfirmModal-BYvz0dzg.js → ConfirmModal-C6bZw6FZ.js} +1 -1
  11. package/dashboard/dist/assets/{Controls-DPElSq0S.js → Controls-Cj22oGL3.js} +1 -1
  12. package/dashboard/dist/assets/{DashboardPage-CN0buIso.js → DashboardPage-DPxiQkQw.js} +1 -1
  13. package/dashboard/dist/assets/{DevicePage-BL9ew9-m.js → DevicePage-YAo4ert4.js} +1 -1
  14. package/dashboard/dist/assets/{DialogClose-cqXrG903.js → DialogClose-hhTDeRp1.js} +1 -1
  15. package/dashboard/dist/assets/DialogDescription-gxEpcUbJ.js +1 -0
  16. package/dashboard/dist/assets/{DialogTitle-Oqnn_dIL.js → DialogTitle-CA5znwms.js} +1 -1
  17. package/dashboard/dist/assets/{FadeIn-CFLwnHqt.js → FadeIn--ff4ZbC-.js} +1 -1
  18. package/dashboard/dist/assets/{HeaderGithubStar-B397ELF4.js → HeaderGithubStar-CTZvanhz.js} +1 -1
  19. package/dashboard/dist/assets/{IpCheckPage-CamsHe6o.js → IpCheckPage-Bg47gNVu.js} +1 -1
  20. package/dashboard/dist/assets/LandingPage-DZ8Mz38n.js +4314 -0
  21. package/dashboard/dist/assets/{LeaderboardAvatar-B39rNGCV.js → LeaderboardAvatar-3T57GKKK.js} +1 -1
  22. package/dashboard/dist/assets/{LeaderboardPage-BFUOxX6P.js → LeaderboardPage-wP_yCU9I.js} +2 -2
  23. package/dashboard/dist/assets/{LeaderboardProfileModal-BIe6gBNu.js → LeaderboardProfileModal-CfVMENby.js} +1 -1
  24. package/dashboard/dist/assets/{LeaderboardProfilePage-DeVBEYtg.js → LeaderboardProfilePage-C4bGXJCE.js} +1 -1
  25. package/dashboard/dist/assets/{LimitsPage-6I5EU7qe.js → LimitsPage-BAQ10X55.js} +1 -1
  26. package/dashboard/dist/assets/{LocalOnlyNotice-AyYKZloH.js → LocalOnlyNotice-gtHCn7Mx.js} +1 -1
  27. package/dashboard/dist/assets/{LoginPage-D8smF1gU.js → LoginPage-TXvIBzoW.js} +1 -1
  28. package/dashboard/dist/assets/{PetPage-BlMYvJn2.js → PetPage-Dj1jrxYe.js} +1 -1
  29. package/dashboard/dist/assets/{PopoverPopup-DRWhz-bY.js → PopoverPopup-CQxxgUXk.js} +1 -1
  30. package/dashboard/dist/assets/{ResetPasswordPage-CzSqJfuA.js → ResetPasswordPage-tN0tmSfP.js} +1 -1
  31. package/dashboard/dist/assets/{Select-Cb8n7kQO.js → Select-DnbHYPS4.js} +1 -1
  32. package/dashboard/dist/assets/{SelectItemText-DEYadcIK.js → SelectItemText-2LjzGvo5.js} +1 -1
  33. package/dashboard/dist/assets/{SettingsPage-DlHXcFdJ.js → SettingsPage-DosKS_L6.js} +1 -1
  34. package/dashboard/dist/assets/{SkillsPage--Kcv1pxG.js → SkillsPage-DaCJ25vT.js} +1 -1
  35. package/dashboard/dist/assets/{WidgetsPage-DwtJ9CKU.js → WidgetsPage-CCGemQVD.js} +1 -1
  36. package/dashboard/dist/assets/{WrappedPage-BuP9KXqF.js → WrappedPage-YxshTScX.js} +1 -1
  37. package/dashboard/dist/assets/{agent-logos-DbwDXgg1.js → agent-logos-SeuxwQsp.js} +1 -1
  38. package/dashboard/dist/assets/{arrow-up-right-cFMBPEVS.js → arrow-up-right-Bm3t-iA0.js} +1 -1
  39. package/dashboard/dist/assets/check-D8Owfjyw.js +1 -0
  40. package/dashboard/dist/assets/{copy-DYx8X3ZO.js → copy-DUNz-xWm.js} +1 -1
  41. package/dashboard/dist/assets/{download-B-hXZWKj.js → download-COSLX73O.js} +1 -1
  42. package/dashboard/dist/assets/{format-tokens-CngV51wN.js → format-tokens-CR28AiQO.js} +1 -1
  43. package/dashboard/dist/assets/{info-qVF2hV50.js → info-Cm7V_KDO.js} +1 -1
  44. package/dashboard/dist/assets/{link-2-DR-yGZFS.js → link-2-CYqDGrkv.js} +1 -1
  45. package/dashboard/dist/assets/{loader-circle-0k9uueb5.js → loader-circle-6XAdy2ec.js} +1 -1
  46. package/dashboard/dist/assets/{main-CU04bNYH.js → main-BCstrDYE.js} +3 -3
  47. package/dashboard/dist/assets/main-BYdJ5vWR.css +1 -0
  48. package/dashboard/dist/assets/{trash-2-DpxBaV_1.js → trash-2-DKLewOQb.js} +1 -1
  49. package/dashboard/dist/assets/{use-community-stats-Cbij-V8j.js → use-community-stats-AdPaun74.js} +1 -1
  50. package/dashboard/dist/assets/{use-limits-display-prefs-q0TuryM0.js → use-limits-display-prefs-Bau4ykMw.js} +1 -1
  51. package/dashboard/dist/assets/{use-native-settings-BCzogs1L.js → use-native-settings-CidwlprT.js} +1 -1
  52. package/dashboard/dist/assets/{use-ordered-list-CH-J7c04.js → use-ordered-list-BVaEJaWa.js} +1 -1
  53. package/dashboard/dist/assets/{use-quality-per-dollar-pref-DKLmkwxe.js → use-quality-per-dollar-pref-Htlc-uFe.js} +1 -1
  54. package/dashboard/dist/assets/{use-usage-limits-DrmF6I_t.js → use-usage-limits-BnzWTJQS.js} +1 -1
  55. package/dashboard/dist/assets/{useCurrency-DwygZqrs.js → useCurrency-bd6TLRsc.js} +1 -1
  56. package/dashboard/dist/assets/{useScrollLock-DVCKuErg.js → useScrollLock-ajoWrfku.js} +1 -1
  57. package/dashboard/dist/index.html +2 -2
  58. package/dashboard/dist/share.html +2 -2
  59. package/package.json +1 -1
  60. package/src/commands/status.js +174 -18
  61. package/src/commands/sync.js +336 -21
  62. package/src/lib/pricing/seed-snapshot.json +1 -1
  63. package/src/lib/rollout.js +1311 -38
  64. package/dashboard/dist/assets/DialogDescription-Bo0iWtYz.js +0 -1
  65. package/dashboard/dist/assets/LandingPage-D4ygTHwO.js +0 -4312
  66. package/dashboard/dist/assets/check-t_ltRp2A.js +0 -1
  67. package/dashboard/dist/assets/main-B8lT0e78.css +0 -1
@@ -24,6 +24,11 @@ const {
24
24
  resolveKiroJsonlPath,
25
25
  resolveHermesPath,
26
26
  resolveCopilotOtelPaths,
27
+ normalizeCopilotDbPath,
28
+ uniqueCopilotDbPaths,
29
+ coalesceCopilotDbStatesByIdentity,
30
+ resolveCopilotSessionStorePaths,
31
+ getCopilotSqliteFingerprint,
27
32
  resolveCopilotAppDbPaths,
28
33
  parseRolloutIncremental,
29
34
  parseClaudeIncremental,
@@ -37,7 +42,10 @@ const {
37
42
  gooseInstallOwnsCursor,
38
43
  zedInstallOwnsCursor,
39
44
  hermesInstallOwnsCursor,
45
+ copilotOtelCursorHasLegacyCliUsage,
46
+ pruneCopilotUsageClaims,
40
47
  parseCopilotIncremental,
48
+ parseCopilotSessionStoreIncremental,
41
49
  parseCopilotAppDbIncremental,
42
50
  resolveKimiWireFiles,
43
51
  parseKimiIncremental,
@@ -218,6 +226,50 @@ function warnProviderParseFailure(label, err, opts) {
218
226
  process.stderr.write(`${label} sync: ${err && err.message ? err.message : err}\n`);
219
227
  }
220
228
 
229
+ function mergeParseResult(total, next) {
230
+ return {
231
+ recordsProcessed: total.recordsProcessed + next.recordsProcessed,
232
+ eventsAggregated: total.eventsAggregated + next.eventsAggregated,
233
+ bucketsQueued: total.bucketsQueued + next.bucketsQueued,
234
+ };
235
+ }
236
+
237
+ function copilotAppBaselineTotal(baseline) {
238
+ return (
239
+ Math.max(0, Number(baseline?.input) || 0) +
240
+ Math.max(0, Number(baseline?.output) || 0) +
241
+ Math.max(0, Number(baseline?.cached) || 0) +
242
+ Math.max(0, Number(baseline?.reasoning) || 0)
243
+ );
244
+ }
245
+
246
+ function mergeCopilotAppDbStates(primary = {}, alias = {}) {
247
+ const primaryUpdatedAt = Date.parse(primary?.updatedAt || "") || 0;
248
+ const aliasUpdatedAt = Date.parse(alias?.updatedAt || "") || 0;
249
+ const newer = aliasUpdatedAt > primaryUpdatedAt ? alias : primary;
250
+ const sessionTotals = {};
251
+ for (const state of [primary, alias]) {
252
+ for (const [sessionId, baseline] of Object.entries(
253
+ state?.sessionTotals || {},
254
+ )) {
255
+ const current = sessionTotals[sessionId];
256
+ const baselineTotal = copilotAppBaselineTotal(baseline);
257
+ const currentTotal = copilotAppBaselineTotal(current);
258
+ const baselineUpdatedAt = Date.parse(baseline?.updatedAt || "") || 0;
259
+ const currentUpdatedAt = Date.parse(current?.updatedAt || "") || 0;
260
+ if (
261
+ !current ||
262
+ baselineTotal > currentTotal ||
263
+ (baselineTotal === currentTotal &&
264
+ baselineUpdatedAt > currentUpdatedAt)
265
+ ) {
266
+ sessionTotals[sessionId] = baseline;
267
+ }
268
+ }
269
+ }
270
+ return { ...primary, ...newer, sessionTotals };
271
+ }
272
+
221
273
  async function cmdSync(argv) {
222
274
  const opts = parseArgs(argv);
223
275
  const home = os.homedir();
@@ -1326,19 +1378,135 @@ async function cmdSync(argv) {
1326
1378
  await repairGrokQueueFromSessionSnapshots({ cursors, queuePath, queueStatePath });
1327
1379
  }
1328
1380
 
1329
- // ── GitHub Copilot CLI / Chat extension (OTEL JSONL files) ──
1381
+ // ── GitHub Copilot App / CLI unified local runtime ──
1382
+ //
1383
+ // First adoption deliberately runs legacy OTEL + App DB once before the
1384
+ // session-store parser records its max-id barrier. Subsequent scans use
1385
+ // session-store as canonical for CLI/App requests: OTEL keeps only Chat
1386
+ // extension LogRecords, while App DB remains an observe-only baseline.
1330
1387
  let copilotResult = { recordsProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
1331
- const copilotPaths = sourceAllowed("copilot") ? resolveCopilotOtelPaths(process.env) : [];
1388
+ const copilotSourceAllowed = sourceAllowed("copilot");
1389
+ const adoptedCopilotStorePaths = new Set(
1390
+ Object.entries(cursors?.copilotStore?.dbs || {})
1391
+ .filter(([, state]) => state?.adoptedAt)
1392
+ .map(([dbPath]) => dbPath),
1393
+ );
1394
+ const copilotStorePaths = copilotSourceAllowed
1395
+ ? Array.from(
1396
+ new Set([
1397
+ ...resolveCopilotSessionStorePaths(process.env),
1398
+ ...adoptedCopilotStorePaths,
1399
+ ]),
1400
+ ).filter((dbPath) => {
1401
+ if (adoptedCopilotStorePaths.has(dbPath)) return true;
1402
+ try { return fssync.existsSync(dbPath); } catch (_e) { return false; }
1403
+ })
1404
+ : [];
1405
+ const copilotStoreWasActive = cursors?.copilotStore?.active === true;
1406
+ const copilotStoreAdoptionFingerprints = {};
1407
+ if (!copilotStoreWasActive) {
1408
+ for (const dbPath of copilotStorePaths) {
1409
+ try {
1410
+ copilotStoreAdoptionFingerprints[dbPath] =
1411
+ getCopilotSqliteFingerprint(dbPath);
1412
+ } catch (_e) {}
1413
+ }
1414
+ }
1415
+ let copilotHadLegacyCliOtelHistory = false;
1416
+ if (!copilotStoreWasActive) {
1417
+ copilotHadLegacyCliOtelHistory =
1418
+ await copilotOtelCursorHasLegacyCliUsage(cursors);
1419
+ if (cursors?.copilot && typeof cursors.copilot === "object") {
1420
+ if (copilotHadLegacyCliOtelHistory) {
1421
+ cursors.copilot.legacyCliHistory = true;
1422
+ } else {
1423
+ cursors.copilot.usageClaimsComplete = true;
1424
+ }
1425
+ }
1426
+ }
1427
+ let copilotLegacyHealthy = true;
1428
+ let copilotOtelUsageClaims =
1429
+ cursors?.copilot?.recentUsageEvents || [];
1430
+ let copilotStoreResult = {
1431
+ active: copilotStoreWasActive,
1432
+ healthy: false,
1433
+ adoptedThisRun: false,
1434
+ canonicalDbPaths: [],
1435
+ recordsProcessed: 0,
1436
+ eventsAggregated: 0,
1437
+ bucketsQueued: 0,
1438
+ usageClaims: cursors?.copilotStore?.recentEvents || [],
1439
+ };
1440
+ if (copilotSourceAllowed && copilotStoreWasActive) {
1441
+ if (progress?.enabled) {
1442
+ progress.start(`Parsing Copilot App/CLI ${renderBar(0)} | buckets 0`);
1443
+ }
1444
+ try {
1445
+ copilotStoreResult = await parseCopilotSessionStoreIncremental({
1446
+ dbPaths: copilotStorePaths,
1447
+ cursors,
1448
+ queuePath,
1449
+ env: process.env,
1450
+ otelUsageEvents: cursors?.copilot?.recentUsageEvents,
1451
+ onProgress: (p) => {
1452
+ if (!progress?.enabled) return;
1453
+ const pct = p.total > 0 ? p.index / p.total : 1;
1454
+ progress.update(
1455
+ `Parsing Copilot App/CLI ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} events | buckets ${formatNumber(p.bucketsQueued)}`,
1456
+ );
1457
+ },
1458
+ });
1459
+ copilotResult = mergeParseResult(copilotResult, copilotStoreResult);
1460
+ } catch (err) {
1461
+ warnProviderParseFailure("Copilot App/CLI session store", err, opts);
1462
+ }
1463
+ }
1464
+ const copilotPathKey = (dbPath) => {
1465
+ const normalized = path.normalize(dbPath);
1466
+ return process.platform === "win32" ? normalized.toLowerCase() : normalized;
1467
+ };
1468
+ const canonicalCopilotStorePaths = new Set(
1469
+ (copilotStoreResult.canonicalDbPaths || []).map(copilotPathKey),
1470
+ );
1471
+ const adoptedCopilotStorePathKeys = new Set(
1472
+ Object.entries(cursors?.copilotStore?.dbs || {})
1473
+ .filter(([, state]) => state?.adoptedAt)
1474
+ .map(([dbPath]) => copilotPathKey(dbPath)),
1475
+ );
1476
+ const explicitCopilotStorePath =
1477
+ typeof process.env.TOKENTRACKER_COPILOT_SESSION_STORE_DB === "string" &&
1478
+ process.env.TOKENTRACKER_COPILOT_SESSION_STORE_DB.trim()
1479
+ ? copilotPathKey(
1480
+ normalizeCopilotDbPath(
1481
+ process.env.TOKENTRACKER_COPILOT_SESSION_STORE_DB,
1482
+ process.env,
1483
+ ),
1484
+ )
1485
+ : null;
1486
+ const explicitStoreIsCanonical =
1487
+ explicitCopilotStorePath !== null &&
1488
+ canonicalCopilotStorePaths.has(explicitCopilotStorePath);
1489
+ const explicitStoreIsAdopted =
1490
+ explicitCopilotStorePath !== null &&
1491
+ adoptedCopilotStorePathKeys.has(explicitCopilotStorePath);
1492
+
1493
+ // ── GitHub Copilot CLI / Chat extension (OTEL JSONL files) ──
1494
+ const copilotPaths = copilotSourceAllowed ? resolveCopilotOtelPaths(process.env) : [];
1332
1495
  if (copilotPaths.length > 0) {
1333
1496
  if (progress?.enabled) {
1334
1497
  progress.start(`Parsing Copilot ${renderBar(0)} | buckets 0`);
1335
1498
  }
1336
1499
  try {
1337
- copilotResult = await parseCopilotIncremental({
1500
+ const copilotOtelResult = await parseCopilotIncremental({
1338
1501
  otelPaths: copilotPaths,
1339
1502
  cursors,
1340
1503
  queuePath,
1341
1504
  env: process.env,
1505
+ storeUsageEvents:
1506
+ copilotStoreResult.usageClaims ||
1507
+ cursors?.copilotStore?.recentEvents ||
1508
+ [],
1509
+ skipCliSpans: copilotStoreWasActive,
1342
1510
  onProgress: (p) => {
1343
1511
  if (!progress?.enabled) return;
1344
1512
  const pct = p.total > 0 ? p.index / p.total : 1;
@@ -1347,42 +1515,189 @@ async function cmdSync(argv) {
1347
1515
  );
1348
1516
  },
1349
1517
  });
1518
+ copilotOtelUsageClaims =
1519
+ copilotOtelResult.usageClaims ||
1520
+ cursors?.copilot?.recentUsageEvents ||
1521
+ [];
1522
+ copilotResult = mergeParseResult(copilotResult, copilotOtelResult);
1350
1523
  } catch (err) {
1524
+ copilotLegacyHealthy = false;
1351
1525
  warnProviderParseFailure("Copilot", err, opts);
1352
1526
  }
1353
1527
  }
1354
1528
 
1355
1529
  // ── GitHub Copilot App (passive data.db session summaries) ──
1356
- const copilotAppDbPaths = sourceAllowed("copilot")
1357
- ? resolveCopilotAppDbPaths(process.env).filter((p) => {
1358
- try { return fssync.existsSync(p); } catch (_e) { return false; }
1530
+ const copilotAppDbStates =
1531
+ cursors?.copilotApp?.dbs && typeof cursors.copilotApp.dbs === "object"
1532
+ ? cursors.copilotApp.dbs
1533
+ : null;
1534
+ const initialTrackedCopilotAppDbPaths = new Set(
1535
+ Object.keys(copilotAppDbStates || {}),
1536
+ );
1537
+ const copilotAppDbCandidates = uniqueCopilotDbPaths(
1538
+ [
1539
+ ...resolveCopilotAppDbPaths(process.env),
1540
+ ...initialTrackedCopilotAppDbPaths,
1541
+ ],
1542
+ process.env,
1543
+ Array.from(initialTrackedCopilotAppDbPaths),
1544
+ );
1545
+ coalesceCopilotDbStatesByIdentity(
1546
+ copilotAppDbStates,
1547
+ copilotAppDbCandidates,
1548
+ mergeCopilotAppDbStates,
1549
+ );
1550
+ const explicitStoreOwnsSingleApp =
1551
+ copilotAppDbCandidates.length === 1 &&
1552
+ explicitStoreIsAdopted;
1553
+ if (copilotAppDbStates && (copilotStoreWasActive || copilotStorePaths.length > 0)) {
1554
+ for (const dbPath of Object.keys(copilotAppDbStates)) {
1555
+ try {
1556
+ fssync.statSync(dbPath);
1557
+ } catch (err) {
1558
+ const matchingStorePath = copilotPathKey(
1559
+ path.join(path.dirname(dbPath), "session-store.db"),
1560
+ );
1561
+ if (err?.code === "ENOENT") {
1562
+ if (
1563
+ canonicalCopilotStorePaths.has(matchingStorePath) ||
1564
+ (explicitStoreOwnsSingleApp && explicitStoreIsCanonical)
1565
+ ) {
1566
+ delete copilotAppDbStates[dbPath];
1567
+ }
1568
+ } else {
1569
+ if (!copilotStoreWasActive) copilotLegacyHealthy = false;
1570
+ }
1571
+ }
1572
+ }
1573
+ }
1574
+ const trackedCopilotAppDbPaths = new Set(
1575
+ Object.keys(cursors?.copilotApp?.dbs || {}),
1576
+ );
1577
+ const rawCopilotAppDbPaths = copilotSourceAllowed
1578
+ ? Array.from(new Set([
1579
+ ...copilotAppDbCandidates,
1580
+ ...trackedCopilotAppDbPaths,
1581
+ ])).filter((dbPath) => {
1582
+ if (trackedCopilotAppDbPaths.has(dbPath)) return true;
1583
+ try { return fssync.existsSync(dbPath); } catch (_e) { return false; }
1359
1584
  })
1360
1585
  : [];
1586
+ const copilotAppDbPaths = uniqueCopilotDbPaths(
1587
+ rawCopilotAppDbPaths,
1588
+ process.env,
1589
+ Array.from(trackedCopilotAppDbPaths),
1590
+ );
1361
1591
  if (copilotAppDbPaths.length > 0) {
1362
1592
  if (progress?.enabled) {
1363
1593
  progress.start(`Parsing Copilot App ${renderBar(0)} | buckets 0`);
1364
1594
  }
1595
+ for (const appDbPath of copilotAppDbPaths) {
1596
+ const storePathKey = copilotPathKey(
1597
+ path.join(path.dirname(appDbPath), "session-store.db"),
1598
+ );
1599
+ const observeOnly =
1600
+ copilotStoreWasActive ||
1601
+ adoptedCopilotStorePathKeys.has(storePathKey) ||
1602
+ (copilotAppDbPaths.length === 1 && explicitStoreIsAdopted);
1603
+ const catchupRelevant =
1604
+ !observeOnly &&
1605
+ !copilotStoreWasActive &&
1606
+ copilotStorePaths.length > 0;
1607
+ try {
1608
+ const copilotAppResult = await parseCopilotAppDbIncremental({
1609
+ dbPaths: [appDbPath],
1610
+ cursors,
1611
+ queuePath,
1612
+ env: process.env,
1613
+ observeOnly,
1614
+ onProgress: (p) => {
1615
+ if (!progress?.enabled) return;
1616
+ const pct = p.total > 0 ? p.index / p.total : 1;
1617
+ progress.update(
1618
+ `Parsing Copilot App ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} sessions | buckets ${formatNumber(p.bucketsQueued)}`,
1619
+ );
1620
+ },
1621
+ });
1622
+ copilotResult = mergeParseResult(copilotResult, copilotAppResult);
1623
+ if (catchupRelevant && copilotAppResult.dbErrors > 0) {
1624
+ copilotLegacyHealthy = false;
1625
+ }
1626
+ } catch (err) {
1627
+ if (catchupRelevant) copilotLegacyHealthy = false;
1628
+ warnProviderParseFailure("Copilot App", err, opts);
1629
+ }
1630
+ }
1631
+ }
1632
+
1633
+ if (
1634
+ copilotSourceAllowed &&
1635
+ !copilotStoreWasActive &&
1636
+ copilotStorePaths.length > 0 &&
1637
+ copilotLegacyHealthy
1638
+ ) {
1639
+ if (progress?.enabled) {
1640
+ progress.start(`Adopting Copilot App/CLI store ${renderBar(0)} | buckets 0`);
1641
+ }
1365
1642
  try {
1366
- const copilotAppResult = await parseCopilotAppDbIncremental({
1367
- dbPaths: copilotAppDbPaths,
1643
+ const copilotHasOtelCursorHistory =
1644
+ (Array.isArray(cursors?.copilot?.seenIds) &&
1645
+ cursors.copilot.seenIds.length > 0) ||
1646
+ (cursors?.copilot?.fileOffsets &&
1647
+ Object.keys(cursors.copilot.fileOffsets).length > 0);
1648
+ const copilotOtelClaimsIncomplete =
1649
+ copilotHadLegacyCliOtelHistory ||
1650
+ (copilotHasOtelCursorHistory &&
1651
+ cursors?.copilot?.usageClaimsComplete !== true);
1652
+ const appSessionIds = [];
1653
+ const appSessionTotals = {};
1654
+ for (const dbState of Object.values(cursors?.copilotApp?.dbs || {})) {
1655
+ for (const [sessionId, totals] of Object.entries(
1656
+ dbState?.sessionTotals || {},
1657
+ )) {
1658
+ const total =
1659
+ Number(totals?.input || 0) +
1660
+ Number(totals?.output || 0) +
1661
+ Number(totals?.cached || 0) +
1662
+ Number(totals?.reasoning || 0);
1663
+ if (total <= 0) continue;
1664
+ appSessionIds.push(sessionId);
1665
+ const prior = appSessionTotals[sessionId];
1666
+ const priorTotal =
1667
+ Number(prior?.input || 0) +
1668
+ Number(prior?.output || 0) +
1669
+ Number(prior?.cached || 0) +
1670
+ Number(prior?.reasoning || 0);
1671
+ if (total > priorTotal) appSessionTotals[sessionId] = totals;
1672
+ }
1673
+ }
1674
+ copilotStoreResult = await parseCopilotSessionStoreIncremental({
1675
+ dbPaths: copilotStorePaths,
1368
1676
  cursors,
1369
1677
  queuePath,
1370
1678
  env: process.env,
1371
- onProgress: (p) => {
1372
- if (!progress?.enabled) return;
1373
- const pct = p.total > 0 ? p.index / p.total : 1;
1374
- progress.update(
1375
- `Parsing Copilot App ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(p.total)} sessions | buckets ${formatNumber(p.bucketsQueued)}`,
1376
- );
1377
- },
1679
+ backfillOnFirstRun: !copilotOtelClaimsIncomplete,
1680
+ excludeSessionIdsOnFirstRun: appSessionIds,
1681
+ excludeSessionTotalsOnFirstRun: appSessionTotals,
1682
+ expectedFingerprints: copilotStoreAdoptionFingerprints,
1683
+ otelUsageEvents: copilotOtelUsageClaims,
1378
1684
  });
1379
- copilotResult = {
1380
- recordsProcessed: copilotResult.recordsProcessed + copilotAppResult.recordsProcessed,
1381
- eventsAggregated: copilotResult.eventsAggregated + copilotAppResult.eventsAggregated,
1382
- bucketsQueued: copilotResult.bucketsQueued + copilotAppResult.bucketsQueued,
1383
- };
1685
+ copilotResult = mergeParseResult(copilotResult, copilotStoreResult);
1384
1686
  } catch (err) {
1385
- warnProviderParseFailure("Copilot App", err, opts);
1687
+ warnProviderParseFailure("Copilot App/CLI session store", err, opts);
1688
+ }
1689
+ }
1690
+
1691
+ if (copilotSourceAllowed) {
1692
+ if (Array.isArray(cursors?.copilot?.recentUsageEvents)) {
1693
+ cursors.copilot.recentUsageEvents = pruneCopilotUsageClaims(
1694
+ cursors.copilot.recentUsageEvents,
1695
+ );
1696
+ }
1697
+ if (Array.isArray(cursors?.copilotStore?.recentEvents)) {
1698
+ cursors.copilotStore.recentEvents = pruneCopilotUsageClaims(
1699
+ cursors.copilotStore.recentEvents,
1700
+ );
1386
1701
  }
1387
1702
  }
1388
1703