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.
@@ -899,10 +899,8 @@ async function parseClaudeFile({
899
899
  if (!usage || typeof usage !== "object") continue;
900
900
 
901
901
  if (seenMessageHashes) {
902
- const msgId = obj?.message?.id;
903
- const reqId = obj?.requestId;
904
- if (msgId && reqId) {
905
- const hash = `${msgId}:${reqId}`;
902
+ const hash = claudeMessageDedupKey(obj);
903
+ if (hash) {
906
904
  if (seenMessageHashes.has(hash)) continue;
907
905
  seenMessageHashes.add(hash);
908
906
  }
@@ -2260,6 +2258,24 @@ function normalizeUsage(u) {
2260
2258
  return out;
2261
2259
  }
2262
2260
 
2261
+ // Stable dedup key for one Claude jsonl entry. Anthropic's official protocol
2262
+ // guarantees `message.id` is globally unique per response, so msgId alone is a
2263
+ // valid dedup key. Older code required both msgId AND requestId, which short-
2264
+ // circuited dedup entirely for jsonl entries where `requestId` is absent
2265
+ // (DeepSeek/Kimi/Mimo/MiniMax anthropic-compatible endpoints don't return the
2266
+ // `request-id` HTTP header, and Claude Code's sub-agent / thinking transport
2267
+ // paths drop the field too). The short-circuit caused 1.6–3.7x overcounting on
2268
+ // every affected provider — see issue #64. Falling back to msgId-only keeps
2269
+ // backward compatibility for the (msgId, reqId) format already persisted in
2270
+ // cursors.claudeHashes (msgId strings don't contain `:`, so the two formats
2271
+ // share the same Set without collision).
2272
+ function claudeMessageDedupKey(obj) {
2273
+ const msgId = typeof obj?.message?.id === "string" && obj.message.id ? obj.message.id : null;
2274
+ if (!msgId) return null;
2275
+ const reqId = typeof obj?.requestId === "string" && obj.requestId ? obj.requestId : null;
2276
+ return reqId ? `${msgId}:${reqId}` : msgId;
2277
+ }
2278
+
2263
2279
  function normalizeClaudeUsage(u) {
2264
2280
  const inputTokens = toNonNegativeInt(u?.input_tokens);
2265
2281
  const outputTokens = toNonNegativeInt(u?.output_tokens);
@@ -5546,5 +5562,6 @@ module.exports = {
5546
5562
  // same key format sync uses elsewhere.
5547
5563
  bucketKey,
5548
5564
  totalsKey,
5565
+ claudeMessageDedupKey,
5549
5566
  groupBucketKey,
5550
5567
  };