@yhong91/vibetime 0.1.27 → 0.1.28

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 (2) hide show
  1. package/bin/vibetime.mjs +123 -60
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -1928,7 +1928,7 @@ function countTextLines(text) {
1928
1928
  }
1929
1929
 
1930
1930
  // src/lib/constants.ts
1931
- var PACKAGE_VERSION = true ? "0.1.27" : "0.1.1";
1931
+ var PACKAGE_VERSION = true ? "0.1.28" : "0.1.1";
1932
1932
  var DEFAULT_API_URL = "http://121.196.224.82:3001";
1933
1933
  var DEFAULT_BACKFILL_BATCH_SIZE = 50;
1934
1934
  var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
@@ -4894,7 +4894,8 @@ async function parseCodexSessionFile(filePath, options) {
4894
4894
  let lastTurnIdForComplete;
4895
4895
  let turnIdAtLastUserMessage;
4896
4896
  let sessionMetaLocked = false;
4897
- let lastTokenUsageKey;
4897
+ let lastTotalTokenUsage;
4898
+ let lastPerCallUsageKey;
4898
4899
  const pendingToolCalls = /* @__PURE__ */ new Map();
4899
4900
  for (const [index, line] of lines.entries()) {
4900
4901
  const lineNumber = index + 1;
@@ -5042,32 +5043,79 @@ async function parseCodexSessionFile(filePath, options) {
5042
5043
  }
5043
5044
  const usage = tokenUsageFromPayload(payload);
5044
5045
  if (usage) {
5045
- const metrics = { ...usage, reasoningEffort };
5046
- const usageKey = [
5047
- usage.tokensInput,
5048
- usage.tokensCachedInput,
5049
- usage.tokensOutput,
5050
- usage.tokensReasoningOutput,
5051
- usage.tokensTotal
5052
- ].join(":");
5053
- if (usageKey === lastTokenUsageKey) {
5046
+ const input = usage.tokensInput ?? 0;
5047
+ const cached = usage.tokensCachedInput ?? 0;
5048
+ const output = usage.tokensOutput ?? 0;
5049
+ const reasoning = usage.tokensReasoningOutput ?? 0;
5050
+ const total = usage.tokensTotal ?? 0;
5051
+ const hasCumulativeUsage = Object.keys(objectField(info, "total_token_usage")).length > 0;
5052
+ const usageKey = [input, cached, output, reasoning, total].join(":");
5053
+ if (!hasCumulativeUsage) {
5054
+ if (usageKey !== lastPerCallUsageKey) {
5055
+ events.push(withBackfillRefs(baseCodexEvent({
5056
+ ts,
5057
+ type: "model.usage",
5058
+ sessionId,
5059
+ turnId: currentTurnId,
5060
+ cwd,
5061
+ project,
5062
+ model: rewriteCodexModelForTier(model, serviceTier),
5063
+ confidence: "partial",
5064
+ metrics: usage
5065
+ }), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
5066
+ lastPerCallUsageKey = usageKey;
5067
+ }
5054
5068
  break;
5055
5069
  }
5056
- lastTokenUsageKey = usageKey;
5057
- events.push(withBackfillRefs(baseCodexEvent({
5058
- ts,
5059
- type: "model.usage",
5060
- sessionId,
5061
- turnId: currentTurnId,
5062
- cwd,
5063
- project,
5064
- // Only the model.usage event carries the -fast suffix; tool and
5065
- // turn events keep the bare model so other queries (e.g. "what
5066
- // model was the user on") don't show tier-specific names.
5067
- model: rewriteCodexModelForTier(model, serviceTier),
5068
- confidence: "partial",
5069
- metrics
5070
- }), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
5070
+ if (lastTotalTokenUsage && total >= lastTotalTokenUsage.total) {
5071
+ const deltaInput = input - lastTotalTokenUsage.input;
5072
+ const deltaCached = cached - lastTotalTokenUsage.cached;
5073
+ const deltaOutput = output - lastTotalTokenUsage.output;
5074
+ const deltaReasoning = reasoning - lastTotalTokenUsage.reasoning;
5075
+ const deltaTotal = total - lastTotalTokenUsage.total;
5076
+ if (deltaInput > 0 || deltaOutput > 0 || deltaTotal > 0) {
5077
+ events.push(withBackfillRefs(baseCodexEvent({
5078
+ ts,
5079
+ type: "model.usage",
5080
+ sessionId,
5081
+ turnId: currentTurnId,
5082
+ cwd,
5083
+ project,
5084
+ model: rewriteCodexModelForTier(model, serviceTier),
5085
+ confidence: "partial",
5086
+ metrics: {
5087
+ tokensInput: deltaInput > 0 ? deltaInput : void 0,
5088
+ tokensCachedInput: deltaCached > 0 ? deltaCached : void 0,
5089
+ tokensOutput: deltaOutput > 0 ? deltaOutput : void 0,
5090
+ tokensReasoningOutput: deltaReasoning > 0 ? deltaReasoning : void 0,
5091
+ tokensTotal: deltaTotal > 0 ? deltaTotal : void 0,
5092
+ modelContextWindow: usage.modelContextWindow,
5093
+ reasoningEffort
5094
+ }
5095
+ }), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
5096
+ }
5097
+ } else {
5098
+ events.push(withBackfillRefs(baseCodexEvent({
5099
+ ts,
5100
+ type: "model.usage",
5101
+ sessionId,
5102
+ turnId: currentTurnId,
5103
+ cwd,
5104
+ project,
5105
+ model: rewriteCodexModelForTier(model, serviceTier),
5106
+ confidence: "partial",
5107
+ metrics: {
5108
+ tokensInput: input,
5109
+ tokensCachedInput: cached,
5110
+ tokensOutput: output,
5111
+ tokensReasoningOutput: reasoning,
5112
+ tokensTotal: total,
5113
+ modelContextWindow: usage.modelContextWindow,
5114
+ reasoningEffort
5115
+ }
5116
+ }), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
5117
+ }
5118
+ lastTotalTokenUsage = { input, cached, output, reasoning, total };
5071
5119
  }
5072
5120
  break;
5073
5121
  }
@@ -5284,7 +5332,7 @@ function headlessCodexUsage(raw) {
5284
5332
  const cached = numberField(usage, "cached_input_tokens") ?? numberField(usage, "cache_read_input_tokens") ?? numberField(usage, "cached_tokens") ?? 0;
5285
5333
  const reasoning = numberField(usage, "reasoning_output_tokens") ?? numberField(usage, "reasoning_tokens") ?? 0;
5286
5334
  const totalField = numberField(usage, "total_tokens");
5287
- const total = totalField !== void 0 && (totalField > 0 || input + output + reasoning === 0) ? totalField : input + output + reasoning;
5335
+ const total = totalField !== void 0 && (totalField > 0 || input + output === 0) ? totalField : input + output;
5288
5336
  if (input === 0 && cached === 0 && output === 0 && reasoning === 0 && total === 0) {
5289
5337
  return;
5290
5338
  }
@@ -5293,7 +5341,7 @@ function headlessCodexUsage(raw) {
5293
5341
  tokensCachedInput: cached || void 0,
5294
5342
  tokensOutput: output || void 0,
5295
5343
  tokensReasoningOutput: reasoning || void 0,
5296
- tokensTotal: total,
5344
+ tokensTotal: total || void 0,
5297
5345
  modelCalls: 1
5298
5346
  };
5299
5347
  }
@@ -5320,16 +5368,26 @@ function headlessCodexTimestamp(raw) {
5320
5368
  }
5321
5369
  function tokenUsageFromPayload(payload) {
5322
5370
  const info = objectField(payload, "info");
5323
- const usage = objectField(info, "last_token_usage");
5371
+ const totalUsage = objectField(info, "total_token_usage");
5372
+ const usage = Object.keys(totalUsage).length > 0 ? totalUsage : objectField(info, "last_token_usage");
5324
5373
  if (Object.keys(usage).length === 0) {
5325
5374
  return;
5326
5375
  }
5376
+ const input = numberField(usage, "input_tokens") ?? 0;
5377
+ const cached = numberField(usage, "cached_input_tokens") ?? 0;
5378
+ const output = numberField(usage, "output_tokens") ?? 0;
5379
+ const reasoning = numberField(usage, "reasoning_output_tokens") ?? 0;
5380
+ const totalField = numberField(usage, "total_tokens");
5381
+ const total = totalField !== void 0 && (totalField > 0 || input + output === 0) ? totalField : input + output;
5382
+ if (input === 0 && cached === 0 && output === 0 && reasoning === 0 && total === 0) {
5383
+ return;
5384
+ }
5327
5385
  return {
5328
- tokensInput: numberField(usage, "input_tokens"),
5329
- tokensCachedInput: numberField(usage, "cached_input_tokens"),
5330
- tokensOutput: numberField(usage, "output_tokens"),
5331
- tokensReasoningOutput: numberField(usage, "reasoning_output_tokens"),
5332
- tokensTotal: numberField(usage, "total_tokens"),
5386
+ tokensInput: input || void 0,
5387
+ tokensCachedInput: cached || void 0,
5388
+ tokensOutput: output || void 0,
5389
+ tokensReasoningOutput: reasoning || void 0,
5390
+ tokensTotal: total || void 0,
5333
5391
  modelContextWindow: numberField(info, "model_context_window")
5334
5392
  };
5335
5393
  }
@@ -5666,8 +5724,7 @@ async function parseCopilotSessionFile(filePath, options) {
5666
5724
  const cacheReadTokens = numberField(usage, "cacheReadTokens") || 0;
5667
5725
  const cacheWriteTokens = numberField(usage, "cacheWriteTokens") || 0;
5668
5726
  const reasoningTokens = numberField(usage, "reasoningTokens") || 0;
5669
- const totalInput = inputTokens + cacheReadTokens + cacheWriteTokens;
5670
- const total = totalInput + outputTokens + reasoningTokens;
5727
+ const total = inputTokens + outputTokens;
5671
5728
  if (total <= 0) {
5672
5729
  continue;
5673
5730
  }
@@ -5682,7 +5739,7 @@ async function parseCopilotSessionFile(filePath, options) {
5682
5739
  model: modelName,
5683
5740
  confidence: "exact",
5684
5741
  metrics: {
5685
- tokensInput: totalInput || void 0,
5742
+ tokensInput: inputTokens || void 0,
5686
5743
  tokensOutput: outputTokens || void 0,
5687
5744
  tokensCachedInput: cacheReadTokens + cacheWriteTokens || void 0,
5688
5745
  tokensCacheReadInput: cacheReadTokens || void 0,
@@ -6199,20 +6256,15 @@ async function loadToolInputsFromUpdates(updatesPath) {
6199
6256
  return map;
6200
6257
  }
6201
6258
  function metricsFromSignals(signals) {
6202
- const contextTokens = numberField(signals, "contextTokensUsed");
6203
6259
  const toolCallCount = numberField(signals, "toolCallCount");
6204
6260
  const turnCount = numberField(signals, "turnCount");
6205
6261
  const durationSec = numberField(signals, "sessionDurationSeconds");
6206
6262
  const linesAdded = numberField(signals, "agentLinesAdded");
6207
6263
  const linesRemoved = numberField(signals, "agentLinesRemoved");
6208
- if (contextTokens === void 0 && toolCallCount === void 0 && turnCount === void 0 && durationSec === void 0) {
6264
+ if (toolCallCount === void 0 && turnCount === void 0 && durationSec === void 0) {
6209
6265
  return void 0;
6210
6266
  }
6211
6267
  const metrics = {};
6212
- if (contextTokens !== void 0 && contextTokens > 0) {
6213
- metrics.tokensTotal = contextTokens;
6214
- metrics.tokensInput = contextTokens;
6215
- }
6216
6268
  if (toolCallCount !== void 0) {
6217
6269
  metrics.toolCalls = toolCallCount;
6218
6270
  }
@@ -6651,13 +6703,14 @@ function opencodeUsageFromInfo(info) {
6651
6703
  const cacheRead = Math.max(0, numberField(cache, "read") || 0);
6652
6704
  const cacheWrite = Math.max(0, numberField(cache, "write") || 0);
6653
6705
  const totalInput = input + cacheRead + cacheWrite;
6654
- const total = Math.max(0, numberField(tokensObj, "total") || totalInput + output + reasoning);
6706
+ const totalOutput = output + reasoning;
6707
+ const total = Math.max(0, numberField(tokensObj, "total") || totalInput + totalOutput);
6655
6708
  if (total <= 0) {
6656
6709
  return void 0;
6657
6710
  }
6658
6711
  return {
6659
6712
  tokensInput: totalInput || void 0,
6660
- tokensOutput: output || void 0,
6713
+ tokensOutput: totalOutput || void 0,
6661
6714
  tokensReasoningOutput: reasoning || void 0,
6662
6715
  tokensCachedInput: cacheRead + cacheWrite || void 0,
6663
6716
  tokensCacheReadInput: cacheRead || void 0,
@@ -8825,28 +8878,29 @@ function workbuddyUsageMetrics(record) {
8825
8878
  if (!input && !output && !total && !cacheRead && !cacheCreate && !reasoning) {
8826
8879
  return void 0;
8827
8880
  }
8881
+ const alignedTotal = total || input + output;
8828
8882
  return {
8829
- tokensInput: input,
8883
+ tokensInput: input || void 0,
8830
8884
  tokensOutput: output,
8831
8885
  tokensCachedInput: cacheRead + cacheCreate,
8832
8886
  tokensCacheCreationInput: cacheCreate,
8833
8887
  tokensCacheReadInput: cacheRead,
8834
8888
  tokensReasoningOutput: reasoning,
8835
- tokensTotal: total || input + output,
8889
+ tokensTotal: alignedTotal,
8836
8890
  modelCalls: 1
8837
8891
  };
8838
8892
  }
8839
8893
  function workbuddyCachedTokens(usage, rawUsage) {
8840
- const rawCache = numberField(rawUsage, "cache_read_input_tokens") ?? numberField(rawUsage, "prompt_cache_hit_tokens") ?? numberField(rawUsage, "cached_tokens");
8841
- if (rawCache !== void 0) {
8842
- return rawCache;
8843
- }
8844
8894
  const details = usage.inputTokensDetails;
8845
- if (Array.isArray(details) && isPlainObject(details[0])) {
8846
- return numberField(details[0], "cached_tokens") || 0;
8847
- }
8848
8895
  const rawDetails = objectField(rawUsage, "prompt_tokens_details");
8849
- return numberField(rawDetails, "cached_tokens") || 0;
8896
+ return Math.max(
8897
+ 0,
8898
+ numberField(rawUsage, "cache_read_input_tokens") || 0,
8899
+ numberField(rawUsage, "prompt_cache_hit_tokens") || 0,
8900
+ numberField(rawUsage, "cached_tokens") || 0,
8901
+ Array.isArray(details) && isPlainObject(details[0]) ? numberField(details[0], "cached_tokens") || 0 : 0,
8902
+ numberField(rawDetails, "cached_tokens") || 0
8903
+ );
8850
8904
  }
8851
8905
  function workbuddyReasoningTokens(usage, rawUsage) {
8852
8906
  const completionDetails = objectField(rawUsage, "completion_tokens_details");
@@ -9249,6 +9303,7 @@ async function readZCodeRows(dbPath) {
9249
9303
  'reasoning_tokens',reasoning_tokens,
9250
9304
  'cache_creation_input_tokens',cache_creation_input_tokens,
9251
9305
  'cache_read_input_tokens',cache_read_input_tokens,
9306
+ 'provider_total_tokens',provider_total_tokens,
9252
9307
  'computed_total_tokens',computed_total_tokens,
9253
9308
  'tool_call_count',tool_call_count
9254
9309
  ) from model_usage order by started_at, id;`,
@@ -9280,16 +9335,24 @@ function modelMetrics(row) {
9280
9335
  const reasoning = numberField(row, "reasoning_tokens") || 0;
9281
9336
  const cacheCreation = numberField(row, "cache_creation_input_tokens") || 0;
9282
9337
  const cacheRead = numberField(row, "cache_read_input_tokens") || 0;
9283
- const total = numberField(row, "computed_total_tokens") || input + output + reasoning;
9338
+ const cached = cacheCreation + cacheRead;
9339
+ const providerTotal = numberField(row, "provider_total_tokens");
9340
+ const computedTotal = numberField(row, "computed_total_tokens");
9341
+ const totalWithoutSeparateCache = input + output;
9342
+ const totalWithSeparateCache = input + cached + output;
9343
+ const comparisonTotal = providerTotal ?? computedTotal ?? 0;
9344
+ const cacheIsSeparate = comparisonTotal > 0 && Math.abs(comparisonTotal - totalWithSeparateCache) < Math.abs(comparisonTotal - totalWithoutSeparateCache);
9345
+ const alignedInput = input > 0 ? input + (cacheIsSeparate ? cached : 0) : cached;
9346
+ const alignedTotal = alignedInput + output;
9284
9347
  const durationMs = numberField(row, "duration_ms");
9285
9348
  return {
9286
- tokensInput: input,
9349
+ tokensInput: alignedInput || void 0,
9287
9350
  tokensOutput: output,
9288
9351
  tokensReasoningOutput: reasoning,
9289
- tokensCachedInput: cacheCreation + cacheRead,
9352
+ tokensCachedInput: cached,
9290
9353
  tokensCacheCreationInput: cacheCreation,
9291
9354
  tokensCacheReadInput: cacheRead,
9292
- tokensTotal: total,
9355
+ tokensTotal: alignedTotal,
9293
9356
  modelDurationMs: durationMs,
9294
9357
  durationMs,
9295
9358
  modelCalls: 1,
@@ -10284,7 +10347,7 @@ function totalTokensFromEvent(event) {
10284
10347
  if (typeof explicit === "number" && explicit > 0) {
10285
10348
  return explicit;
10286
10349
  }
10287
- return Math.max(0, event.metrics?.tokensInput || 0) + Math.max(0, event.metrics?.tokensOutput || 0) + Math.max(0, event.metrics?.tokensReasoningOutput || 0);
10350
+ return Math.max(0, event.metrics?.tokensInput || 0) + Math.max(0, event.metrics?.tokensOutput || 0);
10288
10351
  }
10289
10352
  function lineStatsFromEvent(event) {
10290
10353
  const files = event.fileActivities || [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.27",
4
+ "version": "0.1.28",
5
5
  "description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi) and report activity to vibetime.",
6
6
  "license": "MIT",
7
7
  "publishConfig": {