@yhong91/vibetime 0.1.71 → 0.1.73
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 +280 -47
- 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.73" : "0.1.1";
|
|
2051
2051
|
var GENERATED_MARKER = "Generated by vibetime.";
|
|
2052
2052
|
var DEFAULT_API_URL = "http://121.196.224.82:3001";
|
|
2053
2053
|
var DEFAULT_BACKFILL_BATCH_SIZE = 50;
|
|
@@ -9079,8 +9079,121 @@ function projectFromCwd(cwd, repoRoot) {
|
|
|
9079
9079
|
const basis = repoRoot || cwd;
|
|
9080
9080
|
return basis ? path15.basename(basis) || SOURCE : SOURCE;
|
|
9081
9081
|
}
|
|
9082
|
+
function unixFrom(value) {
|
|
9083
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
9084
|
+
return void 0;
|
|
9085
|
+
}
|
|
9086
|
+
return value;
|
|
9087
|
+
}
|
|
9082
9088
|
function isoFromUnix(value) {
|
|
9083
|
-
|
|
9089
|
+
const unix = unixFrom(value);
|
|
9090
|
+
return unix === void 0 ? void 0 : timestampFrom(unix);
|
|
9091
|
+
}
|
|
9092
|
+
function maxUnix(...values) {
|
|
9093
|
+
let max;
|
|
9094
|
+
for (const value of values) {
|
|
9095
|
+
const unix = unixFrom(value);
|
|
9096
|
+
if (unix === void 0) {
|
|
9097
|
+
continue;
|
|
9098
|
+
}
|
|
9099
|
+
if (max === void 0 || unix > max) {
|
|
9100
|
+
max = unix;
|
|
9101
|
+
}
|
|
9102
|
+
}
|
|
9103
|
+
return max;
|
|
9104
|
+
}
|
|
9105
|
+
function isReapEndReason(reason) {
|
|
9106
|
+
return reason === "ws_orphan_reap" || reason === "startup_orphan_reap";
|
|
9107
|
+
}
|
|
9108
|
+
function isSyntheticUserContent(content) {
|
|
9109
|
+
if (!content) {
|
|
9110
|
+
return false;
|
|
9111
|
+
}
|
|
9112
|
+
const trimmed = content.trim();
|
|
9113
|
+
return trimmed.startsWith("[CONTEXT COMPACTION") || trimmed.startsWith("[IMPORTANT: Background process");
|
|
9114
|
+
}
|
|
9115
|
+
var INT_METRIC_KEYS = [
|
|
9116
|
+
"tokensInput",
|
|
9117
|
+
"tokensOutput",
|
|
9118
|
+
"tokensCacheReadInput",
|
|
9119
|
+
"tokensCacheCreationInput",
|
|
9120
|
+
"tokensCachedInput",
|
|
9121
|
+
"tokensReasoningOutput",
|
|
9122
|
+
"tokensTotal",
|
|
9123
|
+
"modelCalls"
|
|
9124
|
+
];
|
|
9125
|
+
function splitMetricBag(bag, fractions) {
|
|
9126
|
+
if (fractions.length === 0) {
|
|
9127
|
+
return [];
|
|
9128
|
+
}
|
|
9129
|
+
if (fractions.length === 1) {
|
|
9130
|
+
return [{ ...bag }];
|
|
9131
|
+
}
|
|
9132
|
+
const out = fractions.map(() => ({}));
|
|
9133
|
+
for (const key of INT_METRIC_KEYS) {
|
|
9134
|
+
const value = bag[key];
|
|
9135
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value === 0) {
|
|
9136
|
+
continue;
|
|
9137
|
+
}
|
|
9138
|
+
let allocated = 0;
|
|
9139
|
+
for (let i = 0; i < fractions.length; i += 1) {
|
|
9140
|
+
const part = i === fractions.length - 1 ? value - allocated : Math.floor(value * fractions[i]);
|
|
9141
|
+
allocated += part;
|
|
9142
|
+
if (part) {
|
|
9143
|
+
out[i][key] = part;
|
|
9144
|
+
}
|
|
9145
|
+
}
|
|
9146
|
+
}
|
|
9147
|
+
const cost = bag.costUsd;
|
|
9148
|
+
if (typeof cost === "number" && Number.isFinite(cost) && cost > 0) {
|
|
9149
|
+
let allocated = 0;
|
|
9150
|
+
for (let i = 0; i < fractions.length; i += 1) {
|
|
9151
|
+
const part = i === fractions.length - 1 ? Math.round((cost - allocated) * 1e6) / 1e6 : Math.round(cost * fractions[i] * 1e6) / 1e6;
|
|
9152
|
+
allocated += part;
|
|
9153
|
+
if (part) {
|
|
9154
|
+
out[i].costUsd = part;
|
|
9155
|
+
}
|
|
9156
|
+
}
|
|
9157
|
+
}
|
|
9158
|
+
return out;
|
|
9159
|
+
}
|
|
9160
|
+
function metricBagHasWork(bag) {
|
|
9161
|
+
return INT_METRIC_KEYS.some((key) => (bag[key] || 0) > 0);
|
|
9162
|
+
}
|
|
9163
|
+
function allocateUsageAcrossTurns(usage, turns) {
|
|
9164
|
+
const first = unixFrom(usage.first_seen);
|
|
9165
|
+
const last = unixFrom(usage.last_seen);
|
|
9166
|
+
const start = first ?? last;
|
|
9167
|
+
const end = last ?? first;
|
|
9168
|
+
if (start === void 0 || end === void 0) {
|
|
9169
|
+
const turn = turns.at(-1);
|
|
9170
|
+
return [{ turn, fraction: 1, tsUnix: turn?.endUnix || turn?.startedUnix || 0 }];
|
|
9171
|
+
}
|
|
9172
|
+
if (turns.length === 0) {
|
|
9173
|
+
return [{ fraction: 1, tsUnix: end }];
|
|
9174
|
+
}
|
|
9175
|
+
if (end <= start) {
|
|
9176
|
+
const turn = turns.find((item) => start >= item.startedUnix && start < item.endUnix) || [...turns].reverse().find((item) => item.startedUnix <= start) || turns[0];
|
|
9177
|
+
return [{ turn, fraction: 1, tsUnix: start }];
|
|
9178
|
+
}
|
|
9179
|
+
const overlaps = [];
|
|
9180
|
+
for (const turn of turns) {
|
|
9181
|
+
const lo = Math.max(start, turn.startedUnix);
|
|
9182
|
+
const hi = Math.min(end, turn.endUnix);
|
|
9183
|
+
if (hi > lo) {
|
|
9184
|
+
overlaps.push({ turn, overlap: hi - lo });
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
const total = overlaps.reduce((sum, item) => sum + item.overlap, 0);
|
|
9188
|
+
if (total <= 0) {
|
|
9189
|
+
const turn = [...turns].reverse().find((item) => item.startedUnix <= end) || turns[0];
|
|
9190
|
+
return [{ turn, fraction: 1, tsUnix: Math.min(end, turn?.endUnix || end) }];
|
|
9191
|
+
}
|
|
9192
|
+
return overlaps.map((item) => ({
|
|
9193
|
+
turn: item.turn,
|
|
9194
|
+
fraction: item.overlap / total,
|
|
9195
|
+
tsUnix: Math.min(end, item.turn.endUnix)
|
|
9196
|
+
}));
|
|
9084
9197
|
}
|
|
9085
9198
|
function tableColumns(db, table) {
|
|
9086
9199
|
try {
|
|
@@ -9257,6 +9370,7 @@ async function parseHermesDb(filePath, options) {
|
|
|
9257
9370
|
"parent_session_id",
|
|
9258
9371
|
"started_at",
|
|
9259
9372
|
"ended_at",
|
|
9373
|
+
"end_reason",
|
|
9260
9374
|
"message_count",
|
|
9261
9375
|
"tool_call_count",
|
|
9262
9376
|
"input_tokens",
|
|
@@ -9346,8 +9460,9 @@ async function parseHermesDb(filePath, options) {
|
|
|
9346
9460
|
if (!sessionHasWork(session, usageRows) && messages.length === 0) {
|
|
9347
9461
|
continue;
|
|
9348
9462
|
}
|
|
9349
|
-
const
|
|
9350
|
-
|
|
9463
|
+
const startedUnix = unixFrom(session.started_at);
|
|
9464
|
+
const startedTs = isoFromUnix(startedUnix);
|
|
9465
|
+
if (!startedUnix || !startedTs) {
|
|
9351
9466
|
continue;
|
|
9352
9467
|
}
|
|
9353
9468
|
lineNumber += 1;
|
|
@@ -9375,7 +9490,15 @@ async function parseHermesDb(filePath, options) {
|
|
|
9375
9490
|
if (started) {
|
|
9376
9491
|
events.push(started);
|
|
9377
9492
|
}
|
|
9378
|
-
const
|
|
9493
|
+
const lastMessageUnix = maxUnix(...messages.map((message) => message.timestamp));
|
|
9494
|
+
const lastUsageUnix = maxUnix(
|
|
9495
|
+
...usageRows.flatMap((usage) => [usage.last_seen, usage.first_seen])
|
|
9496
|
+
);
|
|
9497
|
+
const lastActivityUnix = unixFrom(session.last_activity_at);
|
|
9498
|
+
const endReason = stringField(session, "end_reason");
|
|
9499
|
+
const closedUnix = isReapEndReason(endReason) ? void 0 : unixFrom(session.ended_at);
|
|
9500
|
+
const endedUnix = maxUnix(lastMessageUnix, lastUsageUnix, closedUnix) ?? lastActivityUnix;
|
|
9501
|
+
const endedTs = isoFromUnix(endedUnix);
|
|
9379
9502
|
if (endedTs && endedTs !== startedTs) {
|
|
9380
9503
|
lineNumber += 1;
|
|
9381
9504
|
const ended = makeEvent({
|
|
@@ -9395,16 +9518,102 @@ async function parseHermesDb(filePath, options) {
|
|
|
9395
9518
|
refs: refs({
|
|
9396
9519
|
sourceId: `${ctx.rawId}:session:ended`,
|
|
9397
9520
|
hermesPlatform: ctx.platform,
|
|
9398
|
-
hermesProfile: ctx.profileName
|
|
9521
|
+
hermesProfile: ctx.profileName,
|
|
9522
|
+
hermesEndReason: endReason
|
|
9399
9523
|
})
|
|
9400
9524
|
}, lineNumber, filePath, options);
|
|
9401
9525
|
if (ended) {
|
|
9402
9526
|
events.push(ended);
|
|
9403
9527
|
}
|
|
9404
9528
|
}
|
|
9529
|
+
const userTurns = [];
|
|
9530
|
+
for (const message of messages) {
|
|
9531
|
+
const tsUnix = unixFrom(message.timestamp);
|
|
9532
|
+
if (!tsUnix) {
|
|
9533
|
+
continue;
|
|
9534
|
+
}
|
|
9535
|
+
const role = stringField(message, "role");
|
|
9536
|
+
if (role === "user") {
|
|
9537
|
+
if (isSyntheticUserContent(stringField(message, "content"))) {
|
|
9538
|
+
continue;
|
|
9539
|
+
}
|
|
9540
|
+
const index = userTurns.length + 1;
|
|
9541
|
+
userTurns.push({
|
|
9542
|
+
index,
|
|
9543
|
+
turnId: `${ctx.sessionId}:turn:${index}`,
|
|
9544
|
+
startedUnix: tsUnix,
|
|
9545
|
+
endUnix: tsUnix
|
|
9546
|
+
});
|
|
9547
|
+
continue;
|
|
9548
|
+
}
|
|
9549
|
+
const current = userTurns[userTurns.length - 1];
|
|
9550
|
+
if (current && (role === "assistant" || role === "tool")) {
|
|
9551
|
+
current.endUnix = Math.max(current.endUnix, tsUnix);
|
|
9552
|
+
}
|
|
9553
|
+
}
|
|
9405
9554
|
if (usageRows.length > 0) {
|
|
9406
9555
|
for (const usage of usageRows) {
|
|
9407
|
-
const
|
|
9556
|
+
const allocations = allocateUsageAcrossTurns(usage, userTurns);
|
|
9557
|
+
const metricsParts = splitMetricBag(usageMetrics2(usage), allocations.map((item) => item.fraction));
|
|
9558
|
+
const model = stringField(usage, "model") || ctx.model;
|
|
9559
|
+
const provider = stringField(usage, "billing_provider") || ctx.provider;
|
|
9560
|
+
const task = stringField(usage, "task") || "main";
|
|
9561
|
+
const billing = stringField(usage, "billing_provider") || "";
|
|
9562
|
+
for (let i = 0; i < allocations.length; i += 1) {
|
|
9563
|
+
const allocation = allocations[i];
|
|
9564
|
+
const metrics = metricsParts[i];
|
|
9565
|
+
if (!metrics || !metricBagHasWork(metrics)) {
|
|
9566
|
+
continue;
|
|
9567
|
+
}
|
|
9568
|
+
const ts = isoFromUnix(allocation.tsUnix) || startedTs;
|
|
9569
|
+
const turnId = allocation.turn?.turnId;
|
|
9570
|
+
const slice = allocation.turn ? `:turn:${allocation.turn.index}` : "";
|
|
9571
|
+
lineNumber += 1;
|
|
9572
|
+
const event = makeEvent({
|
|
9573
|
+
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
9574
|
+
ts,
|
|
9575
|
+
type: "model.usage",
|
|
9576
|
+
source: SOURCE,
|
|
9577
|
+
workspaceId: ctx.workspaceId,
|
|
9578
|
+
project: ctx.project,
|
|
9579
|
+
cwd: ctx.cwd,
|
|
9580
|
+
sessionId: ctx.sessionId,
|
|
9581
|
+
turnId,
|
|
9582
|
+
agent: SOURCE,
|
|
9583
|
+
model,
|
|
9584
|
+
provider,
|
|
9585
|
+
success: true,
|
|
9586
|
+
metrics,
|
|
9587
|
+
confidence: allocations.length > 1 ? "derived" : "exact",
|
|
9588
|
+
refs: refs({
|
|
9589
|
+
sourceId: `${ctx.rawId}:model:${model || "unknown"}:${task}:${billing}${slice}`,
|
|
9590
|
+
hermesPlatform: ctx.platform,
|
|
9591
|
+
hermesProfile: ctx.profileName,
|
|
9592
|
+
hermesTask: stringField(usage, "task"),
|
|
9593
|
+
hermesBillingMode: stringField(usage, "billing_mode")
|
|
9594
|
+
})
|
|
9595
|
+
}, lineNumber, filePath, options);
|
|
9596
|
+
if (event) {
|
|
9597
|
+
events.push(event);
|
|
9598
|
+
}
|
|
9599
|
+
}
|
|
9600
|
+
}
|
|
9601
|
+
} else if ((numberField(session, "input_tokens") || 0) + (numberField(session, "output_tokens") || 0) > 0) {
|
|
9602
|
+
const fallbackUsage = {
|
|
9603
|
+
first_seen: startedUnix,
|
|
9604
|
+
last_seen: endedUnix || startedUnix
|
|
9605
|
+
};
|
|
9606
|
+
const allocations = allocateUsageAcrossTurns(fallbackUsage, userTurns);
|
|
9607
|
+
const metricsParts = splitMetricBag(usageMetrics2(session), allocations.map((item) => item.fraction));
|
|
9608
|
+
for (let i = 0; i < allocations.length; i += 1) {
|
|
9609
|
+
const allocation = allocations[i];
|
|
9610
|
+
const metrics = metricsParts[i];
|
|
9611
|
+
if (!metrics || !metricBagHasWork(metrics)) {
|
|
9612
|
+
continue;
|
|
9613
|
+
}
|
|
9614
|
+
const ts = isoFromUnix(allocation.tsUnix) || startedTs;
|
|
9615
|
+
const turnId = allocation.turn?.turnId;
|
|
9616
|
+
const slice = allocation.turn ? `:turn:${allocation.turn.index}` : "";
|
|
9408
9617
|
lineNumber += 1;
|
|
9409
9618
|
const event = makeEvent({
|
|
9410
9619
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9415,62 +9624,72 @@ async function parseHermesDb(filePath, options) {
|
|
|
9415
9624
|
project: ctx.project,
|
|
9416
9625
|
cwd: ctx.cwd,
|
|
9417
9626
|
sessionId: ctx.sessionId,
|
|
9627
|
+
turnId,
|
|
9418
9628
|
agent: SOURCE,
|
|
9419
|
-
model:
|
|
9420
|
-
provider:
|
|
9629
|
+
model: ctx.model,
|
|
9630
|
+
provider: ctx.provider,
|
|
9421
9631
|
success: true,
|
|
9422
|
-
metrics
|
|
9423
|
-
confidence: "
|
|
9632
|
+
metrics,
|
|
9633
|
+
confidence: "partial",
|
|
9424
9634
|
refs: refs({
|
|
9425
|
-
sourceId: `${ctx.rawId}:model
|
|
9635
|
+
sourceId: `${ctx.rawId}:model:session${slice}`,
|
|
9426
9636
|
hermesPlatform: ctx.platform,
|
|
9427
|
-
hermesProfile: ctx.profileName
|
|
9428
|
-
hermesTask: stringField(usage, "task"),
|
|
9429
|
-
hermesBillingMode: stringField(usage, "billing_mode")
|
|
9637
|
+
hermesProfile: ctx.profileName
|
|
9430
9638
|
})
|
|
9431
9639
|
}, lineNumber, filePath, options);
|
|
9432
9640
|
if (event) {
|
|
9433
9641
|
events.push(event);
|
|
9434
9642
|
}
|
|
9435
9643
|
}
|
|
9436
|
-
}
|
|
9644
|
+
}
|
|
9645
|
+
let turnIndex = 0;
|
|
9646
|
+
let activeTurnId;
|
|
9647
|
+
let activeTurnStartedTs;
|
|
9648
|
+
let lastWorkTs;
|
|
9649
|
+
const closeActiveTurn = (ts) => {
|
|
9650
|
+
if (!activeTurnId) {
|
|
9651
|
+
return;
|
|
9652
|
+
}
|
|
9437
9653
|
lineNumber += 1;
|
|
9438
|
-
const
|
|
9654
|
+
const completed = makeEvent({
|
|
9439
9655
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
9440
|
-
ts
|
|
9441
|
-
type: "
|
|
9656
|
+
ts,
|
|
9657
|
+
type: "turn.completed",
|
|
9442
9658
|
source: SOURCE,
|
|
9443
9659
|
workspaceId: ctx.workspaceId,
|
|
9444
9660
|
project: ctx.project,
|
|
9445
9661
|
cwd: ctx.cwd,
|
|
9446
9662
|
sessionId: ctx.sessionId,
|
|
9663
|
+
turnId: activeTurnId,
|
|
9447
9664
|
agent: SOURCE,
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
success: true,
|
|
9451
|
-
metrics: usageMetrics2(session),
|
|
9452
|
-
confidence: "partial",
|
|
9453
|
-
refs: refs({
|
|
9454
|
-
sourceId: `${ctx.rawId}:model:session`,
|
|
9455
|
-
hermesPlatform: ctx.platform,
|
|
9456
|
-
hermesProfile: ctx.profileName
|
|
9457
|
-
})
|
|
9665
|
+
confidence: "derived",
|
|
9666
|
+
refs: refs({ sourceId: `${ctx.rawId}:turn:${turnIndex}:end` })
|
|
9458
9667
|
}, lineNumber, filePath, options);
|
|
9459
|
-
if (
|
|
9460
|
-
events.push(
|
|
9668
|
+
if (completed) {
|
|
9669
|
+
events.push(completed);
|
|
9461
9670
|
}
|
|
9462
|
-
|
|
9463
|
-
|
|
9464
|
-
|
|
9671
|
+
activeTurnId = void 0;
|
|
9672
|
+
activeTurnStartedTs = void 0;
|
|
9673
|
+
lastWorkTs = void 0;
|
|
9674
|
+
};
|
|
9465
9675
|
for (const message of messages) {
|
|
9466
|
-
const
|
|
9467
|
-
|
|
9676
|
+
const tsUnix = unixFrom(message.timestamp);
|
|
9677
|
+
const ts = isoFromUnix(tsUnix);
|
|
9678
|
+
if (!tsUnix || !ts) {
|
|
9468
9679
|
continue;
|
|
9469
9680
|
}
|
|
9470
9681
|
const role = stringField(message, "role");
|
|
9471
9682
|
if (role === "user") {
|
|
9683
|
+
if (isSyntheticUserContent(stringField(message, "content"))) {
|
|
9684
|
+
continue;
|
|
9685
|
+
}
|
|
9686
|
+
if (activeTurnId && activeTurnStartedTs) {
|
|
9687
|
+
closeActiveTurn(lastWorkTs || activeTurnStartedTs);
|
|
9688
|
+
}
|
|
9472
9689
|
turnIndex += 1;
|
|
9473
9690
|
activeTurnId = `${ctx.sessionId}:turn:${turnIndex}`;
|
|
9691
|
+
activeTurnStartedTs = ts;
|
|
9692
|
+
lastWorkTs = void 0;
|
|
9474
9693
|
lineNumber += 1;
|
|
9475
9694
|
const turnStarted = makeEvent({
|
|
9476
9695
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9511,6 +9730,10 @@ async function parseHermesDb(filePath, options) {
|
|
|
9511
9730
|
}
|
|
9512
9731
|
continue;
|
|
9513
9732
|
}
|
|
9733
|
+
if (role !== "assistant" && role !== "tool") {
|
|
9734
|
+
continue;
|
|
9735
|
+
}
|
|
9736
|
+
lastWorkTs = ts;
|
|
9514
9737
|
if (role !== "assistant") {
|
|
9515
9738
|
continue;
|
|
9516
9739
|
}
|
|
@@ -9565,6 +9788,9 @@ async function parseHermesDb(filePath, options) {
|
|
|
9565
9788
|
}
|
|
9566
9789
|
}
|
|
9567
9790
|
}
|
|
9791
|
+
if (activeTurnId) {
|
|
9792
|
+
closeActiveTurn(lastWorkTs || endedTs || startedTs);
|
|
9793
|
+
}
|
|
9568
9794
|
}
|
|
9569
9795
|
return events.sort((a, b) => a.ts.localeCompare(b.ts) || (a.id || "").localeCompare(b.id || ""));
|
|
9570
9796
|
} finally {
|
|
@@ -9625,7 +9851,6 @@ description: "VibeTime session sync \u2014 thin trigger that runs local backfill
|
|
|
9625
9851
|
author: VibeTime
|
|
9626
9852
|
hooks:
|
|
9627
9853
|
- on_session_start
|
|
9628
|
-
- on_session_end
|
|
9629
9854
|
- on_session_finalize
|
|
9630
9855
|
- post_tool_call
|
|
9631
9856
|
`;
|
|
@@ -9636,6 +9861,10 @@ function pluginInit() {
|
|
|
9636
9861
|
|
|
9637
9862
|
Thin trigger only: spawn \`vibetime hook --agent hermes\` on session/tool
|
|
9638
9863
|
lifecycle events. Token usage is read from state.db by vibetime backfill.
|
|
9864
|
+
|
|
9865
|
+
\`on_session_end\` is a per-turn observer in Hermes (end of every
|
|
9866
|
+
run_conversation). Do not map it to SessionEnd \u2014 that would fire a
|
|
9867
|
+
backfill after every prompt. Real session teardown is on_session_finalize.
|
|
9639
9868
|
"""
|
|
9640
9869
|
from __future__ import annotations
|
|
9641
9870
|
|
|
@@ -9651,15 +9880,20 @@ def _report(payload: dict[str, Any]) -> None:
|
|
|
9651
9880
|
if not command:
|
|
9652
9881
|
return
|
|
9653
9882
|
try:
|
|
9654
|
-
|
|
9883
|
+
# Popen + close stdin: do not block the Hermes tool loop waiting for
|
|
9884
|
+
# vibetime hook / backfill. stdout/stderr inherited would spam the
|
|
9885
|
+
# agent UI, so discard them.
|
|
9886
|
+
proc = subprocess.Popen(
|
|
9655
9887
|
[command, "hook", "--agent", "hermes"],
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
timeout=10,
|
|
9660
|
-
check=False,
|
|
9888
|
+
stdin=subprocess.PIPE,
|
|
9889
|
+
stdout=subprocess.DEVNULL,
|
|
9890
|
+
stderr=subprocess.DEVNULL,
|
|
9661
9891
|
env=os.environ.copy(),
|
|
9892
|
+
start_new_session=True,
|
|
9662
9893
|
)
|
|
9894
|
+
if proc.stdin is not None:
|
|
9895
|
+
proc.stdin.write(json.dumps(payload).encode())
|
|
9896
|
+
proc.stdin.close()
|
|
9663
9897
|
except Exception:
|
|
9664
9898
|
return
|
|
9665
9899
|
|
|
@@ -9668,8 +9902,8 @@ def on_session_start(*, session_id: str = "", **_: Any) -> None:
|
|
|
9668
9902
|
_report({"hook_event_name": "SessionStart", "session_id": session_id})
|
|
9669
9903
|
|
|
9670
9904
|
|
|
9671
|
-
def
|
|
9672
|
-
_report({"hook_event_name": "SessionEnd", "session_id": session_id})
|
|
9905
|
+
def on_session_finalize(*, session_id: str = "", **_: Any) -> None:
|
|
9906
|
+
_report({"hook_event_name": "SessionEnd", "session_id": session_id or ""})
|
|
9673
9907
|
|
|
9674
9908
|
|
|
9675
9909
|
def on_post_tool_call(*, tool_name: str = "", session_id: str = "", **_: Any) -> None:
|
|
@@ -9682,8 +9916,7 @@ def on_post_tool_call(*, tool_name: str = "", session_id: str = "", **_: Any) ->
|
|
|
9682
9916
|
|
|
9683
9917
|
def register(ctx) -> None:
|
|
9684
9918
|
ctx.register_hook("on_session_start", on_session_start)
|
|
9685
|
-
ctx.register_hook("
|
|
9686
|
-
ctx.register_hook("on_session_finalize", on_session_end)
|
|
9919
|
+
ctx.register_hook("on_session_finalize", on_session_finalize)
|
|
9687
9920
|
ctx.register_hook("post_tool_call", on_post_tool_call)
|
|
9688
9921
|
`;
|
|
9689
9922
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yhong91/vibetime",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.73",
|
|
5
5
|
"description": "vibetime CLI — install AI-agent hooks (Claude Code, Codex, OpenCode, Pi, Cursor) and report activity to vibetime.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"publishConfig": {
|