tokentracker-cli 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokentracker-cli",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Token usage tracker for AI agent CLIs (Claude Code, Codex, Cursor, Gemini, Kiro, OpenCode, OpenClaw, Every Code, Hermes, GitHub Copilot, Kimi Code, CodeBuddy, oh-my-pi, pi, Craft Agents)",
5
5
  "main": "src/cli.js",
6
6
  "bin": {
@@ -43,6 +43,7 @@ const {
43
43
  bucketKey,
44
44
  totalsKey,
45
45
  groupBucketKey,
46
+ claudeMessageDedupKey,
46
47
  } = require("../lib/rollout");
47
48
  const { computeClaudeGroundTruthBuckets } = require("../lib/claude-categorizer");
48
49
  const { createProgress, renderBar, formatNumber, formatBytes } = require("../lib/progress");
@@ -83,7 +84,16 @@ const CLAUDE_MEM_OBSERVER_PATH_SEGMENT = "--claude-mem-observer-sessions";
83
84
  // Project Usage panel stayed inflated. v3 drops every claude /
84
85
  // claude-mem row from project.queue.jsonl too, and resets the
85
86
  // matching cursors.projectHourly + project.queue.state offset.
86
- const CLAUDE_GROUND_TRUTH_REPAIR_KEY = "claudeGroundTruthRepair_2026_05_v3";
87
+ // v4 fixes the dedup short-circuit (issue #64): v3's ground-truth scan
88
+ // itself used `if (msgId && reqId)` to build the dedup key, which silently
89
+ // disabled dedup for any provider whose jsonl entries lack `requestId`
90
+ // (DeepSeek/Kimi/Mimo/MiniMax anthropic-compatible endpoints, plus Claude
91
+ // Code's sub-agent / thinking transport paths). The repaired ground truth
92
+ // was therefore inflated by 1.6–3.7x on those providers — v3 left it that
93
+ // way. v4 re-runs the same five-step atomic repair against the corrected
94
+ // `claudeMessageDedupKey()` (msgId is globally unique on its own per the
95
+ // Anthropic protocol, so the reqId requirement was always unnecessary).
96
+ const CLAUDE_GROUND_TRUTH_REPAIR_KEY = "claudeGroundTruthRepair_2026_05_v4";
87
97
 
88
98
  async function cmdSync(argv) {
89
99
  const opts = parseArgs(argv);
@@ -1394,7 +1404,7 @@ async function repairClaudeQueueFromGroundTruth({
1394
1404
  }
1395
1405
  uploadState.offset = 0;
1396
1406
  uploadState.updatedAt = new Date().toISOString();
1397
- uploadState.note = "reset_after_claude_repair_2026_05_v3";
1407
+ uploadState.note = "reset_after_claude_repair_2026_05_v4";
1398
1408
  await fs.writeFile(queueStatePath, JSON.stringify(uploadState));
1399
1409
  }
1400
1410
 
@@ -1461,7 +1471,7 @@ async function repairClaudeQueueFromGroundTruth({
1461
1471
  }
1462
1472
  st.offset = 0;
1463
1473
  st.updatedAt = new Date().toISOString();
1464
- st.note = "reset_after_claude_repair_2026_05_v3";
1474
+ st.note = "reset_after_claude_repair_2026_05_v4";
1465
1475
  await fs.writeFile(projectQueueStatePath, JSON.stringify(st));
1466
1476
  }
1467
1477
  }
@@ -1586,9 +1596,8 @@ async function collectClaudeMessageHashes(filePaths) {
1586
1596
  } catch (_e) {
1587
1597
  continue;
1588
1598
  }
1589
- const msgId = obj?.message?.id;
1590
- const reqId = obj?.requestId;
1591
- if (msgId && reqId) hashes.add(`${msgId}:${reqId}`);
1599
+ const hash = claudeMessageDedupKey(obj);
1600
+ if (hash) hashes.add(hash);
1592
1601
  }
1593
1602
  rl.close();
1594
1603
  stream.close?.();
@@ -26,6 +26,7 @@ const {
26
26
  buildExecStatsEntry,
27
27
  allocateByLargestRemainder,
28
28
  } = require("./categorizer-utils");
29
+ const { claudeMessageDedupKey } = require("./rollout");
29
30
 
30
31
  const CATEGORY_KEYS = [
31
32
  "system_prefix",
@@ -478,10 +479,8 @@ async function categorizeSessionFile(filePath, { fromIso, toIso, seenHashes }, b
478
479
  if (fromIso && ts < fromIso) continue;
479
480
  if (toIso && ts > toIso) continue;
480
481
 
481
- const msgId = obj?.message?.id;
482
- const reqId = obj?.requestId;
483
- if (msgId && reqId) {
484
- const hash = `${msgId}:${reqId}`;
482
+ const hash = claudeMessageDedupKey(obj);
483
+ if (hash) {
485
484
  if (seenHashes.has(hash)) continue;
486
485
  seenHashes.add(hash);
487
486
  }
@@ -1133,10 +1132,8 @@ async function computeClaudeGroundTruthBuckets({ rootDir = null } = {}) {
1133
1132
  const usage = obj?.message?.usage;
1134
1133
  if (!usage || typeof usage !== "object") continue;
1135
1134
 
1136
- const msgId = obj?.message?.id;
1137
- const reqId = obj?.requestId;
1138
- if (msgId && reqId) {
1139
- const hash = `${msgId}:${reqId}`;
1135
+ const hash = claudeMessageDedupKey(obj);
1136
+ if (hash) {
1140
1137
  if (seenHashes.has(hash)) continue;
1141
1138
  seenHashes.add(hash);
1142
1139
  }