@yhong91/vibetime 0.1.34 → 0.1.35

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 +145 -14
  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.34" : "0.1.1";
1931
+ var PACKAGE_VERSION = true ? "0.1.35" : "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;
@@ -9148,7 +9148,6 @@ function normalizeId(id) {
9148
9148
  // src/adapters/workbuddy.ts
9149
9149
  import { readFile as readFile12, readdir as readdir9, stat as stat10 } from "node:fs/promises";
9150
9150
  import path19 from "node:path";
9151
- init_fs();
9152
9151
  function workbuddyProjectsDir(home, env) {
9153
9152
  const override = env?.WORKBUDDY_PROJECTS_DIR || env?.WORKBUDDY_HOME;
9154
9153
  if (override && override.trim()) {
@@ -9156,6 +9155,13 @@ function workbuddyProjectsDir(home, env) {
9156
9155
  }
9157
9156
  return path19.join(home, ".workbuddy", "projects");
9158
9157
  }
9158
+ function workbuddyBaseDir(home, env) {
9159
+ const override = env?.WORKBUDDY_HOME;
9160
+ if (override && override.trim()) {
9161
+ return path19.resolve(override);
9162
+ }
9163
+ return path19.join(home, ".workbuddy");
9164
+ }
9159
9165
  function projectFromCwd(cwd, fallback) {
9160
9166
  if (!cwd) {
9161
9167
  return fallback;
@@ -9180,12 +9186,13 @@ function workbuddyUsageMetrics(record) {
9180
9186
  const providerData = objectField(record, "providerData");
9181
9187
  const usage = objectField(providerData, "usage");
9182
9188
  const rawUsage = objectField(providerData, "rawUsage");
9183
- if (Object.keys(usage).length === 0 && Object.keys(rawUsage).length === 0) {
9189
+ const messageUsage = objectField(objectField(record, "message"), "usage");
9190
+ if (Object.keys(usage).length === 0 && Object.keys(rawUsage).length === 0 && Object.keys(messageUsage).length === 0) {
9184
9191
  return void 0;
9185
9192
  }
9186
- const input = numberField(usage, "inputTokens") ?? numberField(rawUsage, "prompt_tokens") ?? 0;
9187
- const output = numberField(usage, "outputTokens") ?? numberField(rawUsage, "completion_tokens") ?? 0;
9188
- const total = numberField(usage, "totalTokens") ?? numberField(rawUsage, "total_tokens") ?? 0;
9193
+ const input = numberField(usage, "inputTokens") ?? numberField(rawUsage, "prompt_tokens") ?? numberField(messageUsage, "input_tokens") ?? 0;
9194
+ const output = numberField(usage, "outputTokens") ?? numberField(rawUsage, "completion_tokens") ?? numberField(messageUsage, "output_tokens") ?? 0;
9195
+ const total = numberField(usage, "totalTokens") ?? numberField(rawUsage, "total_tokens") ?? numberField(messageUsage, "total_tokens") ?? 0;
9189
9196
  const cacheRead = workbuddyCachedTokens(usage, rawUsage);
9190
9197
  const cacheCreate = numberField(rawUsage, "cache_creation_input_tokens") ?? numberField(rawUsage, "prompt_cache_write_tokens") ?? 0;
9191
9198
  const reasoning = workbuddyReasoningTokens(usage, rawUsage);
@@ -9242,6 +9249,40 @@ function contentLength(value) {
9242
9249
  }
9243
9250
  return 0;
9244
9251
  }
9252
+ function isHiddenUserMessage(record) {
9253
+ const providerData = objectField(record, "providerData");
9254
+ return providerData.skipRun === true || providerData.isMeta === true || providerData.isCompactInternal === true || providerData.isCompact === true || providerData.isTeammateMessage === true || (stringField(providerData, "agentPurpose") || "").toLowerCase().includes("compact");
9255
+ }
9256
+ function workbuddySubagentRefs(lines) {
9257
+ const refs2 = {};
9258
+ const keys = [
9259
+ ["parentSessionId", ["parent_session_id", "parentSessionId"]],
9260
+ ["parentToolCallId", ["parent_tool_call_id", "parentToolCallId"]],
9261
+ ["subagentId", ["agent_id", "agentId"]],
9262
+ ["subagentType", ["agent_type", "agentType"]]
9263
+ ];
9264
+ for (const line of lines) {
9265
+ const providerData = objectField(line.record, "providerData");
9266
+ for (const [ref, aliases] of keys) {
9267
+ if (refs2[ref]) {
9268
+ continue;
9269
+ }
9270
+ for (const alias of aliases) {
9271
+ const value = stringField(line.record, alias) || stringField(providerData, alias);
9272
+ if (value) {
9273
+ refs2[ref] = value;
9274
+ break;
9275
+ }
9276
+ }
9277
+ }
9278
+ }
9279
+ return refs2;
9280
+ }
9281
+ function toolCallFailed(record) {
9282
+ const providerData = objectField(record, "providerData");
9283
+ const status = stringField(record, "status");
9284
+ return status === "failed" || status === "incomplete" || record.is_error === true || providerData.error != null || providerData.isError === true;
9285
+ }
9245
9286
  async function readWorkbuddyLines(filePath) {
9246
9287
  const text = await readFile12(filePath, "utf8");
9247
9288
  return text.split(/\r?\n/).map((line, index) => {
@@ -9270,8 +9311,32 @@ async function parseWorkbuddySessionFile(filePath, options) {
9270
9311
  const workspaceId = createWorkspaceId({ projectName: project, repoRoot: cwd });
9271
9312
  const sessionPrefix = `workbuddy:${sessionId}`;
9272
9313
  const toolStarts = /* @__PURE__ */ new Map();
9314
+ const subagentRefs = workbuddySubagentRefs(lines);
9273
9315
  let currentTurnId = `${sessionPrefix}:turn:0`;
9274
9316
  let turnIndex = 0;
9317
+ let turnLastTs;
9318
+ let turnSawResponse = false;
9319
+ let turnLastAssistantStatus;
9320
+ const closeTurn = (line) => {
9321
+ if (turnIndex === 0 || !turnSawResponse || !turnLastTs) {
9322
+ return void 0;
9323
+ }
9324
+ const failed = turnLastAssistantStatus !== void 0 && ["incomplete", "failed", "cancelled"].includes(turnLastAssistantStatus);
9325
+ return makeEvent({
9326
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
9327
+ ts: turnLastTs,
9328
+ type: failed ? "turn.failed" : "turn.completed",
9329
+ source: "workbuddy",
9330
+ workspaceId,
9331
+ project,
9332
+ cwd,
9333
+ sessionId: sessionPrefix,
9334
+ turnId: currentTurnId,
9335
+ agent: "workbuddy",
9336
+ confidence: "derived",
9337
+ refs: { sourceId: `${currentTurnId}:end` }
9338
+ }, line, filePath, options);
9339
+ };
9275
9340
  const firstTs = lines.map((line) => timestampFrom(numberField(line.record, "timestamp"))).find(Boolean);
9276
9341
  if (firstTs) {
9277
9342
  const event = makeEvent({
@@ -9285,7 +9350,7 @@ async function parseWorkbuddySessionFile(filePath, options) {
9285
9350
  sessionId: sessionPrefix,
9286
9351
  agent: "workbuddy",
9287
9352
  confidence: "derived",
9288
- refs: { sourceId: `${sessionPrefix}:session` }
9353
+ refs: { sourceId: `${sessionPrefix}:session`, ...subagentRefs }
9289
9354
  }, lines[0], filePath, options);
9290
9355
  if (event) events.push(event);
9291
9356
  }
@@ -9298,8 +9363,16 @@ async function parseWorkbuddySessionFile(filePath, options) {
9298
9363
  const type = stringField(record, "type");
9299
9364
  const sourceId = stringField(record, "id") || `${line.lineNumber}`;
9300
9365
  if (type === "message" && stringField(record, "role") === "user") {
9366
+ if (isHiddenUserMessage(record)) {
9367
+ continue;
9368
+ }
9369
+ const ended = closeTurn(line);
9370
+ if (ended) events.push(ended);
9301
9371
  turnIndex += 1;
9302
9372
  currentTurnId = `${sessionPrefix}:turn:${turnIndex}`;
9373
+ turnLastTs = ts;
9374
+ turnSawResponse = false;
9375
+ turnLastAssistantStatus = void 0;
9303
9376
  const promptChars = contentLength(record.content);
9304
9377
  for (const event of [
9305
9378
  makeEvent({
@@ -9336,7 +9409,36 @@ async function parseWorkbuddySessionFile(filePath, options) {
9336
9409
  }
9337
9410
  continue;
9338
9411
  }
9412
+ if (type === "message" && stringField(record, "role") === "assistant") {
9413
+ turnLastTs = ts;
9414
+ turnSawResponse = true;
9415
+ turnLastAssistantStatus = stringField(record, "status") || turnLastAssistantStatus;
9416
+ const metrics = workbuddyUsageMetrics(record);
9417
+ if (metrics) {
9418
+ const model = stringField(objectField(record, "providerData"), "model");
9419
+ const usage = makeEvent({
9420
+ schemaVersion: AGENT_TIME_SCHEMA_VERSION,
9421
+ ts,
9422
+ type: "model.usage",
9423
+ source: "workbuddy",
9424
+ workspaceId,
9425
+ project,
9426
+ cwd,
9427
+ sessionId: sessionPrefix,
9428
+ turnId: currentTurnId,
9429
+ agent: "workbuddy",
9430
+ model,
9431
+ metrics,
9432
+ confidence: "partial",
9433
+ refs: { sourceId: `${sourceId}:usage` }
9434
+ }, line, filePath, options);
9435
+ if (usage) events.push(usage);
9436
+ }
9437
+ continue;
9438
+ }
9339
9439
  if (type === "function_call") {
9440
+ turnLastTs = ts;
9441
+ turnSawResponse = true;
9340
9442
  const tool = stringField(record, "name");
9341
9443
  const callId = stringField(record, "callId");
9342
9444
  if (tool && callId) {
@@ -9383,15 +9485,16 @@ async function parseWorkbuddySessionFile(filePath, options) {
9383
9485
  continue;
9384
9486
  }
9385
9487
  if (type === "function_call_result") {
9488
+ turnLastTs = ts;
9386
9489
  const tool = stringField(record, "name") || "tool";
9387
9490
  const callId = stringField(record, "callId") || `${line.lineNumber}`;
9388
9491
  const start = toolStarts.get(`${sessionId}:${callId}`);
9389
9492
  const durationMs = start?.ts ? Math.max(0, Date.parse(ts) - Date.parse(start.ts)) : void 0;
9390
- const status = stringField(record, "status");
9493
+ const failed = toolCallFailed(record);
9391
9494
  const completed = makeEvent({
9392
9495
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
9393
9496
  ts,
9394
- type: status === "failed" ? "tool.failed" : "tool.completed",
9497
+ type: failed ? "tool.failed" : "tool.completed",
9395
9498
  source: "workbuddy",
9396
9499
  workspaceId,
9397
9500
  project,
@@ -9401,7 +9504,7 @@ async function parseWorkbuddySessionFile(filePath, options) {
9401
9504
  spanId: `${sessionPrefix}:tool:${callId}`,
9402
9505
  agent: "workbuddy",
9403
9506
  tool,
9404
- success: status !== "failed",
9507
+ success: !failed,
9405
9508
  metrics: { toolDurationMs: durationMs, durationMs },
9406
9509
  confidence: start ? "derived" : "partial",
9407
9510
  refs: { sourceId: `${callId}:result` }
@@ -9410,6 +9513,10 @@ async function parseWorkbuddySessionFile(filePath, options) {
9410
9513
  }
9411
9514
  }
9412
9515
  const lastLine = lines.at(-1);
9516
+ if (lastLine) {
9517
+ const ended = closeTurn(lastLine);
9518
+ if (ended) events.push(ended);
9519
+ }
9413
9520
  const lastTs = lastLine ? timestampFrom(numberField(lastLine.record, "timestamp")) : void 0;
9414
9521
  if (lastLine && lastTs) {
9415
9522
  const event = makeEvent({
@@ -9453,6 +9560,23 @@ async function workbuddyBackfillFiles(sourceRoot, home, env) {
9453
9560
  }
9454
9561
  return files.sort((a, b) => a.path.localeCompare(b.path));
9455
9562
  }
9563
+ var workbuddyHandler = (msg) => hookHandler("workbuddy", msg);
9564
+ function hookConfig8() {
9565
+ const anyTool = [{ matcher: ".*", hooks: [workbuddyHandler("Reporting tool activity")] }];
9566
+ return {
9567
+ hooks: {
9568
+ SessionEnd: [{ hooks: [workbuddyHandler("Reporting session end")] }],
9569
+ UserPromptSubmit: [{ hooks: [workbuddyHandler("Reporting prompt activity")] }],
9570
+ PreToolUse: anyTool,
9571
+ PostToolUse: anyTool,
9572
+ PostToolUseFailure: anyTool,
9573
+ Stop: [{ hooks: [workbuddyHandler("Reporting turn completion")] }],
9574
+ StopFailure: [{ hooks: [workbuddyHandler("Reporting turn failure")] }],
9575
+ SubagentStart: anyTool,
9576
+ SubagentStop: [{ hooks: [workbuddyHandler("Reporting subagent completion")] }]
9577
+ }
9578
+ };
9579
+ }
9456
9580
  function createWorkbuddyAdapter() {
9457
9581
  return {
9458
9582
  id: "workbuddy",
@@ -9463,13 +9587,20 @@ function createWorkbuddyAdapter() {
9463
9587
  return workbuddyProjectsDir(home, env);
9464
9588
  },
9465
9589
  installedPath(home, env) {
9466
- return workbuddyProjectsDir(home, env);
9590
+ return path19.join(workbuddyBaseDir(home, env), "settings.json");
9467
9591
  },
9468
9592
  async isInstalled(home, env) {
9469
- return pathExists(workbuddyProjectsDir(home, env));
9593
+ return isHooksJsonInstalled(
9594
+ path19.join(workbuddyBaseDir(home, env), "settings.json"),
9595
+ "vibetime hook --agent workbuddy"
9596
+ );
9470
9597
  },
9471
- installEntries() {
9472
- return [];
9598
+ installEntries(home, env) {
9599
+ return [{
9600
+ kind: "hooks-json",
9601
+ path: path19.join(workbuddyBaseDir(home, env), "settings.json"),
9602
+ content: hookConfig8()
9603
+ }];
9473
9604
  },
9474
9605
  sourcePaths(home, env) {
9475
9606
  return [workbuddyProjectsDir(home, env)];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.34",
4
+ "version": "0.1.35",
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": {