tokentracker-cli 1.3.16 → 1.4.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 (145) hide show
  1. package/README.de.md +5 -4
  2. package/README.ja.md +5 -4
  3. package/README.ko.md +5 -4
  4. package/README.md +5 -4
  5. package/README.zh-CN.md +5 -4
  6. package/dashboard/dist/assets/{AchievementBadge-D5sRTMmp.js → AchievementBadge-WGbdoboF.js} +1 -1
  7. package/dashboard/dist/assets/{AchievementsPage-CaiCfchd.js → AchievementsPage-BvybbsXv.js} +2 -2
  8. package/dashboard/dist/assets/{AchievementsSection-D6dTxdUd.js → AchievementsSection-ChNzTFSR.js} +1 -1
  9. package/dashboard/dist/assets/{BadgeDetailModal-C0UrWb9m.js → BadgeDetailModal-DhI9Ejz3.js} +1 -1
  10. package/dashboard/dist/assets/{BotAnimated-CgpBB5FZ.js → BotAnimated-Bch0Yxej.js} +1 -1
  11. package/dashboard/dist/assets/{Card-CuwBiIYz.js → Card-DWdL7d1T.js} +1 -1
  12. package/dashboard/dist/assets/{CloudCheckoutPage-BNCnBR0h.js → CloudCheckoutPage-BjGoOz6P.js} +1 -1
  13. package/dashboard/dist/assets/{CloudPage-CuYXUQnv.js → CloudPage-Btb6a97x.js} +2 -2
  14. package/dashboard/dist/assets/{CommandPalette-CV6a_7yK.js → CommandPalette-DwP3k7LE.js} +1 -1
  15. package/dashboard/dist/assets/{CommunityStatsModal-Dw-64Tig.js → CommunityStatsModal-mzqLxpwE.js} +1 -1
  16. package/dashboard/dist/assets/{ConfirmModal-CoFO1Nas.js → ConfirmModal-Dm76q4EX.js} +1 -1
  17. package/dashboard/dist/assets/{Controls-DB9eQ_rg.js → Controls-ClZjOBoD.js} +1 -1
  18. package/dashboard/dist/assets/DashboardPage-Doc8f4NL.js +1 -0
  19. package/dashboard/dist/assets/{DateRangePopover-DgZEnn4X.js → DateRangePopover-vfTUePPg.js} +1 -1
  20. package/dashboard/dist/assets/DevicePage-Jjg5q0C2.js +1 -0
  21. package/dashboard/dist/assets/{DialogClose-BR9QrB34.js → DialogClose-Ci5mPZ6X.js} +1 -1
  22. package/dashboard/dist/assets/DialogDescription-Di5ev5K6.js +1 -0
  23. package/dashboard/dist/assets/DialogTitle-DI21oZxH.js +1 -0
  24. package/dashboard/dist/assets/{ElectricLogo-nrlBy-l9.js → ElectricLogo-DfeZBwPZ.js} +1 -1
  25. package/dashboard/dist/assets/{FadeIn-CyJJ3l_P.js → FadeIn-C7SIILTc.js} +1 -1
  26. package/dashboard/dist/assets/{HeaderGithubStar-C7BX7loS.js → HeaderGithubStar-RStLbadn.js} +1 -1
  27. package/dashboard/dist/assets/{HoverTooltip-KGluh6sO.js → HoverTooltip-DI7uAp1_.js} +1 -1
  28. package/dashboard/dist/assets/{IpCheckPage-Ci2ybTDg.js → IpCheckPage-DTZNTJVh.js} +1 -1
  29. package/dashboard/dist/assets/{LandingPage-C9N17IpV.js → LandingPage-Df5scNlw.js} +2 -2
  30. package/dashboard/dist/assets/{LeaderboardPage-Cco7iFen.js → LeaderboardPage-Ck3SqtnI.js} +2 -2
  31. package/dashboard/dist/assets/{LeaderboardProBadge-tmm5NN8p.js → LeaderboardProBadge-CGx6m7R6.js} +1 -1
  32. package/dashboard/dist/assets/{LeaderboardProfileModal-WhjsvZ9Q.js → LeaderboardProfileModal-DuP7rgfv.js} +3 -3
  33. package/dashboard/dist/assets/{LeaderboardProfilePage-Ba3oVu5z.js → LeaderboardProfilePage-B5qUJkZX.js} +1 -1
  34. package/dashboard/dist/assets/{LimitsPage-CUa46XJy.js → LimitsPage-Bv_Lcvcz.js} +1 -1
  35. package/dashboard/dist/assets/{LocalOnlyNotice-BSewrWlY.js → LocalOnlyNotice-Cg4y02JM.js} +1 -1
  36. package/dashboard/dist/assets/{LoginCard-CUWn0Tz4.js → LoginCard-Wz0uJKXx.js} +1 -1
  37. package/dashboard/dist/assets/{LoginModal-UKk3xCsV.js → LoginModal-C13v_Gs0.js} +1 -1
  38. package/dashboard/dist/assets/{LoginPage-BhxiWgc5.js → LoginPage-COH9xXtd.js} +1 -1
  39. package/dashboard/dist/assets/{PetPage-BRyxC-Iu.js → PetPage-BWCnURub.js} +2 -2
  40. package/dashboard/dist/assets/PopoverPopup-Dw811N-_.js +1 -0
  41. package/dashboard/dist/assets/{ProviderIcon-htNu1RTN.js → ProviderIcon-CHxhYzK-.js} +1 -1
  42. package/dashboard/dist/assets/{ResetPasswordPage-DawMNiM1.js → ResetPasswordPage-Ll6jshqg.js} +1 -1
  43. package/dashboard/dist/assets/Select-BbooQ3sa.js +1 -0
  44. package/dashboard/dist/assets/SelectItemText-DBiW70wi.js +1 -0
  45. package/dashboard/dist/assets/{SelfHostPage-BMCMrt5I.js → SelfHostPage-BMHJxz5q.js} +1 -1
  46. package/dashboard/dist/assets/{ServiceStatusPage-e_7xC0RY.js → ServiceStatusPage-D6LH0ac-.js} +1 -1
  47. package/dashboard/dist/assets/{SessionsPage-Xr2FJa6E.js → SessionsPage-Mrer6v17.js} +2 -2
  48. package/dashboard/dist/assets/SettingsPage-D_7P_y_l.js +5 -0
  49. package/dashboard/dist/assets/SkillsPage-Ka7xm-1v.js +1 -0
  50. package/dashboard/dist/assets/{TokenGalaxy--6ijNK6P.js → TokenGalaxy-BEURDPFq.js} +1 -1
  51. package/dashboard/dist/assets/TooltipPopup-ZiuFFARC.js +1 -0
  52. package/dashboard/dist/assets/{TrendMonitor-DjIZyzxp.js → TrendMonitor-H73lyGEf.js} +1 -1
  53. package/dashboard/dist/assets/{WidgetsPage-CzCqGbb4.js → WidgetsPage-_7iCvyTk.js} +1 -1
  54. package/dashboard/dist/assets/{WrappedPage-B0RMtpvr.js → WrappedPage-DEXNb6-k.js} +1 -1
  55. package/dashboard/dist/assets/__vite-browser-external-BkNP5Ecj.js +45 -0
  56. package/dashboard/dist/assets/{agent-logos-CSlyeabY.js → agent-logos-OVBc_0cF.js} +1 -1
  57. package/dashboard/dist/assets/{arrow-left-DaqMiXTO.js → arrow-left-DDphsclm.js} +1 -1
  58. package/dashboard/dist/assets/{arrow-right-B1p9R8vH.js → arrow-right-B1aNq0Ki.js} +1 -1
  59. package/dashboard/dist/assets/{arrow-up-right-CK_9HuEh.js → arrow-up-right-yTM9azz_.js} +1 -1
  60. package/dashboard/dist/assets/{calendar-days-BVyCx4dJ.js → calendar-days-D22ithNF.js} +1 -1
  61. package/dashboard/dist/assets/{check-DIfCVsPJ.js → check-Dk7tknvE.js} +1 -1
  62. package/dashboard/dist/assets/{chevron-down-DrWxRBXX.js → chevron-down-DdyEzQ4f.js} +1 -1
  63. package/dashboard/dist/assets/{cloud-checkout-DOxBG7oS.js → cloud-checkout-BJjbipnH.js} +1 -1
  64. package/dashboard/dist/assets/composite-DEqP00XV.js +1 -0
  65. package/dashboard/dist/assets/{download-CEXAmejX.js → download-C9ixNyzn.js} +1 -1
  66. package/dashboard/dist/assets/{external-link-tDBZy6QP.js → external-link-DL4HSsLw.js} +1 -1
  67. package/dashboard/dist/assets/{flame-86y9Xf0g.js → flame-DqNH65nB.js} +1 -1
  68. package/dashboard/dist/assets/{format-tokens-BNN9k5mV.js → format-tokens-CyVjKz4I.js} +1 -1
  69. package/dashboard/dist/assets/{gift-BEOvq2zc.js → gift-ChJQQe_G.js} +1 -1
  70. package/dashboard/dist/assets/{host-mode-UjbNKxFB.js → host-mode-DpUDpoOz.js} +1 -1
  71. package/dashboard/dist/assets/{icons-DJmZD0si.js → icons-CJiHeOaU.js} +1 -1
  72. package/dashboard/dist/assets/{index-BwUwnqE0.js → index-72LQsAaf.js} +1 -1
  73. package/dashboard/dist/assets/{index-B8kizz-s.js → index-BucEg3fu.js} +1 -1
  74. package/dashboard/dist/assets/{index-Ci3CZ-2g.js → index-CrFlTSra.js} +1 -1
  75. package/dashboard/dist/assets/{info-ynVg8RzK.js → info-DReYqxC7.js} +1 -1
  76. package/dashboard/dist/assets/{link-2-B9SLPcBg.js → link-2-kIZbhK2S.js} +1 -1
  77. package/dashboard/dist/assets/{loader-circle-FTE9k7Sk.js → loader-circle-1An-X85y.js} +1 -1
  78. package/dashboard/dist/assets/{modifiers.esm-B4babCm_.js → modifiers.esm-FoVheEKT.js} +1 -1
  79. package/dashboard/dist/assets/module-DOCYJaxl.js +3 -0
  80. package/dashboard/dist/assets/{monitor-smartphone-CNzgbo-M.js → monitor-smartphone-Ch6lr_P2.js} +1 -1
  81. package/dashboard/dist/assets/{play-9W4Ua21o.js → play-CbJ6ZF1K.js} +1 -1
  82. package/dashboard/dist/assets/{plus-BA85lApI.js → plus-C-5ID3a9.js} +1 -1
  83. package/dashboard/dist/assets/{provider-display-D1ETarsz.js → provider-display-BFzRtoL2.js} +2 -2
  84. package/dashboard/dist/assets/{react-VsHxeqq7.js → react-D4CR_fLQ.js} +1 -1
  85. package/dashboard/dist/assets/safePolygon-DP21Ibqj.js +1 -0
  86. package/dashboard/dist/assets/{search-Svi--7CN.js → search-CaznkY7U.js} +1 -1
  87. package/dashboard/dist/assets/{server-CgHdX6TE.js → server-CUOmC6xZ.js} +1 -1
  88. package/dashboard/dist/assets/{shield-check-Cjv-T4aT.js → shield-check-Dr2KBMmA.js} +1 -1
  89. package/dashboard/dist/assets/{skills-api-Cn8hEYL4.js → skills-api-BAz_fblS.js} +1 -1
  90. package/dashboard/dist/assets/{sortable.esm-g3UcmvFU.js → sortable.esm-knEfm5hE.js} +1 -1
  91. package/dashboard/dist/assets/store-CL97jcij.js +1 -0
  92. package/dashboard/dist/assets/{terminal-o0jvDfI0.js → terminal-EjFyreWj.js} +1 -1
  93. package/dashboard/dist/assets/{trash-0cqpTyN6.js → trash-ByqaZXkQ.js} +1 -1
  94. package/dashboard/dist/assets/{use-community-stats-BFgORaJS.js → use-community-stats-Bs4RDJZE.js} +1 -1
  95. package/dashboard/dist/assets/{use-latest-request-guard-9eZP_jA5.js → use-latest-request-guard-CG3Ir1cP.js} +1 -1
  96. package/dashboard/dist/assets/{use-limits-display-prefs-BjO3hsmw.js → use-limits-display-prefs-tACgCxeR.js} +1 -1
  97. package/dashboard/dist/assets/{use-native-settings-YKn-htVo.js → use-native-settings-CWjXyJXi.js} +1 -1
  98. package/dashboard/dist/assets/{use-ordered-list-D5h4UwhF.js → use-ordered-list-DsuC9JQa.js} +1 -1
  99. package/dashboard/dist/assets/use-reduced-motion-CwFguzkm.js +1 -0
  100. package/dashboard/dist/assets/{use-session-efficiency-pref-BShLuIbk.js → use-session-efficiency-pref-NeoJE8BL.js} +1 -1
  101. package/dashboard/dist/assets/{use-transform-DVZHJPVD.js → use-transform-BWGKOsya.js} +1 -1
  102. package/dashboard/dist/assets/{use-usage-limits-BhJbW76K.js → use-usage-limits-CFWFNwHS.js} +1 -1
  103. package/dashboard/dist/assets/{useCurrency-BmEbDVaa.js → useCurrency-B_C4_P5x.js} +1 -1
  104. package/dashboard/dist/assets/usePositioner-B0L5_o38.js +1 -0
  105. package/dashboard/dist/assets/{useTokenFormat-CBlBP2aA.js → useTokenFormat-DH1GWHSU.js} +1 -1
  106. package/dashboard/dist/assets/{zap-cML2-Nx4.js → zap-Chv1p7pS.js} +1 -1
  107. package/dashboard/dist/index.html +13 -11
  108. package/dashboard/dist/ip-check.html +1 -1
  109. package/dashboard/dist/leaderboard.html +1 -1
  110. package/dashboard/dist/llms.txt +3 -2
  111. package/dashboard/dist/share.html +1 -1
  112. package/package.json +3 -3
  113. package/src/commands/doctor.js +15 -1
  114. package/src/commands/init.js +9 -0
  115. package/src/commands/status.js +21 -3
  116. package/src/commands/sync.js +59 -4
  117. package/src/lib/cloud-device-token.js +77 -10
  118. package/src/lib/cloud-usage-reset.js +115 -0
  119. package/src/lib/diagnostics.js +5 -2
  120. package/src/lib/kimi-model-repair.js +173 -0
  121. package/src/lib/kimi-session.js +216 -0
  122. package/src/lib/local-api.js +46 -1
  123. package/src/lib/pricing/index.js +2 -1
  124. package/src/lib/pricing/seed-snapshot.json +1 -1
  125. package/src/lib/qoder-cn-cli-limits.js +9 -7
  126. package/src/lib/qoder-limits.js +45 -15
  127. package/src/lib/rollout.js +408 -4
  128. package/src/lib/session-analytics.js +47 -9
  129. package/dashboard/dist/assets/DashboardPage-sZKijU6h.js +0 -1
  130. package/dashboard/dist/assets/DevicePage-YCnJnsmz.js +0 -1
  131. package/dashboard/dist/assets/DialogDescription-Cx1R3WT7.js +0 -1
  132. package/dashboard/dist/assets/DialogTitle-ck8f1qeP.js +0 -1
  133. package/dashboard/dist/assets/PopoverPopup-D-Tpl294.js +0 -1
  134. package/dashboard/dist/assets/Select-6WChWtqK.js +0 -1
  135. package/dashboard/dist/assets/SelectItemText-DRmxMwo5.js +0 -1
  136. package/dashboard/dist/assets/SettingsPage-Bxq2Mhxz.js +0 -5
  137. package/dashboard/dist/assets/SkillsPage-CEIBW63c.js +0 -1
  138. package/dashboard/dist/assets/TooltipPopup--TLYUg0c.js +0 -1
  139. package/dashboard/dist/assets/__vite-browser-external-BsOjTB8Q.js +0 -45
  140. package/dashboard/dist/assets/composite-W1R1XwoE.js +0 -1
  141. package/dashboard/dist/assets/module-BNnaKM__.js +0 -3
  142. package/dashboard/dist/assets/safePolygon-CCDOsx-z.js +0 -1
  143. package/dashboard/dist/assets/store-D2gn9bSV.js +0 -1
  144. package/dashboard/dist/assets/use-reduced-motion-C83gmg0F.js +0 -1
  145. package/dashboard/dist/assets/usePositioner-nY0YNa-6.js +0 -1
@@ -37,6 +37,7 @@ const { resolveZcodeNativeDbPath, resolveInstallPaths } = require("./install-res
37
37
  const { readCursorStateSummary } = require("./cursor-store");
38
38
  const { USD_TICKS_PER_USD, normalizeGrokUsage } = require("./grok-usage");
39
39
  const wsl = require("./wsl-probe");
40
+ const { discoverKimiWireFiles, kimiSessionDependencies, readKimiSessions } = require("./kimi-session");
40
41
  const {
41
42
  appendUniqueDirs,
42
43
  dedupeDirsByRealpath,
@@ -85,7 +86,8 @@ const {
85
86
  // v22 adds metadata-only OpenCode database sessions.
86
87
  // v23 excludes database updates and user records from request durations.
87
88
  // v24 keeps OpenCode usage dates at event time and fork history on its ancestor.
88
- const SIDECAR_VERSION = 24;
89
+ // v25 adds Kimi wire sessions, including official/legacy migration dedup.
90
+ const SIDECAR_VERSION = 25;
89
91
  const EDIT_TOOLS = new Set([
90
92
  "apply_patch",
91
93
  "edit",
@@ -1925,6 +1927,14 @@ function groupCodexFiles(filePaths) {
1925
1927
  return ordered;
1926
1928
  }
1927
1929
 
1930
+ async function scanKimiSessions(filePaths) {
1931
+ const rows = await readKimiSessions(Array.isArray(filePaths) ? filePaths : [filePaths]);
1932
+ return rows.map(({ identity, files, ...row }) => finalizeRecord({
1933
+ ...row, version: SIDECAR_VERSION, source: "kimi", session_hash: sessionHash("kimi", identity),
1934
+ project_key: projectKey(row.project_ref, files[0]), ...sessionStorageUsage(files),
1935
+ }));
1936
+ }
1937
+
1928
1938
  async function discoverSessionFiles(home, env = process.env, deps = {}) {
1929
1939
  const grokHome = resolveGrokHome(home);
1930
1940
  // config.scanRoots (#657): read from the tracker config unless the caller
@@ -2007,7 +2017,17 @@ async function discoverSessionFiles(home, env = process.env, deps = {}) {
2007
2017
  const opencodePaths = resolveInstallPaths({ nativeValue: opencodeNativeDir, wslValue: opencodeWslDir }, env, { platform: zcodePlatform });
2008
2018
  const opencode = [...new Set(Object.values(opencodePaths).filter(Boolean)
2009
2019
  .map((dir) => path.join(dir, "opencode.db")).filter((dbPath) => fs.existsSync(dbPath)))];
2010
- return { claude, codex: groupCodexFiles([...codex, ...archived]), grok, zcode, opencode, incomplete };
2020
+ const kimiRoots = [".kimi", ".kimi-code"].flatMap((directory) => {
2021
+ const roots = providerRoots(home, directory, env, deps);
2022
+ const override = env[directory === ".kimi" ? "KIMI_HOME" : "KIMI_CODE_HOME"];
2023
+ if (override && path.resolve(home) === path.resolve((deps.homedir || os.homedir)())) {
2024
+ const native = path.join(home, directory);
2025
+ return roots.map((root) => root === native ? expandHome(override, home) : root);
2026
+ }
2027
+ return roots;
2028
+ });
2029
+ const kimi = discoverKimiWireFiles(dedupeDirsByRealpath(kimiRoots), incomplete);
2030
+ return { claude, codex: groupCodexFiles([...codex, ...archived]), grok, zcode, opencode, kimi, incomplete };
2011
2031
  }
2012
2032
 
2013
2033
  // Warn once per unreadable directory per process: the dashboard polls this
@@ -2108,6 +2128,9 @@ function analyticsEntryStatKey(source, filePath) {
2108
2128
  ));
2109
2129
  return `${sessionStat}|title-index:${titleStats.join("|")}`;
2110
2130
  }
2131
+ if (source === "kimi") {
2132
+ return `${sessionStat}|metadata:${filesSignature(kimiSessionDependencies(filePaths))}`;
2133
+ }
2111
2134
  if (source === "grok") {
2112
2135
  const primary = filePaths[0];
2113
2136
  return `${sessionStat}|summary:${sessionFileStatKey(grokSummaryPathFor(primary)) || "missing"}|signals:${sessionFileStatKey(grokSignalsPathFor(primary)) || "missing"}`;
@@ -2147,13 +2170,15 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2147
2170
  const retryZcodeScan = fs.existsSync(incompleteZcodePath);
2148
2171
  const incompleteOpencodePath = `${metaPath}.opencode-incomplete`;
2149
2172
  const retryOpencodeScan = fs.existsSync(incompleteOpencodePath);
2173
+ const incompleteKimiPath = `${metaPath}.kimi-incomplete`;
2174
+ const retryKimiScan = fs.existsSync(incompleteKimiPath);
2150
2175
  let previousMeta = null;
2151
2176
  if (!force) {
2152
2177
  try {
2153
2178
  previousMeta = JSON.parse(fs.readFileSync(metaPath, "utf8"));
2154
2179
  const checkedAt = Date.parse(previousMeta.checked_at || previousMeta.generated_at || "");
2155
2180
  if (
2156
- !retryZcodeScan && !retryOpencodeScan && previousMeta.version === SIDECAR_VERSION &&
2181
+ !retryZcodeScan && !retryOpencodeScan && !retryKimiScan && previousMeta.version === SIDECAR_VERSION &&
2157
2182
  Number.isFinite(checkedAt) &&
2158
2183
  Date.now() - checkedAt < Math.max(0, Number(cacheTtlMs) || 0)
2159
2184
  ) {
@@ -2183,13 +2208,15 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2183
2208
  ...discovered.claude.flat(),
2184
2209
  ...discovered.codex.flat(),
2185
2210
  ...discovered.codex.flat().map(codexTitleIndexPathFor).filter(Boolean),
2211
+ ...discovered.kimi,
2212
+ ...kimiSessionDependencies(discovered.kimi),
2186
2213
  ...discovered.grok,
2187
2214
  ...discovered.grok.map(grokSummaryPathFor),
2188
2215
  ...discovered.grok.map(grokSignalsPathFor),
2189
2216
  // db + its WAL sidecars: the trio is one logical file for change detection.
2190
2217
  ...[...(discovered.zcode || []), ...(discovered.opencode || [])].flatMap((dbPath) => [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]),
2191
2218
  ]);
2192
- if (!force && !retryZcodeScan && !retryOpencodeScan && previousMeta?.version === SIDECAR_VERSION && previousMeta.signature === signature) {
2219
+ if (!force && !retryZcodeScan && !retryOpencodeScan && !retryKimiScan && previousMeta?.version === SIDECAR_VERSION && previousMeta.signature === signature) {
2193
2220
  await writeAtomic(metaPath, `${JSON.stringify({ ...previousMeta, checked_at: new Date().toISOString() })}\n`);
2194
2221
  return readSidecar(sidecarPath);
2195
2222
  }
@@ -2211,6 +2238,7 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2211
2238
  ...discovered.grok.map((filePath) => ({ source: "grok", filePath, scan: scanGrokSession })),
2212
2239
  ...(discovered.zcode.length ? [{ source: "zcode", filePath: discovered.zcode, scan: scanZcodeSessions }] : []),
2213
2240
  ...(discovered.opencode.length ? [{ source: "opencode", filePath: discovered.opencode, scan: scanOpencodeSessions }] : []),
2241
+ ...(discovered.kimi.length ? [{ source: "kimi", filePath: discovered.kimi, scan: scanKimiSessions }] : []),
2214
2242
  ];
2215
2243
  // Files we could not turn into a row (permission denied, half-written line,
2216
2244
  // vanished mid-scan). Swallowing these silently made sessions disappear with
@@ -2218,6 +2246,7 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2218
2246
  let skippedFiles = 0;
2219
2247
  let incompleteZcodeScan = false;
2220
2248
  let incompleteOpencodeScan = false;
2249
+ let incompleteKimiScan = false;
2221
2250
  for (const entry of entries) {
2222
2251
  const cacheKey = sessionFileCacheKey(entry.source, entry.filePath);
2223
2252
  const statKey = analyticsEntryStatKey(entry.source, entry.filePath);
@@ -2225,11 +2254,12 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2225
2254
  skippedFiles += 1;
2226
2255
  if (entry.source === "zcode") incompleteZcodeScan = true;
2227
2256
  if (entry.source === "opencode") incompleteOpencodeScan = true;
2257
+ if (entry.source === "kimi") incompleteKimiScan = true;
2228
2258
  continue;
2229
2259
  }
2230
2260
  let rows = null;
2231
- if (!force && !(entry.source === "zcode" && retryZcodeScan) && !(entry.source === "opencode" && retryOpencodeScan) && previousFiles[cacheKey]?.stat_key === statKey) {
2232
- if (entry.source === "zcode" || entry.source === "opencode") {
2261
+ if (!force && !(entry.source === "zcode" && retryZcodeScan) && !(entry.source === "opencode" && retryOpencodeScan) && !(entry.source === "kimi" && retryKimiScan) && previousFiles[cacheKey]?.stat_key === statKey) {
2262
+ if (entry.source === "zcode" || entry.source === "opencode" || entry.source === "kimi") {
2233
2263
  // One db scans to many cached rows; recover them all from the previous
2234
2264
  // pass. An empty recovery means the sidecar lost its rows — rescan.
2235
2265
  const cached = previousRows.filter((row) => row?.source === entry.source);
@@ -2249,6 +2279,7 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2249
2279
  skippedFiles += 1;
2250
2280
  if (entry.source === "zcode") incompleteZcodeScan = true;
2251
2281
  if (entry.source === "opencode") incompleteOpencodeScan = true;
2282
+ if (entry.source === "kimi") incompleteKimiScan = true;
2252
2283
  if (!process.env.NODE_TEST_CONTEXT) {
2253
2284
  console.warn(`[session-analytics] skipped ${entry.source} session file: ${error?.message || error}`);
2254
2285
  }
@@ -2262,8 +2293,8 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2262
2293
  // so key each by the db path plus its session id; a bare session id as a
2263
2294
  // separate path element would be path.resolve()d against the process cwd
2264
2295
  // and never match across runs.
2265
- row._cache_key = (entry.source === "zcode" || entry.source === "opencode") && row.session_id
2266
- ? `${cacheKey}#${sessionHash(entry.source, row.session_id)}`
2296
+ row._cache_key = (entry.source === "zcode" || entry.source === "opencode" || entry.source === "kimi") && (row.session_id || row.session_hash)
2297
+ ? `${cacheKey}#${sessionHash(entry.source, row.session_id || row.session_hash)}`
2267
2298
  : cacheKey;
2268
2299
  sessions.push(row);
2269
2300
  }
@@ -2282,10 +2313,14 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2282
2313
  sessions.push(...readSidecar(sidecarPath).filter((row) =>
2283
2314
  row.source === "opencode" && row.version === SIDECAR_VERSION));
2284
2315
  }
2316
+ if (incompleteKimiScan) {
2317
+ await writeAtomic(incompleteKimiPath, "1\n");
2318
+ sessions.push(...readSidecar(sidecarPath).filter((row) => row.source === "kimi" && row.version === SIDECAR_VERSION));
2319
+ }
2285
2320
  sessions.sort((a, b) => String(b.ended_at || "").localeCompare(String(a.ended_at || "")));
2286
2321
  // A failed database scan is an incomplete inventory. Keep the last complete
2287
2322
  // snapshot and signature so an unchanged database is retried next build.
2288
- if (incompleteDirs.length === 0 && !incompleteZcodeScan && !incompleteOpencodeScan) {
2323
+ if (incompleteDirs.length === 0 && !incompleteZcodeScan && !incompleteOpencodeScan && !incompleteKimiScan) {
2289
2324
  const content = sessions.map(serializeSessionRecord).join("\n") + (sessions.length ? "\n" : "");
2290
2325
  await writeAtomic(sidecarPath, content);
2291
2326
  const generatedAt = new Date().toISOString();
@@ -2298,6 +2333,7 @@ async function buildSessionAnalyticsInternal({ home = os.homedir(), force = fals
2298
2333
  })}\n`);
2299
2334
  await fsp.rm(incompleteZcodePath, { force: true });
2300
2335
  await fsp.rm(incompleteOpencodePath, { force: true });
2336
+ await fsp.rm(incompleteKimiPath, { force: true });
2301
2337
  }
2302
2338
  // Non-enumerable so the array still behaves exactly like a plain row list
2303
2339
  // for every existing caller (map/filter/JSON of the rows is unaffected).
@@ -2579,6 +2615,7 @@ function resumeCommandFor(source, sessionId) {
2579
2615
  if (source === "claude") return `claude --resume ${id}`;
2580
2616
  if (source === "codex") return `codex resume ${id}`;
2581
2617
  if (source === "opencode") return `opencode --session ${id}`;
2618
+ if (source === "kimi") return `kimi --session ${id}`;
2582
2619
  // Grok Build: `grok --resume <session-id>` (also accepts a title). The
2583
2620
  // command must still be run from the session's project_ref cwd.
2584
2621
  if (source === "grok") return `grok --resume ${id}`;
@@ -2922,6 +2959,7 @@ module.exports = {
2922
2959
  scanGrokSession,
2923
2960
  scanZcodeSessions,
2924
2961
  scanOpencodeSessions,
2962
+ scanKimiSessions,
2925
2963
  buildSessionAnalytics,
2926
2964
  summarizeSessions,
2927
2965
  listSessionsForBrowser,