tokentracker-cli 0.88.2 → 0.88.3

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 (95) hide show
  1. package/dashboard/dist/assets/{AchievementBadge-BcCrg832.js → AchievementBadge-B6CZtEaS.js} +1 -1
  2. package/dashboard/dist/assets/{AchievementsPage-C95dF_C-.js → AchievementsPage-YJJREeYN.js} +2 -2
  3. package/dashboard/dist/assets/{AchievementsSection-B-VD8CfN.js → AchievementsSection-Bz8kzUKE.js} +1 -1
  4. package/dashboard/dist/assets/{BadgeDetailModal-CDTJ3tR6.js → BadgeDetailModal-B18owwHf.js} +1 -1
  5. package/dashboard/dist/assets/{Card-a0RpvhLn.js → Card-D_HcMRXq.js} +1 -1
  6. package/dashboard/dist/assets/{ClawdAnimated-CaLKUbmC.js → ClawdAnimated-BR4Vm-wp.js} +1 -1
  7. package/dashboard/dist/assets/{CommandPalette-CQpkX7dK.js → CommandPalette-DD0iOGEP.js} +1 -1
  8. package/dashboard/dist/assets/{CommunityStatsModal-Cj53g1ky.js → CommunityStatsModal-CfJn-_nP.js} +1 -1
  9. package/dashboard/dist/assets/{ConfirmModal-FHZmD91i.js → ConfirmModal-8zQyesYk.js} +1 -1
  10. package/dashboard/dist/assets/{Controls-BSk2Eph4.js → Controls-Dd_7ctXC.js} +1 -1
  11. package/dashboard/dist/assets/{DashboardPage-Ci1jVw-F.js → DashboardPage-CyIp3wde.js} +1 -1
  12. package/dashboard/dist/assets/{DevicePage-BnCWVmOI.js → DevicePage-BjNEbSgO.js} +1 -1
  13. package/dashboard/dist/assets/{DialogClose-BUhPBSeF.js → DialogClose-BPRpp4Wp.js} +1 -1
  14. package/dashboard/dist/assets/DialogDescription-FmibVDMx.js +1 -0
  15. package/dashboard/dist/assets/{DialogTitle-CbNKnmTY.js → DialogTitle-Cq4jatmU.js} +1 -1
  16. package/dashboard/dist/assets/{FadeIn-D87p8xfM.js → FadeIn-DoK6rAcp.js} +1 -1
  17. package/dashboard/dist/assets/{HeaderGithubStar-kE4I1PyE.js → HeaderGithubStar-B3konO6W.js} +1 -1
  18. package/dashboard/dist/assets/{HoverTooltip-miw72aLh.js → HoverTooltip-BaDkj8r8.js} +1 -1
  19. package/dashboard/dist/assets/{IpCheckPage-D-JoHTz-.js → IpCheckPage-BkrAKRYK.js} +1 -1
  20. package/dashboard/dist/assets/{LandingPage-Dx_MOVjJ.js → LandingPage-av_mctyf.js} +2 -2
  21. package/dashboard/dist/assets/{LeaderboardAvatar-arnp_cWI.js → LeaderboardAvatar-CgdMLdWa.js} +1 -1
  22. package/dashboard/dist/assets/{LeaderboardPage-zsoFUX5G.js → LeaderboardPage-Vb3i4Qpc.js} +2 -2
  23. package/dashboard/dist/assets/{LeaderboardProfileModal-CrmSTl9n.js → LeaderboardProfileModal-CVxiTLDv.js} +3 -3
  24. package/dashboard/dist/assets/{LeaderboardProfilePage-BSE0fPco.js → LeaderboardProfilePage-BWIwKo1y.js} +1 -1
  25. package/dashboard/dist/assets/{LimitsPage-BrU9e8AV.js → LimitsPage-CA1Ve4x5.js} +1 -1
  26. package/dashboard/dist/assets/{LocalOnlyNotice-BWDZKn00.js → LocalOnlyNotice-Rm3uyEk3.js} +1 -1
  27. package/dashboard/dist/assets/{LoginCard-DS8mxxs_.js → LoginCard-DL0l2lLw.js} +1 -1
  28. package/dashboard/dist/assets/{LoginModal-DhINfVDV.js → LoginModal-C5Fk3Jms.js} +1 -1
  29. package/dashboard/dist/assets/{LoginPage-BKmuhR74.js → LoginPage-Cu7DAOnx.js} +1 -1
  30. package/dashboard/dist/assets/{PetPage-QQk_B1SJ.js → PetPage-BgqjLomc.js} +1 -1
  31. package/dashboard/dist/assets/{PopoverPopup-DS6ml7IE.js → PopoverPopup-CEHZ6QXJ.js} +1 -1
  32. package/dashboard/dist/assets/{ProviderIcon-DkHp1Hrl.js → ProviderIcon-BC3XWoZ3.js} +1 -1
  33. package/dashboard/dist/assets/{ResetPasswordPage-CvJiufPF.js → ResetPasswordPage-BrongFEU.js} +1 -1
  34. package/dashboard/dist/assets/{SegmentedControl-DbYmpdj3.js → SegmentedControl-D8EHNgNi.js} +1 -1
  35. package/dashboard/dist/assets/{Select-DJcVfYm7.js → Select-D9k3JTvC.js} +1 -1
  36. package/dashboard/dist/assets/{SelectItemText-DUQLkHGQ.js → SelectItemText-BZ-2piB5.js} +1 -1
  37. package/dashboard/dist/assets/{ServiceStatusPage-DqPQUM1f.js → ServiceStatusPage-Cv_KU00i.js} +1 -1
  38. package/dashboard/dist/assets/{SessionsPage-CPOSombb.js → SessionsPage-DIPKSBas.js} +1 -1
  39. package/dashboard/dist/assets/{SettingsPage-nIfi1h7D.js → SettingsPage-ClKxRKyx.js} +1 -1
  40. package/dashboard/dist/assets/{SkillsPage-BnngQwIu.js → SkillsPage-DvuwO5Yv.js} +1 -1
  41. package/dashboard/dist/assets/{TokenGalaxy-CAVmlIYW.js → TokenGalaxy-DIM8f80U.js} +1 -1
  42. package/dashboard/dist/assets/{ToolbarRootContext-CyG7xQo2.js → ToolbarRootContext-BprUPTPI.js} +1 -1
  43. package/dashboard/dist/assets/{TrendMonitor-DyuKVbzV.js → TrendMonitor-DWFiGJ35.js} +1 -1
  44. package/dashboard/dist/assets/{WidgetsPage-72n9-HCD.js → WidgetsPage-DL-MG-Q7.js} +1 -1
  45. package/dashboard/dist/assets/{WrappedPage-Dp1aUOyA.js → WrappedPage-CGIDKH61.js} +1 -1
  46. package/dashboard/dist/assets/{__vite-browser-external-8kU62_i_.js → __vite-browser-external-BKzvjCbJ.js} +2 -2
  47. package/dashboard/dist/assets/{agent-logos-ChfjzWPV.js → agent-logos-CbNruoos.js} +1 -1
  48. package/dashboard/dist/assets/{arrow-up-right-CiTMPVa_.js → arrow-up-right-eLrRdfJt.js} +1 -1
  49. package/dashboard/dist/assets/{check-OQW1qgf_.js → check-DZD0Jp7h.js} +1 -1
  50. package/dashboard/dist/assets/{chevron-right-BitImfyV.js → chevron-right-B500Y_Nj.js} +1 -1
  51. package/dashboard/dist/assets/{copy-BM76c1Ob.js → copy-uJNaZG-w.js} +1 -1
  52. package/dashboard/dist/assets/{download-DaKmqoH2.js → download-DzvthQjK.js} +1 -1
  53. package/dashboard/dist/assets/{external-link-AL639M-l.js → external-link-CbRIdeZk.js} +1 -1
  54. package/dashboard/dist/assets/{flame-BeYD0c6y.js → flame-BGvWQF3z.js} +1 -1
  55. package/dashboard/dist/assets/{format-tokens-DK1Fdxfq.js → format-tokens-BmLj1DrP.js} +1 -1
  56. package/dashboard/dist/assets/{icons-BtLj_BDi.js → icons-oryPazn7.js} +1 -1
  57. package/dashboard/dist/assets/{index-BK2Bm5Vp.js → index-BC_awyRs.js} +1 -1
  58. package/dashboard/dist/assets/{index-BHBegQjp.js → index-RZ9PpvhZ.js} +1 -1
  59. package/dashboard/dist/assets/{index-D4kdhgAz.js → index-lCPHhuCN.js} +1 -1
  60. package/dashboard/dist/assets/{info-CIwDFvrE.js → info-DmArnish.js} +1 -1
  61. package/dashboard/dist/assets/{limits-providers-BFkGzLM4.js → limits-providers-DmO_jdt4.js} +1 -1
  62. package/dashboard/dist/assets/{link-2-xFS-GP5p.js → link-2-Cp4yrCYD.js} +1 -1
  63. package/dashboard/dist/assets/{loader-circle-83n_EWic.js → loader-circle-DGUMo8NS.js} +1 -1
  64. package/dashboard/dist/assets/{maximize-2-CevrJZKM.js → maximize-2-BLObxnY1.js} +1 -1
  65. package/dashboard/dist/assets/{modifiers.esm-a1oXNZlE.js → modifiers.esm-B_-ZYAOP.js} +1 -1
  66. package/dashboard/dist/assets/{monitor-smartphone-CSP2FeVP.js → monitor-smartphone-CFtFQCf_.js} +1 -1
  67. package/dashboard/dist/assets/{provider-display-CamXa0pj.js → provider-display-zfwJ-zfN.js} +1 -1
  68. package/dashboard/dist/assets/{react-DV1nb3lW.js → react-C9v6bUwR.js} +1 -1
  69. package/dashboard/dist/assets/{search-wj8Kxgr5.js → search-DcdG91Gl.js} +1 -1
  70. package/dashboard/dist/assets/{skills-api-BNpGEfgh.js → skills-api-Cbj6O4EO.js} +1 -1
  71. package/dashboard/dist/assets/{sortable.esm-Xh3nfzFR.js → sortable.esm-CvVo8fVR.js} +1 -1
  72. package/dashboard/dist/assets/{store-lBXh9-l6.js → store-BCViEsFt.js} +1 -1
  73. package/dashboard/dist/assets/{terminal-uNBlGBuA.js → terminal-BBtxMCI7.js} +1 -1
  74. package/dashboard/dist/assets/{trash-2-08YOSvJh.js → trash-2-Dn_E9JQO.js} +1 -1
  75. package/dashboard/dist/assets/{use-community-stats-CMyCuzcM.js → use-community-stats-DPAzvqWl.js} +1 -1
  76. package/dashboard/dist/assets/{use-limits-display-prefs-BkqvWQ4X.js → use-limits-display-prefs-DC9B7mbP.js} +1 -1
  77. package/dashboard/dist/assets/{use-ordered-list-CJ9F2mdG.js → use-ordered-list-BUUwYoO2.js} +1 -1
  78. package/dashboard/dist/assets/use-reduced-motion-Bs24cR01.js +1 -0
  79. package/dashboard/dist/assets/{use-session-efficiency-pref-Hf4gvuAl.js → use-session-efficiency-pref-OJfrVVxb.js} +1 -1
  80. package/dashboard/dist/assets/{use-transform-BBP_cds4.js → use-transform-CVLI5mjY.js} +1 -1
  81. package/dashboard/dist/assets/{use-usage-limits-DsLD0OEm.js → use-usage-limits-BXgn5HJr.js} +1 -1
  82. package/dashboard/dist/assets/{useCurrency-CXngtOdH.js → useCurrency-RNt_kRXK.js} +1 -1
  83. package/dashboard/dist/assets/{useScrollLock-Q65zPe5d.js → useScrollLock-CGjecig7.js} +1 -1
  84. package/dashboard/dist/assets/{useTokenFormat-CE5zbI_h.js → useTokenFormat-CPg1vv3V.js} +1 -1
  85. package/dashboard/dist/assets/{zap-CHYAc2Be.js → zap-CamjYQ0R.js} +1 -1
  86. package/dashboard/dist/index.html +1 -1
  87. package/dashboard/dist/ip-check.html +1 -1
  88. package/dashboard/dist/leaderboard.html +1 -1
  89. package/dashboard/dist/share.html +1 -1
  90. package/package.json +1 -1
  91. package/src/lib/codex-rollout-parser.js +15 -2
  92. package/src/lib/pricing/seed-snapshot.json +1 -1
  93. package/src/lib/session-analytics.js +128 -96
  94. package/dashboard/dist/assets/DialogDescription-Dp7bBdfJ.js +0 -1
  95. package/dashboard/dist/assets/use-reduced-motion-CD_DKLbl.js +0 -1
@@ -250,8 +250,23 @@ function finalizeRecord(record) {
250
250
  return record;
251
251
  }
252
252
 
253
+ function readableSessionPaths(filePath) {
254
+ const candidates = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
255
+ const readable = candidates.filter((value) => sessionFileStatKey(value));
256
+ if (!readable.length) {
257
+ throw new Error("session files vanished before they could be scanned");
258
+ }
259
+ return readable;
260
+ }
261
+
262
+ function isRecoverableSessionReadError(error) {
263
+ return ["ENOENT", "EACCES", "EPERM", "EISDIR", "EIO", "ESTALE"].includes(error?.code);
264
+ }
265
+
253
266
  async function scanClaudeSession(filePath) {
254
- const filePaths = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
267
+ // Native and WSL mounts can disappear independently after discovery. Keep
268
+ // the surviving mirror instead of dropping the whole logical session.
269
+ const filePaths = readableSessionPaths(filePath);
255
270
  const primaryFilePath = filePaths[0] || String(filePath || "");
256
271
  const tokens = emptyTotals();
257
272
  // One logical cross-root group is cached and scanned as a unit, so widening
@@ -289,68 +304,76 @@ async function scanClaudeSession(filePath) {
289
304
  // already present in an earlier root. Hashes stay in-memory only, preserving
290
305
  // the metadata-only persistence contract.
291
306
  const priorRecordHashes = new Set();
307
+ let scannedFiles = 0;
292
308
  for (const currentFilePath of filePaths) {
293
309
  const currentRecordHashes = new Set();
294
- const input = fs.createReadStream(currentFilePath, { encoding: "utf8" });
295
- const lines = readline.createInterface({ input, crlfDelay: Infinity });
296
- for await (const line of lines) {
297
- if (filePaths.length > 1) {
298
- const recordHash = crypto.createHash("sha256").update(line).digest("base64url");
299
- if (priorRecordHashes.has(recordHash)) continue;
300
- currentRecordHashes.add(recordHash);
301
- }
302
- let obj;
303
- try { obj = JSON.parse(line); } catch { continue; }
304
- updateBounds(bounds, obj.timestamp || obj.message?.timestamp);
305
- if (typeof obj.sessionId === "string" && obj.sessionId) {
306
- rawSessionId = obj.sessionId;
307
- observedSessionId = obj.sessionId;
308
- }
309
- if (typeof obj.cwd === "string" && obj.cwd) cwd = obj.cwd;
310
- // Claude writes its own generated one-line summary as an "ai-title" record.
311
- // It is agent-authored metadata (not the raw prompt body); keep the latest.
312
- if (obj.type === "ai-title" && typeof obj.aiTitle === "string") {
313
- const cleaned = cleanSessionTitle(obj.aiTitle);
314
- if (cleaned) aiTitle = cleaned;
315
- continue;
316
- }
317
- if (obj.type === "user") {
318
- const prompt = extractClaudePrompt(obj);
319
- if (!prompt) continue;
320
- const fingerprint = promptFingerprint(prompt);
321
- if (lastPromptFingerprint && fingerprint === lastPromptFingerprint) retryTurns += 1;
322
- lastPromptFingerprint = fingerprint;
323
- closeTurn();
324
- turns += 1;
325
- continue;
326
- }
327
- if (obj.type !== "assistant" || !obj.message) continue;
328
- const dedupKey = claudeMessageDedupKey(obj);
329
- if (dedupKey && seenMessages.has(dedupKey)) continue;
330
- if (dedupKey) seenMessages.add(dedupKey);
331
- // Claude writes internal summary/observer messages with model
332
- // "<synthetic>". They are not a billable model and can appear after the
333
- // real assistant messages in the same session. Keep the latest real
334
- // model instead of letting that marker overwrite it.
335
- const candidateModel = normalizeSessionModel(obj.message.model);
336
- if (candidateModel) model = candidateModel;
337
- addTotals(tokens, tokenTotals(obj.message.usage));
338
- const content = Array.isArray(obj.message.content) ? obj.message.content : [];
339
- for (const block of content) {
340
- if (!block || block.type !== "tool_use") continue;
341
- const name = String(block.name || "").toLowerCase();
342
- if (EDIT_TOOLS.has(name)) currentHadEdit = true;
343
- if (name === "agent" || name === "task") {
344
- subagentCalls += 1;
345
- const subtype = typeof block.input?.subagent_type === "string"
346
- ? block.input.subagent_type.trim().slice(0, 64)
347
- : "unspecified";
348
- subagentTypes.set(subtype || "unspecified", (subagentTypes.get(subtype || "unspecified") || 0) + 1);
310
+ try {
311
+ const input = fs.createReadStream(currentFilePath, { encoding: "utf8" });
312
+ const lines = readline.createInterface({ input, crlfDelay: Infinity });
313
+ for await (const line of lines) {
314
+ if (filePaths.length > 1) {
315
+ const recordHash = crypto.createHash("sha256").update(line).digest("base64url");
316
+ if (priorRecordHashes.has(recordHash)) continue;
317
+ currentRecordHashes.add(recordHash);
318
+ }
319
+ let obj;
320
+ try { obj = JSON.parse(line); } catch { continue; }
321
+ updateBounds(bounds, obj.timestamp || obj.message?.timestamp);
322
+ if (typeof obj.sessionId === "string" && obj.sessionId) {
323
+ rawSessionId = obj.sessionId;
324
+ observedSessionId = obj.sessionId;
325
+ }
326
+ if (typeof obj.cwd === "string" && obj.cwd) cwd = obj.cwd;
327
+ // Claude writes its own generated one-line summary as an "ai-title" record.
328
+ // It is agent-authored metadata (not the raw prompt body); keep the latest.
329
+ if (obj.type === "ai-title" && typeof obj.aiTitle === "string") {
330
+ const cleaned = cleanSessionTitle(obj.aiTitle);
331
+ if (cleaned) aiTitle = cleaned;
332
+ continue;
333
+ }
334
+ if (obj.type === "user") {
335
+ const prompt = extractClaudePrompt(obj);
336
+ if (!prompt) continue;
337
+ const fingerprint = promptFingerprint(prompt);
338
+ if (lastPromptFingerprint && fingerprint === lastPromptFingerprint) retryTurns += 1;
339
+ lastPromptFingerprint = fingerprint;
340
+ closeTurn();
341
+ turns += 1;
342
+ continue;
343
+ }
344
+ if (obj.type !== "assistant" || !obj.message) continue;
345
+ const dedupKey = claudeMessageDedupKey(obj);
346
+ if (dedupKey && seenMessages.has(dedupKey)) continue;
347
+ if (dedupKey) seenMessages.add(dedupKey);
348
+ // Claude writes internal summary/observer messages with model
349
+ // "<synthetic>". They are not a billable model and can appear after the
350
+ // real assistant messages in the same session. Keep the latest real
351
+ // model instead of letting that marker overwrite it.
352
+ const candidateModel = normalizeSessionModel(obj.message.model);
353
+ if (candidateModel) model = candidateModel;
354
+ addTotals(tokens, tokenTotals(obj.message.usage));
355
+ const content = Array.isArray(obj.message.content) ? obj.message.content : [];
356
+ for (const block of content) {
357
+ if (!block || block.type !== "tool_use") continue;
358
+ const name = String(block.name || "").toLowerCase();
359
+ if (EDIT_TOOLS.has(name)) currentHadEdit = true;
360
+ if (name === "agent" || name === "task") {
361
+ subagentCalls += 1;
362
+ const subtype = typeof block.input?.subagent_type === "string"
363
+ ? block.input.subagent_type.trim().slice(0, 64)
364
+ : "unspecified";
365
+ subagentTypes.set(subtype || "unspecified", (subagentTypes.get(subtype || "unspecified") || 0) + 1);
366
+ }
349
367
  }
350
368
  }
369
+ scannedFiles += 1;
370
+ } catch (error) {
371
+ if (!isRecoverableSessionReadError(error)) throw error;
372
+ } finally {
373
+ for (const recordHash of currentRecordHashes) priorRecordHashes.add(recordHash);
351
374
  }
352
- for (const recordHash of currentRecordHashes) priorRecordHashes.add(recordHash);
353
375
  }
376
+ if (!scannedFiles) throw new Error("all grouped Claude session files failed during read");
354
377
  closeTurn();
355
378
  return finalizeRecord({
356
379
  version: SIDECAR_VERSION,
@@ -377,7 +400,7 @@ async function scanClaudeSession(filePath) {
377
400
  }
378
401
 
379
402
  async function scanCodexDeliverySignals(filePath) {
380
- const filePaths = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
403
+ const filePaths = readableSessionPaths(filePath);
381
404
  const bounds = emptyBounds();
382
405
  let turns = 0;
383
406
  let editTurns = 0;
@@ -404,46 +427,54 @@ async function scanCodexDeliverySignals(filePath) {
404
427
  }
405
428
 
406
429
  const priorRecordHashes = new Set();
430
+ let scannedFiles = 0;
407
431
  for (const currentFilePath of filePaths) {
408
432
  const currentRecordHashes = new Set();
409
- const input = fs.createReadStream(currentFilePath, { encoding: "utf8" });
410
- const lines = readline.createInterface({ input, crlfDelay: Infinity });
411
- for await (const line of lines) {
412
- if (filePaths.length > 1) {
413
- const recordHash = crypto.createHash("sha256").update(line).digest("base64url");
414
- if (priorRecordHashes.has(recordHash)) continue;
415
- currentRecordHashes.add(recordHash);
416
- }
417
- let obj;
418
- try { obj = JSON.parse(line); } catch { continue; }
419
- updateBounds(bounds, obj.timestamp);
420
- if (obj.type === "turn_context") {
421
- hasTurnContext = true;
422
- beginTurn(String(obj.payload?.turn_id || obj.timestamp || turns + 1));
423
- continue;
424
- }
425
- const prompt = extractCodexPrompt(obj);
426
- if (prompt) {
427
- const fingerprint = promptFingerprint(prompt);
428
- if (lastPromptFingerprint && fingerprint === lastPromptFingerprint) retryTurns += 1;
429
- lastPromptFingerprint = fingerprint;
430
- if (!hasTurnContext) beginTurn(String(obj.timestamp || turns + 1));
431
- continue;
432
- }
433
- if (obj.type !== "response_item") continue;
434
- const toolNames = extractCodexSignalTools(obj.payload);
435
- if (!toolNames.length) continue;
436
- if (!currentTurnOpen) beginTurn(String(obj.timestamp || turns + 1));
437
- if (toolNames.some((name) => EDIT_TOOLS.has(name))) currentHadEdit = true;
438
- for (const name of toolNames) {
439
- if (!CODEX_SUBAGENT_TOOLS.has(name)) continue;
440
- subagentCalls += 1;
441
- const displayName = name === "multi_agent_v1__spawn_agent" ? "spawn_agent" : name;
442
- subagentTypes.set(displayName, (subagentTypes.get(displayName) || 0) + 1);
433
+ try {
434
+ const input = fs.createReadStream(currentFilePath, { encoding: "utf8" });
435
+ const lines = readline.createInterface({ input, crlfDelay: Infinity });
436
+ for await (const line of lines) {
437
+ if (filePaths.length > 1) {
438
+ const recordHash = crypto.createHash("sha256").update(line).digest("base64url");
439
+ if (priorRecordHashes.has(recordHash)) continue;
440
+ currentRecordHashes.add(recordHash);
441
+ }
442
+ let obj;
443
+ try { obj = JSON.parse(line); } catch { continue; }
444
+ updateBounds(bounds, obj.timestamp);
445
+ if (obj.type === "turn_context") {
446
+ hasTurnContext = true;
447
+ beginTurn(String(obj.payload?.turn_id || obj.timestamp || turns + 1));
448
+ continue;
449
+ }
450
+ const prompt = extractCodexPrompt(obj);
451
+ if (prompt) {
452
+ const fingerprint = promptFingerprint(prompt);
453
+ if (lastPromptFingerprint && fingerprint === lastPromptFingerprint) retryTurns += 1;
454
+ lastPromptFingerprint = fingerprint;
455
+ if (!hasTurnContext) beginTurn(String(obj.timestamp || turns + 1));
456
+ continue;
457
+ }
458
+ if (obj.type !== "response_item") continue;
459
+ const toolNames = extractCodexSignalTools(obj.payload);
460
+ if (!toolNames.length) continue;
461
+ if (!currentTurnOpen) beginTurn(String(obj.timestamp || turns + 1));
462
+ if (toolNames.some((name) => EDIT_TOOLS.has(name))) currentHadEdit = true;
463
+ for (const name of toolNames) {
464
+ if (!CODEX_SUBAGENT_TOOLS.has(name)) continue;
465
+ subagentCalls += 1;
466
+ const displayName = name === "multi_agent_v1__spawn_agent" ? "spawn_agent" : name;
467
+ subagentTypes.set(displayName, (subagentTypes.get(displayName) || 0) + 1);
468
+ }
443
469
  }
470
+ scannedFiles += 1;
471
+ } catch (error) {
472
+ if (!isRecoverableSessionReadError(error)) throw error;
473
+ } finally {
474
+ for (const recordHash of currentRecordHashes) priorRecordHashes.add(recordHash);
444
475
  }
445
- for (const recordHash of currentRecordHashes) priorRecordHashes.add(recordHash);
446
476
  }
477
+ if (!scannedFiles) throw new Error("all grouped Codex signal files failed during read");
447
478
  closeTurn();
448
479
  return {
449
480
  bounds,
@@ -456,7 +487,7 @@ async function scanCodexDeliverySignals(filePath) {
456
487
  }
457
488
 
458
489
  async function scanCodexSession(filePath) {
459
- const filePaths = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
490
+ const filePaths = readableSessionPaths(filePath);
460
491
  const primaryFilePath = filePaths[0] || String(filePath || "");
461
492
  const [parsed, signals] = await Promise.all([
462
493
  parseCodexRolloutFile(filePaths, { seenTokenEvents: new Set() }),
@@ -1034,8 +1065,8 @@ function loadCodexTitleIndex(filePath) {
1034
1065
  // touching updates.jsonl on some partial flushes).
1035
1066
  function analyticsEntryStatKey(source, filePath) {
1036
1067
  const filePaths = (Array.isArray(filePath) ? filePath : [filePath]).filter(Boolean);
1037
- const sessionStats = filePaths.map((value) => sessionFileStatKey(value));
1038
- if (sessionStats.some((value) => !value)) return null;
1068
+ const sessionStats = filePaths.map((value) => sessionFileStatKey(value) || "missing");
1069
+ if (sessionStats.every((value) => value === "missing")) return null;
1039
1070
  const sessionStat = sessionStats.join("|");
1040
1071
  if (source === "codex") {
1041
1072
  const titleStats = filePaths.map((value) => (
@@ -1446,4 +1477,5 @@ module.exports = {
1446
1477
  sessionsToCsv,
1447
1478
  providerRoots,
1448
1479
  dedupeClaudeFilesAcrossRoots,
1480
+ analyticsEntryStatKey,
1449
1481
  };
@@ -1 +0,0 @@
1
- import{r as n,z as a}from"./__vite-browser-external-8kU62_i_.js";import{u as p}from"./DialogTitle-CbNKnmTY.js";import{u as c}from"./useScrollLock-Q65zPe5d.js";const x=n.forwardRef(function(e,r){const{render:l,className:u,style:m,id:s,...t}=e,{store:i}=p(),o=c(s);return i.useSyncedValueWithCleanup("descriptionElementId",o),a("p",e,{ref:r,props:[{id:o},t]})});export{x as D};
@@ -1 +0,0 @@
1
- import{r as o}from"./__vite-browser-external-8kU62_i_.js";import{h as r,i as t,p as s}from"./react-DV1nb3lW.js";function i(){!r.current&&t();const[e]=o.useState(s.current);return e}export{i as u};