@yhong91/vibetime 0.1.42 → 0.1.44

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 +92 -21
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -1845,7 +1845,7 @@ function operationForTool(tool) {
1845
1845
  if (["write"].includes(normalized)) {
1846
1846
  return "write";
1847
1847
  }
1848
- if (["edit", "multiedit", "notebookedit", "apply_patch", "applypatch"].includes(normalized)) {
1848
+ if (["edit", "multiedit", "notebookedit", "apply_patch", "applypatch", "searchreplace"].includes(normalized)) {
1849
1849
  return "edit";
1850
1850
  }
1851
1851
  return "read";
@@ -1928,7 +1928,7 @@ function countTextLines(text) {
1928
1928
  }
1929
1929
 
1930
1930
  // src/lib/constants.ts
1931
- var PACKAGE_VERSION = true ? "0.1.42" : "0.1.1";
1931
+ var PACKAGE_VERSION = true ? "0.1.44" : "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;
@@ -5815,6 +5815,18 @@ async function parseCopilotSessionFile(filePath, options) {
5815
5815
  }
5816
5816
  const msgTurnId = stringField(data, "turnId");
5817
5817
  const turnId = msgTurnId !== void 0 ? `turn_${createStableHash([sessionId, msgTurnId]).slice(0, 24)}` : currentTurnId;
5818
+ events.push(baseCopilotEvent({
5819
+ ts,
5820
+ type: "agent.operation",
5821
+ operation: "model call",
5822
+ sessionId,
5823
+ turnId,
5824
+ cwd,
5825
+ project,
5826
+ model,
5827
+ confidence: "exact",
5828
+ metrics: { modelCalls: 1 }
5829
+ }));
5818
5830
  const outputTokens = numberField(data, "outputTokens");
5819
5831
  if (outputTokens && outputTokens > 0) {
5820
5832
  const key = turnId || "unknown";
@@ -8138,7 +8150,11 @@ async function parseQoderCnSessionFile(filePath, options) {
8138
8150
  const mapped = {
8139
8151
  ...event,
8140
8152
  sessionId: parentSessionId,
8141
- refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
8153
+ refs: {
8154
+ ...event.refs,
8155
+ sourcePathHash: parentSourcePathHash,
8156
+ ...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
8157
+ }
8142
8158
  };
8143
8159
  return rebuildEventIdentity2(mapped);
8144
8160
  });
@@ -8151,7 +8167,11 @@ async function parseQoderCnSessionFile(filePath, options) {
8151
8167
  return validEvents.map((event) => rebuildEventIdentity2({
8152
8168
  ...event,
8153
8169
  sessionId: dbModelCalls.rootSessionId,
8154
- refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
8170
+ refs: {
8171
+ ...event.refs,
8172
+ sourcePathHash: parentSourcePathHash,
8173
+ ...event.sessionId && event.sessionId !== dbModelCalls.rootSessionId ? { supersededSessionId: event.sessionId } : {}
8174
+ }
8155
8175
  }));
8156
8176
  }
8157
8177
  return validEvents;
@@ -8198,7 +8218,7 @@ function fileActivitiesFromQoderCnToolUse(tool, input, ts, cwd) {
8198
8218
  const metrics = fileMetricsFromQoderCnToolInput(tool, input);
8199
8219
  addResolvedPathActivity(changes, filePath, operation, ts, cwd, workdir, metrics);
8200
8220
  }
8201
- for (const edit of arrayField4(input, "edits")) {
8221
+ for (const edit of [...arrayField4(input, "edits"), ...arrayField4(input, "replacements")]) {
8202
8222
  if (!isPlainObject(edit)) {
8203
8223
  continue;
8204
8224
  }
@@ -8207,8 +8227,8 @@ function fileActivitiesFromQoderCnToolUse(tool, input, ts, cwd) {
8207
8227
  continue;
8208
8228
  }
8209
8229
  addResolvedPathActivity(changes, filePath, "edit", ts, cwd, workdir, {
8210
- linesAdded: countTextLines(stringField(edit, "new_string")),
8211
- linesRemoved: countTextLines(stringField(edit, "old_string"))
8230
+ linesAdded: countTextLines(stringField(edit, "new_string") || stringField(edit, "new_text")),
8231
+ linesRemoved: countTextLines(stringField(edit, "old_string") || stringField(edit, "original_text"))
8212
8232
  });
8213
8233
  }
8214
8234
  const command = stringField(input, "command");
@@ -8225,13 +8245,13 @@ function fileMetricsFromQoderCnToolInput(tool, input) {
8225
8245
  return { linesRead: numberField(input, "limit") };
8226
8246
  }
8227
8247
  if (normalized === "write") {
8228
- const content = stringField(input, "content");
8248
+ const content = stringField(input, "content") || stringField(input, "file_content");
8229
8249
  return {
8230
8250
  linesAdded: countTextLines(content),
8231
8251
  charsWritten: content?.length
8232
8252
  };
8233
8253
  }
8234
- if (normalized === "edit" || normalized === "multiedit") {
8254
+ if (normalized === "edit" || normalized === "multiedit" || normalized === "searchreplace") {
8235
8255
  const oldString = stringField(input, "old_string");
8236
8256
  const newString = stringField(input, "new_string");
8237
8257
  return {
@@ -8964,7 +8984,11 @@ async function parseQoderSessionFile(filePath, options) {
8964
8984
  const mapped = {
8965
8985
  ...event,
8966
8986
  sessionId: parentSessionId,
8967
- refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
8987
+ refs: {
8988
+ ...event.refs,
8989
+ sourcePathHash: parentSourcePathHash,
8990
+ ...event.sessionId && event.sessionId !== parentSessionId ? { supersededSessionId: event.sessionId } : {}
8991
+ }
8968
8992
  };
8969
8993
  return rebuildEventIdentity3(mapped);
8970
8994
  });
@@ -8977,7 +9001,11 @@ async function parseQoderSessionFile(filePath, options) {
8977
9001
  return validEvents.map((event) => rebuildEventIdentity3({
8978
9002
  ...event,
8979
9003
  sessionId: dbModelCalls.rootSessionId,
8980
- refs: { ...event.refs, sourcePathHash: parentSourcePathHash }
9004
+ refs: {
9005
+ ...event.refs,
9006
+ sourcePathHash: parentSourcePathHash,
9007
+ ...event.sessionId && event.sessionId !== dbModelCalls.rootSessionId ? { supersededSessionId: event.sessionId } : {}
9008
+ }
8981
9009
  }));
8982
9010
  }
8983
9011
  return validEvents;
@@ -9024,7 +9052,7 @@ function fileActivitiesFromQoderToolUse(tool, input, ts, cwd) {
9024
9052
  const metrics = fileMetricsFromQoderToolInput(tool, input);
9025
9053
  addResolvedPathActivity(changes, filePath, operation, ts, cwd, workdir, metrics);
9026
9054
  }
9027
- for (const edit of arrayField5(input, "edits")) {
9055
+ for (const edit of [...arrayField5(input, "edits"), ...arrayField5(input, "replacements")]) {
9028
9056
  if (!isPlainObject(edit)) {
9029
9057
  continue;
9030
9058
  }
@@ -9033,8 +9061,8 @@ function fileActivitiesFromQoderToolUse(tool, input, ts, cwd) {
9033
9061
  continue;
9034
9062
  }
9035
9063
  addResolvedPathActivity(changes, filePath, "edit", ts, cwd, workdir, {
9036
- linesAdded: countTextLines(stringField(edit, "new_string")),
9037
- linesRemoved: countTextLines(stringField(edit, "old_string"))
9064
+ linesAdded: countTextLines(stringField(edit, "new_string") || stringField(edit, "new_text")),
9065
+ linesRemoved: countTextLines(stringField(edit, "old_string") || stringField(edit, "original_text"))
9038
9066
  });
9039
9067
  }
9040
9068
  const command = stringField(input, "command");
@@ -9051,13 +9079,13 @@ function fileMetricsFromQoderToolInput(tool, input) {
9051
9079
  return { linesRead: numberField(input, "limit") };
9052
9080
  }
9053
9081
  if (normalized === "write") {
9054
- const content = stringField(input, "content");
9082
+ const content = stringField(input, "content") || stringField(input, "file_content");
9055
9083
  return {
9056
9084
  linesAdded: countTextLines(content),
9057
9085
  charsWritten: content?.length
9058
9086
  };
9059
9087
  }
9060
- if (normalized === "edit" || normalized === "multiedit") {
9088
+ if (normalized === "edit" || normalized === "multiedit" || normalized === "searchreplace") {
9061
9089
  const oldString = stringField(input, "old_string");
9062
9090
  const newString = stringField(input, "new_string");
9063
9091
  return {
@@ -10746,11 +10774,14 @@ function buildSessionRollup(rollupKey, events) {
10746
10774
  const agent = first.agent || ordered.find((event) => event.agent)?.agent;
10747
10775
  const startedAt = ordered[0]?.ts || (/* @__PURE__ */ new Date()).toISOString();
10748
10776
  const lastEventAt = ordered.at(-1)?.ts || startedAt;
10777
+ const supersededSessionIds = [...new Set(ordered.map((event) => event.refs?.supersededSessionId).filter((id) => Boolean(id) && id !== sessionId))];
10749
10778
  const timeBuckets = /* @__PURE__ */ new Map();
10750
10779
  const modelRollups = /* @__PURE__ */ new Map();
10751
10780
  const toolRollups = /* @__PURE__ */ new Map();
10752
10781
  const fileRollups = /* @__PURE__ */ new Map();
10753
10782
  const turnRollups = /* @__PURE__ */ new Map();
10783
+ const turnTimings = /* @__PURE__ */ new Map();
10784
+ let activeTurnId;
10754
10785
  let promptCount = 0;
10755
10786
  let turnCount = 0;
10756
10787
  let toolCallCount = 0;
@@ -10766,6 +10797,22 @@ function buildSessionRollup(rollupKey, events) {
10766
10797
  let linesAdded = 0;
10767
10798
  let linesRemoved = 0;
10768
10799
  for (const event of ordered) {
10800
+ if (event.type === "turn.started" && event.turnId) {
10801
+ activeTurnId = event.turnId;
10802
+ const timing = turnTimings.get(event.turnId) || {};
10803
+ timing.taskStartedAt ??= event.ts;
10804
+ turnTimings.set(event.turnId, timing);
10805
+ }
10806
+ if (event.type === "model.usage" || (event.metrics?.modelCalls || 0) > 0) {
10807
+ const turnId = event.turnId || activeTurnId;
10808
+ if (turnId) {
10809
+ const timing = turnTimings.get(turnId) || {};
10810
+ if (!timing.lastCallAt || event.ts > timing.lastCallAt) {
10811
+ timing.lastCallAt = event.ts;
10812
+ }
10813
+ turnTimings.set(turnId, timing);
10814
+ }
10815
+ }
10769
10816
  const eventInputTokens = Math.max(0, event.metrics?.tokensInput || 0);
10770
10817
  const eventCachedInputTokens = Math.max(0, event.metrics?.tokensCachedInput || 0);
10771
10818
  const eventCacheCreationInputTokens = Math.max(0, event.metrics?.tokensCacheCreationInput || 0);
@@ -10969,6 +11016,9 @@ function buildSessionRollup(rollupKey, events) {
10969
11016
  fileRollups.set(pathHash, fileRollup);
10970
11017
  }
10971
11018
  timeBuckets.set(bucketTs, bucket);
11019
+ if (event.type === "turn.completed" && event.turnId === activeTurnId) {
11020
+ activeTurnId = void 0;
11021
+ }
10972
11022
  }
10973
11023
  const baseRollup = {
10974
11024
  rollupKey,
@@ -10999,10 +11049,16 @@ function buildSessionRollup(rollupKey, events) {
10999
11049
  modelRollups: [...modelRollups.values()].sort((a, b) => b.callCount - a.callCount || a.model.localeCompare(b.model)),
11000
11050
  toolRollups: [...toolRollups.values()].sort((a, b) => b.callCount - a.callCount || a.tool.localeCompare(b.tool)),
11001
11051
  fileRollups: [...fileRollups.values()].sort((a, b) => b.writes - a.writes || b.reads - a.reads || a.displayPath.localeCompare(b.displayPath)),
11002
- turnRollups: [...turnRollups.values()].map((rollup) => ({
11003
- ...rollup,
11004
- durationMs: Math.max(0, Date.parse(rollup.lastEventAt) - Date.parse(rollup.startedAt))
11005
- })).sort((a, b) => a.startedAt.localeCompare(b.startedAt))
11052
+ turnRollups: [...turnRollups.values()].map((rollup) => {
11053
+ const timing = turnTimings.get(rollup.turnId);
11054
+ const startedAt2 = timing?.taskStartedAt || rollup.startedAt;
11055
+ return {
11056
+ ...rollup,
11057
+ startedAt: startedAt2,
11058
+ durationMs: timing?.lastCallAt ? Math.max(0, Date.parse(timing.lastCallAt) - Date.parse(startedAt2)) : 0
11059
+ };
11060
+ }).sort((a, b) => a.startedAt.localeCompare(b.startedAt)),
11061
+ ...supersededSessionIds.length > 0 ? { supersededSessionIds } : {}
11006
11062
  };
11007
11063
  return {
11008
11064
  ...baseRollup,
@@ -11416,7 +11472,7 @@ async function postRollupBatch(remote, rollups, options = {}) {
11416
11472
  };
11417
11473
  }
11418
11474
  async function deleteRollupsBySource(remote, source, machine, options = {}) {
11419
- const query = `source=${encodeURIComponent(source)}${options.preserveTokens ? "&preserveTokens=1" : ""}${options.allowHistoricalRewrite ? "&allowHistoricalRewrite=1" : ""}`;
11475
+ const query = `source=${encodeURIComponent(source)}${options.rollupKey ? `&rollupKey=${encodeURIComponent(options.rollupKey)}` : ""}${options.preserveTokens ? "&preserveTokens=1" : ""}${options.allowHistoricalRewrite ? "&allowHistoricalRewrite=1" : ""}`;
11420
11476
  const response = await remote.fetchImpl(
11421
11477
  joinUrl(remote.baseUrl, `/v3/agent/sessions?${query}`),
11422
11478
  { method: "DELETE", headers: buildHeaders(remote.token, machine) }
@@ -12273,6 +12329,21 @@ async function sendSessionRollupBatch(rollups, options, ctx) {
12273
12329
  machine,
12274
12330
  allowHistoricalRewrite: options["purge-all"] === true
12275
12331
  });
12332
+ if (options.force && result.conflicts === 0) {
12333
+ for (const rollup of rollups) {
12334
+ for (const sessionId of rollup.supersededSessionIds ?? []) {
12335
+ try {
12336
+ await deleteRollupsBySource(remote, rollup.source, machine, {
12337
+ rollupKey: createImportKey(["rollup", rollup.source, sessionId]),
12338
+ allowHistoricalRewrite: options["purge-all"] === true
12339
+ });
12340
+ } catch (error) {
12341
+ debug(ctx, `Failed to delete superseded ${rollup.source} session ${sessionId}: ${error.message}
12342
+ `);
12343
+ }
12344
+ }
12345
+ }
12346
+ }
12276
12347
  return result;
12277
12348
  }
12278
12349
  async function deleteSessionRollupsBySourceAPI(source, options, ctx, preserveTokens) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.42",
4
+ "version": "0.1.44",
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": {