@yhong91/vibetime 0.1.7 → 0.1.8

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 +59 -16
  2. package/package.json +1 -1
package/bin/vibetime.mjs CHANGED
@@ -1195,7 +1195,7 @@ function countTextLines(text) {
1195
1195
  }
1196
1196
 
1197
1197
  // src/lib/constants.ts
1198
- var PACKAGE_VERSION = true ? "0.1.7" : "0.1.1";
1198
+ var PACKAGE_VERSION = true ? "0.1.8" : "0.1.1";
1199
1199
  var DEFAULT_API_URL = "http://121.196.224.82:3001";
1200
1200
  var DEFAULT_BACKFILL_BATCH_SIZE = 50;
1201
1201
  var DEFAULT_BACKFILL_BATCH_BYTES = 800 * 1024;
@@ -1680,6 +1680,27 @@ function resolveModelName(text) {
1680
1680
  }
1681
1681
  return null;
1682
1682
  }
1683
+ var AGY_USAGE_TIME_MATCH_TOLERANCE_MS = 5 * 60 * 1e3;
1684
+ function usageMetadataFromGenerator(item) {
1685
+ const usage = item.chatModel?.usage;
1686
+ if (!usage) {
1687
+ return null;
1688
+ }
1689
+ const chatModel = item.chatModel;
1690
+ const modelCandidates = [
1691
+ chatModel.modelDisplayName,
1692
+ chatModel.responseModel,
1693
+ usage.model,
1694
+ chatModel.model
1695
+ ];
1696
+ const model = modelCandidates.filter((candidate) => typeof candidate === "string" && !candidate.startsWith("MODEL_PLACEHOLDER_")).map(resolveModelName).find(Boolean) ?? null;
1697
+ const occurredAt = Date.parse(chatModel.chatStartMetadata?.createdAt ?? "");
1698
+ return {
1699
+ usage,
1700
+ model,
1701
+ occurredAt: Number.isFinite(occurredAt) ? occurredAt : null
1702
+ };
1703
+ }
1683
1704
  function extractProjectContext(rawLines) {
1684
1705
  let cwd;
1685
1706
  let project;
@@ -1776,30 +1797,50 @@ async function parseAgySessionFile(filePath, options) {
1776
1797
  const usageEntries = [];
1777
1798
  if (generatorMetadata) {
1778
1799
  for (const item of generatorMetadata) {
1779
- const usage = item.chatModel?.usage;
1780
- if (usage) {
1781
- usageEntries.push(usage);
1800
+ const metadata = usageMetadataFromGenerator(item);
1801
+ if (metadata) {
1802
+ usageEntries.push(metadata);
1782
1803
  }
1783
- if (item.stepIndices && Array.isArray(item.stepIndices)) {
1804
+ if (metadata && item.stepIndices && Array.isArray(item.stepIndices)) {
1784
1805
  for (const idx of item.stepIndices) {
1785
- usageMap.set(idx, usage);
1806
+ usageMap.set(idx, metadata);
1786
1807
  }
1787
1808
  }
1788
1809
  }
1789
1810
  const plannerResponses = rawEvents.filter((raw) => raw.type === "PLANNER_RESPONSE");
1790
- const exactMatches = plannerResponses.filter((raw) => usageMap.has(raw.step_index)).length;
1791
- if (exactMatches === 0 && plannerResponses.length > 0 && usageEntries.length > 0) {
1792
- const matchCount = Math.min(plannerResponses.length, usageEntries.length);
1793
- const plannerOffset = plannerResponses.length - matchCount;
1794
- const usageOffset = usageEntries.length - matchCount;
1795
- for (let i = 0; i < matchCount; i += 1) {
1796
- usageMap.set(plannerResponses[plannerOffset + i].step_index, usageEntries[usageOffset + i]);
1811
+ const available = usageEntries.filter((entry) => entry.occurredAt !== null);
1812
+ const used = /* @__PURE__ */ new Set();
1813
+ for (const planner of plannerResponses) {
1814
+ const exact = usageMap.get(planner.step_index);
1815
+ if (exact) {
1816
+ used.add(exact);
1817
+ continue;
1818
+ }
1819
+ const plannerAt = Date.parse(planner.created_at ?? planner.timestamp ?? "");
1820
+ if (!Number.isFinite(plannerAt)) {
1821
+ continue;
1822
+ }
1823
+ let closest;
1824
+ let closestDelta = Number.POSITIVE_INFINITY;
1825
+ for (const entry of available) {
1826
+ if (used.has(entry) || entry.occurredAt === null) {
1827
+ continue;
1828
+ }
1829
+ const delta = Math.abs(entry.occurredAt - plannerAt);
1830
+ if (delta < closestDelta) {
1831
+ closest = entry;
1832
+ closestDelta = delta;
1833
+ }
1834
+ }
1835
+ if (closest && closestDelta <= AGY_USAGE_TIME_MATCH_TOLERANCE_MS) {
1836
+ usageMap.set(planner.step_index, closest);
1837
+ used.add(closest);
1797
1838
  }
1798
1839
  }
1799
1840
  }
1800
1841
  const cwd = detectedCwd;
1801
1842
  const project = detectedProject;
1802
- let model = "gemini-2.5-pro";
1843
+ let model = "unknown";
1803
1844
  const baseAgyEvent = (event) => {
1804
1845
  return {
1805
1846
  schemaVersion: AGENT_TIME_SCHEMA_VERSION,
@@ -1851,8 +1892,9 @@ async function parseAgySessionFile(filePath, options) {
1851
1892
  }), lineNumber, raw.type);
1852
1893
  } else if (raw.type === "PLANNER_RESPONSE") {
1853
1894
  const content = stringField(raw, "content") || "";
1854
- const usage = usageMap.get(raw.step_index);
1855
- if (usage) {
1895
+ const usageMetadata = usageMap.get(raw.step_index);
1896
+ if (usageMetadata) {
1897
+ const usage = usageMetadata.usage;
1856
1898
  const inputTokens = Number.parseInt(usage.inputTokens, 10) || 0;
1857
1899
  const outputTokens = Number.parseInt(usage.outputTokens, 10) || 0;
1858
1900
  const cacheRead = Number.parseInt(usage.cacheReadTokens, 10) || 0;
@@ -1862,6 +1904,7 @@ async function parseAgySessionFile(filePath, options) {
1862
1904
  type: "model.usage",
1863
1905
  sessionId,
1864
1906
  turnId: state.currentTurnId,
1907
+ model: usageMetadata.model ?? model,
1865
1908
  confidence: "exact",
1866
1909
  metrics: {
1867
1910
  modelCalls: 1,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@yhong91/vibetime",
3
3
  "type": "module",
4
- "version": "0.1.7",
4
+ "version": "0.1.8",
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": {