fleetlens 0.7.1 → 0.10.0

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 (130) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/build-manifest.json +3 -3
  3. package/app/apps/web/.next/prerender-manifest.json +3 -3
  4. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  12. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  13. package/app/apps/web/.next/server/app/api/ask/route.js +1 -1
  14. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  15. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +3 -3
  16. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  17. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +3 -3
  18. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/month-index/route.js +2 -2
  20. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +3 -3
  22. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/digest/week-index/route.js +2 -2
  24. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/events/route.js +1 -1
  26. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  27. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  28. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  29. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  30. package/app/apps/web/.next/server/app/api/settings/route.js +4 -4
  31. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  32. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  33. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  34. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  36. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  37. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  38. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  39. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  40. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  41. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  42. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  43. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  44. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  45. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  46. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  47. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  48. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  49. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  50. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  51. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  52. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  53. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  54. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  55. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  56. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  57. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  58. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  59. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  60. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  61. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  62. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  63. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  64. package/app/apps/web/.next/server/chunks/[root-of-the-server]__07qxzx4._.js +9 -0
  65. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0fu2xfy._.js +5 -0
  66. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gfd42k._.js +9 -0
  67. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0i.m8y0._.js +3 -0
  68. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0iy5aqy._.js +9 -0
  69. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nh7-nf._.js +3 -0
  70. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nvdffv._.js +3 -0
  71. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0s2~c3g._.js +3 -0
  72. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +3 -3
  73. package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +3 -0
  74. package/app/apps/web/.next/server/chunks/packages_0o6.gpk._.js +3 -0
  75. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +13 -13
  76. package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_122oe0-.js +1 -1
  77. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0d~2~le._.js +1 -1
  78. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
  79. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0s4dg1y._.js +3 -0
  80. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0tejjy_._.js +1 -1
  81. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +3 -3
  82. package/app/apps/web/.next/server/chunks/ssr/_0jw2sm7._.js +1 -1
  83. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  84. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.js +1 -1
  85. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +1 -1
  86. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_usage-charts-dashboard_tsx_0bgr_uv._.js +1 -1
  87. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0m67-xi._.js +1 -1
  88. package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
  89. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_10_oegc._.js +1 -1
  90. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  91. package/app/apps/web/.next/server/pages/500.html +1 -1
  92. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  93. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  94. package/app/apps/web/.next/static/chunks/04a36uhu~p_z-.css +1 -0
  95. package/app/apps/web/.next/static/chunks/{0q2vd~aj.1hpz.js → 09--oejbzw3q-.js} +3 -3
  96. package/app/apps/web/.next/static/chunks/0l3ck.awv~4t7.js +1 -0
  97. package/app/apps/web/.next/static/chunks/{171za22fv684..js → 0lxuac2v0_gdl.js} +2 -2
  98. package/app/apps/web/.next/static/chunks/0n0n.0279ot6c.js +1 -0
  99. package/app/apps/web/.next/static/chunks/0qcu452dr~edz.js +14 -0
  100. package/app/apps/web/.next/static/chunks/0uev~g~qvbjv9.js +4 -0
  101. package/app/apps/web/components/previous-cycles-trend.tsx +26 -11
  102. package/app/apps/web/components/usage-chart.tsx +35 -37
  103. package/app/apps/web/components/usage-gauges.tsx +18 -12
  104. package/app/apps/web/components/usage-sidebar.tsx +23 -12
  105. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  106. package/app/apps/web/lib/utilization-tone.ts +74 -0
  107. package/app/apps/web/package.json +1 -1
  108. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  109. package/dist/daemon-worker.js +694 -67
  110. package/dist/index.js +1449 -823
  111. package/package.json +1 -1
  112. package/app/apps/web/.next/server/chunks/[externals]__0fpbr8-._.js +0 -3
  113. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0-vgrh1._.js +0 -3
  114. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0.z48ue._.js +0 -3
  115. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_8zh-i._.js +0 -9
  116. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0dctov1._.js +0 -5
  117. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e8n_x4._.js +0 -3
  118. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ej_p3s._.js +0 -9
  119. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jprdks._.js +0 -9
  120. package/app/apps/web/.next/server/chunks/packages_09hmrqx._.js +0 -3
  121. package/app/apps/web/.next/server/chunks/packages_118y1lp._.js +0 -3
  122. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ytdqxt._.js +0 -3
  123. package/app/apps/web/.next/static/chunks/05m.oz3kmf1p7.js +0 -1
  124. package/app/apps/web/.next/static/chunks/07-fq4im3pmjp.js +0 -4
  125. package/app/apps/web/.next/static/chunks/0o9ayt8sknubd.js +0 -1
  126. package/app/apps/web/.next/static/chunks/0wfbj5zr~az.z.css +0 -1
  127. package/app/apps/web/.next/static/chunks/179_t_igg8ghh.js +0 -14
  128. /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_buildManifest.js +0 -0
  129. /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_clientMiddlewareManifest.js +0 -0
  130. /package/app/apps/web/.next/static/{tiK9aMHXkgnKLNmxj9J1P → L0Dy1hOQrZjV9l0TNulvk}/_ssgManifest.js +0 -0
package/dist/index.js CHANGED
@@ -187,6 +187,14 @@ function latestSnapshot(filePath) {
187
187
  const all = readSnapshots(filePath);
188
188
  return all.length > 0 ? all[all.length - 1] : null;
189
189
  }
190
+ function latestClaudeCodeSnapshot(filePath) {
191
+ const all = readSnapshots(filePath);
192
+ for (let i = all.length - 1; i >= 0; i--) {
193
+ const s = all[i];
194
+ if (!s.agent || s.agent === "claude-code") return s;
195
+ }
196
+ return null;
197
+ }
190
198
  var init_storage = __esm({
191
199
  "src/usage/storage.ts"() {
192
200
  "use strict";
@@ -454,9 +462,9 @@ Installed: ${PACKAGE_NAME}@${verify.installedVersion} at ${verify.installedPath
454
462
  console.warn(
455
463
  ` \u2022 reinstall in the correct Node env: 'npm install -g ${PACKAGE_NAME}@latest'`
456
464
  );
457
- } else if (verify.installedVersion !== "0.7.1") {
465
+ } else if (verify.installedVersion !== "0.10.0") {
458
466
  console.log(
459
- ` \u2192 This process is still running ${"0.7.1"}. Next invocation will use ${verify.installedVersion}.`
467
+ ` \u2192 This process is still running ${"0.10.0"}. Next invocation will use ${verify.installedVersion}.`
460
468
  );
461
469
  }
462
470
  }
@@ -492,7 +500,7 @@ async function checkForUpdate() {
492
500
  if (process.env.__FLEETLENS_UPDATED === "1" || process.env.__CCLENS_UPDATED === "1") return;
493
501
  const latest = await fetchLatestVersion();
494
502
  if (latest === null) return;
495
- const current = "0.7.1";
503
+ const current = "0.10.0";
496
504
  if (!shouldUpdate(current, latest)) return;
497
505
  console.log(`Updating ${PACKAGE_NAME} ${current} \u2192 ${latest}...`);
498
506
  await stopRunningServices();
@@ -574,7 +582,7 @@ async function restartServices(state) {
574
582
  }
575
583
  async function forceUpdate() {
576
584
  const latest = await fetchLatestVersion();
577
- const current = "0.7.1";
585
+ const current = "0.10.0";
578
586
  if (latest === null) {
579
587
  console.error("Could not reach npm registry. Check your network.");
580
588
  process.exit(1);
@@ -747,10 +755,10 @@ async function web(args2) {
747
755
  const noOpen = args2.includes("--no-open");
748
756
  const positional = args2.filter((a) => !a.startsWith("--"));
749
757
  const rawPath = positional[0] ?? "";
750
- const path4 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
758
+ const path5 = rawPath.startsWith("/") ? rawPath : rawPath ? `/${rawPath}` : "";
751
759
  const status2 = getServerStatus();
752
760
  if (status2.running) {
753
- const url = `http://localhost:${status2.port}${path4}`;
761
+ const url = `http://localhost:${status2.port}${path5}`;
754
762
  console.log(noOpen ? `Server running at ${url}` : `Opening ${url}`);
755
763
  if (!noOpen) openBrowser(url);
756
764
  return;
@@ -758,7 +766,7 @@ async function web(args2) {
758
766
  console.log("fleetlens is not running. Starting server...");
759
767
  try {
760
768
  const result = await startServer({});
761
- const url = `http://localhost:${result.port}${path4}`;
769
+ const url = `http://localhost:${result.port}${path5}`;
762
770
  console.log(`fleetlens running on ${url} (PID ${result.pid})`);
763
771
  if (!noOpen) openBrowser(url);
764
772
  } catch (err) {
@@ -816,9 +824,9 @@ function readFromCredentialsFile() {
816
824
  join4(homedir3(), ".claude", ".credentials.json"),
817
825
  join4(homedir3(), ".config", "claude", "credentials.json")
818
826
  ];
819
- for (const path4 of candidates) {
827
+ for (const path5 of candidates) {
820
828
  try {
821
- const blob = readFileSync5(path4, "utf8");
829
+ const blob = readFileSync5(path5, "utf8");
822
830
  const creds = extractCredentials(blob);
823
831
  if (creds) return creds;
824
832
  } catch {
@@ -1050,21 +1058,37 @@ function findWorktreeMarker(projectName) {
1050
1058
  }
1051
1059
  return null;
1052
1060
  }
1061
+ function findConductorWorkspace(projectName) {
1062
+ const m = CONDUCTOR_RE.exec(projectName);
1063
+ if (!m)
1064
+ return null;
1065
+ const workspaceName = m[1];
1066
+ const trailingSlash = m[0].endsWith("/") ? 1 : 0;
1067
+ const canonicalEndIdx = m.index + m[0].length - workspaceName.length - 1 - trailingSlash;
1068
+ return { canonicalEndIdx, workspaceName };
1069
+ }
1053
1070
  function canonicalProjectName(projectName) {
1054
1071
  const normalized = projectName.replace(/\/{2,}/g, "/");
1055
- const hit = findWorktreeMarker(normalized);
1056
- if (hit)
1057
- return normalized.slice(0, hit.idx);
1072
+ const wt = findWorktreeMarker(normalized);
1073
+ if (wt)
1074
+ return normalized.slice(0, wt.idx);
1075
+ const cd = findConductorWorkspace(normalized);
1076
+ if (cd)
1077
+ return normalized.slice(0, cd.canonicalEndIdx);
1058
1078
  return normalized;
1059
1079
  }
1060
1080
  function worktreeName(projectName) {
1061
1081
  const normalized = projectName.replace(/\/{2,}/g, "/");
1062
- const hit = findWorktreeMarker(normalized);
1063
- if (!hit)
1064
- return null;
1065
- const tail = normalized.slice(hit.idx + hit.marker.length);
1066
- const slash = tail.indexOf("/");
1067
- return slash >= 0 ? tail.slice(0, slash) : tail;
1082
+ const wt = findWorktreeMarker(normalized);
1083
+ if (wt) {
1084
+ const tail = normalized.slice(wt.idx + wt.marker.length);
1085
+ const slash = tail.indexOf("/");
1086
+ return slash >= 0 ? tail.slice(0, slash) : tail;
1087
+ }
1088
+ const cd = findConductorWorkspace(normalized);
1089
+ if (cd)
1090
+ return cd.workspaceName;
1091
+ return null;
1068
1092
  }
1069
1093
  function makeDay(date) {
1070
1094
  return {
@@ -1533,7 +1557,7 @@ function groupByProject(sessions) {
1533
1557
  }
1534
1558
  return Array.from(map.values()).sort((a, b) => (b.lastActiveMs ?? 0) - (a.lastActiveMs ?? 0));
1535
1559
  }
1536
- var BLANK_USAGE, WORKTREE_MARKERS, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
1560
+ var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
1537
1561
  var init_analytics = __esm({
1538
1562
  "../parser/dist/analytics.js"() {
1539
1563
  "use strict";
@@ -1543,6 +1567,7 @@ var init_analytics = __esm({
1543
1567
  "/.worktrees/",
1544
1568
  "/claude/worktrees/"
1545
1569
  ];
1570
+ CONDUCTOR_RE = /\/conductor\/workspaces\/[^/]+\/([^/]+)(?:\/|$)/;
1546
1571
  PR_CREATE_RE = /gh\s+pr\s+create\b/i;
1547
1572
  PR_NUMBER_RE = /#(\d+)/;
1548
1573
  TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
@@ -1556,7 +1581,7 @@ function getAgentMetadata(kind) {
1556
1581
  function isAgentKind(s) {
1557
1582
  return typeof s === "string" && KNOWN_AGENT_KINDS.has(s);
1558
1583
  }
1559
- var CLAUDE_CODE_METADATA, CODEX_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
1584
+ var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
1560
1585
  var init_agent_metadata = __esm({
1561
1586
  "../parser/dist/agent-metadata.js"() {
1562
1587
  "use strict";
@@ -1574,9 +1599,17 @@ var init_agent_metadata = __esm({
1574
1599
  accentColor: "rgb(16, 163, 127)",
1575
1600
  iconChar: "\u25C6"
1576
1601
  };
1602
+ GEMINI_METADATA = {
1603
+ kind: "gemini",
1604
+ displayName: "Gemini CLI",
1605
+ shortLabel: "Gemini",
1606
+ accentColor: "rgb(124, 92, 246)",
1607
+ iconChar: "\u273A"
1608
+ };
1577
1609
  agentMetadata = [
1578
1610
  CLAUDE_CODE_METADATA,
1579
- CODEX_METADATA
1611
+ CODEX_METADATA,
1612
+ GEMINI_METADATA
1580
1613
  ];
1581
1614
  KNOWN_AGENT_KINDS = new Set(agentMetadata.map((m) => m.kind));
1582
1615
  }
@@ -1767,10 +1800,64 @@ var init_calibration = __esm({
1767
1800
  }
1768
1801
  });
1769
1802
 
1803
+ // ../parser/dist/user-input.js
1804
+ function stripFrameworkBoilerplate(rawText) {
1805
+ if (!rawText)
1806
+ return rawText;
1807
+ let out = rawText;
1808
+ for (const re of STRIPPABLE_WRAPPERS) {
1809
+ out = out.replace(re, "");
1810
+ }
1811
+ return out.trim();
1812
+ }
1813
+ function isFrameworkInjectedUserInput(rawText) {
1814
+ if (!rawText)
1815
+ return false;
1816
+ const stripped = stripFrameworkBoilerplate(rawText);
1817
+ if (!stripped)
1818
+ return true;
1819
+ for (const prefix of ENTIRELY_INJECTED_PREFIXES) {
1820
+ if (stripped.startsWith(prefix))
1821
+ return true;
1822
+ }
1823
+ return false;
1824
+ }
1825
+ var STRIPPABLE_WRAPPERS, ENTIRELY_INJECTED_PREFIXES;
1826
+ var init_user_input = __esm({
1827
+ "../parser/dist/user-input.js"() {
1828
+ "use strict";
1829
+ STRIPPABLE_WRAPPERS = [
1830
+ /<system_instruction\b[^>]*>[\s\S]*?<\/system_instruction>\s*/gi
1831
+ ];
1832
+ ENTIRELY_INJECTED_PREFIXES = [
1833
+ "<command-name>",
1834
+ "<local-command-stdout>",
1835
+ "<local-command-caveat>",
1836
+ "<task-notification>",
1837
+ "Base directory for this skill:"
1838
+ ];
1839
+ }
1840
+ });
1841
+
1770
1842
  // ../parser/dist/parser.js
1771
1843
  function cleanText(s) {
1772
1844
  return s.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
1773
1845
  }
1846
+ function extractRawUserText(rawMsg) {
1847
+ const c = rawMsg?.content;
1848
+ if (typeof c === "string")
1849
+ return c;
1850
+ if (Array.isArray(c)) {
1851
+ for (const block of c) {
1852
+ if (block && typeof block === "object") {
1853
+ const b = block;
1854
+ if (b.type === "text" && typeof b.text === "string")
1855
+ return b.text;
1856
+ }
1857
+ }
1858
+ }
1859
+ return "";
1860
+ }
1774
1861
  function truncate(s, n = 200) {
1775
1862
  const one = cleanText(s);
1776
1863
  return one.length > n ? one.slice(0, n - 1) + "\u2026" : one;
@@ -1869,7 +1956,8 @@ function toEvent(raw, index) {
1869
1956
  const msg = r.message ?? {};
1870
1957
  const c = msg.content;
1871
1958
  if (typeof c === "string") {
1872
- const tm = classifyTeammateMessage(c);
1959
+ const cleaned = stripFrameworkBoilerplate(c);
1960
+ const tm = classifyTeammateMessage(cleaned);
1873
1961
  return {
1874
1962
  index,
1875
1963
  uuid,
@@ -1877,8 +1965,8 @@ function toEvent(raw, index) {
1877
1965
  timestamp,
1878
1966
  role: "user",
1879
1967
  rawType,
1880
- preview: truncate(c, 200),
1881
- blocks: [{ type: "text", text: c }],
1968
+ preview: truncate(cleaned, 200),
1969
+ blocks: cleaned ? [{ type: "text", text: cleaned }] : [],
1882
1970
  raw,
1883
1971
  teammateMessage: tm
1884
1972
  };
@@ -1902,7 +1990,9 @@ function toEvent(raw, index) {
1902
1990
  };
1903
1991
  }
1904
1992
  const textBlock = c.find((b) => b?.type === "text");
1905
- const tm = textBlock ? classifyTeammateMessage(textBlock.text) : void 0;
1993
+ const cleanedText = textBlock ? stripFrameworkBoilerplate(textBlock.text) : "";
1994
+ const cleanedBlocks = textBlock ? c.map((b) => b === textBlock ? { type: "text", text: cleanedText } : b) : c;
1995
+ const tm = cleanedText ? classifyTeammateMessage(cleanedText) : void 0;
1906
1996
  return {
1907
1997
  index,
1908
1998
  uuid,
@@ -1910,8 +2000,8 @@ function toEvent(raw, index) {
1910
2000
  timestamp,
1911
2001
  role: "user",
1912
2002
  rawType,
1913
- preview: textBlock ? truncate(textBlock.text, 200) : truncate(JSON.stringify(c), 200),
1914
- blocks: c,
2003
+ preview: cleanedText ? truncate(cleanedText, 200) : truncate(JSON.stringify(c), 200),
2004
+ blocks: cleanedBlocks,
1915
2005
  raw,
1916
2006
  teammateMessage: tm
1917
2007
  };
@@ -2131,8 +2221,8 @@ function parseTranscript(rawLines) {
2131
2221
  }
2132
2222
  if (e.role === "user") {
2133
2223
  const rawMsg = e.raw?.message;
2134
- const rawContent = typeof rawMsg?.content === "string" ? rawMsg.content : "";
2135
- const isHidden = rawContent.startsWith("<command-name>") || rawContent.startsWith("<local-command-caveat>") || rawContent.startsWith("Base directory for this skill:") || rawContent.startsWith("<task-notification>");
2224
+ const rawContent = extractRawUserText(rawMsg);
2225
+ const isHidden = isFrameworkInjectedUserInput(rawContent);
2136
2226
  const isTeamNoise = e.teammateMessage !== void 0 && !agentName;
2137
2227
  if (!isHidden && !isTeamNoise) {
2138
2228
  if (!firstUserPreview) {
@@ -2217,6 +2307,7 @@ var BLANK_USAGE2, TEAMMATE_MSG_RE;
2217
2307
  var init_parser = __esm({
2218
2308
  "../parser/dist/parser.js"() {
2219
2309
  "use strict";
2310
+ init_user_input();
2220
2311
  BLANK_USAGE2 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
2221
2312
  TEAMMATE_MSG_RE = /^\s*<teammate-message\s+teammate_id="([^"]+)"[^>]*>([\s\S]*?)<\/teammate-message>/;
2222
2313
  }
@@ -3035,7 +3126,8 @@ function parseRollout(file, lines) {
3035
3126
  if (type === "event_msg" && subtype === "user_message") {
3036
3127
  const text = typeof payload.message === "string" ? payload.message : "";
3037
3128
  const preview = previewOf(text);
3038
- if (text) {
3129
+ const isHidden = isFrameworkInjectedUserInput(text);
3130
+ if (text && !isHidden) {
3039
3131
  if (!firstUserPreview)
3040
3132
  firstUserPreview = preview;
3041
3133
  lastUserPreview = preview;
@@ -3352,6 +3444,7 @@ var init_codex = __esm({
3352
3444
  "../parser/dist/codex.js"() {
3353
3445
  "use strict";
3354
3446
  init_analytics();
3447
+ init_user_input();
3355
3448
  DEFAULT_CODEX_ROOT = path2.join(os2.homedir(), ".codex", "sessions");
3356
3449
  ROLLOUT_RE = /^rollout-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})-([0-9a-f-]+)\.jsonl$/;
3357
3450
  IDLE_GAP_MS = 3 * 60 * 1e3;
@@ -3360,10 +3453,512 @@ var init_codex = __esm({
3360
3453
  }
3361
3454
  });
3362
3455
 
3456
+ // ../parser/dist/gemini.js
3457
+ import { promises as fs3 } from "node:fs";
3458
+ import os3 from "node:os";
3459
+ import path3 from "node:path";
3460
+ async function safeReaddir2(p) {
3461
+ return fs3.readdir(p).catch(() => []);
3462
+ }
3463
+ async function listSessionFiles(root) {
3464
+ const out = [];
3465
+ const slugs = await safeReaddir2(root);
3466
+ for (const slug of slugs) {
3467
+ const chatsDir = path3.join(root, slug, "chats");
3468
+ const entries2 = await safeReaddir2(chatsDir);
3469
+ for (const entry of entries2) {
3470
+ if (!SESSION_FILE_RE.test(entry))
3471
+ continue;
3472
+ const full = path3.join(chatsDir, entry);
3473
+ const stat = await fs3.stat(full).catch(() => null);
3474
+ if (!stat || !stat.isFile())
3475
+ continue;
3476
+ const filenameId = entry.replace(/\.(jsonl|json)$/, "").split("-").pop() ?? entry;
3477
+ out.push({
3478
+ filePath: full,
3479
+ slug,
3480
+ filenameId,
3481
+ format: entry.endsWith(".jsonl") ? "jsonl" : "legacy-json",
3482
+ mtimeMs: stat.mtimeMs,
3483
+ sizeBytes: stat.size
3484
+ });
3485
+ }
3486
+ }
3487
+ return out;
3488
+ }
3489
+ async function loadSlugToCwd() {
3490
+ const stat = await fs3.stat(GEMINI_PROJECTS_FILE).catch(() => null);
3491
+ if (!stat)
3492
+ return /* @__PURE__ */ new Map();
3493
+ if (projectsCache && projectsCache.mtimeMs === stat.mtimeMs) {
3494
+ return projectsCache.slugToCwd;
3495
+ }
3496
+ const raw = await fs3.readFile(GEMINI_PROJECTS_FILE, "utf8").catch(() => "");
3497
+ let parsed;
3498
+ try {
3499
+ parsed = JSON.parse(raw);
3500
+ } catch {
3501
+ parsed = {};
3502
+ }
3503
+ const slugToCwd = /* @__PURE__ */ new Map();
3504
+ for (const [cwd, slug] of Object.entries(parsed.projects ?? {})) {
3505
+ if (typeof slug === "string")
3506
+ slugToCwd.set(slug, cwd);
3507
+ }
3508
+ projectsCache = { mtimeMs: stat.mtimeMs, slugToCwd };
3509
+ return slugToCwd;
3510
+ }
3511
+ function isRewindMarker(o) {
3512
+ return !!o && typeof o === "object" && "$rewindTo" in o;
3513
+ }
3514
+ function isSetMarker(o) {
3515
+ return !!o && typeof o === "object" && "$set" in o;
3516
+ }
3517
+ function isMessageRecord(o) {
3518
+ return !!o && typeof o === "object" && typeof o.id === "string" && typeof o.type === "string";
3519
+ }
3520
+ async function readAndReplay(file) {
3521
+ const raw = await fs3.readFile(file.filePath, "utf8");
3522
+ if (file.format === "legacy-json") {
3523
+ let blob;
3524
+ try {
3525
+ blob = JSON.parse(raw);
3526
+ } catch {
3527
+ return { meta: {}, messages: [] };
3528
+ }
3529
+ const meta2 = {
3530
+ sessionId: typeof blob.sessionId === "string" ? blob.sessionId : void 0,
3531
+ projectHash: typeof blob.projectHash === "string" ? blob.projectHash : void 0,
3532
+ startTime: typeof blob.startTime === "string" ? blob.startTime : void 0,
3533
+ lastUpdated: typeof blob.lastUpdated === "string" ? blob.lastUpdated : void 0,
3534
+ summary: typeof blob.summary === "string" ? blob.summary : void 0
3535
+ };
3536
+ const msgs = Array.isArray(blob.messages) ? blob.messages : [];
3537
+ const messages2 = msgs.filter(isMessageRecord);
3538
+ return { meta: meta2, messages: messages2 };
3539
+ }
3540
+ const meta = {};
3541
+ const byId = /* @__PURE__ */ new Map();
3542
+ const order = [];
3543
+ for (const line of raw.split("\n")) {
3544
+ const trimmed = line.trim();
3545
+ if (!trimmed)
3546
+ continue;
3547
+ let obj;
3548
+ try {
3549
+ obj = JSON.parse(trimmed);
3550
+ } catch {
3551
+ continue;
3552
+ }
3553
+ if (isRewindMarker(obj)) {
3554
+ const target = obj.$rewindTo;
3555
+ const idx = order.indexOf(target);
3556
+ if (idx >= 0) {
3557
+ for (const id of order.slice(idx))
3558
+ byId.delete(id);
3559
+ order.length = idx;
3560
+ }
3561
+ continue;
3562
+ }
3563
+ if (isSetMarker(obj)) {
3564
+ Object.assign(meta, obj.$set);
3565
+ continue;
3566
+ }
3567
+ if (isMessageRecord(obj)) {
3568
+ if (!byId.has(obj.id))
3569
+ order.push(obj.id);
3570
+ byId.set(obj.id, obj);
3571
+ continue;
3572
+ }
3573
+ if (typeof obj === "object" && obj !== null) {
3574
+ const m = obj;
3575
+ if (typeof m.sessionId === "string" && !meta.sessionId) {
3576
+ meta.sessionId = m.sessionId;
3577
+ if (typeof m.projectHash === "string")
3578
+ meta.projectHash = m.projectHash;
3579
+ if (typeof m.startTime === "string")
3580
+ meta.startTime = m.startTime;
3581
+ if (typeof m.lastUpdated === "string")
3582
+ meta.lastUpdated = m.lastUpdated;
3583
+ if (typeof m.summary === "string")
3584
+ meta.summary = m.summary;
3585
+ }
3586
+ }
3587
+ }
3588
+ const messages = [];
3589
+ for (const id of order) {
3590
+ const m = byId.get(id);
3591
+ if (m)
3592
+ messages.push(m);
3593
+ }
3594
+ return { meta, messages };
3595
+ }
3596
+ function emptyUsage2() {
3597
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
3598
+ }
3599
+ function previewOf2(text) {
3600
+ return text.replace(/\s+/g, " ").trim().slice(0, 200);
3601
+ }
3602
+ function flattenContent(c) {
3603
+ if (!c)
3604
+ return "";
3605
+ if (typeof c === "string")
3606
+ return c;
3607
+ const parts = Array.isArray(c) ? c : [c];
3608
+ const out = [];
3609
+ for (const p of parts) {
3610
+ if (!p || typeof p !== "object")
3611
+ continue;
3612
+ const part = p;
3613
+ if (typeof part.text === "string") {
3614
+ out.push(part.text);
3615
+ continue;
3616
+ }
3617
+ const fr = part.functionResponse;
3618
+ if (fr) {
3619
+ const resp = fr.response;
3620
+ if (typeof resp === "string") {
3621
+ out.push(resp);
3622
+ } else if (resp && typeof resp === "object") {
3623
+ const r = resp;
3624
+ if (typeof r.output === "string")
3625
+ out.push(r.output);
3626
+ else if (typeof r.error === "string")
3627
+ out.push(`error: ${r.error}`);
3628
+ else
3629
+ out.push(JSON.stringify(r));
3630
+ }
3631
+ }
3632
+ }
3633
+ return out.join("\n");
3634
+ }
3635
+ function truncate3(s, n) {
3636
+ return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
3637
+ }
3638
+ async function parseSession(file) {
3639
+ const { meta: metaRec, messages } = await readAndReplay(file);
3640
+ const slugToCwd = await loadSlugToCwd();
3641
+ const cwd = slugToCwd.get(file.slug);
3642
+ const events = [];
3643
+ const totalUsage = emptyUsage2();
3644
+ let firstTimestamp;
3645
+ let lastTimestamp;
3646
+ let model;
3647
+ let toolCallCount = 0;
3648
+ let turnCount = 0;
3649
+ let firstUserPreview;
3650
+ let lastUserPreview;
3651
+ let lastAgentPreview;
3652
+ const tsMs = [];
3653
+ let idx = 0;
3654
+ function pushEvent(e) {
3655
+ events.push(e);
3656
+ if (e.timestamp && e.role !== "meta") {
3657
+ if (!firstTimestamp)
3658
+ firstTimestamp = e.timestamp;
3659
+ lastTimestamp = e.timestamp;
3660
+ const ms = Date.parse(e.timestamp);
3661
+ if (Number.isFinite(ms))
3662
+ tsMs.push(ms);
3663
+ }
3664
+ }
3665
+ for (const msg of messages) {
3666
+ const ts = msg.timestamp;
3667
+ if (msg.type === "user") {
3668
+ const text = flattenContent(msg.displayContent ?? msg.content);
3669
+ const preview = previewOf2(text);
3670
+ const isHidden = isFrameworkInjectedUserInput(text);
3671
+ if (text && !isHidden) {
3672
+ if (!firstUserPreview)
3673
+ firstUserPreview = preview;
3674
+ lastUserPreview = preview;
3675
+ turnCount += 1;
3676
+ }
3677
+ pushEvent({
3678
+ index: idx++,
3679
+ timestamp: ts,
3680
+ role: "user",
3681
+ rawType: "gemini/user",
3682
+ preview,
3683
+ blocks: text ? [{ type: "text", text }] : [],
3684
+ messageId: msg.id,
3685
+ raw: msg
3686
+ });
3687
+ continue;
3688
+ }
3689
+ if (msg.type === "gemini") {
3690
+ if (typeof msg.model === "string")
3691
+ model = msg.model;
3692
+ const text = flattenContent(msg.content);
3693
+ const preview = previewOf2(text);
3694
+ if (text)
3695
+ lastAgentPreview = preview;
3696
+ let usage2;
3697
+ if (msg.tokens) {
3698
+ const t = msg.tokens;
3699
+ const u = {
3700
+ input: t.input ?? 0,
3701
+ output: t.output ?? 0,
3702
+ cacheRead: t.cached ?? 0,
3703
+ cacheWrite: 0
3704
+ };
3705
+ usage2 = u;
3706
+ totalUsage.input += u.input;
3707
+ totalUsage.output += u.output;
3708
+ totalUsage.cacheRead += u.cacheRead;
3709
+ }
3710
+ for (const th of msg.thoughts ?? []) {
3711
+ const thText = [th.subject, th.description].filter(Boolean).join(": ");
3712
+ if (!thText)
3713
+ continue;
3714
+ pushEvent({
3715
+ index: idx++,
3716
+ timestamp: th.timestamp ?? ts,
3717
+ role: "agent-thinking",
3718
+ rawType: "gemini/thought",
3719
+ preview: previewOf2(thText),
3720
+ blocks: [{ type: "thinking", thinking: thText }],
3721
+ model,
3722
+ raw: th
3723
+ });
3724
+ }
3725
+ if (text) {
3726
+ pushEvent({
3727
+ index: idx++,
3728
+ timestamp: ts,
3729
+ role: "agent",
3730
+ rawType: "gemini/agent",
3731
+ preview,
3732
+ blocks: [{ type: "text", text }],
3733
+ messageId: msg.id,
3734
+ model,
3735
+ usage: usage2,
3736
+ raw: msg
3737
+ });
3738
+ } else if (usage2) {
3739
+ pushEvent({
3740
+ index: idx++,
3741
+ timestamp: ts,
3742
+ role: "agent",
3743
+ rawType: "gemini/agent",
3744
+ preview: "",
3745
+ blocks: [],
3746
+ messageId: msg.id,
3747
+ model,
3748
+ usage: usage2,
3749
+ raw: msg
3750
+ });
3751
+ }
3752
+ for (const tc of msg.toolCalls ?? []) {
3753
+ toolCallCount += 1;
3754
+ const argsStr = tc.args ? JSON.stringify(tc.args) : "";
3755
+ pushEvent({
3756
+ index: idx++,
3757
+ timestamp: tc.timestamp ?? ts,
3758
+ role: "tool-call",
3759
+ rawType: "gemini/tool_call",
3760
+ preview: `${tc.name}(${truncate3(argsStr, 80)})`,
3761
+ blocks: [{ type: "tool_use", id: tc.id, name: tc.name, input: tc.args ?? {} }],
3762
+ toolName: tc.name,
3763
+ toolUseId: tc.id,
3764
+ model,
3765
+ raw: tc
3766
+ });
3767
+ const status2 = (tc.status ?? "").toLowerCase();
3768
+ const isTerminal = status2 === "success" || status2 === "error" || status2 === "cancelled";
3769
+ if (isTerminal) {
3770
+ const resultText = flattenContent(tc.result);
3771
+ pushEvent({
3772
+ index: idx++,
3773
+ timestamp: tc.timestamp ?? ts,
3774
+ role: "tool-result",
3775
+ rawType: "gemini/tool_result",
3776
+ preview: previewOf2(resultText),
3777
+ blocks: [
3778
+ {
3779
+ type: "tool_result",
3780
+ tool_use_id: tc.id,
3781
+ content: resultText,
3782
+ is_error: status2 === "error" || status2 === "cancelled"
3783
+ }
3784
+ ],
3785
+ toolUseId: tc.id,
3786
+ toolResult: resultText,
3787
+ raw: tc
3788
+ });
3789
+ }
3790
+ }
3791
+ continue;
3792
+ }
3793
+ if (msg.type === "info" || msg.type === "warning" || msg.type === "error") {
3794
+ const text = flattenContent(msg.content);
3795
+ pushEvent({
3796
+ index: idx++,
3797
+ timestamp: ts,
3798
+ role: "meta",
3799
+ rawType: `gemini/${msg.type}`,
3800
+ preview: previewOf2(text),
3801
+ blocks: text ? [{ type: "text", text }] : [],
3802
+ raw: msg
3803
+ });
3804
+ continue;
3805
+ }
3806
+ }
3807
+ const startMs = firstTimestamp ? Date.parse(firstTimestamp) : void 0;
3808
+ if (startMs !== void 0 && Number.isFinite(startMs)) {
3809
+ let prevMs;
3810
+ for (const ev of events) {
3811
+ if (!ev.timestamp)
3812
+ continue;
3813
+ const ms = Date.parse(ev.timestamp);
3814
+ if (!Number.isFinite(ms))
3815
+ continue;
3816
+ ev.tOffsetMs = Math.max(0, ms - startMs);
3817
+ if (prevMs !== void 0)
3818
+ ev.gapMs = Math.max(0, ms - prevMs);
3819
+ prevMs = ms;
3820
+ }
3821
+ }
3822
+ const activeSegments = computeActiveSegments3(tsMs);
3823
+ const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
3824
+ const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3825
+ const sessionId = metaRec.sessionId ?? file.filenameId;
3826
+ const projectName = cwd ? canonicalProjectName(cwd) : file.slug;
3827
+ const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.slug;
3828
+ const meta = {
3829
+ agent: "gemini",
3830
+ id: sessionId,
3831
+ filePath: file.filePath,
3832
+ projectName,
3833
+ projectDir,
3834
+ sessionId,
3835
+ firstTimestamp,
3836
+ lastTimestamp,
3837
+ durationMs: durationMs && Number.isFinite(durationMs) ? durationMs : void 0,
3838
+ eventCount: events.length,
3839
+ model,
3840
+ cwd,
3841
+ totalUsage,
3842
+ status: "idle",
3843
+ firstUserPreview,
3844
+ lastUserPreview,
3845
+ lastAgentPreview,
3846
+ toolCallCount,
3847
+ turnCount,
3848
+ airTimeMs,
3849
+ activeSegments
3850
+ };
3851
+ return { meta, events };
3852
+ }
3853
+ function computeActiveSegments3(tsMs) {
3854
+ if (tsMs.length === 0)
3855
+ return [];
3856
+ const sorted = [...tsMs].sort((a, b) => a - b);
3857
+ const out = [];
3858
+ let segStart = sorted[0];
3859
+ let segEnd = sorted[0];
3860
+ for (let i = 1; i < sorted.length; i++) {
3861
+ if (sorted[i] - segEnd > IDLE_GAP_MS2) {
3862
+ out.push({ startMs: segStart, endMs: segEnd });
3863
+ segStart = sorted[i];
3864
+ }
3865
+ segEnd = sorted[i];
3866
+ }
3867
+ out.push({ startMs: segStart, endMs: segEnd });
3868
+ return out;
3869
+ }
3870
+ function clearGeminiCaches() {
3871
+ metaCache3.clear();
3872
+ detailCache3.clear();
3873
+ projectsCache = null;
3874
+ }
3875
+ async function listGeminiSessions(opts = {}) {
3876
+ const root = opts.root ?? DEFAULT_GEMINI_ROOT;
3877
+ const files = await listSessionFiles(root);
3878
+ const out = [];
3879
+ for (const file of files) {
3880
+ const cached = metaCache3.get(file.filePath);
3881
+ if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes) {
3882
+ out.push(cached.meta);
3883
+ continue;
3884
+ }
3885
+ try {
3886
+ const { meta } = await parseSession(file);
3887
+ metaCache3.set(file.filePath, {
3888
+ meta,
3889
+ mtimeMs: file.mtimeMs,
3890
+ sizeBytes: file.sizeBytes
3891
+ });
3892
+ out.push(meta);
3893
+ } catch {
3894
+ }
3895
+ }
3896
+ out.sort((a, b) => (b.firstTimestamp ?? "").localeCompare(a.firstTimestamp ?? ""));
3897
+ if (opts.limit !== void 0)
3898
+ return out.slice(0, opts.limit);
3899
+ return out;
3900
+ }
3901
+ async function getGeminiSession(id, opts = {}) {
3902
+ const root = opts.root ?? DEFAULT_GEMINI_ROOT;
3903
+ const files = await listSessionFiles(root);
3904
+ let chosen;
3905
+ for (const file of files) {
3906
+ if (file.filenameId === id || id.startsWith(file.filenameId)) {
3907
+ chosen = file;
3908
+ break;
3909
+ }
3910
+ }
3911
+ if (!chosen) {
3912
+ for (const file of files) {
3913
+ const { meta: meta2 } = await parseSession(file).catch(() => ({ meta: { id: "" } }));
3914
+ if (meta2.id === id) {
3915
+ chosen = file;
3916
+ break;
3917
+ }
3918
+ }
3919
+ }
3920
+ if (!chosen)
3921
+ return null;
3922
+ const cached = detailCache3.get(chosen.filePath);
3923
+ if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes) {
3924
+ return cached.detail;
3925
+ }
3926
+ const { meta, events } = await parseSession(chosen);
3927
+ const detail = { ...meta, events };
3928
+ detailCache3.set(chosen.filePath, {
3929
+ detail,
3930
+ mtimeMs: chosen.mtimeMs,
3931
+ sizeBytes: chosen.sizeBytes
3932
+ });
3933
+ return detail;
3934
+ }
3935
+ function geminiSessionLocalDay(meta) {
3936
+ if (!meta.firstTimestamp)
3937
+ return void 0;
3938
+ const ms = Date.parse(meta.firstTimestamp);
3939
+ return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
3940
+ }
3941
+ var DEFAULT_GEMINI_ROOT, GEMINI_PROJECTS_FILE, SESSION_FILE_RE, IDLE_GAP_MS2, projectsCache, metaCache3, detailCache3;
3942
+ var init_gemini = __esm({
3943
+ "../parser/dist/gemini.js"() {
3944
+ "use strict";
3945
+ init_analytics();
3946
+ init_user_input();
3947
+ DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
3948
+ GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
3949
+ SESSION_FILE_RE = /^session-.+\.(jsonl|json)$/;
3950
+ IDLE_GAP_MS2 = 3 * 60 * 1e3;
3951
+ projectsCache = null;
3952
+ metaCache3 = /* @__PURE__ */ new Map();
3953
+ detailCache3 = /* @__PURE__ */ new Map();
3954
+ }
3955
+ });
3956
+
3363
3957
  // ../parser/dist/fs.js
3364
3958
  var fs_exports = {};
3365
3959
  __export(fs_exports, {
3366
3960
  DEFAULT_CODEX_ROOT: () => DEFAULT_CODEX_ROOT,
3961
+ DEFAULT_GEMINI_ROOT: () => DEFAULT_GEMINI_ROOT,
3367
3962
  DEFAULT_ROOT: () => DEFAULT_ROOT,
3368
3963
  agentSources: () => agentSources,
3369
3964
  buildCalibrationCurve: () => buildCalibrationCurve,
@@ -3372,14 +3967,17 @@ __export(fs_exports, {
3372
3967
  codexSessionLocalDay: () => codexSessionLocalDay,
3373
3968
  decodeProjectName: () => decodeProjectName,
3374
3969
  findTeamLead: () => findTeamLead,
3970
+ geminiSessionLocalDay: () => geminiSessionLocalDay,
3375
3971
  getAgentSource: () => getAgentSource,
3376
3972
  getAnySession: () => getAnySession,
3377
3973
  getCodexSession: () => getCodexSession,
3974
+ getGeminiSession: () => getGeminiSession,
3378
3975
  getLatestCodexUsage: () => getLatestCodexUsage,
3379
3976
  getSession: () => getSession,
3380
3977
  invalidateFile: () => invalidateFile,
3381
3978
  listAllSessions: () => listAllSessions,
3382
3979
  listCodexSessions: () => listCodexSessions,
3980
+ listGeminiSessions: () => listGeminiSessions,
3383
3981
  listProjects: () => listProjects,
3384
3982
  listSessions: () => listSessions,
3385
3983
  loadCalibrationCurve: () => loadCalibrationCurve,
@@ -3391,15 +3989,16 @@ __export(fs_exports, {
3391
3989
  sessionIdFromFileName: () => sessionIdFromFileName,
3392
3990
  walkJsonlFiles: () => walkJsonlFiles
3393
3991
  });
3394
- import { promises as fs3 } from "node:fs";
3395
- import os3 from "node:os";
3396
- import path3 from "node:path";
3992
+ import { promises as fs4 } from "node:fs";
3993
+ import os4 from "node:os";
3994
+ import path4 from "node:path";
3397
3995
  function cacheStats() {
3398
3996
  return claudeCodeCacheStats();
3399
3997
  }
3400
3998
  function clearCaches() {
3401
3999
  clearClaudeCodeCaches();
3402
4000
  clearCodexCaches();
4001
+ clearGeminiCaches();
3403
4002
  }
3404
4003
  function invalidateFile(fullPath) {
3405
4004
  invalidateClaudeCodeFile(fullPath);
@@ -3428,7 +4027,7 @@ async function getAnySession(id) {
3428
4027
  async function loadUsageByDay(start2, end) {
3429
4028
  let raw;
3430
4029
  try {
3431
- raw = await fs3.readFile(USAGE_LOG2, "utf8");
4030
+ raw = await fs4.readFile(USAGE_LOG2, "utf8");
3432
4031
  } catch {
3433
4032
  return { by_day: [] };
3434
4033
  }
@@ -3473,7 +4072,7 @@ async function loadUsageByDay(start2, end) {
3473
4072
  async function loadCalibrationSnapshots() {
3474
4073
  let raw;
3475
4074
  try {
3476
- raw = await fs3.readFile(USAGE_LOG2, "utf8");
4075
+ raw = await fs4.readFile(USAGE_LOG2, "utf8");
3477
4076
  } catch {
3478
4077
  return [];
3479
4078
  }
@@ -3631,7 +4230,7 @@ async function loadCalibrationCurve(tier = "pro-max-20x", root = DEFAULT_ROOT, g
3631
4230
  ]);
3632
4231
  return buildCalibrationCurve(events, snapshots, tier, granularityMin);
3633
4232
  }
3634
- var codexSource, agentSources, USAGE_LOG2;
4233
+ var codexSource, geminiSource, agentSources, USAGE_LOG2;
3635
4234
  var init_fs = __esm({
3636
4235
  "../parser/dist/fs.js"() {
3637
4236
  "use strict";
@@ -3640,8 +4239,10 @@ var init_fs = __esm({
3640
4239
  init_calibration();
3641
4240
  init_claude_code();
3642
4241
  init_codex();
4242
+ init_gemini();
3643
4243
  init_claude_code();
3644
4244
  init_codex();
4245
+ init_gemini();
3645
4246
  codexSource = {
3646
4247
  ...CODEX_METADATA,
3647
4248
  defaultRoot: DEFAULT_CODEX_ROOT,
@@ -3663,8 +4264,18 @@ var init_fs = __esm({
3663
4264
  };
3664
4265
  }
3665
4266
  };
3666
- agentSources = [claudeCodeSource, codexSource];
3667
- USAGE_LOG2 = path3.join(os3.homedir(), ".cclens", "usage.jsonl");
4267
+ geminiSource = {
4268
+ ...GEMINI_METADATA,
4269
+ defaultRoot: DEFAULT_GEMINI_ROOT,
4270
+ async listSessions(opts) {
4271
+ return listGeminiSessions(opts);
4272
+ },
4273
+ async getSession(id) {
4274
+ return getGeminiSession(id);
4275
+ }
4276
+ };
4277
+ agentSources = [claudeCodeSource, codexSource, geminiSource];
4278
+ USAGE_LOG2 = path4.join(os4.homedir(), ".cclens", "usage.jsonl");
3668
4279
  }
3669
4280
  });
3670
4281
 
@@ -3986,15 +4597,489 @@ var init_types = __esm({
3986
4597
  }
3987
4598
  });
3988
4599
 
4600
+ // ../parser/dist/types.js
4601
+ var init_types2 = __esm({
4602
+ "../parser/dist/types.js"() {
4603
+ "use strict";
4604
+ }
4605
+ });
4606
+
4607
+ // ../parser/dist/presentation.js
4608
+ function rowPrimaryIndex(r) {
4609
+ if (r.kind === "tool-group")
4610
+ return r.events[0].index;
4611
+ return r.event.index;
4612
+ }
4613
+ function buildMegaRows(rows) {
4614
+ const out = [];
4615
+ let buffer = [];
4616
+ const flush = () => {
4617
+ if (buffer.length === 0)
4618
+ return;
4619
+ let agentMessages = 0;
4620
+ let toolCalls = 0;
4621
+ let errors = 0;
4622
+ let firstAgentIndex;
4623
+ const toolMap = /* @__PURE__ */ new Map();
4624
+ const seenMsgIds = /* @__PURE__ */ new Set();
4625
+ const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
4626
+ for (let i = 0; i < buffer.length; i++) {
4627
+ const r = buffer[i];
4628
+ if (r.kind === "agent") {
4629
+ agentMessages++;
4630
+ if (firstAgentIndex === void 0)
4631
+ firstAgentIndex = i;
4632
+ const msgId = r.event.messageId;
4633
+ const usage2 = r.event.usage;
4634
+ if (msgId && !seenMsgIds.has(msgId) && usage2) {
4635
+ seenMsgIds.add(msgId);
4636
+ tokens.input += usage2.input;
4637
+ tokens.output += usage2.output;
4638
+ tokens.cacheRead += usage2.cacheRead;
4639
+ tokens.cacheWrite += usage2.cacheWrite;
4640
+ }
4641
+ } else if (r.kind === "tool-group") {
4642
+ toolCalls += r.count;
4643
+ for (const t of r.toolNames) {
4644
+ toolMap.set(t.name, (toolMap.get(t.name) ?? 0) + t.count);
4645
+ }
4646
+ } else if (r.kind === "error") {
4647
+ errors++;
4648
+ }
4649
+ }
4650
+ let finalAgentIndex;
4651
+ for (let i = buffer.length - 1; i >= 0; i--) {
4652
+ const r = buffer[i];
4653
+ if (r.kind !== "agent")
4654
+ continue;
4655
+ const prev = buffer[i - 1];
4656
+ if (prev?.kind === "task-notification")
4657
+ continue;
4658
+ finalAgentIndex = i;
4659
+ break;
4660
+ }
4661
+ if (finalAgentIndex === void 0) {
4662
+ for (let i = buffer.length - 1; i >= 0; i--) {
4663
+ if (buffer[i].kind === "agent") {
4664
+ finalAgentIndex = i;
4665
+ break;
4666
+ }
4667
+ }
4668
+ }
4669
+ const firstAgentPreview = firstAgentIndex !== void 0 && buffer[firstAgentIndex].kind === "agent" ? buffer[firstAgentIndex].event.preview : void 0;
4670
+ const finalAgentPreview = finalAgentIndex !== void 0 && buffer[finalAgentIndex].kind === "agent" ? buffer[finalAgentIndex].event.preview : void 0;
4671
+ const toolNames = Array.from(toolMap.entries()).sort((a, b) => b[1] - a[1]).map(([name, count]) => ({ name, count }));
4672
+ const first = buffer[0];
4673
+ const last = buffer[buffer.length - 1];
4674
+ const start2 = first.tOffsetMs;
4675
+ const end = last.tOffsetMs;
4676
+ const durationMs = start2 !== void 0 && end !== void 0 ? end - start2 : void 0;
4677
+ out.push({
4678
+ kind: "turn",
4679
+ rows: buffer,
4680
+ firstPrimaryIndex: rowPrimaryIndex(first),
4681
+ tOffsetMs: start2,
4682
+ durationMs,
4683
+ summary: {
4684
+ agentMessages,
4685
+ toolCalls,
4686
+ errors,
4687
+ firstAgentPreview,
4688
+ finalAgentPreview,
4689
+ firstAgentIndex,
4690
+ finalAgentIndex,
4691
+ toolNames,
4692
+ totalTokens: tokens
4693
+ }
4694
+ });
4695
+ buffer = [];
4696
+ };
4697
+ for (const r of rows) {
4698
+ if (r.kind === "user" || r.kind === "interrupt") {
4699
+ flush();
4700
+ out.push(r);
4701
+ } else {
4702
+ buffer.push(r);
4703
+ }
4704
+ }
4705
+ flush();
4706
+ return out;
4707
+ }
4708
+ function parseTaskNotification(text) {
4709
+ if (!TASK_NOTIFICATION_RE.test(text))
4710
+ return null;
4711
+ const pick = (tag) => {
4712
+ const m = text.match(new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`));
4713
+ return m ? m[1].trim() : void 0;
4714
+ };
4715
+ const statusRaw = (pick("status") ?? "").toLowerCase();
4716
+ let status2 = "unknown";
4717
+ if (statusRaw === "success" || statusRaw === "ok" || statusRaw === "done" || statusRaw === "completed")
4718
+ status2 = "success";
4719
+ else if (statusRaw === "failed" || statusRaw === "error" || statusRaw === "fail")
4720
+ status2 = "failed";
4721
+ else if (statusRaw === "running" || statusRaw === "in_progress")
4722
+ status2 = "running";
4723
+ return {
4724
+ status: status2,
4725
+ summary: pick("summary") ?? "Background task notification",
4726
+ taskId: pick("task-id"),
4727
+ toolUseId: pick("tool-use-id"),
4728
+ outputFile: pick("output-file")
4729
+ };
4730
+ }
4731
+ function isAttachmentOrMeta(e) {
4732
+ return e.role === "system" || e.role === "meta";
4733
+ }
4734
+ function firstTextOfBlocks(blocks) {
4735
+ for (const b of blocks) {
4736
+ if (b?.type === "text")
4737
+ return b.text;
4738
+ }
4739
+ return void 0;
4740
+ }
4741
+ function hasZeroUsage(e) {
4742
+ if (!e.usage)
4743
+ return false;
4744
+ const u = e.usage;
4745
+ return u.input === 0 && u.output === 0 && u.cacheRead === 0 && u.cacheWrite === 0;
4746
+ }
4747
+ function buildPresentation(events) {
4748
+ const byMessageId = /* @__PURE__ */ new Map();
4749
+ for (const e of events) {
4750
+ if (!e.messageId)
4751
+ continue;
4752
+ const list = byMessageId.get(e.messageId) ?? [];
4753
+ list.push(e);
4754
+ byMessageId.set(e.messageId, list);
4755
+ }
4756
+ const visible = events.filter((e) => {
4757
+ if (isAttachmentOrMeta(e))
4758
+ return false;
4759
+ if (e.role === "tool-result")
4760
+ return false;
4761
+ if (e.role === "agent-thinking")
4762
+ return false;
4763
+ if (e.role === "user") {
4764
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
4765
+ if (e.blocks.length === 0 || !txt.trim())
4766
+ return false;
4767
+ if (SLASH_COMMAND_RE.test(txt))
4768
+ return true;
4769
+ if (TASK_NOTIFICATION_RE.test(txt))
4770
+ return true;
4771
+ if (isFrameworkInjectedUserInput(txt))
4772
+ return false;
4773
+ }
4774
+ return true;
4775
+ });
4776
+ const rows = [];
4777
+ for (let i = 0; i < visible.length; i++) {
4778
+ const e = visible[i];
4779
+ if (e.role === "tool-call") {
4780
+ const start2 = i;
4781
+ while (i + 1 < visible.length && visible[i + 1].role === "tool-call") {
4782
+ i++;
4783
+ }
4784
+ const group = visible.slice(start2, i + 1);
4785
+ const order = [];
4786
+ const counts = /* @__PURE__ */ new Map();
4787
+ for (const ev of group) {
4788
+ const name = ev.toolName ?? "tool";
4789
+ if (!counts.has(name))
4790
+ order.push(name);
4791
+ counts.set(name, (counts.get(name) ?? 0) + 1);
4792
+ }
4793
+ const toolNames = order.map((name) => ({
4794
+ name,
4795
+ count: counts.get(name) ?? 1
4796
+ }));
4797
+ rows.push({
4798
+ kind: "tool-group",
4799
+ toolNames,
4800
+ count: group.length,
4801
+ events: group,
4802
+ tOffsetMs: group[0].tOffsetMs,
4803
+ gapMs: group[0].gapMs
4804
+ });
4805
+ continue;
4806
+ }
4807
+ if (e.role === "user") {
4808
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
4809
+ const taskNotif = parseTaskNotification(txt);
4810
+ if (taskNotif) {
4811
+ rows.push({
4812
+ kind: "task-notification",
4813
+ event: e,
4814
+ status: taskNotif.status,
4815
+ summary: taskNotif.summary,
4816
+ taskId: taskNotif.taskId,
4817
+ toolUseId: taskNotif.toolUseId,
4818
+ outputFile: taskNotif.outputFile,
4819
+ tOffsetMs: e.tOffsetMs,
4820
+ gapMs: e.gapMs
4821
+ });
4822
+ continue;
4823
+ }
4824
+ if (INTERRUPT_RE.test(txt)) {
4825
+ rows.push({
4826
+ kind: "interrupt",
4827
+ event: e,
4828
+ tOffsetMs: e.tOffsetMs,
4829
+ gapMs: e.gapMs
4830
+ });
4831
+ continue;
4832
+ }
4833
+ const slashMatch = txt.match(SLASH_COMMAND_RE);
4834
+ if (slashMatch) {
4835
+ const cmd = slashMatch[1];
4836
+ const args2 = (slashMatch[2] ?? "").trim();
4837
+ const pretty = args2 ? `${cmd} ${args2}` : cmd;
4838
+ rows.push({
4839
+ kind: "user",
4840
+ event: e,
4841
+ displayPreview: pretty,
4842
+ displayBlocks: [{ type: "text", text: pretty }],
4843
+ tOffsetMs: e.tOffsetMs,
4844
+ gapMs: e.gapMs
4845
+ });
4846
+ continue;
4847
+ }
4848
+ rows.push({
4849
+ kind: "user",
4850
+ event: e,
4851
+ tOffsetMs: e.tOffsetMs,
4852
+ gapMs: e.gapMs
4853
+ });
4854
+ continue;
4855
+ }
4856
+ if (e.role === "agent") {
4857
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
4858
+ if (hasZeroUsage(e)) {
4859
+ rows.push({
4860
+ kind: "model",
4861
+ event: e,
4862
+ tOffsetMs: e.tOffsetMs,
4863
+ gapMs: e.gapMs
4864
+ });
4865
+ if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
4866
+ rows.push({
4867
+ kind: "error",
4868
+ event: e,
4869
+ message: txt,
4870
+ tOffsetMs: e.tOffsetMs,
4871
+ gapMs: 0
4872
+ });
4873
+ }
4874
+ continue;
4875
+ }
4876
+ if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
4877
+ rows.push({
4878
+ kind: "error",
4879
+ event: e,
4880
+ message: txt,
4881
+ tOffsetMs: e.tOffsetMs,
4882
+ gapMs: e.gapMs
4883
+ });
4884
+ continue;
4885
+ }
4886
+ const grouped = e.messageId ? byMessageId.get(e.messageId) ?? [e] : [e];
4887
+ rows.push({
4888
+ kind: "agent",
4889
+ event: e,
4890
+ groupedEvents: grouped,
4891
+ tOffsetMs: e.tOffsetMs,
4892
+ gapMs: e.gapMs
4893
+ });
4894
+ continue;
4895
+ }
4896
+ }
4897
+ return rows;
4898
+ }
4899
+ var INTERRUPT_RE, RATE_LIMIT_RE, SLASH_COMMAND_RE, TASK_NOTIFICATION_RE;
4900
+ var init_presentation = __esm({
4901
+ "../parser/dist/presentation.js"() {
4902
+ "use strict";
4903
+ init_user_input();
4904
+ INTERRUPT_RE = /\[request interrupted|interrupted by user/i;
4905
+ RATE_LIMIT_RE = /rate.?limit|overloaded_error|api error|Error sending message/i;
4906
+ SLASH_COMMAND_RE = /<command-name>(\/[^<\s]+)<\/command-name>(?:[\s\S]*?<command-args>([\s\S]*?)<\/command-args>)?/;
4907
+ TASK_NOTIFICATION_RE = /<task-notification>/;
4908
+ }
4909
+ });
4910
+
4911
+ // ../parser/dist/aggregate.js
4912
+ function isoDay(d) {
4913
+ return toLocalDay(d.getTime());
4914
+ }
4915
+ function rangeDays(start2, end) {
4916
+ const out = [];
4917
+ const cur = new Date(start2.getFullYear(), start2.getMonth(), start2.getDate());
4918
+ const stop2 = new Date(end.getFullYear(), end.getMonth(), end.getDate());
4919
+ while (cur.getTime() <= stop2.getTime()) {
4920
+ out.push(new Date(cur));
4921
+ cur.setDate(cur.getDate() + 1);
4922
+ }
4923
+ return out;
4924
+ }
4925
+ function calendarWeek(ref = /* @__PURE__ */ new Date()) {
4926
+ const d = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate());
4927
+ const dow = d.getDay();
4928
+ const daysSinceMon = (dow + 6) % 7;
4929
+ const monday = new Date(d);
4930
+ monday.setDate(d.getDate() - daysSinceMon);
4931
+ const sunday = new Date(monday);
4932
+ sunday.setDate(monday.getDate() + 6);
4933
+ return { start: monday, end: sunday };
4934
+ }
4935
+ function priorCalendarWeek(ref = /* @__PURE__ */ new Date()) {
4936
+ const cur = calendarWeek(ref);
4937
+ const prevEnd = new Date(cur.start);
4938
+ prevEnd.setDate(cur.start.getDate() - 1);
4939
+ const prevStart = new Date(prevEnd);
4940
+ prevStart.setDate(prevEnd.getDate() - 6);
4941
+ return { start: prevStart, end: prevEnd };
4942
+ }
4943
+ function last4CompletedWeeks(ref = /* @__PURE__ */ new Date()) {
4944
+ const { end } = priorCalendarWeek(ref);
4945
+ const start2 = new Date(end);
4946
+ start2.setDate(end.getDate() - 27);
4947
+ return { start: start2, end };
4948
+ }
4949
+ function calendarMonth(ref = /* @__PURE__ */ new Date()) {
4950
+ const start2 = new Date(ref.getFullYear(), ref.getMonth(), 1);
4951
+ const end = new Date(ref.getFullYear(), ref.getMonth() + 1, 0);
4952
+ return { start: start2, end };
4953
+ }
4954
+ function priorCalendarMonth(ref = /* @__PURE__ */ new Date()) {
4955
+ const start2 = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
4956
+ const end = new Date(ref.getFullYear(), ref.getMonth(), 0);
4957
+ return { start: start2, end };
4958
+ }
4959
+ function aggregateConcurrency(bursts, period) {
4960
+ const days = rangeDays(period.start, period.end);
4961
+ const byDayKey = /* @__PURE__ */ new Map();
4962
+ for (const d of days)
4963
+ byDayKey.set(isoDay(d), { peak: 0, has_cross_project: false });
4964
+ for (const b of bursts) {
4965
+ const dayKey = isoDay(new Date(b.startMs));
4966
+ const cur = byDayKey.get(dayKey);
4967
+ if (!cur)
4968
+ continue;
4969
+ if (b.peak > cur.peak)
4970
+ cur.peak = b.peak;
4971
+ if (b.crossProject)
4972
+ cur.has_cross_project = true;
4973
+ }
4974
+ const by_day = days.map((d) => {
4975
+ const k = isoDay(d);
4976
+ const v = byDayKey.get(k);
4977
+ return { date: k, peak: v.peak, has_cross_project: v.has_cross_project };
4978
+ });
4979
+ let peak = 0;
4980
+ let peak_day;
4981
+ let multi_agent_days = 0;
4982
+ let cross_project_days = 0;
4983
+ for (const d of by_day) {
4984
+ if (d.peak > peak) {
4985
+ peak = d.peak;
4986
+ peak_day = d.date;
4987
+ }
4988
+ if (d.peak >= 3)
4989
+ multi_agent_days++;
4990
+ if (d.has_cross_project)
4991
+ cross_project_days++;
4992
+ }
4993
+ return { by_day, peak, peak_day, multi_agent_days, cross_project_days };
4994
+ }
4995
+ var init_aggregate = __esm({
4996
+ "../parser/dist/aggregate.js"() {
4997
+ "use strict";
4998
+ init_analytics();
4999
+ }
5000
+ });
5001
+
5002
+ // ../parser/dist/index.js
5003
+ var dist_exports = {};
5004
+ __export(dist_exports, {
5005
+ CLAUDE_CODE_METADATA: () => CLAUDE_CODE_METADATA,
5006
+ CODEX_METADATA: () => CODEX_METADATA,
5007
+ GEMINI_METADATA: () => GEMINI_METADATA,
5008
+ PRICES: () => PRICES,
5009
+ RATE_PER_PCT: () => RATE_PER_PCT,
5010
+ RATE_PER_PCT_5H: () => RATE_PER_PCT_5H,
5011
+ RATE_PER_PCT_7D: () => RATE_PER_PCT_7D,
5012
+ agentMetadata: () => agentMetadata,
5013
+ aggregateConcurrency: () => aggregateConcurrency,
5014
+ buildGanttDay: () => buildGanttDay,
5015
+ buildMegaRows: () => buildMegaRows,
5016
+ buildPresentation: () => buildPresentation,
5017
+ buildSpendIndex: () => buildSpendIndex,
5018
+ calendarMonth: () => calendarMonth,
5019
+ calendarWeek: () => calendarWeek,
5020
+ canonicalProjectName: () => canonicalProjectName,
5021
+ collectSnapPairRates: () => collectSnapPairRates,
5022
+ computeActiveSegments: () => computeActiveSegments,
5023
+ computeBurstsFromSessions: () => computeBurstsFromSessions,
5024
+ computeParallelism: () => computeParallelism,
5025
+ computeParallelismBursts: () => computeParallelismBursts,
5026
+ dailyActivity: () => dailyActivity,
5027
+ daysBetween: () => daysBetween,
5028
+ detectParallelRuns: () => detectParallelRuns,
5029
+ detectPrMarkers: () => detectPrMarkers,
5030
+ dollarsInRange: () => dollarsInRange,
5031
+ dollarsInWindow: () => dollarsInWindow,
5032
+ eventDollars: () => eventDollars,
5033
+ getAgentMetadata: () => getAgentMetadata,
5034
+ groupByProject: () => groupByProject,
5035
+ groupByTeam: () => groupByTeam,
5036
+ groupSnapsByCycle: () => groupSnapsByCycle,
5037
+ highLevelMetrics: () => highLevelMetrics,
5038
+ isAgentKind: () => isAgentKind,
5039
+ isFrameworkInjectedUserInput: () => isFrameworkInjectedUserInput,
5040
+ last4CompletedWeeks: () => last4CompletedWeeks,
5041
+ modelFamily: () => modelFamily,
5042
+ parseTranscript: () => parseTranscript,
5043
+ predictAnchored: () => predictAnchored,
5044
+ predictUtilization: () => predictUtilization,
5045
+ priorCalendarMonth: () => priorCalendarMonth,
5046
+ priorCalendarWeek: () => priorCalendarWeek,
5047
+ rowPrimaryIndex: () => rowPrimaryIndex,
5048
+ sessionAirTimeMs: () => sessionAirTimeMs,
5049
+ sessionDay: () => sessionDay,
5050
+ stripFrameworkBoilerplate: () => stripFrameworkBoilerplate,
5051
+ summarizeBursts: () => summarizeBursts,
5052
+ toLocalDay: () => toLocalDay,
5053
+ userSoloRate: () => userSoloRate,
5054
+ worktreeName: () => worktreeName
5055
+ });
5056
+ var init_dist = __esm({
5057
+ "../parser/dist/index.js"() {
5058
+ "use strict";
5059
+ init_types2();
5060
+ init_parser();
5061
+ init_user_input();
5062
+ init_presentation();
5063
+ init_analytics();
5064
+ init_aggregate();
5065
+ init_calibration();
5066
+ init_agent_metadata();
5067
+ init_team();
5068
+ }
5069
+ });
5070
+
3989
5071
  // ../entries/dist/signals.js
3990
5072
  function classifyUserInputSource(text) {
3991
5073
  if (!text)
3992
5074
  return "human";
3993
- if (TEAMMATE_RE.test(text))
5075
+ const stripped = stripFrameworkBoilerplate(text);
5076
+ if (!stripped)
5077
+ return "system_instruction";
5078
+ if (TEAMMATE_RE.test(stripped))
3994
5079
  return "teammate";
3995
- if (SKILL_LOAD_RE.test(text))
5080
+ if (SKILL_LOAD_RE.test(stripped))
3996
5081
  return "skill_load";
3997
- if (SLASH_COMMAND_RE.test(text))
5082
+ if (SLASH_COMMAND_RE2.test(stripped))
3998
5083
  return "slash_command";
3999
5084
  return "human";
4000
5085
  }
@@ -4135,13 +5220,14 @@ function computeEntrySignals(entry) {
4135
5220
  continuation_kind
4136
5221
  };
4137
5222
  }
4138
- var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
5223
+ var TEAMMATE_RE, SKILL_LOAD_RE, SLASH_COMMAND_RE2, STOCK_SUBAGENT_PREFIXES, STOCK_SKILL_PREFIXES, EXTERNAL_REF_PATTERNS;
4139
5224
  var init_signals = __esm({
4140
5225
  "../entries/dist/signals.js"() {
4141
5226
  "use strict";
5227
+ init_dist();
4142
5228
  TEAMMATE_RE = /^<teammate-message\b/;
4143
5229
  SKILL_LOAD_RE = /^Base directory for this skill:/;
4144
- SLASH_COMMAND_RE = /^<command-name>|^<local-command-stdout>/;
5230
+ SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
4145
5231
  STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
4146
5232
  STOCK_SKILL_PREFIXES = /^(superpowers|mcp__|frontend-design|code-review|codex|claude-code-guide|claude-api):/i;
4147
5233
  EXTERNAL_REF_PATTERNS = [
@@ -4161,7 +5247,7 @@ var init_trivial = __esm({
4161
5247
  });
4162
5248
 
4163
5249
  // ../entries/dist/build.js
4164
- var IDLE_GAP_MS2;
5250
+ var IDLE_GAP_MS3;
4165
5251
  var init_build = __esm({
4166
5252
  "../entries/dist/build.js"() {
4167
5253
  "use strict";
@@ -4169,7 +5255,7 @@ var init_build = __esm({
4169
5255
  init_types();
4170
5256
  init_signals();
4171
5257
  init_trivial();
4172
- IDLE_GAP_MS2 = 3 * 60 * 1e3;
5258
+ IDLE_GAP_MS3 = 3 * 60 * 1e3;
4173
5259
  }
4174
5260
  });
4175
5261
 
@@ -4181,7 +5267,7 @@ var init_flag_glossary = __esm({
4181
5267
  });
4182
5268
 
4183
5269
  // ../entries/dist/index.js
4184
- var init_dist = __esm({
5270
+ var init_dist2 = __esm({
4185
5271
  "../entries/dist/index.js"() {
4186
5272
  "use strict";
4187
5273
  init_types();
@@ -4203,11 +5289,11 @@ function digestsDir() {
4203
5289
  digestsDirCached = envOverride ?? join6(homedir5(), ".cclens", "digests");
4204
5290
  return digestsDirCached;
4205
5291
  }
4206
- function __setDigestsDirForTest(path4) {
4207
- digestsDirCached = path4;
4208
- mkdirSync3(join6(path4, "day"), { recursive: true });
4209
- mkdirSync3(join6(path4, "week"), { recursive: true });
4210
- mkdirSync3(join6(path4, "month"), { recursive: true });
5292
+ function __setDigestsDirForTest(path5) {
5293
+ digestsDirCached = path5;
5294
+ mkdirSync3(join6(path5, "day"), { recursive: true });
5295
+ mkdirSync3(join6(path5, "week"), { recursive: true });
5296
+ mkdirSync3(join6(path5, "month"), { recursive: true });
4211
5297
  }
4212
5298
  function dayDigestPath(date) {
4213
5299
  return join6(digestsDir(), "day", `${date}.json`);
@@ -4394,9 +5480,9 @@ function entriesDir() {
4394
5480
  entriesDirCached = envOverride ?? join7(homedir6(), ".cclens", "entries");
4395
5481
  return entriesDirCached;
4396
5482
  }
4397
- function __setEntriesDirForTest(path4) {
4398
- entriesDirCached = path4;
4399
- mkdirSync4(path4, { recursive: true });
5483
+ function __setEntriesDirForTest(path5) {
5484
+ entriesDirCached = path5;
5485
+ mkdirSync4(path5, { recursive: true });
4400
5486
  }
4401
5487
  function pathFor(sessionId, localDay) {
4402
5488
  return join7(entriesDir(), `${entryKey(sessionId, localDay)}.json`);
@@ -4931,8 +6017,8 @@ var init_parseUtil = __esm({
4931
6017
  init_errors();
4932
6018
  init_en();
4933
6019
  makeIssue = (params) => {
4934
- const { data, path: path4, errorMaps, issueData } = params;
4935
- const fullPath = [...path4, ...issueData.path || []];
6020
+ const { data, path: path5, errorMaps, issueData } = params;
6021
+ const fullPath = [...path5, ...issueData.path || []];
4936
6022
  const fullIssue = {
4937
6023
  ...issueData,
4938
6024
  path: fullPath
@@ -5232,7 +6318,7 @@ function custom(check, _params = {}, fatal) {
5232
6318
  return ZodAny.create();
5233
6319
  }
5234
6320
  var ParseInputLazyPath, handleResult, ZodType, cuidRegex, cuid2Regex, ulidRegex, uuidRegex, nanoidRegex, jwtRegex, durationRegex, emailRegex, _emojiRegex, emojiRegex, ipv4Regex, ipv4CidrRegex, ipv6Regex, ipv6CidrRegex, base64Regex, base64urlRegex, dateRegexSource, dateRegex, ZodString, ZodNumber, ZodBigInt, ZodBoolean, ZodDate, ZodSymbol, ZodUndefined, ZodNull, ZodAny, ZodUnknown, ZodNever, ZodVoid, ZodArray, ZodObject, ZodUnion, getDiscriminator, ZodDiscriminatedUnion, ZodIntersection, ZodTuple, ZodRecord, ZodMap, ZodSet, ZodFunction, ZodLazy, ZodLiteral, ZodEnum, ZodNativeEnum, ZodPromise, ZodEffects, ZodOptional, ZodNullable, ZodDefault, ZodCatch, ZodNaN, BRAND, ZodBranded, ZodPipeline, ZodReadonly, late, ZodFirstPartyTypeKind, instanceOfType, stringType, numberType, nanType, bigIntType, booleanType, dateType, symbolType, undefinedType, nullType, anyType, unknownType, neverType, voidType, arrayType, objectType, strictObjectType, unionType, discriminatedUnionType, intersectionType, tupleType, recordType, mapType, setType, functionType, lazyType, literalType, enumType, nativeEnumType, promiseType, effectsType, optionalType, nullableType, preprocessType, pipelineType, ostring, onumber, oboolean, coerce, NEVER;
5235
- var init_types2 = __esm({
6321
+ var init_types3 = __esm({
5236
6322
  "../../node_modules/zod/v3/types.js"() {
5237
6323
  init_ZodError();
5238
6324
  init_errors();
@@ -5240,11 +6326,11 @@ var init_types2 = __esm({
5240
6326
  init_parseUtil();
5241
6327
  init_util();
5242
6328
  ParseInputLazyPath = class {
5243
- constructor(parent, value, path4, key) {
6329
+ constructor(parent, value, path5, key) {
5244
6330
  this._cachedPath = [];
5245
6331
  this.parent = parent;
5246
6332
  this.data = value;
5247
- this._path = path4;
6333
+ this._path = path5;
5248
6334
  this._key = key;
5249
6335
  }
5250
6336
  get path() {
@@ -8612,7 +9698,7 @@ var init_external = __esm({
8612
9698
  init_parseUtil();
8613
9699
  init_typeAliases();
8614
9700
  init_util();
8615
- init_types2();
9701
+ init_types3();
8616
9702
  init_ZodError();
8617
9703
  }
8618
9704
  });
@@ -8765,15 +9851,15 @@ function newRunId(kind) {
8765
9851
  function tracePath(runId) {
8766
9852
  return join8(RUNS_DIR, `${runId}.jsonl`);
8767
9853
  }
8768
- function safeAppend(path4, line) {
9854
+ function safeAppend(path5, line) {
8769
9855
  try {
8770
- appendFileSync2(path4, line);
9856
+ appendFileSync2(path5, line);
8771
9857
  } catch {
8772
9858
  }
8773
9859
  }
8774
- function safeWrite(path4, line) {
9860
+ function safeWrite(path5, line) {
8775
9861
  try {
8776
- writeFileSync4(path4, line);
9862
+ writeFileSync4(path5, line);
8777
9863
  } catch {
8778
9864
  }
8779
9865
  }
@@ -9068,783 +10154,322 @@ function applyEnrichmentSuccess(entry, resp, model, costUsd, generatedAt) {
9068
10154
  };
9069
10155
  return { ...entry, enrichment };
9070
10156
  }
9071
- function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt, rateLimited = false) {
9072
- if (rateLimited) {
9073
- const enrichment2 = {
9074
- ...entry.enrichment,
9075
- status: "pending",
9076
- generated_at: generatedAt,
9077
- model,
9078
- cost_usd: costUsd,
9079
- error: errorMessage
9080
- // retry_count NOT incremented on rate-limit.
9081
- };
9082
- return { ...entry, enrichment: enrichment2 };
9083
- }
9084
- const enrichment = {
9085
- ...entry.enrichment,
9086
- status: "error",
9087
- generated_at: generatedAt,
9088
- model,
9089
- cost_usd: costUsd,
9090
- error: errorMessage,
9091
- retry_count: (entry.enrichment.retry_count ?? 0) + 1
9092
- };
9093
- return { ...entry, enrichment };
9094
- }
9095
- var DEFAULT_MODEL;
9096
- var init_enrich2 = __esm({
9097
- "../entries/dist/enrich.js"() {
9098
- "use strict";
9099
- init_signals();
9100
- init_enrich();
9101
- init_llm_runner();
9102
- DEFAULT_MODEL = "sonnet";
9103
- }
9104
- });
9105
-
9106
- // ../entries/dist/budget.js
9107
- import { appendFileSync as appendFileSync3, readFileSync as readFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "node:fs";
9108
- import { dirname as dirname7, join as join9 } from "node:path";
9109
- import { homedir as homedir8 } from "node:os";
9110
- function spendPath() {
9111
- if (spendPathCached)
9112
- return spendPathCached;
9113
- spendPathCached = join9(homedir8(), ".cclens", "llm-spend.jsonl");
9114
- return spendPathCached;
9115
- }
9116
- function __setSpendPathForTest(path4) {
9117
- spendPathCached = path4;
9118
- }
9119
- function appendSpend(record) {
9120
- const p = spendPath();
9121
- mkdirSync6(dirname7(p), { recursive: true });
9122
- appendFileSync3(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
9123
- }
9124
- function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
9125
- const p = spendPath();
9126
- if (!existsSync8(p))
9127
- return 0;
9128
- const year = now.getUTCFullYear();
9129
- const month2 = now.getUTCMonth();
9130
- let total = 0;
9131
- const raw = readFileSync8(p, "utf8");
9132
- for (const line of raw.split("\n")) {
9133
- if (!line)
9134
- continue;
9135
- let rec;
9136
- try {
9137
- rec = JSON.parse(line);
9138
- } catch {
9139
- continue;
9140
- }
9141
- const ts = new Date(rec.ts);
9142
- if (Number.isNaN(ts.getTime()))
9143
- continue;
9144
- if (ts.getUTCFullYear() !== year || ts.getUTCMonth() !== month2)
9145
- continue;
9146
- total += rec.cost_usd;
9147
- }
9148
- return total;
9149
- }
9150
- var spendPathCached;
9151
- var init_budget = __esm({
9152
- "../entries/dist/budget.js"() {
9153
- "use strict";
9154
- spendPathCached = null;
9155
- }
9156
- });
9157
-
9158
- // ../entries/dist/pipeline-lock.js
9159
- import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
9160
- import { join as join10 } from "node:path";
9161
- import { homedir as homedir9 } from "node:os";
9162
- function lockPath() {
9163
- if (lockPathCached)
9164
- return lockPathCached;
9165
- lockPathCached = join10(homedir9(), ".cclens", "llm-interactive.lock");
9166
- return lockPathCached;
9167
- }
9168
- function __setInteractiveLockPathForTest(path4) {
9169
- lockPathCached = path4;
9170
- }
9171
- function touchLock() {
9172
- writeFileSync5(lockPath(), String(process.pid), "utf8");
9173
- }
9174
- function writeInteractiveLock() {
9175
- touchLock();
9176
- if (heartbeat)
9177
- clearInterval(heartbeat);
9178
- heartbeat = setInterval(() => {
9179
- try {
9180
- touchLock();
9181
- } catch {
9182
- }
9183
- }, HEARTBEAT_MS);
9184
- heartbeat.unref?.();
9185
- }
9186
- function removeInteractiveLock() {
9187
- if (heartbeat) {
9188
- clearInterval(heartbeat);
9189
- heartbeat = null;
9190
- }
9191
- try {
9192
- unlinkSync2(lockPath());
9193
- } catch {
9194
- }
9195
- }
9196
- function pidAlive(pid) {
9197
- try {
9198
- process.kill(pid, 0);
9199
- return true;
9200
- } catch {
9201
- return false;
9202
- }
9203
- }
9204
- function interactiveLockFresh(nowMs) {
9205
- const p = lockPath();
9206
- if (!existsSync9(p))
9207
- return false;
9208
- try {
9209
- const mtime = statSync2(p).mtimeMs;
9210
- if (nowMs - mtime > STALE_MS)
9211
- return false;
9212
- const pid = Number(readFileSync9(p, "utf8").trim());
9213
- if (!Number.isFinite(pid) || pid <= 0)
9214
- return false;
9215
- if (!pidAlive(pid))
9216
- return false;
9217
- return true;
9218
- } catch {
9219
- return false;
9220
- }
9221
- }
9222
- var STALE_MS, HEARTBEAT_MS, lockPathCached, heartbeat;
9223
- var init_pipeline_lock = __esm({
9224
- "../entries/dist/pipeline-lock.js"() {
9225
- "use strict";
9226
- STALE_MS = 60 * 1e3;
9227
- HEARTBEAT_MS = 30 * 1e3;
9228
- lockPathCached = null;
9229
- heartbeat = null;
9230
- }
9231
- });
9232
-
9233
- // ../entries/dist/queue.js
9234
- function toLocalDay2(ms) {
9235
- const d = new Date(ms);
9236
- const y = d.getFullYear();
9237
- const m = String(d.getMonth() + 1).padStart(2, "0");
9238
- const day2 = String(d.getDate()).padStart(2, "0");
9239
- return `${y}-${m}-${day2}`;
9240
- }
9241
- async function runEnrichmentQueue(settings, opts = {}) {
9242
- if (!settings.enabled)
9243
- return { skipped: "disabled" };
9244
- const now = opts.now ?? (() => Date.now());
9245
- if (interactiveLockFresh(now()))
9246
- return { skipped: "interactive_in_progress" };
9247
- const budget = settings.monthlyBudgetUsd ?? Infinity;
9248
- if (monthToDateSpend() >= budget)
9249
- return { skipped: "budget_cap_reached" };
9250
- const queue = listEntriesWithStatus(["pending", "error"]).filter((e) => (e.enrichment.retry_count ?? 0) < MAX_RETRY_COUNT);
9251
- const todayLocal = toLocalDay2(now());
9252
- let enriched = 0, errors = 0, skipped = 0;
9253
- for (const entry of queue) {
9254
- if (entry.local_day === todayLocal) {
9255
- skipped++;
9256
- continue;
9257
- }
9258
- const endMs = Date.parse(entry.end_iso);
9259
- if (!Number.isNaN(endMs) && now() - endMs < THIRTY_MIN_MS) {
9260
- skipped++;
9261
- continue;
9262
- }
9263
- if (monthToDateSpend() >= budget)
9264
- break;
9265
- try {
9266
- const { entry: result, usage: usage2 } = await enrichEntry(entry, {
9267
- model: settings.model,
9268
- callLLM: opts.callLLM
9269
- });
9270
- writeEntry(result);
9271
- if (result.enrichment.status === "done") {
9272
- enriched++;
9273
- appendSpend({
9274
- ts: (/* @__PURE__ */ new Date()).toISOString(),
9275
- caller: "daemon",
9276
- model: result.enrichment.model ?? settings.model,
9277
- input_tokens: usage2?.input_tokens ?? 0,
9278
- output_tokens: usage2?.output_tokens ?? 0,
9279
- cost_usd: result.enrichment.cost_usd ?? 0,
9280
- kind: "entry_enrich",
9281
- ref: `${result.session_id}__${result.local_day}`
9282
- });
9283
- } else {
9284
- errors++;
9285
- }
9286
- } catch (err) {
9287
- errors++;
9288
- const failed = {
9289
- ...entry,
9290
- enrichment: {
9291
- ...entry.enrichment,
9292
- status: "error",
9293
- retry_count: (entry.enrichment.retry_count ?? 0) + 1,
9294
- error: err.message,
9295
- generated_at: (/* @__PURE__ */ new Date()).toISOString()
9296
- }
9297
- };
9298
- writeEntry(failed);
9299
- }
10157
+ function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt, rateLimited = false) {
10158
+ if (rateLimited) {
10159
+ const enrichment2 = {
10160
+ ...entry.enrichment,
10161
+ status: "pending",
10162
+ generated_at: generatedAt,
10163
+ model,
10164
+ cost_usd: costUsd,
10165
+ error: errorMessage
10166
+ // retry_count NOT incremented on rate-limit.
10167
+ };
10168
+ return { ...entry, enrichment: enrichment2 };
9300
10169
  }
9301
- return { enriched, errors, skipped };
10170
+ const enrichment = {
10171
+ ...entry.enrichment,
10172
+ status: "error",
10173
+ generated_at: generatedAt,
10174
+ model,
10175
+ cost_usd: costUsd,
10176
+ error: errorMessage,
10177
+ retry_count: (entry.enrichment.retry_count ?? 0) + 1
10178
+ };
10179
+ return { ...entry, enrichment };
9302
10180
  }
9303
- var THIRTY_MIN_MS, MAX_RETRY_COUNT;
9304
- var init_queue = __esm({
9305
- "../entries/dist/queue.js"() {
10181
+ var DEFAULT_MODEL;
10182
+ var init_enrich2 = __esm({
10183
+ "../entries/dist/enrich.js"() {
9306
10184
  "use strict";
9307
- init_fs2();
9308
- init_enrich2();
9309
- init_budget();
9310
- init_pipeline_lock();
9311
- THIRTY_MIN_MS = 30 * 60 * 1e3;
9312
- MAX_RETRY_COUNT = 3;
10185
+ init_signals();
10186
+ init_enrich();
10187
+ init_llm_runner();
10188
+ DEFAULT_MODEL = "sonnet";
9313
10189
  }
9314
10190
  });
9315
10191
 
9316
- // ../entries/dist/settings.js
9317
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, renameSync as renameSync3, chmodSync as chmodSync2, mkdirSync as mkdirSync7, existsSync as existsSync10 } from "node:fs";
9318
- import { dirname as dirname8, join as join11 } from "node:path";
9319
- import { homedir as homedir10 } from "node:os";
9320
- function settingsPath() {
9321
- if (settingsPathCached)
9322
- return settingsPathCached;
9323
- settingsPathCached = join11(homedir10(), ".cclens", "settings.json");
9324
- return settingsPathCached;
10192
+ // ../entries/dist/budget.js
10193
+ import { appendFileSync as appendFileSync3, readFileSync as readFileSync8, existsSync as existsSync8, mkdirSync as mkdirSync6 } from "node:fs";
10194
+ import { dirname as dirname7, join as join9 } from "node:path";
10195
+ import { homedir as homedir8 } from "node:os";
10196
+ function spendPath() {
10197
+ if (spendPathCached)
10198
+ return spendPathCached;
10199
+ spendPathCached = join9(homedir8(), ".cclens", "llm-spend.jsonl");
10200
+ return spendPathCached;
9325
10201
  }
9326
- function __setSettingsPathForTest(path4) {
9327
- settingsPathCached = path4;
10202
+ function __setSpendPathForTest(path5) {
10203
+ spendPathCached = path5;
9328
10204
  }
9329
- function toDisk(s) {
9330
- return {
9331
- ai_features: {
9332
- enabled: s.ai_features.enabled,
9333
- model: s.ai_features.model,
9334
- monthly_budget_usd: s.ai_features.monthlyBudgetUsd,
9335
- auto_backfill_last_week: s.ai_features.autoBackfillLastWeek,
9336
- auto_backfill_yesterday: s.ai_features.autoBackfillYesterday
9337
- }
9338
- };
10205
+ function appendSpend(record) {
10206
+ const p = spendPath();
10207
+ mkdirSync6(dirname7(p), { recursive: true });
10208
+ appendFileSync3(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
9339
10209
  }
9340
- function fromDisk(d) {
9341
- const af = d.ai_features ?? {};
9342
- return {
9343
- ai_features: {
9344
- enabled: af.enabled ?? DEFAULT_SETTINGS.ai_features.enabled,
9345
- model: af.model ?? DEFAULT_SETTINGS.ai_features.model,
9346
- monthlyBudgetUsd: af.monthly_budget_usd ?? null,
9347
- autoBackfillLastWeek: af.auto_backfill_last_week ?? DEFAULT_SETTINGS.ai_features.autoBackfillLastWeek,
9348
- autoBackfillYesterday: af.auto_backfill_yesterday ?? DEFAULT_SETTINGS.ai_features.autoBackfillYesterday
10210
+ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
10211
+ const p = spendPath();
10212
+ if (!existsSync8(p))
10213
+ return 0;
10214
+ const year = now.getUTCFullYear();
10215
+ const month2 = now.getUTCMonth();
10216
+ let total = 0;
10217
+ const raw = readFileSync8(p, "utf8");
10218
+ for (const line of raw.split("\n")) {
10219
+ if (!line)
10220
+ continue;
10221
+ let rec;
10222
+ try {
10223
+ rec = JSON.parse(line);
10224
+ } catch {
10225
+ continue;
9349
10226
  }
9350
- };
9351
- }
9352
- function readSettings() {
9353
- const p = settingsPath();
9354
- if (!existsSync10(p))
9355
- return DEFAULT_SETTINGS;
9356
- try {
9357
- const raw = readFileSync10(p, "utf8");
9358
- return fromDisk(JSON.parse(raw));
9359
- } catch {
9360
- return DEFAULT_SETTINGS;
9361
- }
9362
- }
9363
- function writeSettings(s) {
9364
- const p = settingsPath();
9365
- mkdirSync7(dirname8(p), { recursive: true });
9366
- const tmp = `${p}.tmp`;
9367
- writeFileSync6(tmp, JSON.stringify(toDisk(s), null, 2), { encoding: "utf8" });
9368
- if (process.platform !== "win32") {
9369
- chmodSync2(tmp, 384);
10227
+ const ts = new Date(rec.ts);
10228
+ if (Number.isNaN(ts.getTime()))
10229
+ continue;
10230
+ if (ts.getUTCFullYear() !== year || ts.getUTCMonth() !== month2)
10231
+ continue;
10232
+ total += rec.cost_usd;
9370
10233
  }
9371
- renameSync3(tmp, p);
10234
+ return total;
9372
10235
  }
9373
- var DEFAULT_SETTINGS, settingsPathCached;
9374
- var init_settings = __esm({
9375
- "../entries/dist/settings.js"() {
9376
- "use strict";
9377
- DEFAULT_SETTINGS = {
9378
- ai_features: {
9379
- enabled: true,
9380
- model: "sonnet",
9381
- monthlyBudgetUsd: null,
9382
- autoBackfillLastWeek: true,
9383
- autoBackfillYesterday: true
9384
- }
9385
- };
9386
- settingsPathCached = null;
9387
- }
9388
- });
9389
-
9390
- // ../parser/dist/types.js
9391
- var init_types3 = __esm({
9392
- "../parser/dist/types.js"() {
10236
+ var spendPathCached;
10237
+ var init_budget = __esm({
10238
+ "../entries/dist/budget.js"() {
9393
10239
  "use strict";
10240
+ spendPathCached = null;
9394
10241
  }
9395
10242
  });
9396
10243
 
9397
- // ../parser/dist/presentation.js
9398
- function rowPrimaryIndex(r) {
9399
- if (r.kind === "tool-group")
9400
- return r.events[0].index;
9401
- return r.event.index;
10244
+ // ../entries/dist/pipeline-lock.js
10245
+ import { writeFileSync as writeFileSync5, unlinkSync as unlinkSync2, existsSync as existsSync9, readFileSync as readFileSync9, statSync as statSync2 } from "node:fs";
10246
+ import { join as join10 } from "node:path";
10247
+ import { homedir as homedir9 } from "node:os";
10248
+ function lockPath() {
10249
+ if (lockPathCached)
10250
+ return lockPathCached;
10251
+ lockPathCached = join10(homedir9(), ".cclens", "llm-interactive.lock");
10252
+ return lockPathCached;
9402
10253
  }
9403
- function buildMegaRows(rows) {
9404
- const out = [];
9405
- let buffer = [];
9406
- const flush = () => {
9407
- if (buffer.length === 0)
9408
- return;
9409
- let agentMessages = 0;
9410
- let toolCalls = 0;
9411
- let errors = 0;
9412
- let firstAgentIndex;
9413
- const toolMap = /* @__PURE__ */ new Map();
9414
- const seenMsgIds = /* @__PURE__ */ new Set();
9415
- const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
9416
- for (let i = 0; i < buffer.length; i++) {
9417
- const r = buffer[i];
9418
- if (r.kind === "agent") {
9419
- agentMessages++;
9420
- if (firstAgentIndex === void 0)
9421
- firstAgentIndex = i;
9422
- const msgId = r.event.messageId;
9423
- const usage2 = r.event.usage;
9424
- if (msgId && !seenMsgIds.has(msgId) && usage2) {
9425
- seenMsgIds.add(msgId);
9426
- tokens.input += usage2.input;
9427
- tokens.output += usage2.output;
9428
- tokens.cacheRead += usage2.cacheRead;
9429
- tokens.cacheWrite += usage2.cacheWrite;
9430
- }
9431
- } else if (r.kind === "tool-group") {
9432
- toolCalls += r.count;
9433
- for (const t of r.toolNames) {
9434
- toolMap.set(t.name, (toolMap.get(t.name) ?? 0) + t.count);
9435
- }
9436
- } else if (r.kind === "error") {
9437
- errors++;
9438
- }
9439
- }
9440
- let finalAgentIndex;
9441
- for (let i = buffer.length - 1; i >= 0; i--) {
9442
- const r = buffer[i];
9443
- if (r.kind !== "agent")
9444
- continue;
9445
- const prev = buffer[i - 1];
9446
- if (prev?.kind === "task-notification")
9447
- continue;
9448
- finalAgentIndex = i;
9449
- break;
9450
- }
9451
- if (finalAgentIndex === void 0) {
9452
- for (let i = buffer.length - 1; i >= 0; i--) {
9453
- if (buffer[i].kind === "agent") {
9454
- finalAgentIndex = i;
9455
- break;
9456
- }
9457
- }
9458
- }
9459
- const firstAgentPreview = firstAgentIndex !== void 0 && buffer[firstAgentIndex].kind === "agent" ? buffer[firstAgentIndex].event.preview : void 0;
9460
- const finalAgentPreview = finalAgentIndex !== void 0 && buffer[finalAgentIndex].kind === "agent" ? buffer[finalAgentIndex].event.preview : void 0;
9461
- const toolNames = Array.from(toolMap.entries()).sort((a, b) => b[1] - a[1]).map(([name, count]) => ({ name, count }));
9462
- const first = buffer[0];
9463
- const last = buffer[buffer.length - 1];
9464
- const start2 = first.tOffsetMs;
9465
- const end = last.tOffsetMs;
9466
- const durationMs = start2 !== void 0 && end !== void 0 ? end - start2 : void 0;
9467
- out.push({
9468
- kind: "turn",
9469
- rows: buffer,
9470
- firstPrimaryIndex: rowPrimaryIndex(first),
9471
- tOffsetMs: start2,
9472
- durationMs,
9473
- summary: {
9474
- agentMessages,
9475
- toolCalls,
9476
- errors,
9477
- firstAgentPreview,
9478
- finalAgentPreview,
9479
- firstAgentIndex,
9480
- finalAgentIndex,
9481
- toolNames,
9482
- totalTokens: tokens
9483
- }
9484
- });
9485
- buffer = [];
9486
- };
9487
- for (const r of rows) {
9488
- if (r.kind === "user" || r.kind === "interrupt") {
9489
- flush();
9490
- out.push(r);
9491
- } else {
9492
- buffer.push(r);
9493
- }
9494
- }
9495
- flush();
9496
- return out;
10254
+ function __setInteractiveLockPathForTest(path5) {
10255
+ lockPathCached = path5;
9497
10256
  }
9498
- function parseTaskNotification(text) {
9499
- if (!TASK_NOTIFICATION_RE.test(text))
9500
- return null;
9501
- const pick = (tag) => {
9502
- const m = text.match(new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`));
9503
- return m ? m[1].trim() : void 0;
9504
- };
9505
- const statusRaw = (pick("status") ?? "").toLowerCase();
9506
- let status2 = "unknown";
9507
- if (statusRaw === "success" || statusRaw === "ok" || statusRaw === "done" || statusRaw === "completed")
9508
- status2 = "success";
9509
- else if (statusRaw === "failed" || statusRaw === "error" || statusRaw === "fail")
9510
- status2 = "failed";
9511
- else if (statusRaw === "running" || statusRaw === "in_progress")
9512
- status2 = "running";
9513
- return {
9514
- status: status2,
9515
- summary: pick("summary") ?? "Background task notification",
9516
- taskId: pick("task-id"),
9517
- toolUseId: pick("tool-use-id"),
9518
- outputFile: pick("output-file")
9519
- };
10257
+ function touchLock() {
10258
+ writeFileSync5(lockPath(), String(process.pid), "utf8");
9520
10259
  }
9521
- function isAttachmentOrMeta(e) {
9522
- return e.role === "system" || e.role === "meta";
10260
+ function writeInteractiveLock() {
10261
+ touchLock();
10262
+ if (heartbeat)
10263
+ clearInterval(heartbeat);
10264
+ heartbeat = setInterval(() => {
10265
+ try {
10266
+ touchLock();
10267
+ } catch {
10268
+ }
10269
+ }, HEARTBEAT_MS);
10270
+ heartbeat.unref?.();
9523
10271
  }
9524
- function firstTextOfBlocks(blocks) {
9525
- for (const b of blocks) {
9526
- if (b?.type === "text")
9527
- return b.text;
10272
+ function removeInteractiveLock() {
10273
+ if (heartbeat) {
10274
+ clearInterval(heartbeat);
10275
+ heartbeat = null;
10276
+ }
10277
+ try {
10278
+ unlinkSync2(lockPath());
10279
+ } catch {
9528
10280
  }
9529
- return void 0;
9530
10281
  }
9531
- function hasZeroUsage(e) {
9532
- if (!e.usage)
10282
+ function pidAlive(pid) {
10283
+ try {
10284
+ process.kill(pid, 0);
10285
+ return true;
10286
+ } catch {
9533
10287
  return false;
9534
- const u = e.usage;
9535
- return u.input === 0 && u.output === 0 && u.cacheRead === 0 && u.cacheWrite === 0;
9536
- }
9537
- function buildPresentation(events) {
9538
- const byMessageId = /* @__PURE__ */ new Map();
9539
- for (const e of events) {
9540
- if (!e.messageId)
9541
- continue;
9542
- const list = byMessageId.get(e.messageId) ?? [];
9543
- list.push(e);
9544
- byMessageId.set(e.messageId, list);
9545
10288
  }
9546
- const visible = events.filter((e) => {
9547
- if (isAttachmentOrMeta(e))
10289
+ }
10290
+ function interactiveLockFresh(nowMs) {
10291
+ const p = lockPath();
10292
+ if (!existsSync9(p))
10293
+ return false;
10294
+ try {
10295
+ const mtime = statSync2(p).mtimeMs;
10296
+ if (nowMs - mtime > STALE_MS)
9548
10297
  return false;
9549
- if (e.role === "tool-result")
10298
+ const pid = Number(readFileSync9(p, "utf8").trim());
10299
+ if (!Number.isFinite(pid) || pid <= 0)
9550
10300
  return false;
9551
- if (e.role === "agent-thinking")
10301
+ if (!pidAlive(pid))
9552
10302
  return false;
9553
- if (e.role === "user") {
9554
- const txt = firstTextOfBlocks(e.blocks) ?? "";
9555
- if (SKILL_CONTENT_RE.test(txt))
9556
- return false;
9557
- }
9558
10303
  return true;
9559
- });
9560
- const rows = [];
9561
- for (let i = 0; i < visible.length; i++) {
9562
- const e = visible[i];
9563
- if (e.role === "tool-call") {
9564
- const start2 = i;
9565
- while (i + 1 < visible.length && visible[i + 1].role === "tool-call") {
9566
- i++;
9567
- }
9568
- const group = visible.slice(start2, i + 1);
9569
- const order = [];
9570
- const counts = /* @__PURE__ */ new Map();
9571
- for (const ev of group) {
9572
- const name = ev.toolName ?? "tool";
9573
- if (!counts.has(name))
9574
- order.push(name);
9575
- counts.set(name, (counts.get(name) ?? 0) + 1);
9576
- }
9577
- const toolNames = order.map((name) => ({
9578
- name,
9579
- count: counts.get(name) ?? 1
9580
- }));
9581
- rows.push({
9582
- kind: "tool-group",
9583
- toolNames,
9584
- count: group.length,
9585
- events: group,
9586
- tOffsetMs: group[0].tOffsetMs,
9587
- gapMs: group[0].gapMs
9588
- });
10304
+ } catch {
10305
+ return false;
10306
+ }
10307
+ }
10308
+ var STALE_MS, HEARTBEAT_MS, lockPathCached, heartbeat;
10309
+ var init_pipeline_lock = __esm({
10310
+ "../entries/dist/pipeline-lock.js"() {
10311
+ "use strict";
10312
+ STALE_MS = 60 * 1e3;
10313
+ HEARTBEAT_MS = 30 * 1e3;
10314
+ lockPathCached = null;
10315
+ heartbeat = null;
10316
+ }
10317
+ });
10318
+
10319
+ // ../entries/dist/queue.js
10320
+ function toLocalDay2(ms) {
10321
+ const d = new Date(ms);
10322
+ const y = d.getFullYear();
10323
+ const m = String(d.getMonth() + 1).padStart(2, "0");
10324
+ const day2 = String(d.getDate()).padStart(2, "0");
10325
+ return `${y}-${m}-${day2}`;
10326
+ }
10327
+ async function runEnrichmentQueue(settings, opts = {}) {
10328
+ if (!settings.enabled)
10329
+ return { skipped: "disabled" };
10330
+ const now = opts.now ?? (() => Date.now());
10331
+ if (interactiveLockFresh(now()))
10332
+ return { skipped: "interactive_in_progress" };
10333
+ const budget = settings.monthlyBudgetUsd ?? Infinity;
10334
+ if (monthToDateSpend() >= budget)
10335
+ return { skipped: "budget_cap_reached" };
10336
+ const queue = listEntriesWithStatus(["pending", "error"]).filter((e) => (e.enrichment.retry_count ?? 0) < MAX_RETRY_COUNT);
10337
+ const todayLocal = toLocalDay2(now());
10338
+ let enriched = 0, errors = 0, skipped = 0;
10339
+ for (const entry of queue) {
10340
+ if (entry.local_day === todayLocal) {
10341
+ skipped++;
9589
10342
  continue;
9590
10343
  }
9591
- if (e.role === "user") {
9592
- const txt = firstTextOfBlocks(e.blocks) ?? "";
9593
- const taskNotif = parseTaskNotification(txt);
9594
- if (taskNotif) {
9595
- rows.push({
9596
- kind: "task-notification",
9597
- event: e,
9598
- status: taskNotif.status,
9599
- summary: taskNotif.summary,
9600
- taskId: taskNotif.taskId,
9601
- toolUseId: taskNotif.toolUseId,
9602
- outputFile: taskNotif.outputFile,
9603
- tOffsetMs: e.tOffsetMs,
9604
- gapMs: e.gapMs
9605
- });
9606
- continue;
9607
- }
9608
- if (INTERRUPT_RE.test(txt)) {
9609
- rows.push({
9610
- kind: "interrupt",
9611
- event: e,
9612
- tOffsetMs: e.tOffsetMs,
9613
- gapMs: e.gapMs
9614
- });
9615
- continue;
9616
- }
9617
- const slashMatch = txt.match(SLASH_COMMAND_RE2);
9618
- if (slashMatch) {
9619
- const cmd = slashMatch[1];
9620
- const args2 = (slashMatch[2] ?? "").trim();
9621
- const pretty = args2 ? `${cmd} ${args2}` : cmd;
9622
- rows.push({
9623
- kind: "user",
9624
- event: e,
9625
- displayPreview: pretty,
9626
- displayBlocks: [{ type: "text", text: pretty }],
9627
- tOffsetMs: e.tOffsetMs,
9628
- gapMs: e.gapMs
9629
- });
9630
- continue;
9631
- }
9632
- rows.push({
9633
- kind: "user",
9634
- event: e,
9635
- tOffsetMs: e.tOffsetMs,
9636
- gapMs: e.gapMs
9637
- });
10344
+ const endMs = Date.parse(entry.end_iso);
10345
+ if (!Number.isNaN(endMs) && now() - endMs < THIRTY_MIN_MS) {
10346
+ skipped++;
9638
10347
  continue;
9639
10348
  }
9640
- if (e.role === "agent") {
9641
- const txt = firstTextOfBlocks(e.blocks) ?? "";
9642
- if (hasZeroUsage(e)) {
9643
- rows.push({
9644
- kind: "model",
9645
- event: e,
9646
- tOffsetMs: e.tOffsetMs,
9647
- gapMs: e.gapMs
9648
- });
9649
- if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
9650
- rows.push({
9651
- kind: "error",
9652
- event: e,
9653
- message: txt,
9654
- tOffsetMs: e.tOffsetMs,
9655
- gapMs: 0
9656
- });
9657
- }
9658
- continue;
9659
- }
9660
- if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
9661
- rows.push({
9662
- kind: "error",
9663
- event: e,
9664
- message: txt,
9665
- tOffsetMs: e.tOffsetMs,
9666
- gapMs: e.gapMs
9667
- });
9668
- continue;
9669
- }
9670
- const grouped = e.messageId ? byMessageId.get(e.messageId) ?? [e] : [e];
9671
- rows.push({
9672
- kind: "agent",
9673
- event: e,
9674
- groupedEvents: grouped,
9675
- tOffsetMs: e.tOffsetMs,
9676
- gapMs: e.gapMs
10349
+ if (monthToDateSpend() >= budget)
10350
+ break;
10351
+ try {
10352
+ const { entry: result, usage: usage2 } = await enrichEntry(entry, {
10353
+ model: settings.model,
10354
+ callLLM: opts.callLLM
9677
10355
  });
9678
- continue;
10356
+ writeEntry(result);
10357
+ if (result.enrichment.status === "done") {
10358
+ enriched++;
10359
+ appendSpend({
10360
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
10361
+ caller: "daemon",
10362
+ model: result.enrichment.model ?? settings.model,
10363
+ input_tokens: usage2?.input_tokens ?? 0,
10364
+ output_tokens: usage2?.output_tokens ?? 0,
10365
+ cost_usd: result.enrichment.cost_usd ?? 0,
10366
+ kind: "entry_enrich",
10367
+ ref: `${result.session_id}__${result.local_day}`
10368
+ });
10369
+ } else {
10370
+ errors++;
10371
+ }
10372
+ } catch (err) {
10373
+ errors++;
10374
+ const failed = {
10375
+ ...entry,
10376
+ enrichment: {
10377
+ ...entry.enrichment,
10378
+ status: "error",
10379
+ retry_count: (entry.enrichment.retry_count ?? 0) + 1,
10380
+ error: err.message,
10381
+ generated_at: (/* @__PURE__ */ new Date()).toISOString()
10382
+ }
10383
+ };
10384
+ writeEntry(failed);
9679
10385
  }
9680
10386
  }
9681
- return rows;
10387
+ return { enriched, errors, skipped };
9682
10388
  }
9683
- var INTERRUPT_RE, RATE_LIMIT_RE, SKILL_CONTENT_RE, SLASH_COMMAND_RE2, TASK_NOTIFICATION_RE;
9684
- var init_presentation = __esm({
9685
- "../parser/dist/presentation.js"() {
10389
+ var THIRTY_MIN_MS, MAX_RETRY_COUNT;
10390
+ var init_queue = __esm({
10391
+ "../entries/dist/queue.js"() {
9686
10392
  "use strict";
9687
- INTERRUPT_RE = /\[request interrupted|interrupted by user/i;
9688
- RATE_LIMIT_RE = /rate.?limit|overloaded_error|api error|Error sending message/i;
9689
- SKILL_CONTENT_RE = /^Base directory for this skill:/;
9690
- SLASH_COMMAND_RE2 = /<command-name>(\/[^<\s]+)<\/command-name>(?:[\s\S]*?<command-args>([\s\S]*?)<\/command-args>)?/;
9691
- TASK_NOTIFICATION_RE = /<task-notification>/;
10393
+ init_fs2();
10394
+ init_enrich2();
10395
+ init_budget();
10396
+ init_pipeline_lock();
10397
+ THIRTY_MIN_MS = 30 * 60 * 1e3;
10398
+ MAX_RETRY_COUNT = 3;
9692
10399
  }
9693
10400
  });
9694
10401
 
9695
- // ../parser/dist/aggregate.js
9696
- function isoDay(d) {
9697
- return toLocalDay(d.getTime());
9698
- }
9699
- function rangeDays(start2, end) {
9700
- const out = [];
9701
- const cur = new Date(start2.getFullYear(), start2.getMonth(), start2.getDate());
9702
- const stop2 = new Date(end.getFullYear(), end.getMonth(), end.getDate());
9703
- while (cur.getTime() <= stop2.getTime()) {
9704
- out.push(new Date(cur));
9705
- cur.setDate(cur.getDate() + 1);
9706
- }
9707
- return out;
9708
- }
9709
- function calendarWeek(ref = /* @__PURE__ */ new Date()) {
9710
- const d = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate());
9711
- const dow = d.getDay();
9712
- const daysSinceMon = (dow + 6) % 7;
9713
- const monday = new Date(d);
9714
- monday.setDate(d.getDate() - daysSinceMon);
9715
- const sunday = new Date(monday);
9716
- sunday.setDate(monday.getDate() + 6);
9717
- return { start: monday, end: sunday };
9718
- }
9719
- function priorCalendarWeek(ref = /* @__PURE__ */ new Date()) {
9720
- const cur = calendarWeek(ref);
9721
- const prevEnd = new Date(cur.start);
9722
- prevEnd.setDate(cur.start.getDate() - 1);
9723
- const prevStart = new Date(prevEnd);
9724
- prevStart.setDate(prevEnd.getDate() - 6);
9725
- return { start: prevStart, end: prevEnd };
9726
- }
9727
- function last4CompletedWeeks(ref = /* @__PURE__ */ new Date()) {
9728
- const { end } = priorCalendarWeek(ref);
9729
- const start2 = new Date(end);
9730
- start2.setDate(end.getDate() - 27);
9731
- return { start: start2, end };
10402
+ // ../entries/dist/settings.js
10403
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, renameSync as renameSync3, chmodSync as chmodSync2, mkdirSync as mkdirSync7, existsSync as existsSync10 } from "node:fs";
10404
+ import { dirname as dirname8, join as join11 } from "node:path";
10405
+ import { homedir as homedir10 } from "node:os";
10406
+ function settingsPath() {
10407
+ if (settingsPathCached)
10408
+ return settingsPathCached;
10409
+ settingsPathCached = join11(homedir10(), ".cclens", "settings.json");
10410
+ return settingsPathCached;
9732
10411
  }
9733
- function calendarMonth(ref = /* @__PURE__ */ new Date()) {
9734
- const start2 = new Date(ref.getFullYear(), ref.getMonth(), 1);
9735
- const end = new Date(ref.getFullYear(), ref.getMonth() + 1, 0);
9736
- return { start: start2, end };
10412
+ function __setSettingsPathForTest(path5) {
10413
+ settingsPathCached = path5;
9737
10414
  }
9738
- function priorCalendarMonth(ref = /* @__PURE__ */ new Date()) {
9739
- const start2 = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
9740
- const end = new Date(ref.getFullYear(), ref.getMonth(), 0);
9741
- return { start: start2, end };
10415
+ function toDisk(s) {
10416
+ return {
10417
+ ai_features: {
10418
+ enabled: s.ai_features.enabled,
10419
+ model: s.ai_features.model,
10420
+ monthly_budget_usd: s.ai_features.monthlyBudgetUsd,
10421
+ auto_backfill_last_week: s.ai_features.autoBackfillLastWeek,
10422
+ auto_backfill_yesterday: s.ai_features.autoBackfillYesterday
10423
+ }
10424
+ };
9742
10425
  }
9743
- function aggregateConcurrency(bursts, period) {
9744
- const days = rangeDays(period.start, period.end);
9745
- const byDayKey = /* @__PURE__ */ new Map();
9746
- for (const d of days)
9747
- byDayKey.set(isoDay(d), { peak: 0, has_cross_project: false });
9748
- for (const b of bursts) {
9749
- const dayKey = isoDay(new Date(b.startMs));
9750
- const cur = byDayKey.get(dayKey);
9751
- if (!cur)
9752
- continue;
9753
- if (b.peak > cur.peak)
9754
- cur.peak = b.peak;
9755
- if (b.crossProject)
9756
- cur.has_cross_project = true;
9757
- }
9758
- const by_day = days.map((d) => {
9759
- const k = isoDay(d);
9760
- const v = byDayKey.get(k);
9761
- return { date: k, peak: v.peak, has_cross_project: v.has_cross_project };
9762
- });
9763
- let peak = 0;
9764
- let peak_day;
9765
- let multi_agent_days = 0;
9766
- let cross_project_days = 0;
9767
- for (const d of by_day) {
9768
- if (d.peak > peak) {
9769
- peak = d.peak;
9770
- peak_day = d.date;
10426
+ function fromDisk(d) {
10427
+ const af = d.ai_features ?? {};
10428
+ return {
10429
+ ai_features: {
10430
+ enabled: af.enabled ?? DEFAULT_SETTINGS.ai_features.enabled,
10431
+ model: af.model ?? DEFAULT_SETTINGS.ai_features.model,
10432
+ monthlyBudgetUsd: af.monthly_budget_usd ?? null,
10433
+ autoBackfillLastWeek: af.auto_backfill_last_week ?? DEFAULT_SETTINGS.ai_features.autoBackfillLastWeek,
10434
+ autoBackfillYesterday: af.auto_backfill_yesterday ?? DEFAULT_SETTINGS.ai_features.autoBackfillYesterday
9771
10435
  }
9772
- if (d.peak >= 3)
9773
- multi_agent_days++;
9774
- if (d.has_cross_project)
9775
- cross_project_days++;
10436
+ };
10437
+ }
10438
+ function readSettings() {
10439
+ const p = settingsPath();
10440
+ if (!existsSync10(p))
10441
+ return DEFAULT_SETTINGS;
10442
+ try {
10443
+ const raw = readFileSync10(p, "utf8");
10444
+ return fromDisk(JSON.parse(raw));
10445
+ } catch {
10446
+ return DEFAULT_SETTINGS;
9776
10447
  }
9777
- return { by_day, peak, peak_day, multi_agent_days, cross_project_days };
9778
10448
  }
9779
- var init_aggregate = __esm({
9780
- "../parser/dist/aggregate.js"() {
9781
- "use strict";
9782
- init_analytics();
10449
+ function writeSettings(s) {
10450
+ const p = settingsPath();
10451
+ mkdirSync7(dirname8(p), { recursive: true });
10452
+ const tmp = `${p}.tmp`;
10453
+ writeFileSync6(tmp, JSON.stringify(toDisk(s), null, 2), { encoding: "utf8" });
10454
+ if (process.platform !== "win32") {
10455
+ chmodSync2(tmp, 384);
9783
10456
  }
9784
- });
9785
-
9786
- // ../parser/dist/index.js
9787
- var dist_exports = {};
9788
- __export(dist_exports, {
9789
- CLAUDE_CODE_METADATA: () => CLAUDE_CODE_METADATA,
9790
- CODEX_METADATA: () => CODEX_METADATA,
9791
- PRICES: () => PRICES,
9792
- RATE_PER_PCT: () => RATE_PER_PCT,
9793
- RATE_PER_PCT_5H: () => RATE_PER_PCT_5H,
9794
- RATE_PER_PCT_7D: () => RATE_PER_PCT_7D,
9795
- agentMetadata: () => agentMetadata,
9796
- aggregateConcurrency: () => aggregateConcurrency,
9797
- buildGanttDay: () => buildGanttDay,
9798
- buildMegaRows: () => buildMegaRows,
9799
- buildPresentation: () => buildPresentation,
9800
- buildSpendIndex: () => buildSpendIndex,
9801
- calendarMonth: () => calendarMonth,
9802
- calendarWeek: () => calendarWeek,
9803
- canonicalProjectName: () => canonicalProjectName,
9804
- collectSnapPairRates: () => collectSnapPairRates,
9805
- computeActiveSegments: () => computeActiveSegments,
9806
- computeBurstsFromSessions: () => computeBurstsFromSessions,
9807
- computeParallelism: () => computeParallelism,
9808
- computeParallelismBursts: () => computeParallelismBursts,
9809
- dailyActivity: () => dailyActivity,
9810
- daysBetween: () => daysBetween,
9811
- detectParallelRuns: () => detectParallelRuns,
9812
- detectPrMarkers: () => detectPrMarkers,
9813
- dollarsInRange: () => dollarsInRange,
9814
- dollarsInWindow: () => dollarsInWindow,
9815
- eventDollars: () => eventDollars,
9816
- getAgentMetadata: () => getAgentMetadata,
9817
- groupByProject: () => groupByProject,
9818
- groupByTeam: () => groupByTeam,
9819
- groupSnapsByCycle: () => groupSnapsByCycle,
9820
- highLevelMetrics: () => highLevelMetrics,
9821
- isAgentKind: () => isAgentKind,
9822
- last4CompletedWeeks: () => last4CompletedWeeks,
9823
- modelFamily: () => modelFamily,
9824
- parseTranscript: () => parseTranscript,
9825
- predictAnchored: () => predictAnchored,
9826
- predictUtilization: () => predictUtilization,
9827
- priorCalendarMonth: () => priorCalendarMonth,
9828
- priorCalendarWeek: () => priorCalendarWeek,
9829
- rowPrimaryIndex: () => rowPrimaryIndex,
9830
- sessionAirTimeMs: () => sessionAirTimeMs,
9831
- sessionDay: () => sessionDay,
9832
- summarizeBursts: () => summarizeBursts,
9833
- toLocalDay: () => toLocalDay,
9834
- userSoloRate: () => userSoloRate,
9835
- worktreeName: () => worktreeName
9836
- });
9837
- var init_dist2 = __esm({
9838
- "../parser/dist/index.js"() {
10457
+ renameSync3(tmp, p);
10458
+ }
10459
+ var DEFAULT_SETTINGS, settingsPathCached;
10460
+ var init_settings = __esm({
10461
+ "../entries/dist/settings.js"() {
9839
10462
  "use strict";
9840
- init_types3();
9841
- init_parser();
9842
- init_presentation();
9843
- init_analytics();
9844
- init_aggregate();
9845
- init_calibration();
9846
- init_agent_metadata();
9847
- init_team();
10463
+ DEFAULT_SETTINGS = {
10464
+ ai_features: {
10465
+ enabled: true,
10466
+ model: "sonnet",
10467
+ monthlyBudgetUsd: null,
10468
+ autoBackfillLastWeek: true,
10469
+ autoBackfillYesterday: true
10470
+ }
10471
+ };
10472
+ settingsPathCached = null;
9848
10473
  }
9849
10474
  });
9850
10475
 
@@ -9911,7 +10536,7 @@ var init_digest_day = __esm({
9911
10536
  "../entries/dist/prompts/digest-day.js"() {
9912
10537
  "use strict";
9913
10538
  init_zod();
9914
- init_dist2();
10539
+ init_dist();
9915
10540
  AGENT_NAMING_TABLE = agentMetadata.map((m) => ` - agent="${m.kind}" \u2192 render as "${m.displayName}"`).join("\n");
9916
10541
  SuggestionSchema = external_exports.object({
9917
10542
  headline: external_exports.string().min(1).max(80),
@@ -10021,7 +10646,7 @@ function rollupOutcomeDay(entries2) {
10021
10646
  return "exploratory";
10022
10647
  return "trivial";
10023
10648
  }
10024
- function truncate3(s, max) {
10649
+ function truncate4(s, max) {
10025
10650
  if (s.length <= max)
10026
10651
  return s;
10027
10652
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -10077,7 +10702,7 @@ function computeDaySignals(entries2) {
10077
10702
  type,
10078
10703
  count: v.count,
10079
10704
  sample_description: v.sample_description,
10080
- sample_prompt_preview: truncate3(v.sample_prompt_preview, 200)
10705
+ sample_prompt_preview: truncate4(v.sample_prompt_preview, 200)
10081
10706
  }));
10082
10707
  const frameCounts = /* @__PURE__ */ new Map();
10083
10708
  for (const s of signalsList) {
@@ -10096,7 +10721,7 @@ function computeDaySignals(entries2) {
10096
10721
  if ((e.first_user ?? "").length > 0)
10097
10722
  verbosity_distribution[s.verbosity] += 1;
10098
10723
  for (const ref of s.external_refs) {
10099
- external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate3(ref.preview, 200) });
10724
+ external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate4(ref.preview, 200) });
10100
10725
  }
10101
10726
  const ints = e.numbers.interrupts ?? 0;
10102
10727
  interrupts += ints;
@@ -10502,7 +11127,7 @@ var init_digest_day_pipeline = __esm({
10502
11127
  init_budget();
10503
11128
  init_pipeline_lock();
10504
11129
  init_fs();
10505
- init_dist2();
11130
+ init_dist();
10506
11131
  THIRTY_MIN_MS2 = 30 * 60 * 1e3;
10507
11132
  MAX_RETRY_COUNT2 = 3;
10508
11133
  }
@@ -10698,7 +11323,7 @@ var init_digest_week = __esm({
10698
11323
  "../entries/dist/prompts/digest-week.js"() {
10699
11324
  "use strict";
10700
11325
  init_zod();
10701
- init_dist2();
11326
+ init_dist();
10702
11327
  AGENT_NAMING_TABLE2 = agentMetadata.map((m) => ` - agent="${m.kind}" \u2192 render as "${m.displayName}"`).join("\n");
10703
11328
  DateRegex = /^\d{4}-\d{2}-\d{2}$/;
10704
11329
  ProjectAreaResponseSchema = external_exports.object({
@@ -10969,7 +11594,7 @@ function computeInteractionModes(entries2) {
10969
11594
  date: entry.local_day,
10970
11595
  project_display: prettyProject3(entry.project),
10971
11596
  type: sa.type,
10972
- prompt_preview: truncate4(sa.prompt_preview, 200)
11597
+ prompt_preview: truncate5(sa.prompt_preview, 200)
10973
11598
  });
10974
11599
  }
10975
11600
  const skillExamples = [];
@@ -10986,7 +11611,7 @@ function computeInteractionModes(entries2) {
10986
11611
  skillExamples.push({
10987
11612
  date: e.local_day,
10988
11613
  skill,
10989
- first_user_preview: truncate4(e.first_user, 200)
11614
+ first_user_preview: truncate5(e.first_user, 200)
10990
11615
  });
10991
11616
  }
10992
11617
  }
@@ -11003,7 +11628,7 @@ function computeInteractionModes(entries2) {
11003
11628
  project_display: prettyProject3(longestTurnEntry.project),
11004
11629
  active_min: longestTurnEntry.numbers.active_min,
11005
11630
  top_tools: longestTurnEntry.top_tools.slice(0, 5),
11006
- first_user_preview: truncate4(longestTurnEntry.first_user, 200)
11631
+ first_user_preview: truncate5(longestTurnEntry.first_user, 200)
11007
11632
  } : null;
11008
11633
  const tools_per_turn = turn_count > 0 ? tools_total / turn_count : 0;
11009
11634
  const label = tools_per_turn < 5 ? "rapid" : tools_per_turn < 15 ? "mixed" : "batch";
@@ -11034,7 +11659,7 @@ function computeInteractionModes(entries2) {
11034
11659
  }
11035
11660
  };
11036
11661
  }
11037
- function truncate4(s, max) {
11662
+ function truncate5(s, max) {
11038
11663
  if (s.length <= max)
11039
11664
  return s;
11040
11665
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -11081,7 +11706,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
11081
11706
  evidence_subagent = {
11082
11707
  type: sa.type,
11083
11708
  description: sa.description,
11084
- prompt_preview: truncate4(sa.prompt_preview, 200)
11709
+ prompt_preview: truncate5(sa.prompt_preview, 200)
11085
11710
  };
11086
11711
  }
11087
11712
  const project_display = repEntry ? prettyProject3(repEntry.project) : dd.projects[0]?.display_name ?? "";
@@ -11092,7 +11717,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
11092
11717
  outcome: dayOutcome.get(dd.key) ?? null,
11093
11718
  helpfulness: dayHelp.get(dd.key) ?? null,
11094
11719
  evidence_subagent,
11095
- evidence_first_user: repEntry?.first_user ? truncate4(repEntry.first_user, 200) : null,
11720
+ evidence_first_user: repEntry?.first_user ? truncate5(repEntry.first_user, 200) : null,
11096
11721
  day_signature: dd.day_signature ?? null
11097
11722
  };
11098
11723
  });
@@ -11167,7 +11792,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
11167
11792
  date: day2,
11168
11793
  session_id: ref.session_id,
11169
11794
  ref_kind: ref.kind,
11170
- preview: truncate4(ref.preview, 200)
11795
+ preview: truncate5(ref.preview, 200)
11171
11796
  });
11172
11797
  }
11173
11798
  total_interrupts += signals.comm_style.steering.interrupts;
@@ -11239,7 +11864,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
11239
11864
  count: v.count,
11240
11865
  days: [...v.days].sort(),
11241
11866
  sample_description: v.sample_description,
11242
- sample_prompt_preview: truncate4(v.sample_prompt_preview, 200)
11867
+ sample_prompt_preview: truncate5(v.sample_prompt_preview, 200)
11243
11868
  })),
11244
11869
  threads,
11245
11870
  communication_style: {
@@ -11887,7 +12512,7 @@ function activeMs(timestamps) {
11887
12512
  let active = 0;
11888
12513
  for (let i = 1; i < ts.length; i++) {
11889
12514
  const dt = ts[i] - ts[i - 1];
11890
- if (dt < IDLE_GAP_MS3)
12515
+ if (dt < IDLE_GAP_MS4)
11891
12516
  active += dt;
11892
12517
  }
11893
12518
  return active;
@@ -12058,7 +12683,7 @@ function buildSessionSlice(entry, events, daySignature) {
12058
12683
  let intEnd = sortedTs[0];
12059
12684
  for (let i = 1; i < sortedTs.length; i++) {
12060
12685
  const dt = sortedTs[i] - intEnd;
12061
- if (dt >= IDLE_GAP_MS3) {
12686
+ if (dt >= IDLE_GAP_MS4) {
12062
12687
  intervals.push({
12063
12688
  start_min: fmtMin(intStart - sliceStartMs),
12064
12689
  end_min: fmtMin(intEnd - sliceStartMs)
@@ -12455,14 +13080,14 @@ async function generateTopSession(slice, opts = {}) {
12455
13080
  void lastModel;
12456
13081
  }
12457
13082
  }
12458
- var IDLE_GAP_MS3, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE, INTERRUPT_RE2, PR_TITLE_RE, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
13083
+ var IDLE_GAP_MS4, LONG_AUTONOMOUS_MIN, N_USER, N_FIRST_AGENT, N_LAST_AGENT, N_USER_TURN1, N_SUBAGENT, SHELL_NOISE, INTERRUPT_RE2, PR_TITLE_RE, PinResponseSchema, TopSessionResponseSchema, SYSTEM_PROMPT5, TOP_SESSION_SYSTEM_PROMPT, DEFAULT_MODEL5, defaultCallLLMTopSession, validateTopSession;
12459
13084
  var init_top_sessions = __esm({
12460
13085
  "../entries/dist/top-sessions.js"() {
12461
13086
  "use strict";
12462
13087
  init_zod();
12463
13088
  init_signals();
12464
13089
  init_llm_runner();
12465
- IDLE_GAP_MS3 = 3 * 60 * 1e3;
13090
+ IDLE_GAP_MS4 = 3 * 60 * 1e3;
12466
13091
  LONG_AUTONOMOUS_MIN = 20;
12467
13092
  N_USER = 150;
12468
13093
  N_FIRST_AGENT = 150;
@@ -13215,7 +13840,7 @@ Exit codes:
13215
13840
  var init_entries = __esm({
13216
13841
  "src/commands/entries.ts"() {
13217
13842
  "use strict";
13218
- init_dist();
13843
+ init_dist2();
13219
13844
  init_fs2();
13220
13845
  init_args();
13221
13846
  }
@@ -13716,11 +14341,12 @@ async function runTeamBackfill(log = noopLog, filePath = USAGE_LOG4, configOverr
13716
14341
  return { paired: false, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
13717
14342
  }
13718
14343
  const raw = readSnapshots(filePath);
13719
- if (raw.length === 0) {
14344
+ const claudeOnly = raw.filter((s) => !s.agent || s.agent === "claude-code");
14345
+ if (claudeOnly.length === 0) {
13720
14346
  log("info", "team backfill: no usage snapshots to send");
13721
14347
  return { paired: true, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
13722
14348
  }
13723
- const wire = raw.map(rawToWire);
14349
+ const wire = claudeOnly.map(rawToWire);
13724
14350
  const batches = chunk(wire, BATCH_SIZE);
13725
14351
  const planTier = await getPlanTier(PROFILE_CACHE).catch(() => null) ?? void 0;
13726
14352
  let inserted = 0;
@@ -13784,7 +14410,7 @@ var init_backfill = __esm({
13784
14410
  // src/team/push.ts
13785
14411
  import { randomUUID as randomUUID2 } from "node:crypto";
13786
14412
  function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
13787
- const raw = latestSnapshot(filePath);
14413
+ const raw = latestClaudeCodeSnapshot(filePath);
13788
14414
  if (!raw) return null;
13789
14415
  const capturedMs = Date.parse(raw.captured_at);
13790
14416
  if (Number.isNaN(capturedMs)) return null;
@@ -13854,7 +14480,7 @@ var SNAPSHOT_FRESHNESS_MS;
13854
14480
  var init_push = __esm({
13855
14481
  "src/team/push.ts"() {
13856
14482
  "use strict";
13857
- init_dist2();
14483
+ init_dist();
13858
14484
  init_storage();
13859
14485
  SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
13860
14486
  }
@@ -13929,7 +14555,7 @@ async function runTeamSync(log = noopLog2, configOverride) {
13929
14555
  if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
13930
14556
  try {
13931
14557
  const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
13932
- const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist2(), dist_exports));
14558
+ const { toLocalDay: toLocalDay4 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
13933
14559
  const today = toLocalDay4(Date.now());
13934
14560
  const sessions = await listSessions2({ limit: 1e4 });
13935
14561
  const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
@@ -14537,9 +15163,9 @@ function listTraceFiles() {
14537
15163
  if (!existsSync12(RUNS_DIR2)) return [];
14538
15164
  const entries2 = readdirSync3(RUNS_DIR2).filter((f) => f.endsWith(".jsonl"));
14539
15165
  return entries2.map((f) => {
14540
- const path4 = join17(RUNS_DIR2, f);
14541
- const st = statSync4(path4);
14542
- return { runId: f.replace(/\.jsonl$/, ""), path: path4, mtimeMs: st.mtimeMs };
15166
+ const path5 = join17(RUNS_DIR2, f);
15167
+ const st = statSync4(path5);
15168
+ return { runId: f.replace(/\.jsonl$/, ""), path: path5, mtimeMs: st.mtimeMs };
14543
15169
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
14544
15170
  }
14545
15171
  function resolveRunId(arg) {
@@ -14561,8 +15187,8 @@ function resolveRunId(arg) {
14561
15187
  }
14562
15188
  return null;
14563
15189
  }
14564
- function readTrace(path4) {
14565
- return readFileSync15(path4, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
15190
+ function readTrace(path5) {
15191
+ return readFileSync15(path5, "utf-8").split("\n").filter((l) => l.trim()).map((l) => {
14566
15192
  try {
14567
15193
  return JSON.parse(l);
14568
15194
  } catch {
@@ -14743,7 +15369,7 @@ async function main() {
14743
15369
  case "version":
14744
15370
  case "--version":
14745
15371
  case "-v":
14746
- console.log(`fleetlens ${"0.7.1"}`);
15372
+ console.log(`fleetlens ${"0.10.0"}`);
14747
15373
  break;
14748
15374
  case "help":
14749
15375
  case "--help":