@yhong91/vibetime 0.1.34 → 0.1.36

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