@yhong91/vibetime 0.1.46 → 0.1.48
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 +249 -15
- package/package.json +1 -1
package/bin/vibetime.mjs
CHANGED
|
@@ -2047,7 +2047,7 @@ function claudeStyleFileMetrics(tool, input) {
|
|
|
2047
2047
|
}
|
|
2048
2048
|
|
|
2049
2049
|
// src/lib/constants.ts
|
|
2050
|
-
var PACKAGE_VERSION = true ? "0.1.
|
|
2050
|
+
var PACKAGE_VERSION = true ? "0.1.48" : "0.1.1";
|
|
2051
2051
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2052
2052
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
2053
2053
|
var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
|
|
@@ -5157,7 +5157,7 @@ async function parseCodexSessionFile(filePath, options) {
|
|
|
5157
5157
|
let sessionId = sessionIdFromFilePath(filePath, "codex");
|
|
5158
5158
|
let cwd;
|
|
5159
5159
|
let project;
|
|
5160
|
-
let model;
|
|
5160
|
+
let model = firstTurnContextModel(lines);
|
|
5161
5161
|
let sessionStartEmitted = false;
|
|
5162
5162
|
let serviceTier;
|
|
5163
5163
|
let reasoningEffort;
|
|
@@ -5191,7 +5191,6 @@ async function parseCodexSessionFile(filePath, options) {
|
|
|
5191
5191
|
sessionId = stringField(payload, "id") || sessionId;
|
|
5192
5192
|
cwd = inherited?.cwd || stringField(payload, "cwd") || cwd;
|
|
5193
5193
|
project = inherited?.project || (cwd ? path11.basename(cwd) : project);
|
|
5194
|
-
model = stringField(payload, "model_provider") || model;
|
|
5195
5194
|
events.push(withBackfillRefs({
|
|
5196
5195
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
5197
5196
|
ts,
|
|
@@ -5599,6 +5598,21 @@ async function parseCodexSessionFile(filePath, options) {
|
|
|
5599
5598
|
}
|
|
5600
5599
|
return events.filter((event) => validateCanonicalEvent(event).valid);
|
|
5601
5600
|
}
|
|
5601
|
+
function firstTurnContextModel(lines) {
|
|
5602
|
+
for (const line of lines) {
|
|
5603
|
+
if (!line.includes("turn_context")) {
|
|
5604
|
+
continue;
|
|
5605
|
+
}
|
|
5606
|
+
const raw = parseJsonLine(line);
|
|
5607
|
+
if (raw && stringField(raw, "type") === "turn_context") {
|
|
5608
|
+
const model = stringField(objectField(raw, "payload"), "model");
|
|
5609
|
+
if (model) {
|
|
5610
|
+
return model;
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
return void 0;
|
|
5615
|
+
}
|
|
5602
5616
|
function rewriteCodexModelForTier(model, serviceTier) {
|
|
5603
5617
|
if (!model) {
|
|
5604
5618
|
return model;
|
|
@@ -6222,6 +6236,7 @@ function createCopilotAdapter() {
|
|
|
6222
6236
|
import { readdir as readdir6, readFile as readFile8, stat as stat7 } from "node:fs/promises";
|
|
6223
6237
|
import path13 from "node:path";
|
|
6224
6238
|
init_fs();
|
|
6239
|
+
var GROK_COST_TICKS_PER_USD = 1e9;
|
|
6225
6240
|
var SOURCE_ID = "grok-build";
|
|
6226
6241
|
var AGENT_NAME = "grok-build";
|
|
6227
6242
|
var HOOK_COMMAND = `vibetime hook --agent ${SOURCE_ID}`;
|
|
@@ -6488,18 +6503,76 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6488
6503
|
}), lineNumber, type, "permission");
|
|
6489
6504
|
}
|
|
6490
6505
|
}
|
|
6506
|
+
let nextLine = lineNumber + 1;
|
|
6507
|
+
const turnUsages = await loadTurnUsagesFromUpdates(updatesPath);
|
|
6508
|
+
const hasTurnUsage = turnUsages.length > 0;
|
|
6509
|
+
for (const turn of turnUsages) {
|
|
6510
|
+
for (const entry of turn.entries) {
|
|
6511
|
+
push(base({
|
|
6512
|
+
ts: turn.ts,
|
|
6513
|
+
type: "model.usage",
|
|
6514
|
+
model: entry.model || model,
|
|
6515
|
+
confidence: "exact",
|
|
6516
|
+
metrics: entry.metrics,
|
|
6517
|
+
refs: stringRefs({
|
|
6518
|
+
sourceId: `${sessionId}:usage:${turn.promptId}:${entry.model || "model"}`,
|
|
6519
|
+
promptId: turn.promptId
|
|
6520
|
+
})
|
|
6521
|
+
}), nextLine, "updates", "model.usage");
|
|
6522
|
+
nextLine += 1;
|
|
6523
|
+
}
|
|
6524
|
+
}
|
|
6525
|
+
const hunkPath = path13.join(sessionDir, "hunk_records.jsonl");
|
|
6526
|
+
const hunkActivities = await loadHunkFileActivities(hunkPath, cwd);
|
|
6527
|
+
if (hunkActivities.length > 0) {
|
|
6528
|
+
for (const event of events) {
|
|
6529
|
+
for (const file of event.fileActivities || []) {
|
|
6530
|
+
delete file.linesAdded;
|
|
6531
|
+
delete file.linesRemoved;
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
}
|
|
6535
|
+
let hasFileLineData = hunkActivities.length > 0;
|
|
6536
|
+
for (const activity of hunkActivities) {
|
|
6537
|
+
push(base({
|
|
6538
|
+
ts: activity.ts,
|
|
6539
|
+
type: "file.changed",
|
|
6540
|
+
confidence: "exact",
|
|
6541
|
+
fileActivities: [activity],
|
|
6542
|
+
metrics: {
|
|
6543
|
+
linesAdded: activity.linesAdded,
|
|
6544
|
+
linesRemoved: activity.linesRemoved,
|
|
6545
|
+
filesChanged: 1
|
|
6546
|
+
},
|
|
6547
|
+
refs: stringRefs({
|
|
6548
|
+
sourceId: `${sessionId}:hunk:${activity.fileActivityId || activity.path}`
|
|
6549
|
+
})
|
|
6550
|
+
}), nextLine, "hunk_records", "file.changed");
|
|
6551
|
+
nextLine += 1;
|
|
6552
|
+
}
|
|
6553
|
+
if (!hasFileLineData) {
|
|
6554
|
+
hasFileLineData = events.some(
|
|
6555
|
+
(event) => (event.fileActivities || []).some(
|
|
6556
|
+
(file) => (file.linesAdded || 0) > 0 || (file.linesRemoved || 0) > 0
|
|
6557
|
+
)
|
|
6558
|
+
);
|
|
6559
|
+
}
|
|
6491
6560
|
const signals = await readJsonIfExists(signalsPath);
|
|
6492
6561
|
if (isPlainObject(signals) && updatedAt) {
|
|
6493
|
-
const metrics = metricsFromSignals(signals
|
|
6562
|
+
const metrics = metricsFromSignals(signals, {
|
|
6563
|
+
includeLines: !hasFileLineData,
|
|
6564
|
+
includeModelCalls: !hasTurnUsage
|
|
6565
|
+
});
|
|
6494
6566
|
if (metrics) {
|
|
6495
6567
|
push(base({
|
|
6496
6568
|
ts: updatedAt,
|
|
6497
|
-
type: "model.usage",
|
|
6569
|
+
type: hasTurnUsage ? "agent.operation" : "model.usage",
|
|
6498
6570
|
model: stringField(signals, "primaryModelId") || model,
|
|
6499
6571
|
confidence: "partial",
|
|
6500
6572
|
metrics,
|
|
6501
|
-
refs: stringRefs({ sourceId: `${sessionId}:
|
|
6502
|
-
}),
|
|
6573
|
+
refs: stringRefs({ sourceId: `${sessionId}:signals` })
|
|
6574
|
+
}), nextLine, "signals", hasTurnUsage ? "agent.operation" : "model.usage");
|
|
6575
|
+
nextLine += 1;
|
|
6503
6576
|
}
|
|
6504
6577
|
}
|
|
6505
6578
|
if (updatedAt) {
|
|
@@ -6508,7 +6581,7 @@ async function parseGrokSessionFile(filePath, options) {
|
|
|
6508
6581
|
type: "session.ended",
|
|
6509
6582
|
confidence: "derived",
|
|
6510
6583
|
refs: stringRefs({ sourceId: `${sessionId}:ended` })
|
|
6511
|
-
}),
|
|
6584
|
+
}), nextLine, "summary", "session.ended");
|
|
6512
6585
|
}
|
|
6513
6586
|
return events;
|
|
6514
6587
|
}
|
|
@@ -6566,16 +6639,36 @@ function fileActivitiesFromTool(toolName, input, ts, cwd) {
|
|
|
6566
6639
|
confidence: "exact"
|
|
6567
6640
|
}];
|
|
6568
6641
|
}
|
|
6569
|
-
if (lower === "search_replace" || lower
|
|
6642
|
+
if (lower === "search_replace" || lower.endsWith("__search_replace")) {
|
|
6570
6643
|
const target = stringField(input, "file_path") || stringField(input, "target_file") || stringField(input, "path");
|
|
6571
6644
|
if (!target) {
|
|
6572
6645
|
return [];
|
|
6573
6646
|
}
|
|
6647
|
+
const oldString = stringField(input, "old_string");
|
|
6648
|
+
const newString = stringField(input, "new_string");
|
|
6574
6649
|
return [{
|
|
6575
6650
|
ts,
|
|
6576
6651
|
path: displayPath(target, cwd),
|
|
6577
6652
|
operation: "edit",
|
|
6578
|
-
confidence: "exact"
|
|
6653
|
+
confidence: "exact",
|
|
6654
|
+
linesAdded: countTextLines(newString),
|
|
6655
|
+
linesRemoved: countTextLines(oldString),
|
|
6656
|
+
charsWritten: newString?.length
|
|
6657
|
+
}];
|
|
6658
|
+
}
|
|
6659
|
+
if (lower === "write" || lower.endsWith("__write")) {
|
|
6660
|
+
const target = stringField(input, "file_path") || stringField(input, "target_file") || stringField(input, "path");
|
|
6661
|
+
if (!target) {
|
|
6662
|
+
return [];
|
|
6663
|
+
}
|
|
6664
|
+
const content = stringField(input, "content");
|
|
6665
|
+
return [{
|
|
6666
|
+
ts,
|
|
6667
|
+
path: displayPath(target, cwd),
|
|
6668
|
+
operation: "write",
|
|
6669
|
+
confidence: "exact",
|
|
6670
|
+
linesAdded: countTextLines(content),
|
|
6671
|
+
charsWritten: content?.length
|
|
6579
6672
|
}];
|
|
6580
6673
|
}
|
|
6581
6674
|
return [];
|
|
@@ -6625,20 +6718,23 @@ async function loadToolInputsFromUpdates(updatesPath) {
|
|
|
6625
6718
|
}
|
|
6626
6719
|
return map;
|
|
6627
6720
|
}
|
|
6628
|
-
function metricsFromSignals(signals) {
|
|
6721
|
+
function metricsFromSignals(signals, options = {}) {
|
|
6722
|
+
const includeLines = options.includeLines !== false;
|
|
6723
|
+
const includeModelCalls = options.includeModelCalls !== false;
|
|
6629
6724
|
const toolCallCount = numberField(signals, "toolCallCount");
|
|
6630
6725
|
const turnCount = numberField(signals, "turnCount");
|
|
6631
6726
|
const durationSec = numberField(signals, "sessionDurationSeconds");
|
|
6632
|
-
const linesAdded = numberField(signals, "agentLinesAdded");
|
|
6633
|
-
const linesRemoved = numberField(signals, "agentLinesRemoved");
|
|
6634
|
-
|
|
6727
|
+
const linesAdded = includeLines ? numberField(signals, "agentLinesAdded") : void 0;
|
|
6728
|
+
const linesRemoved = includeLines ? numberField(signals, "agentLinesRemoved") : void 0;
|
|
6729
|
+
const filesTouched = includeLines ? numberField(signals, "agentFilesTouched") : void 0;
|
|
6730
|
+
if (toolCallCount === void 0 && (!includeModelCalls || turnCount === void 0) && durationSec === void 0 && linesAdded === void 0 && linesRemoved === void 0) {
|
|
6635
6731
|
return void 0;
|
|
6636
6732
|
}
|
|
6637
6733
|
const metrics = {};
|
|
6638
6734
|
if (toolCallCount !== void 0) {
|
|
6639
6735
|
metrics.toolCalls = toolCallCount;
|
|
6640
6736
|
}
|
|
6641
|
-
if (turnCount !== void 0) {
|
|
6737
|
+
if (includeModelCalls && turnCount !== void 0) {
|
|
6642
6738
|
metrics.modelCalls = turnCount;
|
|
6643
6739
|
}
|
|
6644
6740
|
if (durationSec !== void 0) {
|
|
@@ -6650,8 +6746,146 @@ function metricsFromSignals(signals) {
|
|
|
6650
6746
|
if (linesRemoved !== void 0) {
|
|
6651
6747
|
metrics.linesRemoved = linesRemoved;
|
|
6652
6748
|
}
|
|
6749
|
+
if (filesTouched !== void 0) {
|
|
6750
|
+
metrics.filesChanged = filesTouched;
|
|
6751
|
+
}
|
|
6752
|
+
return metrics;
|
|
6753
|
+
}
|
|
6754
|
+
async function loadTurnUsagesFromUpdates(updatesPath) {
|
|
6755
|
+
const turns = [];
|
|
6756
|
+
let text = "";
|
|
6757
|
+
try {
|
|
6758
|
+
text = await readFile8(updatesPath, "utf8");
|
|
6759
|
+
} catch {
|
|
6760
|
+
return turns;
|
|
6761
|
+
}
|
|
6762
|
+
for (const line of text.split("\n")) {
|
|
6763
|
+
if (!line.trim()) {
|
|
6764
|
+
continue;
|
|
6765
|
+
}
|
|
6766
|
+
const raw = parseJsonLine(line);
|
|
6767
|
+
if (!raw) {
|
|
6768
|
+
continue;
|
|
6769
|
+
}
|
|
6770
|
+
const params = objectField(raw, "params");
|
|
6771
|
+
const update = objectField(params, "update");
|
|
6772
|
+
if (stringField(update, "sessionUpdate") !== "turn_completed") {
|
|
6773
|
+
continue;
|
|
6774
|
+
}
|
|
6775
|
+
const usage = objectField(update, "usage");
|
|
6776
|
+
if (Object.keys(usage).length === 0) {
|
|
6777
|
+
continue;
|
|
6778
|
+
}
|
|
6779
|
+
const meta = objectField(params, "_meta");
|
|
6780
|
+
const ts = timestampFrom(meta.agentTimestampMs) || timestampFrom(raw.timestamp) || timestampFrom(raw.ts);
|
|
6781
|
+
if (!ts) {
|
|
6782
|
+
continue;
|
|
6783
|
+
}
|
|
6784
|
+
const promptId = stringField(update, "prompt_id") || stringField(meta, "eventId") || `turn_${turns.length + 1}`;
|
|
6785
|
+
const entries = [];
|
|
6786
|
+
const modelUsage = objectField(usage, "modelUsage");
|
|
6787
|
+
const modelKeys = Object.keys(modelUsage);
|
|
6788
|
+
if (modelKeys.length > 0) {
|
|
6789
|
+
for (const modelName of modelKeys) {
|
|
6790
|
+
const perModel = objectField(modelUsage, modelName);
|
|
6791
|
+
const metrics = metricsFromGrokUsage(
|
|
6792
|
+
Object.keys(perModel).length > 0 ? perModel : usage
|
|
6793
|
+
);
|
|
6794
|
+
if (!hasTokenMetrics(metrics)) {
|
|
6795
|
+
continue;
|
|
6796
|
+
}
|
|
6797
|
+
entries.push({ model: modelName, metrics });
|
|
6798
|
+
}
|
|
6799
|
+
} else {
|
|
6800
|
+
const metrics = metricsFromGrokUsage(usage);
|
|
6801
|
+
if (hasTokenMetrics(metrics)) {
|
|
6802
|
+
entries.push({ metrics });
|
|
6803
|
+
}
|
|
6804
|
+
}
|
|
6805
|
+
if (entries.length > 0) {
|
|
6806
|
+
turns.push({ ts, promptId, entries });
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
return turns;
|
|
6810
|
+
}
|
|
6811
|
+
function metricsFromGrokUsage(usage) {
|
|
6812
|
+
const inputTokens = numberField(usage, "inputTokens") || 0;
|
|
6813
|
+
const outputTokens = numberField(usage, "outputTokens") || 0;
|
|
6814
|
+
const cachedRead = numberField(usage, "cachedReadTokens") || 0;
|
|
6815
|
+
const cacheCreation = numberField(usage, "cacheCreationTokens") || 0;
|
|
6816
|
+
const reasoning = numberField(usage, "reasoningTokens") || 0;
|
|
6817
|
+
const totalTokens = numberField(usage, "totalTokens") || inputTokens + outputTokens;
|
|
6818
|
+
const modelCalls = numberField(usage, "modelCalls");
|
|
6819
|
+
const apiDurationMs = numberField(usage, "apiDurationMs");
|
|
6820
|
+
const costTicks = numberField(usage, "costUsdTicks");
|
|
6821
|
+
const metrics = {
|
|
6822
|
+
tokensInput: inputTokens || void 0,
|
|
6823
|
+
tokensOutput: outputTokens || void 0,
|
|
6824
|
+
tokensCachedInput: cachedRead + cacheCreation || void 0,
|
|
6825
|
+
tokensCacheReadInput: cachedRead || void 0,
|
|
6826
|
+
tokensCacheCreationInput: cacheCreation || void 0,
|
|
6827
|
+
tokensReasoningOutput: reasoning || void 0,
|
|
6828
|
+
tokensTotal: totalTokens || void 0,
|
|
6829
|
+
modelCalls: modelCalls || void 0,
|
|
6830
|
+
modelDurationMs: apiDurationMs || void 0,
|
|
6831
|
+
durationMs: apiDurationMs || void 0
|
|
6832
|
+
};
|
|
6833
|
+
if (costTicks !== void 0 && costTicks > 0) {
|
|
6834
|
+
metrics.costUsd = costTicks / GROK_COST_TICKS_PER_USD;
|
|
6835
|
+
}
|
|
6653
6836
|
return metrics;
|
|
6654
6837
|
}
|
|
6838
|
+
function hasTokenMetrics(metrics) {
|
|
6839
|
+
return Boolean(
|
|
6840
|
+
metrics.tokensInput || metrics.tokensOutput || metrics.tokensCachedInput || metrics.tokensTotal
|
|
6841
|
+
);
|
|
6842
|
+
}
|
|
6843
|
+
async function loadHunkFileActivities(hunkPath, cwd) {
|
|
6844
|
+
const activities = [];
|
|
6845
|
+
let text = "";
|
|
6846
|
+
try {
|
|
6847
|
+
text = await readFile8(hunkPath, "utf8");
|
|
6848
|
+
} catch {
|
|
6849
|
+
return activities;
|
|
6850
|
+
}
|
|
6851
|
+
for (const line of text.split("\n")) {
|
|
6852
|
+
if (!line.trim()) {
|
|
6853
|
+
continue;
|
|
6854
|
+
}
|
|
6855
|
+
const raw = parseJsonLine(line);
|
|
6856
|
+
if (!raw) {
|
|
6857
|
+
continue;
|
|
6858
|
+
}
|
|
6859
|
+
const authorType = stringField(raw, "authorType");
|
|
6860
|
+
if (authorType && authorType !== "agent") {
|
|
6861
|
+
continue;
|
|
6862
|
+
}
|
|
6863
|
+
const filePath = stringField(raw, "filePath") || stringField(raw, "path");
|
|
6864
|
+
if (!filePath) {
|
|
6865
|
+
continue;
|
|
6866
|
+
}
|
|
6867
|
+
const ts = timestampFrom(raw.timestamp) || timestampFrom(raw.ts);
|
|
6868
|
+
if (!ts) {
|
|
6869
|
+
continue;
|
|
6870
|
+
}
|
|
6871
|
+
const linesAdded = numberField(raw, "linesAdded");
|
|
6872
|
+
const linesRemoved = numberField(raw, "linesRemoved");
|
|
6873
|
+
if ((linesAdded || 0) <= 0 && (linesRemoved || 0) <= 0) {
|
|
6874
|
+
continue;
|
|
6875
|
+
}
|
|
6876
|
+
const hunkId = stringField(raw, "hunkId");
|
|
6877
|
+
activities.push({
|
|
6878
|
+
ts,
|
|
6879
|
+
path: displayPath(filePath, cwd),
|
|
6880
|
+
operation: "edit",
|
|
6881
|
+
confidence: "exact",
|
|
6882
|
+
fileActivityId: hunkId,
|
|
6883
|
+
linesAdded: linesAdded || void 0,
|
|
6884
|
+
linesRemoved: linesRemoved || void 0
|
|
6885
|
+
});
|
|
6886
|
+
}
|
|
6887
|
+
return activities;
|
|
6888
|
+
}
|
|
6655
6889
|
var grokHandler = (msg) => hookHandler(SOURCE_ID, msg);
|
|
6656
6890
|
function hookConfig5() {
|
|
6657
6891
|
const anyTool = [{ matcher: ".*", hooks: [grokHandler("Reporting tool activity")] }];
|
package/package.json
CHANGED