@yhong91/vibetime 0.1.72 → 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 +278 -48
- 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,11 +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
|
|
9082
|
+
function unixFrom(value) {
|
|
9083
9083
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
9084
9084
|
return void 0;
|
|
9085
9085
|
}
|
|
9086
|
-
return
|
|
9086
|
+
return value;
|
|
9087
|
+
}
|
|
9088
|
+
function isoFromUnix(value) {
|
|
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
|
+
}));
|
|
9087
9197
|
}
|
|
9088
9198
|
function tableColumns(db, table) {
|
|
9089
9199
|
try {
|
|
@@ -9260,6 +9370,7 @@ async function parseHermesDb(filePath, options) {
|
|
|
9260
9370
|
"parent_session_id",
|
|
9261
9371
|
"started_at",
|
|
9262
9372
|
"ended_at",
|
|
9373
|
+
"end_reason",
|
|
9263
9374
|
"message_count",
|
|
9264
9375
|
"tool_call_count",
|
|
9265
9376
|
"input_tokens",
|
|
@@ -9349,8 +9460,9 @@ async function parseHermesDb(filePath, options) {
|
|
|
9349
9460
|
if (!sessionHasWork(session, usageRows) && messages.length === 0) {
|
|
9350
9461
|
continue;
|
|
9351
9462
|
}
|
|
9352
|
-
const
|
|
9353
|
-
|
|
9463
|
+
const startedUnix = unixFrom(session.started_at);
|
|
9464
|
+
const startedTs = isoFromUnix(startedUnix);
|
|
9465
|
+
if (!startedUnix || !startedTs) {
|
|
9354
9466
|
continue;
|
|
9355
9467
|
}
|
|
9356
9468
|
lineNumber += 1;
|
|
@@ -9378,7 +9490,15 @@ async function parseHermesDb(filePath, options) {
|
|
|
9378
9490
|
if (started) {
|
|
9379
9491
|
events.push(started);
|
|
9380
9492
|
}
|
|
9381
|
-
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);
|
|
9382
9502
|
if (endedTs && endedTs !== startedTs) {
|
|
9383
9503
|
lineNumber += 1;
|
|
9384
9504
|
const ended = makeEvent({
|
|
@@ -9398,16 +9518,102 @@ async function parseHermesDb(filePath, options) {
|
|
|
9398
9518
|
refs: refs({
|
|
9399
9519
|
sourceId: `${ctx.rawId}:session:ended`,
|
|
9400
9520
|
hermesPlatform: ctx.platform,
|
|
9401
|
-
hermesProfile: ctx.profileName
|
|
9521
|
+
hermesProfile: ctx.profileName,
|
|
9522
|
+
hermesEndReason: endReason
|
|
9402
9523
|
})
|
|
9403
9524
|
}, lineNumber, filePath, options);
|
|
9404
9525
|
if (ended) {
|
|
9405
9526
|
events.push(ended);
|
|
9406
9527
|
}
|
|
9407
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
|
+
}
|
|
9408
9554
|
if (usageRows.length > 0) {
|
|
9409
9555
|
for (const usage of usageRows) {
|
|
9410
|
-
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}` : "";
|
|
9411
9617
|
lineNumber += 1;
|
|
9412
9618
|
const event = makeEvent({
|
|
9413
9619
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9418,62 +9624,72 @@ async function parseHermesDb(filePath, options) {
|
|
|
9418
9624
|
project: ctx.project,
|
|
9419
9625
|
cwd: ctx.cwd,
|
|
9420
9626
|
sessionId: ctx.sessionId,
|
|
9627
|
+
turnId,
|
|
9421
9628
|
agent: SOURCE,
|
|
9422
|
-
model:
|
|
9423
|
-
provider:
|
|
9629
|
+
model: ctx.model,
|
|
9630
|
+
provider: ctx.provider,
|
|
9424
9631
|
success: true,
|
|
9425
|
-
metrics
|
|
9426
|
-
confidence: "
|
|
9632
|
+
metrics,
|
|
9633
|
+
confidence: "partial",
|
|
9427
9634
|
refs: refs({
|
|
9428
|
-
sourceId: `${ctx.rawId}:model
|
|
9635
|
+
sourceId: `${ctx.rawId}:model:session${slice}`,
|
|
9429
9636
|
hermesPlatform: ctx.platform,
|
|
9430
|
-
hermesProfile: ctx.profileName
|
|
9431
|
-
hermesTask: stringField(usage, "task"),
|
|
9432
|
-
hermesBillingMode: stringField(usage, "billing_mode")
|
|
9637
|
+
hermesProfile: ctx.profileName
|
|
9433
9638
|
})
|
|
9434
9639
|
}, lineNumber, filePath, options);
|
|
9435
9640
|
if (event) {
|
|
9436
9641
|
events.push(event);
|
|
9437
9642
|
}
|
|
9438
9643
|
}
|
|
9439
|
-
}
|
|
9644
|
+
}
|
|
9645
|
+
let turnIndex = 0;
|
|
9646
|
+
let activeTurnId;
|
|
9647
|
+
let activeTurnStartedTs;
|
|
9648
|
+
let lastWorkTs;
|
|
9649
|
+
const closeActiveTurn = (ts) => {
|
|
9650
|
+
if (!activeTurnId) {
|
|
9651
|
+
return;
|
|
9652
|
+
}
|
|
9440
9653
|
lineNumber += 1;
|
|
9441
|
-
const
|
|
9654
|
+
const completed = makeEvent({
|
|
9442
9655
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
9443
|
-
ts
|
|
9444
|
-
type: "
|
|
9656
|
+
ts,
|
|
9657
|
+
type: "turn.completed",
|
|
9445
9658
|
source: SOURCE,
|
|
9446
9659
|
workspaceId: ctx.workspaceId,
|
|
9447
9660
|
project: ctx.project,
|
|
9448
9661
|
cwd: ctx.cwd,
|
|
9449
9662
|
sessionId: ctx.sessionId,
|
|
9663
|
+
turnId: activeTurnId,
|
|
9450
9664
|
agent: SOURCE,
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
success: true,
|
|
9454
|
-
metrics: usageMetrics2(session),
|
|
9455
|
-
confidence: "partial",
|
|
9456
|
-
refs: refs({
|
|
9457
|
-
sourceId: `${ctx.rawId}:model:session`,
|
|
9458
|
-
hermesPlatform: ctx.platform,
|
|
9459
|
-
hermesProfile: ctx.profileName
|
|
9460
|
-
})
|
|
9665
|
+
confidence: "derived",
|
|
9666
|
+
refs: refs({ sourceId: `${ctx.rawId}:turn:${turnIndex}:end` })
|
|
9461
9667
|
}, lineNumber, filePath, options);
|
|
9462
|
-
if (
|
|
9463
|
-
events.push(
|
|
9668
|
+
if (completed) {
|
|
9669
|
+
events.push(completed);
|
|
9464
9670
|
}
|
|
9465
|
-
|
|
9466
|
-
|
|
9467
|
-
|
|
9671
|
+
activeTurnId = void 0;
|
|
9672
|
+
activeTurnStartedTs = void 0;
|
|
9673
|
+
lastWorkTs = void 0;
|
|
9674
|
+
};
|
|
9468
9675
|
for (const message of messages) {
|
|
9469
|
-
const
|
|
9470
|
-
|
|
9676
|
+
const tsUnix = unixFrom(message.timestamp);
|
|
9677
|
+
const ts = isoFromUnix(tsUnix);
|
|
9678
|
+
if (!tsUnix || !ts) {
|
|
9471
9679
|
continue;
|
|
9472
9680
|
}
|
|
9473
9681
|
const role = stringField(message, "role");
|
|
9474
9682
|
if (role === "user") {
|
|
9683
|
+
if (isSyntheticUserContent(stringField(message, "content"))) {
|
|
9684
|
+
continue;
|
|
9685
|
+
}
|
|
9686
|
+
if (activeTurnId && activeTurnStartedTs) {
|
|
9687
|
+
closeActiveTurn(lastWorkTs || activeTurnStartedTs);
|
|
9688
|
+
}
|
|
9475
9689
|
turnIndex += 1;
|
|
9476
9690
|
activeTurnId = `${ctx.sessionId}:turn:${turnIndex}`;
|
|
9691
|
+
activeTurnStartedTs = ts;
|
|
9692
|
+
lastWorkTs = void 0;
|
|
9477
9693
|
lineNumber += 1;
|
|
9478
9694
|
const turnStarted = makeEvent({
|
|
9479
9695
|
schemaVersion: AGENT_TIME_SCHEMA_VERSION,
|
|
@@ -9514,6 +9730,10 @@ async function parseHermesDb(filePath, options) {
|
|
|
9514
9730
|
}
|
|
9515
9731
|
continue;
|
|
9516
9732
|
}
|
|
9733
|
+
if (role !== "assistant" && role !== "tool") {
|
|
9734
|
+
continue;
|
|
9735
|
+
}
|
|
9736
|
+
lastWorkTs = ts;
|
|
9517
9737
|
if (role !== "assistant") {
|
|
9518
9738
|
continue;
|
|
9519
9739
|
}
|
|
@@ -9568,6 +9788,9 @@ async function parseHermesDb(filePath, options) {
|
|
|
9568
9788
|
}
|
|
9569
9789
|
}
|
|
9570
9790
|
}
|
|
9791
|
+
if (activeTurnId) {
|
|
9792
|
+
closeActiveTurn(lastWorkTs || endedTs || startedTs);
|
|
9793
|
+
}
|
|
9571
9794
|
}
|
|
9572
9795
|
return events.sort((a, b) => a.ts.localeCompare(b.ts) || (a.id || "").localeCompare(b.id || ""));
|
|
9573
9796
|
} finally {
|
|
@@ -9628,7 +9851,6 @@ description: "VibeTime session sync \u2014 thin trigger that runs local backfill
|
|
|
9628
9851
|
author: VibeTime
|
|
9629
9852
|
hooks:
|
|
9630
9853
|
- on_session_start
|
|
9631
|
-
- on_session_end
|
|
9632
9854
|
- on_session_finalize
|
|
9633
9855
|
- post_tool_call
|
|
9634
9856
|
`;
|
|
@@ -9639,6 +9861,10 @@ function pluginInit() {
|
|
|
9639
9861
|
|
|
9640
9862
|
Thin trigger only: spawn \`vibetime hook --agent hermes\` on session/tool
|
|
9641
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.
|
|
9642
9868
|
"""
|
|
9643
9869
|
from __future__ import annotations
|
|
9644
9870
|
|
|
@@ -9654,15 +9880,20 @@ def _report(payload: dict[str, Any]) -> None:
|
|
|
9654
9880
|
if not command:
|
|
9655
9881
|
return
|
|
9656
9882
|
try:
|
|
9657
|
-
|
|
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(
|
|
9658
9887
|
[command, "hook", "--agent", "hermes"],
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
timeout=10,
|
|
9663
|
-
check=False,
|
|
9888
|
+
stdin=subprocess.PIPE,
|
|
9889
|
+
stdout=subprocess.DEVNULL,
|
|
9890
|
+
stderr=subprocess.DEVNULL,
|
|
9664
9891
|
env=os.environ.copy(),
|
|
9892
|
+
start_new_session=True,
|
|
9665
9893
|
)
|
|
9894
|
+
if proc.stdin is not None:
|
|
9895
|
+
proc.stdin.write(json.dumps(payload).encode())
|
|
9896
|
+
proc.stdin.close()
|
|
9666
9897
|
except Exception:
|
|
9667
9898
|
return
|
|
9668
9899
|
|
|
@@ -9671,8 +9902,8 @@ def on_session_start(*, session_id: str = "", **_: Any) -> None:
|
|
|
9671
9902
|
_report({"hook_event_name": "SessionStart", "session_id": session_id})
|
|
9672
9903
|
|
|
9673
9904
|
|
|
9674
|
-
def
|
|
9675
|
-
_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 ""})
|
|
9676
9907
|
|
|
9677
9908
|
|
|
9678
9909
|
def on_post_tool_call(*, tool_name: str = "", session_id: str = "", **_: Any) -> None:
|
|
@@ -9685,8 +9916,7 @@ def on_post_tool_call(*, tool_name: str = "", session_id: str = "", **_: Any) ->
|
|
|
9685
9916
|
|
|
9686
9917
|
def register(ctx) -> None:
|
|
9687
9918
|
ctx.register_hook("on_session_start", on_session_start)
|
|
9688
|
-
ctx.register_hook("
|
|
9689
|
-
ctx.register_hook("on_session_finalize", on_session_end)
|
|
9919
|
+
ctx.register_hook("on_session_finalize", on_session_finalize)
|
|
9690
9920
|
ctx.register_hook("post_tool_call", on_post_tool_call)
|
|
9691
9921
|
`;
|
|
9692
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": {
|