@yhong91/vibetime 0.1.27 → 0.1.29
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/bin/vibetime.mjs +191 -65
- 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.
|
|
1931
|
+
var PACKAGE_VERSION = true ? "0.1.29" : "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;
|
|
@@ -4069,6 +4069,19 @@ function prefixCoworkId(value) {
|
|
|
4069
4069
|
}
|
|
4070
4070
|
return value.startsWith(COWORK_PREFIX) ? value : `${COWORK_PREFIX}${value}`;
|
|
4071
4071
|
}
|
|
4072
|
+
function parseCoworkSubagentPath(filePath) {
|
|
4073
|
+
const parts = filePath.split(path8.sep);
|
|
4074
|
+
const subagentsIdx = parts.lastIndexOf("subagents");
|
|
4075
|
+
if (subagentsIdx < 2) {
|
|
4076
|
+
return void 0;
|
|
4077
|
+
}
|
|
4078
|
+
const cliSessionId = parts[subagentsIdx - 1];
|
|
4079
|
+
if (!cliSessionId) {
|
|
4080
|
+
return void 0;
|
|
4081
|
+
}
|
|
4082
|
+
const mainTranscriptPath = [...parts.slice(0, subagentsIdx - 1), `${cliSessionId}.jsonl`].join(path8.sep);
|
|
4083
|
+
return { cliSessionId, mainTranscriptPath };
|
|
4084
|
+
}
|
|
4072
4085
|
function rebuildEventIdentity(event) {
|
|
4073
4086
|
const importKey = createImportKey([
|
|
4074
4087
|
event.source,
|
|
@@ -4094,15 +4107,21 @@ async function parseClaudeCoworkSessionFile(filePath, options) {
|
|
|
4094
4107
|
if (!parser) {
|
|
4095
4108
|
return [];
|
|
4096
4109
|
}
|
|
4110
|
+
const subagentInfo = parseCoworkSubagentPath(filePath);
|
|
4111
|
+
const parentSourcePathHash = subagentInfo ? `sha256:${createStableHash(subagentInfo.mainTranscriptPath)}` : void 0;
|
|
4112
|
+
const parentSessionId = subagentInfo ? prefixCoworkId(subagentInfo.cliSessionId) : void 0;
|
|
4097
4113
|
const events = await parser(filePath, options);
|
|
4098
4114
|
return events.map((event) => {
|
|
4099
|
-
const sessionId = prefixCoworkId(event.sessionId);
|
|
4115
|
+
const sessionId = parentSessionId || prefixCoworkId(event.sessionId);
|
|
4100
4116
|
const turnId = prefixCoworkId(event.turnId);
|
|
4101
4117
|
const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
|
|
4102
4118
|
const refs2 = { ...event.refs };
|
|
4103
4119
|
if (metadata.title) {
|
|
4104
4120
|
refs2.coworkTitleHash = `sha256:${createStableHash(metadata.title)}`;
|
|
4105
4121
|
}
|
|
4122
|
+
if (parentSourcePathHash) {
|
|
4123
|
+
refs2.sourcePathHash = parentSourcePathHash;
|
|
4124
|
+
}
|
|
4106
4125
|
const mapped = {
|
|
4107
4126
|
...event,
|
|
4108
4127
|
schemaVersion: event.schemaVersion || AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -4894,7 +4913,8 @@ async function parseCodexSessionFile(filePath, options) {
|
|
|
4894
4913
|
let lastTurnIdForComplete;
|
|
4895
4914
|
let turnIdAtLastUserMessage;
|
|
4896
4915
|
let sessionMetaLocked = false;
|
|
4897
|
-
let
|
|
4916
|
+
let lastTotalTokenUsage;
|
|
4917
|
+
let lastPerCallUsageKey;
|
|
4898
4918
|
const pendingToolCalls = /* @__PURE__ */ new Map();
|
|
4899
4919
|
for (const [index, line] of lines.entries()) {
|
|
4900
4920
|
const lineNumber = index + 1;
|
|
@@ -5042,32 +5062,79 @@ async function parseCodexSessionFile(filePath, options) {
|
|
|
5042
5062
|
}
|
|
5043
5063
|
const usage = tokenUsageFromPayload(payload);
|
|
5044
5064
|
if (usage) {
|
|
5045
|
-
const
|
|
5046
|
-
const
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5065
|
+
const input = usage.tokensInput ?? 0;
|
|
5066
|
+
const cached = usage.tokensCachedInput ?? 0;
|
|
5067
|
+
const output = usage.tokensOutput ?? 0;
|
|
5068
|
+
const reasoning = usage.tokensReasoningOutput ?? 0;
|
|
5069
|
+
const total = usage.tokensTotal ?? 0;
|
|
5070
|
+
const hasCumulativeUsage = Object.keys(objectField(info, "total_token_usage")).length > 0;
|
|
5071
|
+
const usageKey = [input, cached, output, reasoning, total].join(":");
|
|
5072
|
+
if (!hasCumulativeUsage) {
|
|
5073
|
+
if (usageKey !== lastPerCallUsageKey) {
|
|
5074
|
+
events.push(withBackfillRefs(baseCodexEvent({
|
|
5075
|
+
ts,
|
|
5076
|
+
type: "model.usage",
|
|
5077
|
+
sessionId,
|
|
5078
|
+
turnId: currentTurnId,
|
|
5079
|
+
cwd,
|
|
5080
|
+
project,
|
|
5081
|
+
model: rewriteCodexModelForTier(model, serviceTier),
|
|
5082
|
+
confidence: "partial",
|
|
5083
|
+
metrics: usage
|
|
5084
|
+
}), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
|
|
5085
|
+
lastPerCallUsageKey = usageKey;
|
|
5086
|
+
}
|
|
5054
5087
|
break;
|
|
5055
5088
|
}
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5089
|
+
if (lastTotalTokenUsage && total >= lastTotalTokenUsage.total) {
|
|
5090
|
+
const deltaInput = input - lastTotalTokenUsage.input;
|
|
5091
|
+
const deltaCached = cached - lastTotalTokenUsage.cached;
|
|
5092
|
+
const deltaOutput = output - lastTotalTokenUsage.output;
|
|
5093
|
+
const deltaReasoning = reasoning - lastTotalTokenUsage.reasoning;
|
|
5094
|
+
const deltaTotal = total - lastTotalTokenUsage.total;
|
|
5095
|
+
if (deltaInput > 0 || deltaOutput > 0 || deltaTotal > 0) {
|
|
5096
|
+
events.push(withBackfillRefs(baseCodexEvent({
|
|
5097
|
+
ts,
|
|
5098
|
+
type: "model.usage",
|
|
5099
|
+
sessionId,
|
|
5100
|
+
turnId: currentTurnId,
|
|
5101
|
+
cwd,
|
|
5102
|
+
project,
|
|
5103
|
+
model: rewriteCodexModelForTier(model, serviceTier),
|
|
5104
|
+
confidence: "partial",
|
|
5105
|
+
metrics: {
|
|
5106
|
+
tokensInput: deltaInput > 0 ? deltaInput : void 0,
|
|
5107
|
+
tokensCachedInput: deltaCached > 0 ? deltaCached : void 0,
|
|
5108
|
+
tokensOutput: deltaOutput > 0 ? deltaOutput : void 0,
|
|
5109
|
+
tokensReasoningOutput: deltaReasoning > 0 ? deltaReasoning : void 0,
|
|
5110
|
+
tokensTotal: deltaTotal > 0 ? deltaTotal : void 0,
|
|
5111
|
+
modelContextWindow: usage.modelContextWindow,
|
|
5112
|
+
reasoningEffort
|
|
5113
|
+
}
|
|
5114
|
+
}), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
|
|
5115
|
+
}
|
|
5116
|
+
} else {
|
|
5117
|
+
events.push(withBackfillRefs(baseCodexEvent({
|
|
5118
|
+
ts,
|
|
5119
|
+
type: "model.usage",
|
|
5120
|
+
sessionId,
|
|
5121
|
+
turnId: currentTurnId,
|
|
5122
|
+
cwd,
|
|
5123
|
+
project,
|
|
5124
|
+
model: rewriteCodexModelForTier(model, serviceTier),
|
|
5125
|
+
confidence: "partial",
|
|
5126
|
+
metrics: {
|
|
5127
|
+
tokensInput: input,
|
|
5128
|
+
tokensCachedInput: cached,
|
|
5129
|
+
tokensOutput: output,
|
|
5130
|
+
tokensReasoningOutput: reasoning,
|
|
5131
|
+
tokensTotal: total,
|
|
5132
|
+
modelContextWindow: usage.modelContextWindow,
|
|
5133
|
+
reasoningEffort
|
|
5134
|
+
}
|
|
5135
|
+
}), { filePath, sourcePathHash, lineNumber, topType, payloadType, options }));
|
|
5136
|
+
}
|
|
5137
|
+
lastTotalTokenUsage = { input, cached, output, reasoning, total };
|
|
5071
5138
|
}
|
|
5072
5139
|
break;
|
|
5073
5140
|
}
|
|
@@ -5284,7 +5351,7 @@ function headlessCodexUsage(raw) {
|
|
|
5284
5351
|
const cached = numberField(usage, "cached_input_tokens") ?? numberField(usage, "cache_read_input_tokens") ?? numberField(usage, "cached_tokens") ?? 0;
|
|
5285
5352
|
const reasoning = numberField(usage, "reasoning_output_tokens") ?? numberField(usage, "reasoning_tokens") ?? 0;
|
|
5286
5353
|
const totalField = numberField(usage, "total_tokens");
|
|
5287
|
-
const total = totalField !== void 0 && (totalField > 0 || input + output
|
|
5354
|
+
const total = totalField !== void 0 && (totalField > 0 || input + output === 0) ? totalField : input + output;
|
|
5288
5355
|
if (input === 0 && cached === 0 && output === 0 && reasoning === 0 && total === 0) {
|
|
5289
5356
|
return;
|
|
5290
5357
|
}
|
|
@@ -5293,7 +5360,7 @@ function headlessCodexUsage(raw) {
|
|
|
5293
5360
|
tokensCachedInput: cached || void 0,
|
|
5294
5361
|
tokensOutput: output || void 0,
|
|
5295
5362
|
tokensReasoningOutput: reasoning || void 0,
|
|
5296
|
-
tokensTotal: total,
|
|
5363
|
+
tokensTotal: total || void 0,
|
|
5297
5364
|
modelCalls: 1
|
|
5298
5365
|
};
|
|
5299
5366
|
}
|
|
@@ -5320,16 +5387,26 @@ function headlessCodexTimestamp(raw) {
|
|
|
5320
5387
|
}
|
|
5321
5388
|
function tokenUsageFromPayload(payload) {
|
|
5322
5389
|
const info = objectField(payload, "info");
|
|
5323
|
-
const
|
|
5390
|
+
const totalUsage = objectField(info, "total_token_usage");
|
|
5391
|
+
const usage = Object.keys(totalUsage).length > 0 ? totalUsage : objectField(info, "last_token_usage");
|
|
5324
5392
|
if (Object.keys(usage).length === 0) {
|
|
5325
5393
|
return;
|
|
5326
5394
|
}
|
|
5395
|
+
const input = numberField(usage, "input_tokens") ?? 0;
|
|
5396
|
+
const cached = numberField(usage, "cached_input_tokens") ?? 0;
|
|
5397
|
+
const output = numberField(usage, "output_tokens") ?? 0;
|
|
5398
|
+
const reasoning = numberField(usage, "reasoning_output_tokens") ?? 0;
|
|
5399
|
+
const totalField = numberField(usage, "total_tokens");
|
|
5400
|
+
const total = totalField !== void 0 && (totalField > 0 || input + output === 0) ? totalField : input + output;
|
|
5401
|
+
if (input === 0 && cached === 0 && output === 0 && reasoning === 0 && total === 0) {
|
|
5402
|
+
return;
|
|
5403
|
+
}
|
|
5327
5404
|
return {
|
|
5328
|
-
tokensInput:
|
|
5329
|
-
tokensCachedInput:
|
|
5330
|
-
tokensOutput:
|
|
5331
|
-
tokensReasoningOutput:
|
|
5332
|
-
tokensTotal:
|
|
5405
|
+
tokensInput: input || void 0,
|
|
5406
|
+
tokensCachedInput: cached || void 0,
|
|
5407
|
+
tokensOutput: output || void 0,
|
|
5408
|
+
tokensReasoningOutput: reasoning || void 0,
|
|
5409
|
+
tokensTotal: total || void 0,
|
|
5333
5410
|
modelContextWindow: numberField(info, "model_context_window")
|
|
5334
5411
|
};
|
|
5335
5412
|
}
|
|
@@ -5666,8 +5743,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5666
5743
|
const cacheReadTokens = numberField(usage, "cacheReadTokens") || 0;
|
|
5667
5744
|
const cacheWriteTokens = numberField(usage, "cacheWriteTokens") || 0;
|
|
5668
5745
|
const reasoningTokens = numberField(usage, "reasoningTokens") || 0;
|
|
5669
|
-
const
|
|
5670
|
-
const total = totalInput + outputTokens + reasoningTokens;
|
|
5746
|
+
const total = inputTokens + outputTokens;
|
|
5671
5747
|
if (total <= 0) {
|
|
5672
5748
|
continue;
|
|
5673
5749
|
}
|
|
@@ -5682,7 +5758,7 @@ async function parseCopilotSessionFile(filePath, options) {
|
|
|
5682
5758
|
model: modelName,
|
|
5683
5759
|
confidence: "exact",
|
|
5684
5760
|
metrics: {
|
|
5685
|
-
tokensInput:
|
|
5761
|
+
tokensInput: inputTokens || void 0,
|
|
5686
5762
|
tokensOutput: outputTokens || void 0,
|
|
5687
5763
|
tokensCachedInput: cacheReadTokens + cacheWriteTokens || void 0,
|
|
5688
5764
|
tokensCacheReadInput: cacheReadTokens || void 0,
|
|
@@ -6199,20 +6275,15 @@ async function loadToolInputsFromUpdates(updatesPath) {
|
|
|
6199
6275
|
return map;
|
|
6200
6276
|
}
|
|
6201
6277
|
function metricsFromSignals(signals) {
|
|
6202
|
-
const contextTokens = numberField(signals, "contextTokensUsed");
|
|
6203
6278
|
const toolCallCount = numberField(signals, "toolCallCount");
|
|
6204
6279
|
const turnCount = numberField(signals, "turnCount");
|
|
6205
6280
|
const durationSec = numberField(signals, "sessionDurationSeconds");
|
|
6206
6281
|
const linesAdded = numberField(signals, "agentLinesAdded");
|
|
6207
6282
|
const linesRemoved = numberField(signals, "agentLinesRemoved");
|
|
6208
|
-
if (
|
|
6283
|
+
if (toolCallCount === void 0 && turnCount === void 0 && durationSec === void 0) {
|
|
6209
6284
|
return void 0;
|
|
6210
6285
|
}
|
|
6211
6286
|
const metrics = {};
|
|
6212
|
-
if (contextTokens !== void 0 && contextTokens > 0) {
|
|
6213
|
-
metrics.tokensTotal = contextTokens;
|
|
6214
|
-
metrics.tokensInput = contextTokens;
|
|
6215
|
-
}
|
|
6216
6287
|
if (toolCallCount !== void 0) {
|
|
6217
6288
|
metrics.toolCalls = toolCallCount;
|
|
6218
6289
|
}
|
|
@@ -6296,6 +6367,7 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
6296
6367
|
const hasDirectory = sessionCols.has("directory");
|
|
6297
6368
|
const hasPath = sessionCols.has("path");
|
|
6298
6369
|
const hasArchived = sessionCols.has("time_archived");
|
|
6370
|
+
const hasParentId = sessionCols.has("parent_id");
|
|
6299
6371
|
const selectCols = ["id", "title", "time_created"];
|
|
6300
6372
|
if (hasDirectory) {
|
|
6301
6373
|
selectCols.push("directory");
|
|
@@ -6306,11 +6378,36 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
6306
6378
|
if (hasArchived) {
|
|
6307
6379
|
selectCols.push("time_archived");
|
|
6308
6380
|
}
|
|
6381
|
+
if (hasParentId) {
|
|
6382
|
+
selectCols.push("parent_id");
|
|
6383
|
+
}
|
|
6309
6384
|
const sessions = db.prepare(
|
|
6310
6385
|
`SELECT ${selectCols.join(", ")} FROM session WHERE time_created IS NOT NULL ORDER BY time_created`
|
|
6311
6386
|
).all();
|
|
6387
|
+
const rootIdByRawId = /* @__PURE__ */ new Map();
|
|
6388
|
+
if (hasParentId) {
|
|
6389
|
+
const byId = new Map(sessions.map((s) => [s.id, s]));
|
|
6390
|
+
for (const session of sessions) {
|
|
6391
|
+
let current = session.id;
|
|
6392
|
+
const visited = /* @__PURE__ */ new Set();
|
|
6393
|
+
while (true) {
|
|
6394
|
+
if (visited.has(current)) {
|
|
6395
|
+
break;
|
|
6396
|
+
}
|
|
6397
|
+
visited.add(current);
|
|
6398
|
+
const node = byId.get(current);
|
|
6399
|
+
const parentId = node?.parent_id || void 0;
|
|
6400
|
+
if (!parentId || !byId.has(parentId)) {
|
|
6401
|
+
break;
|
|
6402
|
+
}
|
|
6403
|
+
current = parentId;
|
|
6404
|
+
}
|
|
6405
|
+
rootIdByRawId.set(session.id, current);
|
|
6406
|
+
}
|
|
6407
|
+
}
|
|
6312
6408
|
for (const session of sessions) {
|
|
6313
|
-
const
|
|
6409
|
+
const rawSessionId = session.id;
|
|
6410
|
+
const sessionId = rootIdByRawId.get(rawSessionId) || rawSessionId;
|
|
6314
6411
|
const cwd = session.directory || session.path || void 0;
|
|
6315
6412
|
const project = cwd ? path14.basename(cwd) : void 0;
|
|
6316
6413
|
const sessionTs = msToIso(session.time_created);
|
|
@@ -6324,14 +6421,14 @@ async function parseOpenCodeSessionFile(dbPath, options) {
|
|
|
6324
6421
|
}));
|
|
6325
6422
|
const messages = db.prepare(
|
|
6326
6423
|
"SELECT id, data FROM message WHERE session_id = ? ORDER BY time_created"
|
|
6327
|
-
).all(
|
|
6424
|
+
).all(rawSessionId);
|
|
6328
6425
|
let currentTurnId;
|
|
6329
6426
|
let currentProvider;
|
|
6330
6427
|
let turnTs;
|
|
6331
6428
|
let reasoningEffort;
|
|
6332
6429
|
const modelSwitchedEvents = db.prepare(
|
|
6333
6430
|
"SELECT data FROM event WHERE aggregate_id = ? AND type = 'session.next.model.switched.1' ORDER BY seq"
|
|
6334
|
-
).all(
|
|
6431
|
+
).all(rawSessionId);
|
|
6335
6432
|
for (const evt of modelSwitchedEvents) {
|
|
6336
6433
|
try {
|
|
6337
6434
|
const evtData = JSON.parse(evt.data);
|
|
@@ -6651,13 +6748,14 @@ function opencodeUsageFromInfo(info) {
|
|
|
6651
6748
|
const cacheRead = Math.max(0, numberField(cache, "read") || 0);
|
|
6652
6749
|
const cacheWrite = Math.max(0, numberField(cache, "write") || 0);
|
|
6653
6750
|
const totalInput = input + cacheRead + cacheWrite;
|
|
6654
|
-
const
|
|
6751
|
+
const totalOutput = output + reasoning;
|
|
6752
|
+
const total = Math.max(0, numberField(tokensObj, "total") || totalInput + totalOutput);
|
|
6655
6753
|
if (total <= 0) {
|
|
6656
6754
|
return void 0;
|
|
6657
6755
|
}
|
|
6658
6756
|
return {
|
|
6659
6757
|
tokensInput: totalInput || void 0,
|
|
6660
|
-
tokensOutput:
|
|
6758
|
+
tokensOutput: totalOutput || void 0,
|
|
6661
6759
|
tokensReasoningOutput: reasoning || void 0,
|
|
6662
6760
|
tokensCachedInput: cacheRead + cacheWrite || void 0,
|
|
6663
6761
|
tokensCacheReadInput: cacheRead || void 0,
|
|
@@ -8825,28 +8923,29 @@ function workbuddyUsageMetrics(record) {
|
|
|
8825
8923
|
if (!input && !output && !total && !cacheRead && !cacheCreate && !reasoning) {
|
|
8826
8924
|
return void 0;
|
|
8827
8925
|
}
|
|
8926
|
+
const alignedTotal = total || input + output;
|
|
8828
8927
|
return {
|
|
8829
|
-
tokensInput: input,
|
|
8928
|
+
tokensInput: input || void 0,
|
|
8830
8929
|
tokensOutput: output,
|
|
8831
8930
|
tokensCachedInput: cacheRead + cacheCreate,
|
|
8832
8931
|
tokensCacheCreationInput: cacheCreate,
|
|
8833
8932
|
tokensCacheReadInput: cacheRead,
|
|
8834
8933
|
tokensReasoningOutput: reasoning,
|
|
8835
|
-
tokensTotal:
|
|
8934
|
+
tokensTotal: alignedTotal,
|
|
8836
8935
|
modelCalls: 1
|
|
8837
8936
|
};
|
|
8838
8937
|
}
|
|
8839
8938
|
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
8939
|
const details = usage.inputTokensDetails;
|
|
8845
|
-
if (Array.isArray(details) && isPlainObject(details[0])) {
|
|
8846
|
-
return numberField(details[0], "cached_tokens") || 0;
|
|
8847
|
-
}
|
|
8848
8940
|
const rawDetails = objectField(rawUsage, "prompt_tokens_details");
|
|
8849
|
-
return
|
|
8941
|
+
return Math.max(
|
|
8942
|
+
0,
|
|
8943
|
+
numberField(rawUsage, "cache_read_input_tokens") || 0,
|
|
8944
|
+
numberField(rawUsage, "prompt_cache_hit_tokens") || 0,
|
|
8945
|
+
numberField(rawUsage, "cached_tokens") || 0,
|
|
8946
|
+
Array.isArray(details) && isPlainObject(details[0]) ? numberField(details[0], "cached_tokens") || 0 : 0,
|
|
8947
|
+
numberField(rawDetails, "cached_tokens") || 0
|
|
8948
|
+
);
|
|
8850
8949
|
}
|
|
8851
8950
|
function workbuddyReasoningTokens(usage, rawUsage) {
|
|
8852
8951
|
const completionDetails = objectField(rawUsage, "completion_tokens_details");
|
|
@@ -9249,6 +9348,7 @@ async function readZCodeRows(dbPath) {
|
|
|
9249
9348
|
'reasoning_tokens',reasoning_tokens,
|
|
9250
9349
|
'cache_creation_input_tokens',cache_creation_input_tokens,
|
|
9251
9350
|
'cache_read_input_tokens',cache_read_input_tokens,
|
|
9351
|
+
'provider_total_tokens',provider_total_tokens,
|
|
9252
9352
|
'computed_total_tokens',computed_total_tokens,
|
|
9253
9353
|
'tool_call_count',tool_call_count
|
|
9254
9354
|
) from model_usage order by started_at, id;`,
|
|
@@ -9280,30 +9380,56 @@ function modelMetrics(row) {
|
|
|
9280
9380
|
const reasoning = numberField(row, "reasoning_tokens") || 0;
|
|
9281
9381
|
const cacheCreation = numberField(row, "cache_creation_input_tokens") || 0;
|
|
9282
9382
|
const cacheRead = numberField(row, "cache_read_input_tokens") || 0;
|
|
9283
|
-
const
|
|
9383
|
+
const cached = cacheCreation + cacheRead;
|
|
9384
|
+
const providerTotal = numberField(row, "provider_total_tokens");
|
|
9385
|
+
const computedTotal = numberField(row, "computed_total_tokens");
|
|
9386
|
+
const totalWithoutSeparateCache = input + output;
|
|
9387
|
+
const totalWithSeparateCache = input + cached + output;
|
|
9388
|
+
const comparisonTotal = providerTotal ?? computedTotal ?? 0;
|
|
9389
|
+
const cacheIsSeparate = comparisonTotal > 0 && Math.abs(comparisonTotal - totalWithSeparateCache) < Math.abs(comparisonTotal - totalWithoutSeparateCache);
|
|
9390
|
+
const alignedInput = input > 0 ? input + (cacheIsSeparate ? cached : 0) : cached;
|
|
9391
|
+
const alignedTotal = alignedInput + output;
|
|
9284
9392
|
const durationMs = numberField(row, "duration_ms");
|
|
9285
9393
|
return {
|
|
9286
|
-
tokensInput:
|
|
9394
|
+
tokensInput: alignedInput || void 0,
|
|
9287
9395
|
tokensOutput: output,
|
|
9288
9396
|
tokensReasoningOutput: reasoning,
|
|
9289
|
-
tokensCachedInput:
|
|
9397
|
+
tokensCachedInput: cached,
|
|
9290
9398
|
tokensCacheCreationInput: cacheCreation,
|
|
9291
9399
|
tokensCacheReadInput: cacheRead,
|
|
9292
|
-
tokensTotal:
|
|
9400
|
+
tokensTotal: alignedTotal,
|
|
9293
9401
|
modelDurationMs: durationMs,
|
|
9294
9402
|
durationMs,
|
|
9295
9403
|
modelCalls: 1,
|
|
9296
9404
|
toolCalls: numberField(row, "tool_call_count")
|
|
9297
9405
|
};
|
|
9298
9406
|
}
|
|
9407
|
+
function resolveRootSessionId(sessionId, sessions) {
|
|
9408
|
+
let current = sessionId;
|
|
9409
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9410
|
+
while (true) {
|
|
9411
|
+
if (visited.has(current)) {
|
|
9412
|
+
break;
|
|
9413
|
+
}
|
|
9414
|
+
visited.add(current);
|
|
9415
|
+
const session = sessions.get(current);
|
|
9416
|
+
const parentId = stringField(session, "parent_id");
|
|
9417
|
+
if (!parentId || !sessions.has(parentId)) {
|
|
9418
|
+
break;
|
|
9419
|
+
}
|
|
9420
|
+
current = parentId;
|
|
9421
|
+
}
|
|
9422
|
+
return current;
|
|
9423
|
+
}
|
|
9299
9424
|
function sessionContext(row, sessions) {
|
|
9300
9425
|
const sessionId = stringField(row, "session_id") || stringField(row, "id") || "unknown";
|
|
9301
9426
|
const session = sessions.get(sessionId);
|
|
9302
9427
|
const cwd = stringField(session, "directory");
|
|
9303
9428
|
const project = projectFromDirectory(cwd);
|
|
9429
|
+
const rootSessionId = resolveRootSessionId(sessionId, sessions);
|
|
9304
9430
|
return {
|
|
9305
9431
|
rawSessionId: sessionId,
|
|
9306
|
-
sessionId: `zcode:${
|
|
9432
|
+
sessionId: `zcode:${rootSessionId}`,
|
|
9307
9433
|
cwd,
|
|
9308
9434
|
project,
|
|
9309
9435
|
workspaceId: createWorkspaceId({ projectName: project, repoRoot: cwd }),
|
|
@@ -10284,7 +10410,7 @@ function totalTokensFromEvent(event) {
|
|
|
10284
10410
|
if (typeof explicit === "number" && explicit > 0) {
|
|
10285
10411
|
return explicit;
|
|
10286
10412
|
}
|
|
10287
|
-
return Math.max(0, event.metrics?.tokensInput || 0) + Math.max(0, event.metrics?.tokensOutput || 0)
|
|
10413
|
+
return Math.max(0, event.metrics?.tokensInput || 0) + Math.max(0, event.metrics?.tokensOutput || 0);
|
|
10288
10414
|
}
|
|
10289
10415
|
function lineStatsFromEvent(event) {
|
|
10290
10416
|
const files = event.fileActivities || [];
|
package/package.json
CHANGED