fleetlens 0.11.2 → 0.12.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 (179) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +3 -0
  3. package/app/apps/web/.next/build-manifest.json +3 -3
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +22 -0
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
  17. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  18. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  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 +1 -1
  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.nft.json +1 -1
  24. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  26. package/app/apps/web/.next/server/app/api/pdf/[key]/route/app-paths-manifest.json +3 -0
  27. package/app/apps/web/.next/server/app/api/pdf/[key]/route/build-manifest.json +9 -0
  28. package/app/apps/web/.next/server/app/api/pdf/[key]/route/server-reference-manifest.json +4 -0
  29. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +10 -0
  30. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.map +5 -0
  31. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -0
  32. package/app/apps/web/.next/server/app/api/pdf/[key]/route_client-reference-manifest.js +3 -0
  33. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  34. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
  36. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  37. package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +1 -1
  38. package/app/apps/web/.next/server/app/api/workflow-agent/route/app-paths-manifest.json +3 -0
  39. package/app/apps/web/.next/server/app/api/workflow-agent/route/build-manifest.json +9 -0
  40. package/app/apps/web/.next/server/app/api/workflow-agent/route/server-reference-manifest.json +4 -0
  41. package/app/apps/web/.next/server/app/api/workflow-agent/route.js +7 -0
  42. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.map +5 -0
  43. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -0
  44. package/app/apps/web/.next/server/app/api/workflow-agent/route_client-reference-manifest.js +3 -0
  45. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  46. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  47. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  48. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  49. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  50. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  51. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  52. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  53. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  54. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  55. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  56. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  57. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  58. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  59. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  60. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  61. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  62. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  63. package/app/apps/web/.next/server/app/print/[key]/page/app-paths-manifest.json +3 -0
  64. package/app/apps/web/.next/server/app/print/[key]/page/build-manifest.json +17 -0
  65. package/app/apps/web/.next/server/app/print/[key]/page/next-font-manifest.json +6 -0
  66. package/app/apps/web/.next/server/app/print/[key]/page/react-loadable-manifest.json +1 -0
  67. package/app/apps/web/.next/server/app/print/[key]/page/server-reference-manifest.json +4 -0
  68. package/app/apps/web/.next/server/app/print/[key]/page.js +18 -0
  69. package/app/apps/web/.next/server/app/print/[key]/page.js.map +5 -0
  70. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -0
  71. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +3 -0
  72. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  73. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  74. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  75. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  77. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  78. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  79. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  80. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  81. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  82. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  83. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  84. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  85. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  86. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  88. package/app/apps/web/.next/server/app-paths-manifest.json +3 -0
  89. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0455vu3._.js +2 -2
  90. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0_8zh-i._.js → [root-of-the-server]__04j3tng._.js} +2 -2
  91. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0jprdks._.js → [root-of-the-server]__0cnh3k4._.js} +2 -2
  92. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e.f-t.._.js +3 -0
  93. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0kc9e1n._.js +3 -0
  94. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0y5p73y._.js +3 -0
  95. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0ej_p3s._.js → [root-of-the-server]__132eosx._.js} +2 -2
  96. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_pdf_[key]_route_actions_0intcw9.js +3 -0
  97. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_workflow-agent_route_actions_0gxw9tv.js +3 -0
  98. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +1 -1
  99. package/app/apps/web/.next/server/chunks/packages_parser_dist_04rh1zd._.js +1 -1
  100. package/app/apps/web/.next/server/chunks/packages_parser_dist_0ndsxe6._.js +1 -1
  101. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0_xpuh7._.js → [root-of-the-server]__03y~03u._.js} +2 -2
  102. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04eq6z2._.js +1 -1
  103. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04il6m2._.js +11 -0
  104. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +1 -1
  105. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o03ymy._.js +2 -2
  106. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0rvzdnx._.js +1 -1
  107. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0z7mg0b._.js +1 -1
  108. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +12 -0
  109. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +8 -0
  110. package/app/apps/web/.next/server/chunks/ssr/_0cgg5zi._.js +3 -0
  111. package/app/apps/web/.next/server/chunks/ssr/_0jw2sm7._.js +1 -1
  112. package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +3 -0
  113. package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +8 -0
  114. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  115. package/app/apps/web/.next/server/chunks/ssr/apps_web_0.mu7c7._.js +1 -1
  116. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_print_[key]_page_actions_0_ygul-.js +3 -0
  117. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +6 -0
  118. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_week-digest_tsx_0c9ha31._.js +3 -0
  119. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0af94gl._.js +1 -1
  120. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_07orc4m.js +4 -0
  121. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_12.endt._.js +1 -1
  122. package/app/apps/web/.next/server/edge/chunks/[root-of-the-server]__0.sx~id._.js +11 -0
  123. package/app/apps/web/.next/server/edge/chunks/node_modules_next_dist_esm_build_templates_edge-wrapper_0p1t1z8.js +3 -0
  124. package/app/apps/web/.next/server/edge/chunks/turbopack-node_modules_next_dist_esm_build_templates_edge-wrapper_0mw-sqe.js +3 -0
  125. package/app/apps/web/.next/server/functions-config-manifest.json +4 -1
  126. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  127. package/app/apps/web/.next/server/middleware-manifest.json +30 -2
  128. package/app/apps/web/.next/server/pages/500.html +1 -1
  129. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  130. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  131. package/app/apps/web/.next/static/WH20Ct_NdpZTKsqI_yquG/_clientMiddlewareManifest.js +6 -0
  132. package/app/apps/web/.next/static/chunks/00xkqnvk4-d1-.js +10 -0
  133. package/app/apps/web/.next/static/chunks/0716x58e1_2c5.js +1 -0
  134. package/app/apps/web/.next/static/chunks/{0s4eyofh28l~u.js → 0ck03i0l4zeun.js} +1 -1
  135. package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +4 -0
  136. package/app/apps/web/.next/static/chunks/0okg13phslr1..js +4 -0
  137. package/app/apps/web/.next/static/chunks/0vf-3rauafbek.js +6 -0
  138. package/app/apps/web/.next/static/chunks/0vl27-vjob2_9.js +6 -0
  139. package/app/apps/web/.next/static/chunks/0yo458-7577lr.js +1 -0
  140. package/app/apps/web/.next/static/chunks/14bjditmmrn7l.js +1 -0
  141. package/app/apps/web/app/api/events/route.ts +50 -2
  142. package/app/apps/web/app/api/pdf/[key]/route.ts +199 -0
  143. package/app/apps/web/app/api/workflow-agent/route.ts +27 -0
  144. package/app/apps/web/app/insights/[key]/page.tsx +4 -1
  145. package/app/apps/web/app/layout.tsx +23 -1
  146. package/app/apps/web/app/page.tsx +24 -1
  147. package/app/apps/web/app/print/[key]/page.tsx +91 -0
  148. package/app/apps/web/app/projects/[slug]/page.tsx +6 -7
  149. package/app/apps/web/app/projects/page.tsx +2 -3
  150. package/app/apps/web/app/sessions/[id]/session-view.tsx +1297 -19
  151. package/app/apps/web/app/sessions/sessions-grid.tsx +10 -1
  152. package/app/apps/web/components/dashboard-view.tsx +2 -1
  153. package/app/apps/web/components/export-pdf-button.tsx +76 -0
  154. package/app/apps/web/components/month-digest-view.tsx +10 -0
  155. package/app/apps/web/components/week-digest-view.tsx +10 -0
  156. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  157. package/app/apps/web/lib/entries-index.ts +5 -4
  158. package/app/apps/web/lib/find-chrome.ts +71 -0
  159. package/app/apps/web/middleware.ts +22 -0
  160. package/app/apps/web/package.json +1 -1
  161. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  162. package/dist/daemon-worker.js +1592 -252
  163. package/dist/index.js +1495 -168
  164. package/package.json +1 -1
  165. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0d~2~le._.js +0 -8
  166. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +0 -12
  167. package/app/apps/web/.next/server/chunks/ssr/_02s1n-x._.js +0 -8
  168. package/app/apps/web/.next/server/chunks/ssr/_0fjbf--._.js +0 -3
  169. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.js +0 -6
  170. package/app/apps/web/.next/static/chunks/0.3mqbj24kwtc.js +0 -6
  171. package/app/apps/web/.next/static/chunks/0byz8fqw.exef.js +0 -1
  172. package/app/apps/web/.next/static/chunks/0l3ck.awv~4t7.js +0 -1
  173. package/app/apps/web/.next/static/chunks/0lxuac2v0_gdl.js +0 -6
  174. package/app/apps/web/.next/static/chunks/0m62vx92~0_0d.js +0 -1
  175. package/app/apps/web/.next/static/chunks/0rtba0saz74e1.js +0 -10
  176. package/app/apps/web/.next/static/chunks/0uev~g~qvbjv9.js +0 -4
  177. package/app/apps/web/.next/static/m6ibRSdAu8jD10VBA2OFH/_clientMiddlewareManifest.js +0 -1
  178. /package/app/apps/web/.next/static/{m6ibRSdAu8jD10VBA2OFH → WH20Ct_NdpZTKsqI_yquG}/_buildManifest.js +0 -0
  179. /package/app/apps/web/.next/static/{m6ibRSdAu8jD10VBA2OFH → WH20Ct_NdpZTKsqI_yquG}/_ssgManifest.js +0 -0
@@ -75,6 +75,16 @@ function findConductorWorkspace(projectName) {
75
75
  const canonicalEndIdx = m.index + m[0].length - workspaceName.length - 1 - trailingSlash;
76
76
  return { canonicalEndIdx, workspaceName };
77
77
  }
78
+ function findSupersetWorkspace(projectName) {
79
+ const idx = projectName.indexOf(SUPERSET_MARKER);
80
+ if (idx < 0)
81
+ return null;
82
+ const segs = projectName.slice(idx + SUPERSET_MARKER.length).split("/").filter(Boolean);
83
+ if (segs.length === 0)
84
+ return null;
85
+ const workspaceName = UUID_SEG_RE.test(segs[0]) ? segs[1] ?? segs[0] : segs[0];
86
+ return { canonical: projectName.slice(0, idx) + SUPERSET_MARKER + workspaceName, workspaceName };
87
+ }
78
88
  function canonicalProjectName(projectName) {
79
89
  const normalized = projectName.replace(/\/{2,}/g, "/");
80
90
  const wt = findWorktreeMarker(normalized);
@@ -83,6 +93,9 @@ function canonicalProjectName(projectName) {
83
93
  const cd = findConductorWorkspace(normalized);
84
94
  if (cd)
85
95
  return normalized.slice(0, cd.canonicalEndIdx);
96
+ const ss = findSupersetWorkspace(normalized);
97
+ if (ss)
98
+ return ss.canonical;
86
99
  return normalized;
87
100
  }
88
101
  function worktreeName(projectName) {
@@ -96,8 +109,16 @@ function worktreeName(projectName) {
96
109
  const cd = findConductorWorkspace(normalized);
97
110
  if (cd)
98
111
  return cd.workspaceName;
112
+ const ss = findSupersetWorkspace(normalized);
113
+ if (ss)
114
+ return ss.workspaceName;
99
115
  return null;
100
116
  }
117
+ function projectRepoName(projectName) {
118
+ const canonical = canonicalProjectName(projectName);
119
+ const segs = canonical.replace(/\/+$/, "").split("/").filter(Boolean);
120
+ return segs[segs.length - 1] || canonical;
121
+ }
101
122
  function makeDay(date) {
102
123
  return {
103
124
  date,
@@ -517,13 +538,13 @@ function summarizeBursts(bursts) {
517
538
  function groupByProject(sessions) {
518
539
  const map = /* @__PURE__ */ new Map();
519
540
  for (const s of sessions) {
520
- const canonical = canonicalProjectName(s.projectName);
521
- const key = canonical;
541
+ const repo = projectRepoName(s.projectName);
542
+ const key = repo;
522
543
  let cur = map.get(key);
523
544
  if (!cur) {
524
545
  cur = {
525
- projectDir: canonical,
526
- projectName: canonical,
546
+ projectDir: repo,
547
+ projectName: repo,
527
548
  sessions: [],
528
549
  rawProjectDirs: [],
529
550
  worktreeCount: 0,
@@ -565,7 +586,7 @@ function groupByProject(sessions) {
565
586
  }
566
587
  return Array.from(map.values()).sort((a, b) => (b.lastActiveMs ?? 0) - (a.lastActiveMs ?? 0));
567
588
  }
568
- var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
589
+ var BLANK_USAGE, WORKTREE_MARKERS, CONDUCTOR_RE, SUPERSET_MARKER, UUID_SEG_RE, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
569
590
  var init_analytics = __esm({
570
591
  "../parser/dist/analytics.js"() {
571
592
  "use strict";
@@ -576,6 +597,8 @@ var init_analytics = __esm({
576
597
  "/claude/worktrees/"
577
598
  ];
578
599
  CONDUCTOR_RE = /\/conductor\/workspaces\/[^/]+\/([^/]+)(?:\/|$)/;
600
+ SUPERSET_MARKER = "/.superset/worktrees/";
601
+ UUID_SEG_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
579
602
  PR_CREATE_RE = /gh\s+pr\s+create\b/i;
580
603
  PR_NUMBER_RE = /#(\d+)/;
581
604
  TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
@@ -589,7 +612,7 @@ function getAgentMetadata(kind) {
589
612
  function isAgentKind(s) {
590
613
  return typeof s === "string" && KNOWN_AGENT_KINDS.has(s);
591
614
  }
592
- var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
615
+ var CLAUDE_CODE_METADATA, CODEX_METADATA, GEMINI_METADATA, ANTIGRAVITY_METADATA, COWORK_METADATA, agentMetadata, KNOWN_AGENT_KINDS;
593
616
  var init_agent_metadata = __esm({
594
617
  "../parser/dist/agent-metadata.js"() {
595
618
  "use strict";
@@ -614,10 +637,26 @@ var init_agent_metadata = __esm({
614
637
  accentColor: "rgb(124, 92, 246)",
615
638
  iconChar: "\u273A"
616
639
  };
640
+ ANTIGRAVITY_METADATA = {
641
+ kind: "antigravity",
642
+ displayName: "Antigravity",
643
+ shortLabel: "Agy",
644
+ accentColor: "rgb(236, 72, 153)",
645
+ iconChar: "\u23C3"
646
+ };
647
+ COWORK_METADATA = {
648
+ kind: "cowork",
649
+ displayName: "Cowork",
650
+ shortLabel: "Cowork",
651
+ accentColor: "rgb(217, 119, 87)",
652
+ iconChar: "\u2318"
653
+ };
617
654
  agentMetadata = [
618
655
  CLAUDE_CODE_METADATA,
619
656
  CODEX_METADATA,
620
- GEMINI_METADATA
657
+ GEMINI_METADATA,
658
+ ANTIGRAVITY_METADATA,
659
+ COWORK_METADATA
621
660
  ];
622
661
  KNOWN_AGENT_KINDS = new Set(agentMetadata.map((m) => m.kind));
623
662
  }
@@ -1643,6 +1682,52 @@ async function computeSessionUsageWithSubagents(parentLines, projectDirPath, ses
1643
1682
  }));
1644
1683
  return usage;
1645
1684
  }
1685
+ async function scanWorkflowAggregate(projectDirPath, sessionId) {
1686
+ const dir = path.join(projectDirPath, sessionId, "workflows");
1687
+ let entries;
1688
+ try {
1689
+ entries = await fs.readdir(dir);
1690
+ } catch {
1691
+ return { workflowCount: 0, spawnedAgentCount: 0, spans: [] };
1692
+ }
1693
+ const jsonFiles = entries.filter((f) => f.startsWith("wf_") && f.endsWith(".json"));
1694
+ if (jsonFiles.length === 0)
1695
+ return { workflowCount: 0, spawnedAgentCount: 0, spans: [] };
1696
+ let spawnedAgentCount = 0;
1697
+ const spans = [];
1698
+ const parsed = await Promise.all(jsonFiles.map(async (f) => {
1699
+ try {
1700
+ const j = JSON.parse(await fs.readFile(path.join(dir, f), "utf8"));
1701
+ if (typeof j.agentCount === "number")
1702
+ spawnedAgentCount += j.agentCount;
1703
+ if (typeof j.startTime === "number" && typeof j.durationMs === "number" && j.durationMs > 0) {
1704
+ spans.push({ startMs: j.startTime, endMs: j.startTime + j.durationMs });
1705
+ }
1706
+ return true;
1707
+ } catch {
1708
+ return false;
1709
+ }
1710
+ }));
1711
+ return { workflowCount: parsed.filter(Boolean).length, spawnedAgentCount, spans };
1712
+ }
1713
+ function foldWorkflowAgentTime(activeSegments, spans) {
1714
+ if (spans.length === 0)
1715
+ return {};
1716
+ const all = [...activeSegments ?? [], ...spans].filter((s) => s.endMs > s.startMs).sort((a, b) => a.startMs - b.startMs);
1717
+ if (all.length === 0)
1718
+ return {};
1719
+ const merged = [{ ...all[0] }];
1720
+ for (let i = 1; i < all.length; i++) {
1721
+ const cur = all[i];
1722
+ const last = merged[merged.length - 1];
1723
+ if (cur.startMs <= last.endMs)
1724
+ last.endMs = Math.max(last.endMs, cur.endMs);
1725
+ else
1726
+ merged.push({ ...cur });
1727
+ }
1728
+ const airTimeMs = merged.reduce((n, s) => n + (s.endMs - s.startMs), 0);
1729
+ return { activeSegments: merged, airTimeMs };
1730
+ }
1646
1731
  async function getCachedMeta(f) {
1647
1732
  const cached = metaCache.get(f.fullPath);
1648
1733
  if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
@@ -1654,12 +1739,15 @@ async function getCachedMeta(f) {
1654
1739
  const sessionId = sessionIdFromFileName(f.fileName);
1655
1740
  const projectDirPath = path.dirname(f.fullPath);
1656
1741
  meta.totalUsage = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId);
1742
+ const wf = await scanWorkflowAggregate(projectDirPath, sessionId);
1657
1743
  const full = {
1658
1744
  ...meta,
1659
1745
  id: sessionId,
1660
1746
  filePath: f.fullPath,
1661
1747
  projectDir: f.projectDir,
1662
- projectName: meta.cwd ?? decodeProjectName(f.projectDir)
1748
+ projectName: meta.cwd ?? decodeProjectName(f.projectDir),
1749
+ ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1750
+ ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
1663
1751
  };
1664
1752
  metaCache.set(f.fullPath, { meta: full, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
1665
1753
  return full;
@@ -1675,13 +1763,17 @@ async function getCachedDetail(f) {
1675
1763
  try {
1676
1764
  const rawLines = await readJsonlFile(f.fullPath);
1677
1765
  const { meta, events } = parseTranscript(rawLines);
1766
+ const sessionId = sessionIdFromFileName(f.fileName);
1767
+ const wf = await scanWorkflowAggregate(path.dirname(f.fullPath), sessionId);
1678
1768
  const detail = {
1679
1769
  ...meta,
1680
- id: sessionIdFromFileName(f.fileName),
1770
+ id: sessionId,
1681
1771
  filePath: f.fullPath,
1682
1772
  projectDir: f.projectDir,
1683
1773
  projectName: meta.cwd ?? decodeProjectName(f.projectDir),
1684
- events
1774
+ events,
1775
+ ...wf.workflowCount > 0 ? { workflowCount: wf.workflowCount, spawnedAgentCount: wf.spawnedAgentCount } : {},
1776
+ ...foldWorkflowAgentTime(meta.activeSegments, wf.spans)
1685
1777
  };
1686
1778
  detailCache.set(f.fullPath, { detail, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
1687
1779
  const metaOnly = { ...detail };
@@ -1713,8 +1805,18 @@ async function getSession(id, opts = {}) {
1713
1805
  return detail;
1714
1806
  const sessionStartMs = detail.firstTimestamp ? Date.parse(detail.firstTimestamp) : void 0;
1715
1807
  const subagentsDir = path.join(path.dirname(hit.fullPath), sessionIdFromFileName(hit.fileName), "subagents");
1716
- const subagents = await loadSubagents(subagentsDir, sessionStartMs, detail.events);
1717
- return { ...detail, subagents };
1808
+ const workflowsDir = path.join(path.dirname(hit.fullPath), sessionIdFromFileName(hit.fileName), "workflows");
1809
+ const [subagents, workflows] = await Promise.all([
1810
+ loadSubagents(subagentsDir, sessionStartMs, detail.events),
1811
+ loadWorkflows(workflowsDir, sessionStartMs, detail.events)
1812
+ ]);
1813
+ const spawnedAgentCount = workflows.reduce((n, w) => n + w.agentCount, 0);
1814
+ return {
1815
+ ...detail,
1816
+ subagents,
1817
+ workflows,
1818
+ ...workflows.length > 0 ? { workflowCount: workflows.length, spawnedAgentCount } : {}
1819
+ };
1718
1820
  }
1719
1821
  function summarizeSubagentLines(lines) {
1720
1822
  let startMs;
@@ -1904,6 +2006,248 @@ async function loadSubagents(dir, sessionStartMs, parentEvents) {
1904
2006
  }));
1905
2007
  return runs.filter((r) => r !== null).sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
1906
2008
  }
2009
+ async function loadWorkflows(dir, sessionStartMs, parentEvents) {
2010
+ let entries;
2011
+ try {
2012
+ entries = await fs.readdir(dir);
2013
+ } catch {
2014
+ return [];
2015
+ }
2016
+ const jsonFiles = entries.filter((f) => f.startsWith("wf_") && f.endsWith(".json"));
2017
+ if (jsonFiles.length === 0)
2018
+ return [];
2019
+ const dispatches = [];
2020
+ for (const e of parentEvents) {
2021
+ if (e.role !== "tool-call" || e.toolName !== "Workflow")
2022
+ continue;
2023
+ const tsMs = e.timestamp ? Date.parse(e.timestamp) : NaN;
2024
+ if (Number.isNaN(tsMs))
2025
+ continue;
2026
+ for (const b of e.blocks) {
2027
+ if (b?.type !== "tool_use" || b.name !== "Workflow")
2028
+ continue;
2029
+ dispatches.push({ toolUseId: b.id, parentUuid: e.uuid ?? "", tsMs });
2030
+ }
2031
+ }
2032
+ dispatches.sort((a, b) => a.tsMs - b.tsMs);
2033
+ const matchDispatch = (startMs) => {
2034
+ if (startMs === void 0 || dispatches.length === 0)
2035
+ return void 0;
2036
+ const TOLERANCE_MS = 6e4;
2037
+ let before;
2038
+ let beforeDelta = Infinity;
2039
+ let nearest;
2040
+ let nearestDelta = Infinity;
2041
+ for (const d of dispatches) {
2042
+ const delta = Math.abs(startMs - d.tsMs);
2043
+ if (delta > TOLERANCE_MS)
2044
+ continue;
2045
+ if (delta < nearestDelta) {
2046
+ nearest = d;
2047
+ nearestDelta = delta;
2048
+ }
2049
+ if (d.tsMs <= startMs && delta < beforeDelta) {
2050
+ before = d;
2051
+ beforeDelta = delta;
2052
+ }
2053
+ }
2054
+ return before ?? nearest;
2055
+ };
2056
+ const num2 = (v) => typeof v === "number" ? v : void 0;
2057
+ const str = (v) => typeof v === "string" && v.length > 0 ? v : void 0;
2058
+ const runs = await Promise.all(jsonFiles.map(async (f) => {
2059
+ let j;
2060
+ try {
2061
+ j = JSON.parse(await fs.readFile(path.join(dir, f), "utf8"));
2062
+ } catch {
2063
+ return null;
2064
+ }
2065
+ const runId = str(j.runId) ?? f.replace(/\.json$/, "");
2066
+ const startMs = num2(j.startTime);
2067
+ const durationMs = num2(j.durationMs);
2068
+ const endMs = startMs !== void 0 && durationMs !== void 0 ? startMs + durationMs : void 0;
2069
+ const startTOffsetMs = sessionStartMs !== void 0 && startMs !== void 0 ? Math.max(0, startMs - sessionStartMs) : void 0;
2070
+ const endTOffsetMs = sessionStartMs !== void 0 && endMs !== void 0 ? Math.max(0, endMs - sessionStartMs) : void 0;
2071
+ const phases = Array.isArray(j.phases) ? j.phases.filter((p) => !!p && typeof p === "object" && typeof p.title === "string").map((p) => ({ title: p.title, detail: str(p.detail) })) : [];
2072
+ const logs = Array.isArray(j.logs) ? j.logs.filter((l) => typeof l === "string") : [];
2073
+ const cap = (v, n) => {
2074
+ const s = str(v);
2075
+ return s === void 0 ? void 0 : s.length > n ? s.slice(0, n) + "\u2026" : s;
2076
+ };
2077
+ const progress = Array.isArray(j.workflowProgress) ? j.workflowProgress : [];
2078
+ const agents = progress.filter((e) => !!e && typeof e === "object" && e.type === "workflow_agent").map((e) => ({
2079
+ index: num2(e.index) ?? 0,
2080
+ label: str(e.label) ?? "(agent)",
2081
+ phaseIndex: num2(e.phaseIndex),
2082
+ phaseTitle: str(e.phaseTitle),
2083
+ agentId: str(e.agentId),
2084
+ model: str(e.model),
2085
+ state: str(e.state),
2086
+ startedAt: num2(e.startedAt),
2087
+ durationMs: num2(e.durationMs),
2088
+ tokens: num2(e.tokens),
2089
+ toolCalls: num2(e.toolCalls),
2090
+ lastToolSummary: cap(e.lastToolSummary, 200),
2091
+ promptPreview: cap(e.promptPreview, 600),
2092
+ resultPreview: cap(e.resultPreview, 600)
2093
+ })).sort((a, b) => a.index - b.index);
2094
+ const parent = matchDispatch(startMs);
2095
+ return {
2096
+ runId,
2097
+ name: str(j.workflowName) ?? runId,
2098
+ description: str(j.summary),
2099
+ status: str(j.status) ?? "unknown",
2100
+ agentCount: num2(j.agentCount) ?? 0,
2101
+ toolCallCount: num2(j.totalToolCalls) ?? 0,
2102
+ totalTokens: num2(j.totalTokens) ?? 0,
2103
+ durationMs,
2104
+ startMs,
2105
+ endMs,
2106
+ startTOffsetMs,
2107
+ endTOffsetMs,
2108
+ model: str(j.defaultModel),
2109
+ phases,
2110
+ logs,
2111
+ agents,
2112
+ parentToolUseId: parent?.toolUseId,
2113
+ parentUuid: parent?.parentUuid
2114
+ };
2115
+ }));
2116
+ return runs.filter((r) => r !== null).sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
2117
+ }
2118
+ function salientToolInput(input) {
2119
+ if (!input || typeof input !== "object")
2120
+ return "";
2121
+ const o = input;
2122
+ const pick = (k) => typeof o[k] === "string" ? o[k] : void 0;
2123
+ return pick("command") ?? pick("file_path") ?? pick("path") ?? pick("pattern") ?? pick("url") ?? pick("description") ?? pick("query") ?? pick("prompt") ?? pick("old_string") ?? JSON.stringify(o);
2124
+ }
2125
+ async function loadWorkflowAgentDetail(sessionId, runId, agentId, opts = {}) {
2126
+ const { root = DEFAULT_ROOT } = opts;
2127
+ if (!/^[\w.-]+$/.test(runId) || !/^[\w.-]+$/.test(agentId))
2128
+ return null;
2129
+ const files = await walkJsonlFiles(root);
2130
+ const hit = files.find((f) => sessionIdFromFileName(f.fileName) === sessionId);
2131
+ if (!hit)
2132
+ return null;
2133
+ const file = path.join(path.dirname(hit.fullPath), sessionId, "subagents", "workflows", runId, `agent-${agentId}.jsonl`);
2134
+ let lines;
2135
+ try {
2136
+ lines = await readJsonlFile(file);
2137
+ } catch {
2138
+ return null;
2139
+ }
2140
+ const errored = /* @__PURE__ */ new Set();
2141
+ for (const raw of lines) {
2142
+ if (!raw || typeof raw !== "object")
2143
+ continue;
2144
+ const content = raw.message?.content;
2145
+ if (!Array.isArray(content))
2146
+ continue;
2147
+ for (const b of content) {
2148
+ if (b && typeof b === "object" && b.type === "tool_result" && b.is_error === true && typeof b.tool_use_id === "string") {
2149
+ errored.add(b.tool_use_id);
2150
+ }
2151
+ }
2152
+ }
2153
+ const steps = [];
2154
+ const toolCounts = /* @__PURE__ */ new Map();
2155
+ const totalUsage = { ...BLANK_USAGE3 };
2156
+ const seen = /* @__PURE__ */ new Set();
2157
+ let prompt;
2158
+ let finalText;
2159
+ let model;
2160
+ let assistantMessageCount = 0;
2161
+ let eventCount = 0;
2162
+ let startMs;
2163
+ let endMs;
2164
+ for (const raw of lines) {
2165
+ if (!raw || typeof raw !== "object")
2166
+ continue;
2167
+ const r = raw;
2168
+ eventCount++;
2169
+ const ts = typeof r.timestamp === "string" ? Date.parse(r.timestamp) : NaN;
2170
+ if (!Number.isNaN(ts)) {
2171
+ if (startMs === void 0 || ts < startMs)
2172
+ startMs = ts;
2173
+ if (endMs === void 0 || ts > endMs)
2174
+ endMs = ts;
2175
+ }
2176
+ if (prompt === void 0 && r.type === "user" && (r.parentUuid === null || r.parentUuid === void 0)) {
2177
+ const c = r.message?.content;
2178
+ if (typeof c === "string")
2179
+ prompt = c;
2180
+ else if (Array.isArray(c)) {
2181
+ const t = c.find((b) => b && typeof b === "object" && b.type === "text");
2182
+ if (t?.text)
2183
+ prompt = t.text;
2184
+ }
2185
+ }
2186
+ if (r.type !== "assistant")
2187
+ continue;
2188
+ const m = r.message;
2189
+ if (!m)
2190
+ continue;
2191
+ if (!model && typeof m.model === "string")
2192
+ model = m.model;
2193
+ const mid = typeof m.id === "string" ? m.id : void 0;
2194
+ const rid = typeof r.requestId === "string" ? r.requestId : void 0;
2195
+ const key = mid != null && rid != null ? `${mid}:${rid}` : void 0;
2196
+ const fresh = key ? !seen.has(key) : true;
2197
+ if (key && fresh)
2198
+ seen.add(key);
2199
+ if (fresh) {
2200
+ assistantMessageCount++;
2201
+ const u = m.usage;
2202
+ if (u) {
2203
+ const n = (v) => typeof v === "number" ? v : 0;
2204
+ totalUsage.input += n(u.input_tokens);
2205
+ totalUsage.output += n(u.output_tokens);
2206
+ totalUsage.cacheRead += n(u.cache_read_input_tokens);
2207
+ totalUsage.cacheWrite += n(u.cache_creation_input_tokens);
2208
+ }
2209
+ }
2210
+ const content = m.content;
2211
+ if (!Array.isArray(content))
2212
+ continue;
2213
+ for (const b of content) {
2214
+ if (!b || typeof b !== "object")
2215
+ continue;
2216
+ if (b.type === "text" && typeof b.text === "string") {
2217
+ finalText = b.text;
2218
+ } else if (b.type === "tool_use" && typeof b.name === "string") {
2219
+ toolCounts.set(b.name, (toolCounts.get(b.name) ?? 0) + 1);
2220
+ if (steps.length < 2e3) {
2221
+ const salient = salientToolInput(b.input);
2222
+ const preview = salient.replace(/\s+/g, " ").trim().slice(0, 160);
2223
+ const full = salient.length > 4e3 ? salient.slice(0, 4e3) + "\u2026" : salient;
2224
+ steps.push({
2225
+ index: steps.length + 1,
2226
+ tool: b.name,
2227
+ preview,
2228
+ // Only carry `full` when it adds something over the one-line preview.
2229
+ full: full.length > preview.length || full.includes("\n") ? full : void 0,
2230
+ isError: typeof b.id === "string" && errored.has(b.id)
2231
+ });
2232
+ }
2233
+ }
2234
+ }
2235
+ }
2236
+ const toolCalls = Array.from(toolCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
2237
+ return {
2238
+ agentId,
2239
+ prompt,
2240
+ finalText,
2241
+ steps,
2242
+ toolCalls,
2243
+ toolCallCount: steps.length,
2244
+ assistantMessageCount,
2245
+ eventCount,
2246
+ totalUsage,
2247
+ model,
2248
+ durationMs: startMs !== void 0 && endMs !== void 0 ? endMs - startMs : void 0
2249
+ };
2250
+ }
1907
2251
  async function listProjects(root = DEFAULT_ROOT) {
1908
2252
  const files = await walkJsonlFiles(root);
1909
2253
  const byRawDir = /* @__PURE__ */ new Map();
@@ -1925,10 +2269,10 @@ async function listProjects(root = DEFAULT_ROOT) {
1925
2269
  const canonicalMap = /* @__PURE__ */ new Map();
1926
2270
  for (const [rawDir, { count, lastActiveMs }] of byRawDir) {
1927
2271
  const cwdOrDecoded = cwdForProject(rawDir) ?? decodeProjectName(rawDir);
1928
- const canonical = canonicalProjectName(cwdOrDecoded);
2272
+ const repo = projectRepoName(cwdOrDecoded);
1929
2273
  const wt = worktreeName(cwdOrDecoded);
1930
- const agg = canonicalMap.get(canonical) ?? {
1931
- canonicalName: canonical,
2274
+ const agg = canonicalMap.get(repo) ?? {
2275
+ canonicalName: repo,
1932
2276
  count: 0,
1933
2277
  lastActiveMs: 0,
1934
2278
  worktreeBranches: /* @__PURE__ */ new Set()
@@ -1938,7 +2282,7 @@ async function listProjects(root = DEFAULT_ROOT) {
1938
2282
  agg.lastActiveMs = lastActiveMs;
1939
2283
  if (wt)
1940
2284
  agg.worktreeBranches.add(wt);
1941
- canonicalMap.set(canonical, agg);
2285
+ canonicalMap.set(repo, agg);
1942
2286
  }
1943
2287
  return Array.from(canonicalMap.values()).map((a) => ({
1944
2288
  projectDir: a.canonicalName,
@@ -2838,76 +3182,705 @@ async function parseSession(file) {
2838
3182
  prevMs = ms;
2839
3183
  }
2840
3184
  }
2841
- const activeSegments = computeActiveSegments3(tsMs);
2842
- const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
2843
- const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
2844
- const sessionId = metaRec.sessionId ?? file.filenameId;
2845
- const projectName = cwd ? canonicalProjectName(cwd) : file.slug;
2846
- const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.slug;
3185
+ const activeSegments = computeActiveSegments3(tsMs);
3186
+ const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
3187
+ const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3188
+ const sessionId = metaRec.sessionId ?? file.filenameId;
3189
+ const projectName = cwd ? canonicalProjectName(cwd) : file.slug;
3190
+ const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.slug;
3191
+ const meta = {
3192
+ agent: "gemini",
3193
+ id: sessionId,
3194
+ filePath: file.filePath,
3195
+ projectName,
3196
+ projectDir,
3197
+ sessionId,
3198
+ firstTimestamp,
3199
+ lastTimestamp,
3200
+ durationMs: durationMs && Number.isFinite(durationMs) ? durationMs : void 0,
3201
+ eventCount: events.length,
3202
+ model,
3203
+ cwd,
3204
+ totalUsage,
3205
+ status: "idle",
3206
+ firstUserPreview,
3207
+ lastUserPreview,
3208
+ lastAgentPreview,
3209
+ toolCallCount,
3210
+ turnCount,
3211
+ airTimeMs,
3212
+ activeSegments
3213
+ };
3214
+ return { meta, events };
3215
+ }
3216
+ function computeActiveSegments3(tsMs) {
3217
+ if (tsMs.length === 0)
3218
+ return [];
3219
+ const sorted = [...tsMs].sort((a, b) => a - b);
3220
+ const out = [];
3221
+ let segStart = sorted[0];
3222
+ let segEnd = sorted[0];
3223
+ for (let i = 1; i < sorted.length; i++) {
3224
+ if (sorted[i] - segEnd > IDLE_GAP_MS2) {
3225
+ out.push({ startMs: segStart, endMs: segEnd });
3226
+ segStart = sorted[i];
3227
+ }
3228
+ segEnd = sorted[i];
3229
+ }
3230
+ out.push({ startMs: segStart, endMs: segEnd });
3231
+ return out;
3232
+ }
3233
+ function clearGeminiCaches() {
3234
+ metaCache3.clear();
3235
+ detailCache3.clear();
3236
+ projectsCache = null;
3237
+ }
3238
+ async function listGeminiSessions(opts = {}) {
3239
+ const root = opts.root ?? DEFAULT_GEMINI_ROOT;
3240
+ const files = await listSessionFiles(root);
3241
+ const out = [];
3242
+ for (const file of files) {
3243
+ const cached = metaCache3.get(file.filePath);
3244
+ if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes) {
3245
+ out.push(cached.meta);
3246
+ continue;
3247
+ }
3248
+ try {
3249
+ const { meta } = await parseSession(file);
3250
+ metaCache3.set(file.filePath, {
3251
+ meta,
3252
+ mtimeMs: file.mtimeMs,
3253
+ sizeBytes: file.sizeBytes
3254
+ });
3255
+ out.push(meta);
3256
+ } catch {
3257
+ }
3258
+ }
3259
+ out.sort((a, b) => (b.firstTimestamp ?? "").localeCompare(a.firstTimestamp ?? ""));
3260
+ if (opts.limit !== void 0)
3261
+ return out.slice(0, opts.limit);
3262
+ return out;
3263
+ }
3264
+ async function getGeminiSession(id, opts = {}) {
3265
+ const root = opts.root ?? DEFAULT_GEMINI_ROOT;
3266
+ const files = await listSessionFiles(root);
3267
+ let chosen;
3268
+ for (const file of files) {
3269
+ if (file.filenameId === id || id.startsWith(file.filenameId)) {
3270
+ chosen = file;
3271
+ break;
3272
+ }
3273
+ }
3274
+ if (!chosen) {
3275
+ for (const file of files) {
3276
+ const { meta: meta2 } = await parseSession(file).catch(() => ({ meta: { id: "" } }));
3277
+ if (meta2.id === id) {
3278
+ chosen = file;
3279
+ break;
3280
+ }
3281
+ }
3282
+ }
3283
+ if (!chosen)
3284
+ return null;
3285
+ const cached = detailCache3.get(chosen.filePath);
3286
+ if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes) {
3287
+ return cached.detail;
3288
+ }
3289
+ const { meta, events } = await parseSession(chosen);
3290
+ const detail = { ...meta, events };
3291
+ detailCache3.set(chosen.filePath, {
3292
+ detail,
3293
+ mtimeMs: chosen.mtimeMs,
3294
+ sizeBytes: chosen.sizeBytes
3295
+ });
3296
+ return detail;
3297
+ }
3298
+ function geminiSessionLocalDay(meta) {
3299
+ if (!meta.firstTimestamp)
3300
+ return void 0;
3301
+ const ms = Date.parse(meta.firstTimestamp);
3302
+ return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
3303
+ }
3304
+ var DEFAULT_GEMINI_ROOT, GEMINI_PROJECTS_FILE, SESSION_FILE_RE, IDLE_GAP_MS2, projectsCache, metaCache3, detailCache3;
3305
+ var init_gemini = __esm({
3306
+ "../parser/dist/gemini.js"() {
3307
+ "use strict";
3308
+ init_analytics();
3309
+ init_user_input();
3310
+ DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
3311
+ GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
3312
+ SESSION_FILE_RE = /^session-.+\.(jsonl|json)$/;
3313
+ IDLE_GAP_MS2 = 3 * 60 * 1e3;
3314
+ projectsCache = null;
3315
+ metaCache3 = /* @__PURE__ */ new Map();
3316
+ detailCache3 = /* @__PURE__ */ new Map();
3317
+ }
3318
+ });
3319
+
3320
+ // ../parser/dist/antigravity.js
3321
+ import { promises as fs4 } from "node:fs";
3322
+ import os4 from "node:os";
3323
+ import path4 from "node:path";
3324
+ async function safeReaddir3(p) {
3325
+ return fs4.readdir(p).catch(() => []);
3326
+ }
3327
+ async function loadSlugToCwd2(root) {
3328
+ const historyFile = path4.join(root, "history.jsonl");
3329
+ const stat = await fs4.stat(historyFile).catch(() => null);
3330
+ if (!stat)
3331
+ return /* @__PURE__ */ new Map();
3332
+ if (historyCache && historyCache.mtimeMs === stat.mtimeMs) {
3333
+ return historyCache.slugToCwd;
3334
+ }
3335
+ const raw = await fs4.readFile(historyFile, "utf8").catch(() => "");
3336
+ const slugToCwd = /* @__PURE__ */ new Map();
3337
+ for (const line of raw.split("\n")) {
3338
+ const trimmed = line.trim();
3339
+ if (!trimmed)
3340
+ continue;
3341
+ try {
3342
+ const parsed = JSON.parse(trimmed);
3343
+ if (parsed.conversationId && parsed.workspace) {
3344
+ slugToCwd.set(parsed.conversationId, parsed.workspace);
3345
+ }
3346
+ } catch {
3347
+ }
3348
+ }
3349
+ historyCache = { mtimeMs: stat.mtimeMs, slugToCwd };
3350
+ return slugToCwd;
3351
+ }
3352
+ async function listSessionFiles2(root) {
3353
+ const slugToCwd = await loadSlugToCwd2(root);
3354
+ const out = [];
3355
+ const brainDir = path4.join(root, "brain");
3356
+ const entries = await safeReaddir3(brainDir);
3357
+ for (const entry of entries) {
3358
+ const transcriptPath = path4.join(brainDir, entry, ".system_generated", "logs", "transcript.jsonl");
3359
+ const stat = await fs4.stat(transcriptPath).catch(() => null);
3360
+ if (stat && stat.isFile()) {
3361
+ out.push({
3362
+ filePath: transcriptPath,
3363
+ conversationId: entry,
3364
+ cwd: slugToCwd.get(entry),
3365
+ mtimeMs: stat.mtimeMs,
3366
+ sizeBytes: stat.size
3367
+ });
3368
+ }
3369
+ }
3370
+ return out;
3371
+ }
3372
+ function emptyUsage3() {
3373
+ return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
3374
+ }
3375
+ function previewOf3(text) {
3376
+ return text.replace(/\s+/g, " ").trim().slice(0, 200);
3377
+ }
3378
+ function truncate4(s, n) {
3379
+ return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
3380
+ }
3381
+ async function parseSession2(file) {
3382
+ const raw = await fs4.readFile(file.filePath, "utf8");
3383
+ const events = [];
3384
+ const totalUsage = emptyUsage3();
3385
+ let firstTimestamp;
3386
+ let lastTimestamp;
3387
+ let model;
3388
+ let toolCallCount = 0;
3389
+ let turnCount = 0;
3390
+ let firstUserPreview;
3391
+ let lastUserPreview;
3392
+ let lastAgentPreview;
3393
+ const tsMs = [];
3394
+ let idx = 0;
3395
+ function pushEvent(e) {
3396
+ events.push(e);
3397
+ if (e.timestamp && e.role !== "meta") {
3398
+ if (!firstTimestamp)
3399
+ firstTimestamp = e.timestamp;
3400
+ lastTimestamp = e.timestamp;
3401
+ const ms = Date.parse(e.timestamp);
3402
+ if (Number.isFinite(ms))
3403
+ tsMs.push(ms);
3404
+ }
3405
+ }
3406
+ const pendingToolCalls = [];
3407
+ const parsedObjects = [];
3408
+ const indexMap = /* @__PURE__ */ new Map();
3409
+ for (const line of raw.split("\n")) {
3410
+ const trimmed = line.trim();
3411
+ if (!trimmed)
3412
+ continue;
3413
+ try {
3414
+ const obj = JSON.parse(trimmed);
3415
+ const stepIdx = obj.step_index;
3416
+ if (typeof stepIdx === "number") {
3417
+ if (indexMap.has(stepIdx)) {
3418
+ parsedObjects[indexMap.get(stepIdx)] = obj;
3419
+ } else {
3420
+ indexMap.set(stepIdx, parsedObjects.length);
3421
+ parsedObjects.push(obj);
3422
+ }
3423
+ } else {
3424
+ parsedObjects.push(obj);
3425
+ }
3426
+ } catch {
3427
+ }
3428
+ }
3429
+ for (const eventObj of parsedObjects) {
3430
+ const ts = eventObj.created_at || eventObj.timestamp;
3431
+ if (eventObj.type === "USER_INPUT") {
3432
+ if (typeof eventObj.content === "string") {
3433
+ const matchNoNeed = eventObj.content.match(/Model Selection` from .*? to (.*?)\.\s*No need to/);
3434
+ if (matchNoNeed) {
3435
+ let rawModel = matchNoNeed[1].trim();
3436
+ rawModel = rawModel.replace(/\s*\(.*?\)\s*$/, "").trim();
3437
+ model = rawModel;
3438
+ } else {
3439
+ const matchDot = eventObj.content.match(/Model Selection` from .*? to ([^\n]+)/);
3440
+ if (matchDot) {
3441
+ let rawModel = matchDot[1].trim();
3442
+ if (rawModel.endsWith(".")) {
3443
+ rawModel = rawModel.slice(0, -1).trim();
3444
+ }
3445
+ rawModel = rawModel.replace(/\s*\(.*?\)\s*$/, "").trim();
3446
+ model = rawModel;
3447
+ }
3448
+ }
3449
+ }
3450
+ let text2 = eventObj.content || "";
3451
+ const requestMatch = text2.match(/<USER_REQUEST>([\s\S]*?)<\/USER_REQUEST>/);
3452
+ if (requestMatch) {
3453
+ text2 = requestMatch[1].trim();
3454
+ }
3455
+ const preview = previewOf3(text2);
3456
+ const isHidden = isFrameworkInjectedUserInput(text2);
3457
+ if (text2 && !isHidden) {
3458
+ if (!firstUserPreview)
3459
+ firstUserPreview = preview;
3460
+ lastUserPreview = preview;
3461
+ turnCount += 1;
3462
+ }
3463
+ pushEvent({
3464
+ index: idx++,
3465
+ timestamp: ts,
3466
+ role: "user",
3467
+ rawType: "antigravity/user",
3468
+ preview,
3469
+ blocks: text2 ? [{ type: "text", text: text2 }] : [],
3470
+ messageId: eventObj.step_index?.toString(),
3471
+ raw: eventObj
3472
+ });
3473
+ continue;
3474
+ }
3475
+ if (eventObj.type === "PLANNER_RESPONSE") {
3476
+ const thinking = eventObj.thinking;
3477
+ const content = eventObj.content;
3478
+ if (thinking) {
3479
+ pushEvent({
3480
+ index: idx++,
3481
+ timestamp: ts,
3482
+ role: "agent-thinking",
3483
+ rawType: "antigravity/thought",
3484
+ preview: previewOf3(thinking),
3485
+ blocks: [{ type: "thinking", thinking }],
3486
+ model,
3487
+ raw: eventObj
3488
+ });
3489
+ }
3490
+ if (content) {
3491
+ const preview = previewOf3(content);
3492
+ lastAgentPreview = preview;
3493
+ pushEvent({
3494
+ index: idx++,
3495
+ timestamp: ts,
3496
+ role: "agent",
3497
+ rawType: "antigravity/agent",
3498
+ preview,
3499
+ blocks: [{ type: "text", text: content }],
3500
+ messageId: eventObj.step_index?.toString(),
3501
+ model,
3502
+ raw: eventObj
3503
+ });
3504
+ }
3505
+ const toolCalls = eventObj.tool_calls || [];
3506
+ for (let cIdx = 0; cIdx < toolCalls.length; cIdx++) {
3507
+ const tc = toolCalls[cIdx];
3508
+ toolCallCount += 1;
3509
+ const toolUseId = `tool-${eventObj.step_index}-${cIdx}`;
3510
+ const argsStr = tc.args ? JSON.stringify(tc.args) : "";
3511
+ pendingToolCalls.push({ id: toolUseId, name: tc.name });
3512
+ pushEvent({
3513
+ index: idx++,
3514
+ timestamp: ts,
3515
+ role: "tool-call",
3516
+ rawType: "antigravity/tool_call",
3517
+ preview: `${tc.name}(${truncate4(argsStr, 80)})`,
3518
+ blocks: [{ type: "tool_use", id: toolUseId, name: tc.name, input: tc.args || {} }],
3519
+ toolName: tc.name,
3520
+ toolUseId,
3521
+ model,
3522
+ raw: tc
3523
+ });
3524
+ }
3525
+ continue;
3526
+ }
3527
+ if (eventObj.source === "MODEL" && eventObj.type !== "PLANNER_RESPONSE" && eventObj.type !== "ERROR_MESSAGE" && pendingToolCalls.length > 0) {
3528
+ const matchedCall = pendingToolCalls.shift();
3529
+ const toolUseId = matchedCall ? matchedCall.id : `tool-${eventObj.step_index}-unknown`;
3530
+ const contentStr = typeof eventObj.content === "string" ? eventObj.content : JSON.stringify(eventObj.content || "");
3531
+ const isError = eventObj.status === "ERROR" || eventObj.status === "FAILED" || eventObj.status === "CANCELED" || !!eventObj.error;
3532
+ pushEvent({
3533
+ index: idx++,
3534
+ timestamp: ts,
3535
+ role: "tool-result",
3536
+ rawType: "antigravity/tool_result",
3537
+ preview: previewOf3(contentStr),
3538
+ blocks: [{
3539
+ type: "tool_result",
3540
+ tool_use_id: toolUseId,
3541
+ content: contentStr,
3542
+ is_error: isError
3543
+ }],
3544
+ toolUseId,
3545
+ toolResult: contentStr,
3546
+ raw: eventObj
3547
+ });
3548
+ continue;
3549
+ }
3550
+ const text = eventObj.content || eventObj.error || "";
3551
+ pushEvent({
3552
+ index: idx++,
3553
+ timestamp: ts,
3554
+ role: "meta",
3555
+ rawType: `antigravity/${eventObj.type?.toLowerCase() || "unknown"}`,
3556
+ preview: previewOf3(text),
3557
+ blocks: text ? [{ type: "text", text }] : [],
3558
+ raw: eventObj
3559
+ });
3560
+ }
3561
+ const startMs = firstTimestamp ? Date.parse(firstTimestamp) : void 0;
3562
+ if (startMs !== void 0 && Number.isFinite(startMs)) {
3563
+ let prevMs;
3564
+ for (const ev of events) {
3565
+ if (!ev.timestamp)
3566
+ continue;
3567
+ const ms = Date.parse(ev.timestamp);
3568
+ if (!Number.isFinite(ms))
3569
+ continue;
3570
+ ev.tOffsetMs = Math.max(0, ms - startMs);
3571
+ if (prevMs !== void 0)
3572
+ ev.gapMs = Math.max(0, ms - prevMs);
3573
+ prevMs = ms;
3574
+ }
3575
+ }
3576
+ const activeSegments = computeActiveSegments4(tsMs);
3577
+ const airTimeMs = activeSegments.reduce((acc, s) => acc + (s.endMs - s.startMs), 0);
3578
+ const durationMs = firstTimestamp && lastTimestamp ? Date.parse(lastTimestamp) - Date.parse(firstTimestamp) : void 0;
3579
+ const sessionId = file.conversationId;
3580
+ const cwd = file.cwd;
3581
+ const projectName = cwd ? canonicalProjectName(cwd) : file.conversationId;
3582
+ const projectDir = cwd ? cwd.replace(/^\//, "-").replace(/\//g, "-") : file.conversationId;
3583
+ const meta = {
3584
+ agent: "antigravity",
3585
+ id: sessionId,
3586
+ filePath: file.filePath,
3587
+ projectName,
3588
+ projectDir,
3589
+ sessionId,
3590
+ firstTimestamp,
3591
+ lastTimestamp,
3592
+ durationMs: durationMs && Number.isFinite(durationMs) ? durationMs : void 0,
3593
+ eventCount: events.length,
3594
+ model,
3595
+ cwd,
3596
+ totalUsage,
3597
+ status: "idle",
3598
+ firstUserPreview,
3599
+ lastUserPreview,
3600
+ lastAgentPreview,
3601
+ toolCallCount,
3602
+ turnCount,
3603
+ airTimeMs,
3604
+ activeSegments
3605
+ };
3606
+ return { meta, events };
3607
+ }
3608
+ function computeActiveSegments4(tsMs) {
3609
+ if (tsMs.length === 0)
3610
+ return [];
3611
+ const sorted = [...tsMs].sort((a, b) => a - b);
3612
+ const out = [];
3613
+ let segStart = sorted[0];
3614
+ let segEnd = sorted[0];
3615
+ for (let i = 1; i < sorted.length; i++) {
3616
+ if (sorted[i] - segEnd > IDLE_GAP_MS3) {
3617
+ out.push({ startMs: segStart, endMs: segEnd });
3618
+ segStart = sorted[i];
3619
+ }
3620
+ segEnd = sorted[i];
3621
+ }
3622
+ out.push({ startMs: segStart, endMs: segEnd });
3623
+ return out;
3624
+ }
3625
+ function clearAntigravityCaches() {
3626
+ metaCache4.clear();
3627
+ detailCache4.clear();
3628
+ historyCache = null;
3629
+ }
3630
+ async function listAntigravitySessions(opts = {}) {
3631
+ const root = opts.root ?? DEFAULT_ANTIGRAVITY_ROOT;
3632
+ const files = await listSessionFiles2(root);
3633
+ const out = [];
3634
+ for (const file of files) {
3635
+ const cached = metaCache4.get(file.filePath);
3636
+ if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes && cached.cwd === file.cwd) {
3637
+ out.push(cached.meta);
3638
+ continue;
3639
+ }
3640
+ try {
3641
+ const { meta } = await parseSession2(file);
3642
+ metaCache4.set(file.filePath, {
3643
+ meta,
3644
+ mtimeMs: file.mtimeMs,
3645
+ sizeBytes: file.sizeBytes,
3646
+ cwd: file.cwd
3647
+ });
3648
+ out.push(meta);
3649
+ } catch {
3650
+ }
3651
+ }
3652
+ out.sort((a, b) => (b.firstTimestamp ?? "").localeCompare(a.firstTimestamp ?? ""));
3653
+ if (opts.limit !== void 0)
3654
+ return out.slice(0, opts.limit);
3655
+ return out;
3656
+ }
3657
+ async function getAntigravitySession(id, opts = {}) {
3658
+ const root = opts.root ?? DEFAULT_ANTIGRAVITY_ROOT;
3659
+ const files = await listSessionFiles2(root);
3660
+ let chosen;
3661
+ for (const file of files) {
3662
+ if (file.conversationId === id) {
3663
+ chosen = file;
3664
+ break;
3665
+ }
3666
+ }
3667
+ if (!chosen)
3668
+ return null;
3669
+ const cached = detailCache4.get(chosen.filePath);
3670
+ if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes && cached.cwd === chosen.cwd) {
3671
+ return cached.detail;
3672
+ }
3673
+ const { meta, events } = await parseSession2(chosen);
3674
+ const detail = { ...meta, events };
3675
+ detailCache4.set(chosen.filePath, {
3676
+ detail,
3677
+ mtimeMs: chosen.mtimeMs,
3678
+ sizeBytes: chosen.sizeBytes,
3679
+ cwd: chosen.cwd
3680
+ });
3681
+ return detail;
3682
+ }
3683
+ function antigravitySessionLocalDay(meta) {
3684
+ if (!meta.firstTimestamp)
3685
+ return void 0;
3686
+ const ms = Date.parse(meta.firstTimestamp);
3687
+ return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
3688
+ }
3689
+ var DEFAULT_ANTIGRAVITY_ROOT, IDLE_GAP_MS3, historyCache, metaCache4, detailCache4;
3690
+ var init_antigravity = __esm({
3691
+ "../parser/dist/antigravity.js"() {
3692
+ "use strict";
3693
+ init_analytics();
3694
+ init_user_input();
3695
+ DEFAULT_ANTIGRAVITY_ROOT = path4.join(os4.homedir(), ".gemini", "antigravity-cli");
3696
+ IDLE_GAP_MS3 = 3 * 60 * 1e3;
3697
+ historyCache = null;
3698
+ metaCache4 = /* @__PURE__ */ new Map();
3699
+ detailCache4 = /* @__PURE__ */ new Map();
3700
+ }
3701
+ });
3702
+
3703
+ // ../parser/dist/cowork.js
3704
+ import { promises as fs5 } from "node:fs";
3705
+ import os5 from "node:os";
3706
+ import path5 from "node:path";
3707
+ function defaultCoworkRoot() {
3708
+ const home = os5.homedir();
3709
+ if (process.platform === "darwin") {
3710
+ return path5.join(home, "Library", "Application Support", "Claude", "local-agent-mode-sessions");
3711
+ }
3712
+ if (process.platform === "win32") {
3713
+ const appData = process.env.APPDATA ?? path5.join(home, "AppData", "Roaming");
3714
+ return path5.join(appData, "Claude", "local-agent-mode-sessions");
3715
+ }
3716
+ return path5.join(home, ".config", "Claude", "local-agent-mode-sessions");
3717
+ }
3718
+ async function safeReaddir4(p) {
3719
+ return fs5.readdir(p).catch(() => []);
3720
+ }
3721
+ async function statMs(p) {
3722
+ const s = await fs5.stat(p).catch(() => null);
3723
+ return s ? s.mtimeMs : 0;
3724
+ }
3725
+ async function listSessionFiles3(root) {
3726
+ const out = [];
3727
+ const accounts = await safeReaddir4(root);
3728
+ for (const accountId of accounts) {
3729
+ const accountDir = path5.join(root, accountId);
3730
+ const workspaces = await safeReaddir4(accountDir);
3731
+ for (const workspaceId of workspaces) {
3732
+ const workspaceDir = path5.join(accountDir, workspaceId);
3733
+ const spacesMtimeMs = await statMs(path5.join(workspaceDir, "spaces.json"));
3734
+ const entries = await safeReaddir4(workspaceDir);
3735
+ for (const entry of entries) {
3736
+ if (!SESSION_ID_RE.test(entry))
3737
+ continue;
3738
+ const auditPath = path5.join(workspaceDir, entry, "audit.jsonl");
3739
+ const auditStat = await fs5.stat(auditPath).catch(() => null);
3740
+ if (!auditStat || !auditStat.isFile() || auditStat.size === 0)
3741
+ continue;
3742
+ const metaPath = path5.join(workspaceDir, `${entry}.json`);
3743
+ const metaMtimeMs = await statMs(metaPath);
3744
+ out.push({
3745
+ sessionId: entry,
3746
+ auditPath,
3747
+ metaPath,
3748
+ workspaceDir,
3749
+ auditMtimeMs: auditStat.mtimeMs,
3750
+ auditSize: auditStat.size,
3751
+ metaMtimeMs,
3752
+ spacesMtimeMs
3753
+ });
3754
+ }
3755
+ }
3756
+ }
3757
+ return out;
3758
+ }
3759
+ async function loadSpacesForWorkspace(workspaceDir) {
3760
+ const spacesPath = path5.join(workspaceDir, "spaces.json");
3761
+ const stat = await fs5.stat(spacesPath).catch(() => null);
3762
+ if (!stat)
3763
+ return /* @__PURE__ */ new Map();
3764
+ const cached = spacesCache.get(workspaceDir);
3765
+ if (cached && cached.mtimeMs === stat.mtimeMs)
3766
+ return cached.spaceIdToPath;
3767
+ const raw = await fs5.readFile(spacesPath, "utf8").catch(() => "");
3768
+ const out = /* @__PURE__ */ new Map();
3769
+ try {
3770
+ const parsed = JSON.parse(raw);
3771
+ for (const space of parsed.spaces ?? []) {
3772
+ const id = typeof space.id === "string" ? space.id : void 0;
3773
+ const folders = Array.isArray(space.folders) ? space.folders : [];
3774
+ const first = folders[0];
3775
+ const folderPath = typeof first?.path === "string" ? first.path : void 0;
3776
+ if (id && folderPath)
3777
+ out.set(id, folderPath);
3778
+ }
3779
+ } catch {
3780
+ }
3781
+ spacesCache.set(workspaceDir, { mtimeMs: stat.mtimeMs, spaceIdToPath: out });
3782
+ return out;
3783
+ }
3784
+ async function readCoworkMeta(metaPath) {
3785
+ const raw = await fs5.readFile(metaPath, "utf8").catch(() => "");
3786
+ if (!raw)
3787
+ return {};
3788
+ try {
3789
+ const j = JSON.parse(raw);
3790
+ return {
3791
+ spaceId: typeof j.spaceId === "string" ? j.spaceId : void 0,
3792
+ userSelectedFolders: Array.isArray(j.userSelectedFolders) ? j.userSelectedFolders.filter((s) => typeof s === "string") : void 0,
3793
+ cwd: typeof j.cwd === "string" ? j.cwd : void 0,
3794
+ model: typeof j.model === "string" ? j.model : void 0,
3795
+ title: typeof j.title === "string" ? j.title : void 0,
3796
+ createdAt: typeof j.createdAt === "number" ? j.createdAt : void 0,
3797
+ lastActivityAt: typeof j.lastActivityAt === "number" ? j.lastActivityAt : void 0
3798
+ };
3799
+ } catch {
3800
+ return {};
3801
+ }
3802
+ }
3803
+ function resolveProject(meta, spaceIdToPath) {
3804
+ let projectPath;
3805
+ if (meta.spaceId)
3806
+ projectPath = spaceIdToPath.get(meta.spaceId);
3807
+ if (!projectPath && meta.userSelectedFolders && meta.userSelectedFolders.length > 0) {
3808
+ projectPath = meta.userSelectedFolders[0];
3809
+ }
3810
+ if (!projectPath) {
3811
+ return { projectName: "cowork:unspaced", projectDir: "cowork-unspaced" };
3812
+ }
3813
+ return {
3814
+ projectName: canonicalProjectName(projectPath),
3815
+ projectDir: projectPath.replace(/^\//, "-").replace(/\//g, "-"),
3816
+ cwd: projectPath
3817
+ };
3818
+ }
3819
+ async function readJsonl2(filePath) {
3820
+ const raw = await fs5.readFile(filePath, "utf8");
3821
+ const out = [];
3822
+ for (const line of raw.split("\n")) {
3823
+ const trimmed = line.trim();
3824
+ if (!trimmed)
3825
+ continue;
3826
+ try {
3827
+ out.push(JSON.parse(trimmed));
3828
+ } catch {
3829
+ }
3830
+ }
3831
+ return out;
3832
+ }
3833
+ async function parseCoworkSession(file) {
3834
+ const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
3835
+ readJsonl2(file.auditPath),
3836
+ readCoworkMeta(file.metaPath),
3837
+ loadSpacesForWorkspace(file.workspaceDir)
3838
+ ]);
3839
+ const { meta: baseMeta, events } = parseTranscript(lines);
3840
+ const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
2847
3841
  const meta = {
2848
- agent: "gemini",
2849
- id: sessionId,
2850
- filePath: file.filePath,
3842
+ ...baseMeta,
3843
+ agent: "cowork",
3844
+ id: file.sessionId,
3845
+ sessionId: file.sessionId,
3846
+ filePath: file.auditPath,
2851
3847
  projectName,
2852
3848
  projectDir,
2853
- sessionId,
2854
- firstTimestamp,
2855
- lastTimestamp,
2856
- durationMs: durationMs && Number.isFinite(durationMs) ? durationMs : void 0,
2857
- eventCount: events.length,
2858
- model,
2859
- cwd,
2860
- totalUsage,
2861
- status: "idle",
2862
- firstUserPreview,
2863
- lastUserPreview,
2864
- lastAgentPreview,
2865
- toolCallCount,
2866
- turnCount,
2867
- airTimeMs,
2868
- activeSegments
3849
+ cwd: cwd ?? baseMeta.cwd,
3850
+ model: baseMeta.model ?? coworkMeta.model
2869
3851
  };
2870
3852
  return { meta, events };
2871
3853
  }
2872
- function computeActiveSegments3(tsMs) {
2873
- if (tsMs.length === 0)
2874
- return [];
2875
- const sorted = [...tsMs].sort((a, b) => a - b);
2876
- const out = [];
2877
- let segStart = sorted[0];
2878
- let segEnd = sorted[0];
2879
- for (let i = 1; i < sorted.length; i++) {
2880
- if (sorted[i] - segEnd > IDLE_GAP_MS2) {
2881
- out.push({ startMs: segStart, endMs: segEnd });
2882
- segStart = sorted[i];
2883
- }
2884
- segEnd = sorted[i];
2885
- }
2886
- out.push({ startMs: segStart, endMs: segEnd });
2887
- return out;
3854
+ function cacheKeyFor(file) {
3855
+ return {
3856
+ auditMtimeMs: file.auditMtimeMs,
3857
+ auditSize: file.auditSize,
3858
+ metaMtimeMs: file.metaMtimeMs,
3859
+ spacesMtimeMs: file.spacesMtimeMs
3860
+ };
2888
3861
  }
2889
- function clearGeminiCaches() {
2890
- metaCache3.clear();
2891
- detailCache3.clear();
2892
- projectsCache = null;
3862
+ function cacheKeyMatches(a, b) {
3863
+ return a.auditMtimeMs === b.auditMtimeMs && a.auditSize === b.auditSize && a.metaMtimeMs === b.metaMtimeMs && a.spacesMtimeMs === b.spacesMtimeMs;
2893
3864
  }
2894
- async function listGeminiSessions(opts = {}) {
2895
- const root = opts.root ?? DEFAULT_GEMINI_ROOT;
2896
- const files = await listSessionFiles(root);
3865
+ function clearCoworkCaches() {
3866
+ metaCache5.clear();
3867
+ detailCache5.clear();
3868
+ spacesCache.clear();
3869
+ }
3870
+ async function listCoworkSessions(opts = {}) {
3871
+ const root = opts.root ?? DEFAULT_COWORK_ROOT;
3872
+ const files = await listSessionFiles3(root);
2897
3873
  const out = [];
2898
3874
  for (const file of files) {
2899
- const cached = metaCache3.get(file.filePath);
2900
- if (cached && cached.mtimeMs === file.mtimeMs && cached.sizeBytes === file.sizeBytes) {
3875
+ const key = cacheKeyFor(file);
3876
+ const cached = metaCache5.get(file.auditPath);
3877
+ if (cached && cacheKeyMatches(cached.key, key)) {
2901
3878
  out.push(cached.meta);
2902
3879
  continue;
2903
3880
  }
2904
3881
  try {
2905
- const { meta } = await parseSession(file);
2906
- metaCache3.set(file.filePath, {
2907
- meta,
2908
- mtimeMs: file.mtimeMs,
2909
- sizeBytes: file.sizeBytes
2910
- });
3882
+ const { meta } = await parseCoworkSession(file);
3883
+ metaCache5.set(file.auditPath, { meta, key });
2911
3884
  out.push(meta);
2912
3885
  } catch {
2913
3886
  }
@@ -2917,59 +3890,38 @@ async function listGeminiSessions(opts = {}) {
2917
3890
  return out.slice(0, opts.limit);
2918
3891
  return out;
2919
3892
  }
2920
- async function getGeminiSession(id, opts = {}) {
2921
- const root = opts.root ?? DEFAULT_GEMINI_ROOT;
2922
- const files = await listSessionFiles(root);
2923
- let chosen;
2924
- for (const file of files) {
2925
- if (file.filenameId === id || id.startsWith(file.filenameId)) {
2926
- chosen = file;
2927
- break;
2928
- }
2929
- }
2930
- if (!chosen) {
2931
- for (const file of files) {
2932
- const { meta: meta2 } = await parseSession(file).catch(() => ({ meta: { id: "" } }));
2933
- if (meta2.id === id) {
2934
- chosen = file;
2935
- break;
2936
- }
2937
- }
2938
- }
2939
- if (!chosen)
3893
+ async function getCoworkSession(id, opts = {}) {
3894
+ const root = opts.root ?? DEFAULT_COWORK_ROOT;
3895
+ const files = await listSessionFiles3(root);
3896
+ const file = files.find((f) => f.sessionId === id);
3897
+ if (!file)
2940
3898
  return null;
2941
- const cached = detailCache3.get(chosen.filePath);
2942
- if (cached && cached.mtimeMs === chosen.mtimeMs && cached.sizeBytes === chosen.sizeBytes) {
3899
+ const key = cacheKeyFor(file);
3900
+ const cached = detailCache5.get(file.auditPath);
3901
+ if (cached && cacheKeyMatches(cached.key, key))
2943
3902
  return cached.detail;
2944
- }
2945
- const { meta, events } = await parseSession(chosen);
3903
+ const { meta, events } = await parseCoworkSession(file);
2946
3904
  const detail = { ...meta, events };
2947
- detailCache3.set(chosen.filePath, {
2948
- detail,
2949
- mtimeMs: chosen.mtimeMs,
2950
- sizeBytes: chosen.sizeBytes
2951
- });
3905
+ detailCache5.set(file.auditPath, { detail, key });
2952
3906
  return detail;
2953
3907
  }
2954
- function geminiSessionLocalDay(meta) {
3908
+ function coworkSessionLocalDay(meta) {
2955
3909
  if (!meta.firstTimestamp)
2956
3910
  return void 0;
2957
3911
  const ms = Date.parse(meta.firstTimestamp);
2958
3912
  return Number.isFinite(ms) ? toLocalDay(ms) : void 0;
2959
3913
  }
2960
- var DEFAULT_GEMINI_ROOT, GEMINI_PROJECTS_FILE, SESSION_FILE_RE, IDLE_GAP_MS2, projectsCache, metaCache3, detailCache3;
2961
- var init_gemini = __esm({
2962
- "../parser/dist/gemini.js"() {
3914
+ var DEFAULT_COWORK_ROOT, SESSION_ID_RE, spacesCache, metaCache5, detailCache5;
3915
+ var init_cowork = __esm({
3916
+ "../parser/dist/cowork.js"() {
2963
3917
  "use strict";
2964
3918
  init_analytics();
2965
- init_user_input();
2966
- DEFAULT_GEMINI_ROOT = path3.join(os3.homedir(), ".gemini", "tmp");
2967
- GEMINI_PROJECTS_FILE = path3.join(os3.homedir(), ".gemini", "projects.json");
2968
- SESSION_FILE_RE = /^session-.+\.(jsonl|json)$/;
2969
- IDLE_GAP_MS2 = 3 * 60 * 1e3;
2970
- projectsCache = null;
2971
- metaCache3 = /* @__PURE__ */ new Map();
2972
- detailCache3 = /* @__PURE__ */ new Map();
3919
+ init_parser();
3920
+ DEFAULT_COWORK_ROOT = defaultCoworkRoot();
3921
+ SESSION_ID_RE = /^local_[0-9a-f-]+$/;
3922
+ spacesCache = /* @__PURE__ */ new Map();
3923
+ metaCache5 = /* @__PURE__ */ new Map();
3924
+ detailCache5 = /* @__PURE__ */ new Map();
2973
3925
  }
2974
3926
  });
2975
3927
 
@@ -3017,11 +3969,14 @@ var init_team_wire = __esm({
3017
3969
  // ../parser/dist/fs.js
3018
3970
  var fs_exports = {};
3019
3971
  __export(fs_exports, {
3972
+ DEFAULT_ANTIGRAVITY_ROOT: () => DEFAULT_ANTIGRAVITY_ROOT,
3020
3973
  DEFAULT_CODEX_ROOT: () => DEFAULT_CODEX_ROOT,
3974
+ DEFAULT_COWORK_ROOT: () => DEFAULT_COWORK_ROOT,
3021
3975
  DEFAULT_GEMINI_ROOT: () => DEFAULT_GEMINI_ROOT,
3022
3976
  DEFAULT_ROOT: () => DEFAULT_ROOT,
3023
3977
  RICH_ROLLUP_SCHEMA_VERSION: () => RICH_ROLLUP_SCHEMA_VERSION,
3024
3978
  agentSources: () => agentSources,
3979
+ antigravitySessionLocalDay: () => antigravitySessionLocalDay,
3025
3980
  buildCalibrationCurve: () => buildCalibrationCurve,
3026
3981
  cacheStats: () => cacheStats,
3027
3982
  cclensHome: () => cclensHome,
@@ -3029,19 +3984,24 @@ __export(fs_exports, {
3029
3984
  clearCaches: () => clearCaches,
3030
3985
  clearTeamConfig: () => clearTeamConfig,
3031
3986
  codexSessionLocalDay: () => codexSessionLocalDay,
3987
+ coworkSessionLocalDay: () => coworkSessionLocalDay,
3032
3988
  decodeProjectName: () => decodeProjectName,
3033
3989
  findTeamLead: () => findTeamLead,
3034
3990
  geminiSessionLocalDay: () => geminiSessionLocalDay,
3035
3991
  getAgentSource: () => getAgentSource,
3992
+ getAntigravitySession: () => getAntigravitySession,
3036
3993
  getAnySession: () => getAnySession,
3037
3994
  getCodexSession: () => getCodexSession,
3995
+ getCoworkSession: () => getCoworkSession,
3038
3996
  getGeminiSession: () => getGeminiSession,
3039
3997
  getLatestCodexUsage: () => getLatestCodexUsage,
3040
3998
  getSession: () => getSession,
3041
3999
  invalidateFile: () => invalidateFile,
3042
4000
  isFleetlensRuntimeDir: () => isFleetlensRuntimeDir,
3043
4001
  listAllSessions: () => listAllSessions,
4002
+ listAntigravitySessions: () => listAntigravitySessions,
3044
4003
  listCodexSessions: () => listCodexSessions,
4004
+ listCoworkSessions: () => listCoworkSessions,
3045
4005
  listGeminiSessions: () => listGeminiSessions,
3046
4006
  listProjects: () => listProjects,
3047
4007
  listSessions: () => listSessions,
@@ -3050,20 +4010,21 @@ __export(fs_exports, {
3050
4010
  loadCalibrationSnapshots: () => loadCalibrationSnapshots,
3051
4011
  loadTeamForSession: () => loadTeamForSession,
3052
4012
  loadUsageByDay: () => loadUsageByDay,
4013
+ loadWorkflowAgentDetail: () => loadWorkflowAgentDetail,
3053
4014
  readJsonlFile: () => readJsonlFile,
3054
4015
  readTeamConfig: () => readTeamConfig,
3055
4016
  sessionIdFromFileName: () => sessionIdFromFileName,
3056
4017
  walkJsonlFiles: () => walkJsonlFiles,
3057
4018
  writeTeamConfig: () => writeTeamConfig
3058
4019
  });
3059
- import { promises as fs4 } from "node:fs";
3060
- import os4 from "node:os";
3061
- import path4 from "node:path";
4020
+ import { promises as fs6 } from "node:fs";
4021
+ import os6 from "node:os";
4022
+ import path6 from "node:path";
3062
4023
  function cclensHome() {
3063
- return process.env.CCLENS_HOME || path4.join(os4.homedir(), ".cclens");
4024
+ return process.env.CCLENS_HOME || path6.join(os6.homedir(), ".cclens");
3064
4025
  }
3065
4026
  function cclensPath(...parts) {
3066
- return path4.join(cclensHome(), ...parts);
4027
+ return path6.join(cclensHome(), ...parts);
3067
4028
  }
3068
4029
  function cacheStats() {
3069
4030
  return claudeCodeCacheStats();
@@ -3072,6 +4033,8 @@ function clearCaches() {
3072
4033
  clearClaudeCodeCaches();
3073
4034
  clearCodexCaches();
3074
4035
  clearGeminiCaches();
4036
+ clearAntigravityCaches();
4037
+ clearCoworkCaches();
3075
4038
  }
3076
4039
  function invalidateFile(fullPath) {
3077
4040
  invalidateClaudeCodeFile(fullPath);
@@ -3103,7 +4066,7 @@ function usageLogPath() {
3103
4066
  async function loadUsageByDay(start, end) {
3104
4067
  let raw;
3105
4068
  try {
3106
- raw = await fs4.readFile(usageLogPath(), "utf8");
4069
+ raw = await fs6.readFile(usageLogPath(), "utf8");
3107
4070
  } catch {
3108
4071
  return { by_day: [] };
3109
4072
  }
@@ -3148,7 +4111,7 @@ async function loadUsageByDay(start, end) {
3148
4111
  async function loadCalibrationSnapshots() {
3149
4112
  let raw;
3150
4113
  try {
3151
- raw = await fs4.readFile(usageLogPath(), "utf8");
4114
+ raw = await fs6.readFile(usageLogPath(), "utf8");
3152
4115
  } catch {
3153
4116
  return [];
3154
4117
  }
@@ -3306,7 +4269,7 @@ async function loadCalibrationCurve(tier = "pro-max-20x", root = DEFAULT_ROOT, g
3306
4269
  ]);
3307
4270
  return buildCalibrationCurve(events, snapshots, tier, granularityMin);
3308
4271
  }
3309
- var codexSource, geminiSource, agentSources;
4272
+ var codexSource, geminiSource, antigravitySource, coworkSource, agentSources;
3310
4273
  var init_fs = __esm({
3311
4274
  "../parser/dist/fs.js"() {
3312
4275
  "use strict";
@@ -3316,9 +4279,13 @@ var init_fs = __esm({
3316
4279
  init_claude_code();
3317
4280
  init_codex();
3318
4281
  init_gemini();
4282
+ init_antigravity();
4283
+ init_cowork();
3319
4284
  init_claude_code();
3320
4285
  init_codex();
3321
4286
  init_gemini();
4287
+ init_antigravity();
4288
+ init_cowork();
3322
4289
  init_team_config();
3323
4290
  init_team_wire();
3324
4291
  codexSource = {
@@ -3352,7 +4319,33 @@ var init_fs = __esm({
3352
4319
  return getGeminiSession(id);
3353
4320
  }
3354
4321
  };
3355
- agentSources = [claudeCodeSource, codexSource, geminiSource];
4322
+ antigravitySource = {
4323
+ ...ANTIGRAVITY_METADATA,
4324
+ defaultRoot: DEFAULT_ANTIGRAVITY_ROOT,
4325
+ async listSessions(opts) {
4326
+ return listAntigravitySessions(opts);
4327
+ },
4328
+ async getSession(id) {
4329
+ return getAntigravitySession(id);
4330
+ }
4331
+ };
4332
+ coworkSource = {
4333
+ ...COWORK_METADATA,
4334
+ defaultRoot: DEFAULT_COWORK_ROOT,
4335
+ async listSessions(opts) {
4336
+ return listCoworkSessions(opts);
4337
+ },
4338
+ async getSession(id) {
4339
+ return getCoworkSession(id);
4340
+ }
4341
+ };
4342
+ agentSources = [
4343
+ claudeCodeSource,
4344
+ codexSource,
4345
+ geminiSource,
4346
+ antigravitySource,
4347
+ coworkSource
4348
+ ];
3356
4349
  }
3357
4350
  });
3358
4351
 
@@ -3764,8 +4757,10 @@ var init_aggregate = __esm({
3764
4757
  // ../parser/dist/index.js
3765
4758
  var dist_exports = {};
3766
4759
  __export(dist_exports, {
4760
+ ANTIGRAVITY_METADATA: () => ANTIGRAVITY_METADATA,
3767
4761
  CLAUDE_CODE_METADATA: () => CLAUDE_CODE_METADATA,
3768
4762
  CODEX_METADATA: () => CODEX_METADATA,
4763
+ COWORK_METADATA: () => COWORK_METADATA,
3769
4764
  GEMINI_METADATA: () => GEMINI_METADATA,
3770
4765
  PRICES: () => PRICES,
3771
4766
  RATE_PER_PCT: () => RATE_PER_PCT,
@@ -3806,6 +4801,7 @@ __export(dist_exports, {
3806
4801
  predictUtilization: () => predictUtilization,
3807
4802
  priorCalendarMonth: () => priorCalendarMonth,
3808
4803
  priorCalendarWeek: () => priorCalendarWeek,
4804
+ projectRepoName: () => projectRepoName,
3809
4805
  rowPrimaryIndex: () => rowPrimaryIndex,
3810
4806
  sessionAirTimeMs: () => sessionAirTimeMs,
3811
4807
  sessionDay: () => sessionDay,
@@ -3830,10 +4826,198 @@ var init_dist = __esm({
3830
4826
  }
3831
4827
  });
3832
4828
 
4829
+ // src/perception/file-probe.ts
4830
+ var file_probe_exports = {};
4831
+ __export(file_probe_exports, {
4832
+ pathHash: () => pathHash,
4833
+ probeArtifactSignals: () => probeArtifactSignals
4834
+ });
4835
+ import { createHash } from "node:crypto";
4836
+ import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
4837
+ import { homedir as homedir2 } from "node:os";
4838
+ import { join as join7, relative, resolve as resolve2, sep } from "node:path";
4839
+ import { execFileSync as execFileSync2 } from "node:child_process";
4840
+ function pathHash(relPath) {
4841
+ return createHash("sha256").update(relPath.split(sep).join("/")).digest("hex").slice(0, 32);
4842
+ }
4843
+ function defaultScanRoots(extraRoots = []) {
4844
+ const roots = [];
4845
+ const home = join7(homedir2(), ".claude");
4846
+ if (existsSync6(home)) roots.push({ root: home, label: "user" });
4847
+ for (const e of extraRoots) {
4848
+ const r = resolve2(e, ".claude");
4849
+ if (existsSync6(r) && r !== home) roots.push({ root: r, label: "project" });
4850
+ }
4851
+ return roots;
4852
+ }
4853
+ function listMarkdownFiles(dir) {
4854
+ if (!existsSync6(dir)) return [];
4855
+ const out = [];
4856
+ let entries;
4857
+ try {
4858
+ entries = readdirSync3(dir);
4859
+ } catch {
4860
+ return [];
4861
+ }
4862
+ for (const name of entries) {
4863
+ if (name.startsWith(".")) continue;
4864
+ const full = join7(dir, name);
4865
+ let stat;
4866
+ try {
4867
+ stat = statSync3(full);
4868
+ } catch {
4869
+ continue;
4870
+ }
4871
+ if (stat.isDirectory()) {
4872
+ out.push(...listMarkdownFiles(full));
4873
+ } else if (stat.isFile() && name.endsWith(".md")) {
4874
+ out.push(full);
4875
+ }
4876
+ }
4877
+ return out;
4878
+ }
4879
+ function firstSeenDateOf(file) {
4880
+ try {
4881
+ const out = execFileSync2(
4882
+ "git",
4883
+ ["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
4884
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname6(file) }
4885
+ ).trim().split("\n").filter(Boolean);
4886
+ if (out.length > 0) return out[out.length - 1];
4887
+ } catch {
4888
+ }
4889
+ try {
4890
+ const s = statSync3(file);
4891
+ return new Date((s.birthtime || s.mtime).getTime()).toISOString().slice(0, 10);
4892
+ } catch {
4893
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4894
+ }
4895
+ }
4896
+ function dirname6(p) {
4897
+ const i = p.lastIndexOf(sep);
4898
+ return i < 0 ? "." : p.slice(0, i);
4899
+ }
4900
+ function detectGitEmail(cwd) {
4901
+ try {
4902
+ return execFileSync2("git", ["config", "user.email"], {
4903
+ encoding: "utf8",
4904
+ stdio: ["ignore", "pipe", "ignore"],
4905
+ cwd
4906
+ }).trim() || null;
4907
+ } catch {
4908
+ return null;
4909
+ }
4910
+ }
4911
+ function todayLocal() {
4912
+ const d = /* @__PURE__ */ new Date();
4913
+ const y = d.getFullYear();
4914
+ const m = String(d.getMonth() + 1).padStart(2, "0");
4915
+ const day = String(d.getDate()).padStart(2, "0");
4916
+ return `${y}-${m}-${day}`;
4917
+ }
4918
+ function scanAuthored(roots, subdir, day) {
4919
+ const out = [];
4920
+ for (const r of roots) {
4921
+ const dir = join7(r.root, subdir);
4922
+ if (!existsSync6(dir)) continue;
4923
+ for (const f of listMarkdownFiles(dir)) {
4924
+ const firstSeen = firstSeenDateOf(f);
4925
+ if (firstSeen !== day) continue;
4926
+ const rel = relative(dir, f);
4927
+ out.push({ pathHash: pathHash(`${r.label}:${subdir}:${rel}`), firstSeenDate: firstSeen });
4928
+ }
4929
+ }
4930
+ return out;
4931
+ }
4932
+ function scanEdited(roots, day) {
4933
+ const out = [];
4934
+ for (const r of roots) {
4935
+ const dir = join7(r.root, "skills");
4936
+ if (!existsSync6(dir)) continue;
4937
+ for (const f of listMarkdownFiles(dir)) {
4938
+ const firstSeen = firstSeenDateOf(f);
4939
+ if (firstSeen === day) continue;
4940
+ let stat;
4941
+ try {
4942
+ stat = statSync3(f);
4943
+ } catch {
4944
+ continue;
4945
+ }
4946
+ const mtimeDay = new Date(stat.mtime.getTime()).toISOString().slice(0, 10);
4947
+ if (mtimeDay !== day) continue;
4948
+ const rel = relative(dir, f);
4949
+ out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
4950
+ }
4951
+ }
4952
+ return out;
4953
+ }
4954
+ function claudemdLineDelta(extraRoots, authorEmail, day) {
4955
+ let delta = 0;
4956
+ const candidates = [];
4957
+ for (const e of extraRoots) {
4958
+ const f = resolve2(e, "CLAUDE.md");
4959
+ if (existsSync6(f)) candidates.push(f);
4960
+ }
4961
+ const homeClaudeMd = join7(homedir2(), "CLAUDE.md");
4962
+ if (existsSync6(homeClaudeMd)) candidates.push(homeClaudeMd);
4963
+ for (const file of candidates) {
4964
+ try {
4965
+ const out = execFileSync2(
4966
+ "git",
4967
+ ["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
4968
+ { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname6(file) }
4969
+ );
4970
+ let currentDate = "";
4971
+ let currentEmail = "";
4972
+ for (const line of out.split("\n")) {
4973
+ if (line.startsWith("COMMIT ")) {
4974
+ const parts = line.slice("COMMIT ".length).trim().split(/\s+/);
4975
+ currentDate = parts[0] ?? "";
4976
+ currentEmail = parts.slice(1).join(" ");
4977
+ } else if (currentDate === day && line.trim().length > 0) {
4978
+ if (authorEmail && currentEmail !== authorEmail) continue;
4979
+ const parts = line.split(/\s+/);
4980
+ const added = Number(parts[0]);
4981
+ const removed = Number(parts[1]);
4982
+ if (Number.isFinite(added) && Number.isFinite(removed)) {
4983
+ delta += added - removed;
4984
+ }
4985
+ }
4986
+ }
4987
+ } catch {
4988
+ }
4989
+ }
4990
+ return delta;
4991
+ }
4992
+ function probeArtifactSignals(opts = {}) {
4993
+ const day = opts.day ?? todayLocal();
4994
+ const roots = defaultScanRoots(opts.extraRoots);
4995
+ const skillsAuthored = scanAuthored(roots, "skills", day);
4996
+ const subagentsAuthored = scanAuthored(roots, "agents", day);
4997
+ const slashCommandsAuthored = scanAuthored(roots, "commands", day);
4998
+ const skillsEdited = scanEdited(roots, day);
4999
+ const authorEmail = opts.authorEmail ?? detectGitEmail(opts.extraRoots?.[0] ?? process.cwd()) ?? void 0;
5000
+ const claudemd = claudemdLineDelta(opts.extraRoots ?? [], authorEmail, day);
5001
+ const empty = skillsAuthored.length === 0 && subagentsAuthored.length === 0 && slashCommandsAuthored.length === 0 && skillsEdited.length === 0 && claudemd === 0;
5002
+ if (empty) return null;
5003
+ return {
5004
+ skillsAuthored,
5005
+ skillsEdited,
5006
+ subagentsAuthored,
5007
+ slashCommandsAuthored,
5008
+ claudemdLineDelta: claudemd
5009
+ };
5010
+ }
5011
+ var init_file_probe = __esm({
5012
+ "src/perception/file-probe.ts"() {
5013
+ "use strict";
5014
+ }
5015
+ });
5016
+
3833
5017
  // src/daemon-worker.ts
3834
5018
  import { spawn as spawn2 } from "node:child_process";
3835
- import { appendFileSync as appendFileSync5, closeSync, existsSync as existsSync11, mkdirSync as mkdirSync13, openSync, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "node:fs";
3836
- import { dirname as dirname8, join as join9 } from "node:path";
5019
+ import { appendFileSync as appendFileSync5, closeSync, existsSync as existsSync13, mkdirSync as mkdirSync13, openSync, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "node:fs";
5020
+ import { dirname as dirname10, join as join11 } from "node:path";
3837
5021
  import { fileURLToPath } from "node:url";
3838
5022
 
3839
5023
  // src/usage/token.ts
@@ -3864,9 +5048,9 @@ function readFromCredentialsFile() {
3864
5048
  join(homedir(), ".claude", ".credentials.json"),
3865
5049
  join(homedir(), ".config", "claude", "credentials.json")
3866
5050
  ];
3867
- for (const path5 of candidates) {
5051
+ for (const path7 of candidates) {
3868
5052
  try {
3869
- const blob = readFileSync(path5, "utf8");
5053
+ const blob = readFileSync(path7, "utf8");
3870
5054
  const creds = extractCredentials(blob);
3871
5055
  if (creds) return creds;
3872
5056
  } catch {
@@ -4322,7 +5506,7 @@ function sessionTouchesDay(s, day) {
4322
5506
  }
4323
5507
  return sessionDay(s) === day;
4324
5508
  }
4325
- function buildRichRollupBlocks(day, daySessions, entries, privateProjects) {
5509
+ function buildRichRollupBlocks(day, daySessions, entries, privateProjects, resolveRepo) {
4326
5510
  const bounds = dayBoundsMs(day);
4327
5511
  const clippedSessions = [];
4328
5512
  for (const s of daySessions) {
@@ -4332,16 +5516,40 @@ function buildRichRollupBlocks(day, daySessions, entries, privateProjects) {
4332
5516
  }
4333
5517
  const bursts = computeBurstsFromSessions(clippedSessions);
4334
5518
  const stats = summarizeBursts(bursts);
5519
+ const repoForEntry = (e) => {
5520
+ if (resolveRepo) {
5521
+ const own = resolveRepo(e.project);
5522
+ if (own) return own;
5523
+ const counts = /* @__PURE__ */ new Map();
5524
+ for (const d of e.edited_dirs ?? []) {
5525
+ const r = resolveRepo(d);
5526
+ if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
5527
+ }
5528
+ const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
5529
+ if (dominant) return dominant;
5530
+ }
5531
+ return e.github_repos?.length === 1 ? e.github_repos[0] : null;
5532
+ };
5533
+ const sessionRepo = /* @__PURE__ */ new Map();
5534
+ for (const e of entries) {
5535
+ if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
5536
+ const r = repoForEntry(e);
5537
+ if (r) sessionRepo.set(e.session_id, r);
5538
+ }
4335
5539
  const projects = /* @__PURE__ */ new Map();
4336
5540
  for (const s of clippedSessions) {
4337
- const name = canonicalProjectName(s.projectName);
4338
- if (privateProjects.has(name)) continue;
5541
+ const canonical = canonicalProjectName(s.projectName);
5542
+ const name = projectRepoName(s.projectName);
5543
+ if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
5544
+ const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
5545
+ const key = repo ?? name;
4339
5546
  const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
4340
- const cur = projects.get(name) ?? { agentTimeMs: 0, sessions: 0 };
5547
+ const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
4341
5548
  cur.agentTimeMs += ms;
4342
5549
  cur.sessions += 1;
4343
- projects.set(name, cur);
5550
+ projects.set(key, cur);
4344
5551
  }
5552
+ const projectRepos = /* @__PURE__ */ new Map();
4345
5553
  const workingShapes = /* @__PURE__ */ new Map();
4346
5554
  const skills = /* @__PURE__ */ new Map();
4347
5555
  const subagents = /* @__PURE__ */ new Map();
@@ -4355,12 +5563,12 @@ function buildRichRollupBlocks(day, daySessions, entries, privateProjects) {
4355
5563
  let longTotalMin = 0;
4356
5564
  let longMaxSingleMin = 0;
4357
5565
  for (const e of entries) {
4358
- if (privateProjects.has(e.project)) continue;
5566
+ if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
4359
5567
  const shape = e.signals?.working_shape ?? null;
4360
5568
  if (shape) {
4361
5569
  const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
4362
5570
  cur.sessions += 1;
4363
- cur.agentTimeMs += e.numbers.active_min * 6e4;
5571
+ cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
4364
5572
  workingShapes.set(shape, cur);
4365
5573
  }
4366
5574
  for (const [name, count] of Object.entries(e.skills)) {
@@ -4375,6 +5583,12 @@ function buildRichRollupBlocks(day, daySessions, entries, privateProjects) {
4375
5583
  prs += e.numbers.prs;
4376
5584
  commits += e.numbers.commits;
4377
5585
  pushes += e.numbers.pushes;
5586
+ if (e.github_repos?.length) {
5587
+ const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
5588
+ const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
5589
+ for (const r of e.github_repos) set.add(r);
5590
+ projectRepos.set(key, set);
5591
+ }
4378
5592
  if (e.flags.includes("long_autonomous")) {
4379
5593
  longCount += 1;
4380
5594
  longTotalMin += e.numbers.active_min;
@@ -4382,11 +5596,21 @@ function buildRichRollupBlocks(day, daySessions, entries, privateProjects) {
4382
5596
  }
4383
5597
  }
4384
5598
  return {
4385
- projects: Array.from(projects.entries()).map(([project, v]) => ({ project, ...v })).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
5599
+ projects: Array.from(projects.entries()).map(([key, v]) => {
5600
+ const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
5601
+ const repos = v.resolved ? [v.resolved, ...extras] : extras;
5602
+ return {
5603
+ project: v.project,
5604
+ agentTimeMs: v.agentTimeMs,
5605
+ sessions: v.sessions,
5606
+ ...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
5607
+ };
5608
+ }).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
4386
5609
  workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
4387
5610
  concurrencyPeak: stats.peakConcurrent,
4388
5611
  parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
4389
- longAutonomous: { count: longCount, totalMin: longTotalMin, maxSingleMin: longMaxSingleMin },
5612
+ // Wire schema requires integer minutes (active_min is a float).
5613
+ longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
4390
5614
  toolErrors,
4391
5615
  skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
4392
5616
  subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
@@ -4415,10 +5639,10 @@ function buildEnrichedExtras(entries) {
4415
5639
  }
4416
5640
  return { outcomeMix, helpfulnessMix, goalMix };
4417
5641
  }
4418
- function buildRichBlocksForDay(day, daySessions, privateProjects, enrichmentOptIn) {
5642
+ function buildRichBlocksForDay(day, daySessions, privateProjects, enrichmentOptIn, resolveRepo) {
4419
5643
  const entries = listEntriesForDay(day);
4420
5644
  if (entries.length === 0) return void 0;
4421
- const rich = buildRichRollupBlocks(day, daySessions, entries, privateProjects);
5645
+ const rich = buildRichRollupBlocks(day, daySessions, entries, privateProjects, resolveRepo);
4422
5646
  const enriched = enrichmentOptIn ? buildEnrichedExtras(entries) : void 0;
4423
5647
  return enriched ? { rich, enriched } : { rich };
4424
5648
  }
@@ -4427,10 +5651,11 @@ function buildIngestPayload(inputs) {
4427
5651
  return {
4428
5652
  ingestId: randomUUID(),
4429
5653
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
4430
- ...richRollup || inputs.enrichedExtras ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
5654
+ ...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
4431
5655
  ...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
4432
5656
  ...richRollup ? { richRollup } : {},
4433
5657
  ...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
5658
+ ...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
4434
5659
  ...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
4435
5660
  ...inputs.planTier ? { planTier: inputs.planTier } : {},
4436
5661
  ...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
@@ -4450,19 +5675,81 @@ async function pushToTeamServer(config, payload) {
4450
5675
  return { ok: res.ok, status: res.status, body };
4451
5676
  }
4452
5677
 
5678
+ // src/team/git-remote.ts
5679
+ import { existsSync as existsSync4, readFileSync as readFileSync6, statSync } from "node:fs";
5680
+ import { dirname as dirname3, isAbsolute, join as join5, resolve } from "node:path";
5681
+ var GITHUB_URL_RE = /github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/;
5682
+ function parseGithubRemote(url) {
5683
+ const m = url.trim().match(GITHUB_URL_RE);
5684
+ return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
5685
+ }
5686
+ function originUrl(configText) {
5687
+ let inOrigin = false;
5688
+ for (const line of configText.split("\n")) {
5689
+ const t = line.trim();
5690
+ if (t.startsWith("[")) {
5691
+ inOrigin = /^\[remote\s+"origin"\]/.test(t);
5692
+ continue;
5693
+ }
5694
+ if (!inOrigin) continue;
5695
+ const m = t.match(/^url\s*=\s*(.+)$/);
5696
+ if (m) return m[1].trim();
5697
+ }
5698
+ return null;
5699
+ }
5700
+ function gitConfigPathFor(dir) {
5701
+ const dotGit = join5(dir, ".git");
5702
+ const st = statSync(dotGit, { throwIfNoEntry: false });
5703
+ if (!st) return null;
5704
+ if (st.isDirectory()) return join5(dotGit, "config");
5705
+ const m = readFileSync6(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
5706
+ if (!m) return null;
5707
+ const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
5708
+ const commondirFile = join5(gd, "commondir");
5709
+ if (existsSync4(commondirFile)) {
5710
+ const common = readFileSync6(commondirFile, "utf8").trim();
5711
+ return join5(isAbsolute(common) ? common : resolve(gd, common), "config");
5712
+ }
5713
+ return join5(gd, "config");
5714
+ }
5715
+ function createRepoResolver() {
5716
+ const cache = /* @__PURE__ */ new Map();
5717
+ return (dir) => {
5718
+ if (!dir || !isAbsolute(dir)) return null;
5719
+ const hit = cache.get(dir);
5720
+ if (hit !== void 0) return hit;
5721
+ let repo = null;
5722
+ try {
5723
+ for (let d = dir; ; d = dirname3(d)) {
5724
+ const config = gitConfigPathFor(d);
5725
+ if (config && existsSync4(config)) {
5726
+ const url = originUrl(readFileSync6(config, "utf8"));
5727
+ repo = url ? parseGithubRemote(url) : null;
5728
+ break;
5729
+ }
5730
+ if (d === dirname3(d)) break;
5731
+ }
5732
+ } catch {
5733
+ repo = null;
5734
+ }
5735
+ cache.set(dir, repo);
5736
+ return repo;
5737
+ };
5738
+ }
5739
+
4453
5740
  // src/team/queue.ts
4454
5741
  init_fs();
4455
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, appendFileSync as appendFileSync2, statSync, mkdirSync as mkdirSync5 } from "node:fs";
4456
- import { dirname as dirname3 } from "node:path";
5742
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, appendFileSync as appendFileSync2, statSync as statSync2, mkdirSync as mkdirSync5 } from "node:fs";
5743
+ import { dirname as dirname4 } from "node:path";
4457
5744
  var QUEUE_PATH = cclensPath("ingest-queue.jsonl");
4458
5745
  var MAX_SIZE_BYTES = 10 * 1024 * 1024;
4459
5746
  var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
4460
5747
  function enqueuePayload(payload, queuePath = QUEUE_PATH) {
4461
- mkdirSync5(dirname3(queuePath), { recursive: true });
5748
+ mkdirSync5(dirname4(queuePath), { recursive: true });
4462
5749
  const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
4463
5750
  appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
4464
5751
  try {
4465
- const { size } = statSync(queuePath);
5752
+ const { size } = statSync2(queuePath);
4466
5753
  if (size > MAX_SIZE_BYTES) {
4467
5754
  pruneOldest(queuePath);
4468
5755
  }
@@ -4472,7 +5759,7 @@ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
4472
5759
  function dequeuePayloads(queuePath = QUEUE_PATH) {
4473
5760
  let lines;
4474
5761
  try {
4475
- lines = readFileSync6(queuePath, "utf8").trim().split("\n").filter(Boolean);
5762
+ lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
4476
5763
  } catch {
4477
5764
  return [];
4478
5765
  }
@@ -4493,7 +5780,7 @@ function dequeuePayloads(queuePath = QUEUE_PATH) {
4493
5780
  function pruneOldest(queuePath) {
4494
5781
  let lines;
4495
5782
  try {
4496
- lines = readFileSync6(queuePath, "utf8").trim().split("\n").filter(Boolean);
5783
+ lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
4497
5784
  } catch {
4498
5785
  return;
4499
5786
  }
@@ -4506,8 +5793,8 @@ init_storage();
4506
5793
  import { randomUUID as randomUUID2 } from "node:crypto";
4507
5794
 
4508
5795
  // src/usage/profile.ts
4509
- import { existsSync as existsSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
4510
- import { dirname as dirname4 } from "node:path";
5796
+ import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
5797
+ import { dirname as dirname5 } from "node:path";
4511
5798
  var PROFILE_ENDPOINT = "https://api.anthropic.com/api/oauth/profile";
4512
5799
  var BETA_HEADER2 = "oauth-2025-04-20";
4513
5800
  var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -4550,9 +5837,9 @@ async function fetchProfile(nowMs = Date.now()) {
4550
5837
  };
4551
5838
  }
4552
5839
  function readCachedProfile(cachePath) {
4553
- if (!existsSync4(cachePath)) return null;
5840
+ if (!existsSync5(cachePath)) return null;
4554
5841
  try {
4555
- const raw = JSON.parse(readFileSync7(cachePath, "utf8"));
5842
+ const raw = JSON.parse(readFileSync8(cachePath, "utf8"));
4556
5843
  if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
4557
5844
  return raw;
4558
5845
  } catch {
@@ -4560,7 +5847,7 @@ function readCachedProfile(cachePath) {
4560
5847
  }
4561
5848
  }
4562
5849
  function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
4563
- mkdirSync6(dirname4(cachePath), { recursive: true });
5850
+ mkdirSync6(dirname5(cachePath), { recursive: true });
4564
5851
  writeFileSync5(
4565
5852
  cachePath,
4566
5853
  JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
@@ -4722,12 +6009,12 @@ async function runTeamBackfill(log3 = noopLog, filePath = USAGE_LOG, configOverr
4722
6009
  // src/team/last-push.ts
4723
6010
  init_fs();
4724
6011
  import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, mkdirSync as mkdirSync7 } from "node:fs";
4725
- import { join as join5 } from "node:path";
6012
+ import { join as join6 } from "node:path";
4726
6013
  var LAST_PUSH_FILE = "team-last-push.json";
4727
6014
  function write(record, dir) {
4728
6015
  const d = dir ?? cclensHome();
4729
6016
  mkdirSync7(d, { recursive: true });
4730
- writeFileSync6(join5(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
6017
+ writeFileSync6(join6(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
4731
6018
  }
4732
6019
  function writeLastPushSuccess(payload, dir) {
4733
6020
  write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: true, payload }, dir);
@@ -4753,6 +6040,7 @@ async function dispatchCommand(command, config, log3) {
4753
6040
  async function runActivityBackfill(command, config, log3) {
4754
6041
  const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
4755
6042
  const { toLocalDay: toLocalDay3 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
6043
+ const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
4756
6044
  const days = command.params.days;
4757
6045
  const todayMs = Date.now();
4758
6046
  const targetDayMs = todayMs - (days - 1) * 24 * 60 * 60 * 1e3;
@@ -4778,9 +6066,24 @@ async function runActivityBackfill(command, config, log3) {
4778
6066
  summary: { pushed: 0, fromDay: targetDay }
4779
6067
  };
4780
6068
  }
6069
+ const privateProjects = new Set(config.privateProjects ?? []);
6070
+ const enrichmentOptIn = !!config.enrichmentOptIn;
6071
+ const resolveRepo = createRepoResolver();
4781
6072
  let pushed = 0;
4782
6073
  for (const rollup of rollups) {
4783
- const payload = buildIngestPayload({ rollup });
6074
+ const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
6075
+ const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, privateProjects, enrichmentOptIn, resolveRepo);
6076
+ let artifactSignals = null;
6077
+ try {
6078
+ artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
6079
+ } catch {
6080
+ }
6081
+ const payload = buildIngestPayload({
6082
+ rollup,
6083
+ richExtras: richBlocks?.rich,
6084
+ enrichedExtras: richBlocks?.enriched,
6085
+ artifactSignals: artifactSignals ?? void 0
6086
+ });
4784
6087
  const result = await pushToTeamServer(config, payload);
4785
6088
  if (!result.ok) {
4786
6089
  return {
@@ -4906,6 +6209,8 @@ async function runTeamSync(log3 = noopLog2, configOverride, options = {}) {
4906
6209
  let lastPushedDay;
4907
6210
  const privateProjects = new Set(config.privateProjects ?? []);
4908
6211
  const enrichmentOptIn = !!config.enrichmentOptIn;
6212
+ const resolveRepo = createRepoResolver();
6213
+ const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
4909
6214
  for (let i = 0; i < rollups.length; i++) {
4910
6215
  const rollup = rollups[i];
4911
6216
  const isLatest = i === rollups.length - 1;
@@ -4914,12 +6219,22 @@ async function runTeamSync(log3 = noopLog2, configOverride, options = {}) {
4914
6219
  rollup.day,
4915
6220
  daySessions,
4916
6221
  privateProjects,
4917
- enrichmentOptIn
6222
+ enrichmentOptIn,
6223
+ resolveRepo
4918
6224
  );
6225
+ let artifactSignals = null;
6226
+ try {
6227
+ artifactSignals = probeArtifactSignals2({
6228
+ day: rollup.day,
6229
+ extraRoots: [process.cwd()]
6230
+ });
6231
+ } catch {
6232
+ }
4919
6233
  const payload = buildIngestPayload({
4920
6234
  rollup,
4921
6235
  richExtras: richBlocks?.rich,
4922
6236
  enrichedExtras: richBlocks?.enriched,
6237
+ artifactSignals: artifactSignals ?? void 0,
4923
6238
  usageSnapshot: isLatest ? usageSnapshot : void 0,
4924
6239
  planTier,
4925
6240
  // Same rationale as usageSnapshot — current cycle data only on the
@@ -5038,8 +6353,8 @@ async function buildCyclePeaksForPush(planTier, loadCurve) {
5038
6353
  // src/perception/worker.ts
5039
6354
  init_dist();
5040
6355
  init_fs();
5041
- import { statSync as statSync2, readFileSync as readFileSync9 } from "node:fs";
5042
- import { basename, dirname as dirname6 } from "node:path";
6356
+ import { statSync as statSync4, readFileSync as readFileSync10 } from "node:fs";
6357
+ import { basename, dirname as dirname8 } from "node:path";
5043
6358
 
5044
6359
  // ../entries/dist/signals.js
5045
6360
  init_dist();
@@ -5271,7 +6586,7 @@ function viewEvent(ev) {
5271
6586
  cwd: rawCwd
5272
6587
  };
5273
6588
  }
5274
- var IDLE_GAP_MS3 = 3 * 60 * 1e3;
6589
+ var IDLE_GAP_MS4 = 3 * 60 * 1e3;
5275
6590
  var INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
5276
6591
  var PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
5277
6592
  var BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
@@ -5383,7 +6698,7 @@ function closeTurn(t, closed, cwd) {
5383
6698
  let active = 0;
5384
6699
  for (let i = 1; i < ts.length; i++) {
5385
6700
  const dt = ts[i] - ts[i - 1];
5386
- if (dt < IDLE_GAP_MS3)
6701
+ if (dt < IDLE_GAP_MS4)
5387
6702
  active += dt;
5388
6703
  }
5389
6704
  closed.push({
@@ -5410,6 +6725,9 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5410
6725
  const prTitles = [];
5411
6726
  let commits = 0;
5412
6727
  let pushes = 0;
6728
+ const gitToolUseIds = /* @__PURE__ */ new Set();
6729
+ const githubRepos = /* @__PURE__ */ new Set();
6730
+ const editedDirCounts = /* @__PURE__ */ new Map();
5413
6731
  const modelTurns = /* @__PURE__ */ new Map();
5414
6732
  let totalToolErrors = 0;
5415
6733
  let totalInterrupts = 0;
@@ -5451,11 +6769,23 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5451
6769
  } else {
5452
6770
  if (Array.isArray(content)) {
5453
6771
  for (const c of content) {
5454
- if (c && typeof c === "object" && c.type === "tool_result" && c.is_error) {
6772
+ if (!c || typeof c !== "object" || c.type !== "tool_result")
6773
+ continue;
6774
+ if (c.is_error) {
5455
6775
  totalToolErrors++;
5456
6776
  if (cur)
5457
6777
  cur.toolErrors++;
5458
6778
  }
6779
+ const tuId = c.tool_use_id;
6780
+ if (tuId && gitToolUseIds.has(tuId)) {
6781
+ const rc = c.content;
6782
+ const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
6783
+ for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
6784
+ if (m[1].toLowerCase() === "user-attachments")
6785
+ continue;
6786
+ githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
6787
+ }
6788
+ }
5459
6789
  }
5460
6790
  }
5461
6791
  if (cur && tsMs !== void 0) {
@@ -5511,10 +6841,6 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5511
6841
  if (name === "Skill") {
5512
6842
  const sk = String(ct.input?.skill ?? "unknown");
5513
6843
  cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
5514
- } else if (name === "ToolSearch") {
5515
- const q = String(ct.input?.query ?? "");
5516
- const key = `(ToolSearch: ${trunc(q, 60)})`;
5517
- cur.skills.set(key, (cur.skills.get(key) ?? 0) + 1);
5518
6844
  }
5519
6845
  if (name === "Agent") {
5520
6846
  const inp = ct.input ?? {};
@@ -5544,10 +6870,20 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5544
6870
  commits++;
5545
6871
  if (/\bgit\s+push\b/.test(cmd))
5546
6872
  pushes++;
6873
+ if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
6874
+ gitToolUseIds.add(ct.id);
5547
6875
  }
5548
6876
  if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
5549
6877
  cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
5550
6878
  }
6879
+ if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
6880
+ const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
6881
+ const slash = fp.lastIndexOf("/");
6882
+ if (slash > 0) {
6883
+ const dir = fp.slice(0, slash);
6884
+ editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
6885
+ }
6886
+ }
5551
6887
  }
5552
6888
  }
5553
6889
  }
@@ -5559,7 +6895,7 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5559
6895
  let activeMs2 = 0;
5560
6896
  for (let i = 1; i < tsSorted.length; i++) {
5561
6897
  const dt = tsSorted[i] - tsSorted[i - 1];
5562
- if (dt < IDLE_GAP_MS3)
6898
+ if (dt < IDLE_GAP_MS4)
5563
6899
  activeMs2 += dt;
5564
6900
  }
5565
6901
  const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
@@ -5572,6 +6908,8 @@ function aggregateDay(dayEvents, _sessionFallbackProject) {
5572
6908
  prTitles,
5573
6909
  commits,
5574
6910
  pushes,
6911
+ githubRepos: [...githubRepos].slice(0, 5),
6912
+ editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
5575
6913
  modelTurns,
5576
6914
  totalToolErrors,
5577
6915
  totalInterrupts,
@@ -5725,6 +7063,8 @@ function buildEntries(sessionDetail) {
5725
7063
  first_user,
5726
7064
  final_agent,
5727
7065
  pr_titles: agg.prTitles,
7066
+ ...agg.githubRepos.length ? { github_repos: agg.githubRepos } : {},
7067
+ ...agg.editedDirs.length ? { edited_dirs: agg.editedDirs } : {},
5728
7068
  top_tools,
5729
7069
  skills,
5730
7070
  subagents,
@@ -5744,8 +7084,8 @@ function buildEntries(sessionDetail) {
5744
7084
 
5745
7085
  // src/perception/state.ts
5746
7086
  init_fs();
5747
- import { existsSync as existsSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, renameSync as renameSync3 } from "node:fs";
5748
- import { dirname as dirname5 } from "node:path";
7087
+ import { existsSync as existsSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, renameSync as renameSync3 } from "node:fs";
7088
+ import { dirname as dirname7 } from "node:path";
5749
7089
  var STALE_MS = 15 * 60 * 1e3;
5750
7090
  var pathCached = null;
5751
7091
  function statePath() {
@@ -5755,7 +7095,7 @@ function statePath() {
5755
7095
  }
5756
7096
  function readState() {
5757
7097
  const p = statePath();
5758
- if (!existsSync5(p)) {
7098
+ if (!existsSync7(p)) {
5759
7099
  return {
5760
7100
  sweep_in_progress: false,
5761
7101
  last_sweep_started_at: null,
@@ -5763,12 +7103,12 @@ function readState() {
5763
7103
  file_checkpoints: {}
5764
7104
  };
5765
7105
  }
5766
- const raw = readFileSync8(p, "utf8");
7106
+ const raw = readFileSync9(p, "utf8");
5767
7107
  return JSON.parse(raw);
5768
7108
  }
5769
7109
  function writeState(s) {
5770
7110
  const p = statePath();
5771
- mkdirSync8(dirname5(p), { recursive: true });
7111
+ mkdirSync8(dirname7(p), { recursive: true });
5772
7112
  const tmp = `${p}.tmp`;
5773
7113
  writeFileSync7(tmp, JSON.stringify(s, null, 2), "utf8");
5774
7114
  renameSync3(tmp, p);
@@ -5799,18 +7139,18 @@ function updateCheckpoint(jsonlPath, cp) {
5799
7139
  }
5800
7140
 
5801
7141
  // src/perception/scan.ts
5802
- import { readdirSync as readdirSync3 } from "node:fs";
5803
- import { join as join6 } from "node:path";
5804
- import { homedir as homedir2 } from "node:os";
7142
+ import { readdirSync as readdirSync4 } from "node:fs";
7143
+ import { join as join8 } from "node:path";
7144
+ import { homedir as homedir3 } from "node:os";
5805
7145
  async function listAllSessionJsonls(root) {
5806
- const projectsRoot = root ?? join6(homedir2(), ".claude", "projects");
7146
+ const projectsRoot = root ?? join8(homedir3(), ".claude", "projects");
5807
7147
  const out = [];
5808
7148
  try {
5809
- for (const project of readdirSync3(projectsRoot)) {
5810
- const dir = join6(projectsRoot, project);
7149
+ for (const project of readdirSync4(projectsRoot)) {
7150
+ const dir = join8(projectsRoot, project);
5811
7151
  try {
5812
- for (const f of readdirSync3(dir)) {
5813
- if (f.endsWith(".jsonl")) out.push(join6(dir, f));
7152
+ for (const f of readdirSync4(dir)) {
7153
+ if (f.endsWith(".jsonl")) out.push(join8(dir, f));
5814
7154
  }
5815
7155
  } catch {
5816
7156
  }
@@ -5843,10 +7183,10 @@ async function runPerceptionSweep(opts = {}) {
5843
7183
  const files = await listAllSessionJsonls(opts.projectsRoot);
5844
7184
  for (const f of files) {
5845
7185
  try {
5846
- const stat = statSync2(f);
7186
+ const stat = statSync4(f);
5847
7187
  const prev = state.file_checkpoints[f];
5848
7188
  if (prev && prev.byte_offset >= stat.size) continue;
5849
- const raw = readFileSync9(f, "utf8");
7189
+ const raw = readFileSync10(f, "utf8");
5850
7190
  const rawLines = raw.split("\n").filter(Boolean).map((l) => {
5851
7191
  try {
5852
7192
  return JSON.parse(l);
@@ -5857,7 +7197,7 @@ async function runPerceptionSweep(opts = {}) {
5857
7197
  if (rawLines.length === 0) continue;
5858
7198
  const { meta, events } = parseTranscript(rawLines);
5859
7199
  const fileName = basename(f);
5860
- const projectDir = basename(dirname6(f));
7200
+ const projectDir = basename(dirname8(f));
5861
7201
  const sessionId = fileName.replace(/\.jsonl$/, "");
5862
7202
  const sd = {
5863
7203
  ...meta,
@@ -5896,7 +7236,7 @@ async function runPerceptionSweep(opts = {}) {
5896
7236
  const built = buildEntries(detail);
5897
7237
  for (const e of built) {
5898
7238
  try {
5899
- const stat = statSync2(meta.filePath);
7239
+ const stat = statSync4(meta.filePath);
5900
7240
  e.source_checkpoint.byte_offset = stat.size;
5901
7241
  } catch {
5902
7242
  }
@@ -6398,8 +7738,8 @@ function getErrorMap() {
6398
7738
 
6399
7739
  // ../../node_modules/zod/v3/helpers/parseUtil.js
6400
7740
  var makeIssue = (params) => {
6401
- const { data, path: path5, errorMaps, issueData } = params;
6402
- const fullPath = [...path5, ...issueData.path || []];
7741
+ const { data, path: path7, errorMaps, issueData } = params;
7742
+ const fullPath = [...path7, ...issueData.path || []];
6403
7743
  const fullIssue = {
6404
7744
  ...issueData,
6405
7745
  path: fullPath
@@ -6515,11 +7855,11 @@ var errorUtil;
6515
7855
 
6516
7856
  // ../../node_modules/zod/v3/types.js
6517
7857
  var ParseInputLazyPath = class {
6518
- constructor(parent, value, path5, key) {
7858
+ constructor(parent, value, path7, key) {
6519
7859
  this._cachedPath = [];
6520
7860
  this.parent = parent;
6521
7861
  this.data = value;
6522
- this._path = path5;
7862
+ this._path = path7;
6523
7863
  this._key = key;
6524
7864
  }
6525
7865
  get path() {
@@ -10053,14 +11393,14 @@ var ENRICHMENT_SYSTEM_PROMPT = SYSTEM_PROMPT;
10053
11393
  // ../entries/dist/llm-runner.js
10054
11394
  init_fs();
10055
11395
  import { spawn } from "node:child_process";
10056
- import { mkdirSync as mkdirSync10, appendFileSync as appendFileSync3, writeFileSync as writeFileSync9, existsSync as existsSync7 } from "node:fs";
10057
- import { join as join8 } from "node:path";
11396
+ import { mkdirSync as mkdirSync10, appendFileSync as appendFileSync3, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "node:fs";
11397
+ import { join as join10 } from "node:path";
10058
11398
  import { randomUUID as randomUUID4 } from "node:crypto";
10059
11399
 
10060
11400
  // ../entries/dist/tmux-runner.js
10061
- import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync6, readdirSync as readdirSync4, realpathSync, readFileSync as readFileSync10, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
10062
- import { join as join7 } from "node:path";
10063
- import { homedir as homedir3 } from "node:os";
11401
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync8, readdirSync as readdirSync5, realpathSync, readFileSync as readFileSync11, unlinkSync as unlinkSync3, renameSync as renameSync4 } from "node:fs";
11402
+ import { join as join9 } from "node:path";
11403
+ import { homedir as homedir4 } from "node:os";
10064
11404
  import { spawnSync } from "node:child_process";
10065
11405
  var TmuxUnavailableError = class extends Error {
10066
11406
  constructor(message) {
@@ -10088,16 +11428,16 @@ function tmuxRunnerAvailable() {
10088
11428
  return { ok: true };
10089
11429
  }
10090
11430
  function runtimeCwd() {
10091
- const dir = join7(homedir3(), ".cclens", "runtime");
10092
- if (!existsSync6(dir))
11431
+ const dir = join9(homedir4(), ".cclens", "runtime");
11432
+ if (!existsSync8(dir))
10093
11433
  mkdirSync9(dir, { recursive: true });
10094
11434
  return realpathSync(dir);
10095
11435
  }
10096
11436
  function ensureRuntimeCwdTrusted(cwd) {
10097
- const cfgPath = join7(homedir3(), ".claude.json");
11437
+ const cfgPath = join9(homedir4(), ".claude.json");
10098
11438
  let cfg;
10099
11439
  try {
10100
- cfg = JSON.parse(readFileSync10(cfgPath, "utf8"));
11440
+ cfg = JSON.parse(readFileSync11(cfgPath, "utf8"));
10101
11441
  } catch {
10102
11442
  return;
10103
11443
  }
@@ -10140,9 +11480,9 @@ async function runClaudeUnderTmuxImpl(args) {
10140
11480
  ensureRuntimeCwdTrusted(cwd);
10141
11481
  const runId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
10142
11482
  const sessionName = `cclens-${runId}`;
10143
- const systemFile = join7(cwd, `system-${runId}.txt`);
10144
- const userFile = join7(cwd, `user-${runId}.txt`);
10145
- const wrapper = join7(cwd, `run-${runId}.sh`);
11483
+ const systemFile = join9(cwd, `system-${runId}.txt`);
11484
+ const userFile = join9(cwd, `user-${runId}.txt`);
11485
+ const wrapper = join9(cwd, `run-${runId}.sh`);
10146
11486
  writeFileSync8(systemFile, args.systemPrompt);
10147
11487
  writeFileSync8(userFile, args.reminder ? `${args.userPrompt}
10148
11488
 
@@ -10221,35 +11561,35 @@ function num(v) {
10221
11561
  function sleep(ms) {
10222
11562
  return new Promise((r) => setTimeout(r, ms));
10223
11563
  }
10224
- var PROJECTS_ROOT = () => join7(homedir3(), ".claude", "projects");
11564
+ var PROJECTS_ROOT = () => join9(homedir4(), ".claude", "projects");
10225
11565
  function snapshotAllTranscripts() {
10226
11566
  const out = /* @__PURE__ */ new Set();
10227
11567
  const root = PROJECTS_ROOT();
10228
11568
  let subs;
10229
11569
  try {
10230
- subs = readdirSync4(root);
11570
+ subs = readdirSync5(root);
10231
11571
  } catch {
10232
11572
  return out;
10233
11573
  }
10234
11574
  for (const sub of subs) {
10235
- const dir = join7(root, sub);
11575
+ const dir = join9(root, sub);
10236
11576
  let files;
10237
11577
  try {
10238
- files = readdirSync4(dir);
11578
+ files = readdirSync5(dir);
10239
11579
  } catch {
10240
11580
  continue;
10241
11581
  }
10242
11582
  for (const f of files) {
10243
11583
  if (f.endsWith(".jsonl"))
10244
- out.add(join7(dir, f));
11584
+ out.add(join9(dir, f));
10245
11585
  }
10246
11586
  }
10247
11587
  return out;
10248
11588
  }
10249
- function readJsonlCwd(path5, maxLines = 8) {
11589
+ function readJsonlCwd(path7, maxLines = 8) {
10250
11590
  let raw;
10251
11591
  try {
10252
- raw = readFileSync10(path5, "utf8");
11592
+ raw = readFileSync11(path7, "utf8");
10253
11593
  } catch {
10254
11594
  return void 0;
10255
11595
  }
@@ -10277,22 +11617,22 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
10277
11617
  const root = PROJECTS_ROOT();
10278
11618
  let subs;
10279
11619
  try {
10280
- subs = readdirSync4(root);
11620
+ subs = readdirSync5(root);
10281
11621
  } catch {
10282
11622
  subs = [];
10283
11623
  }
10284
11624
  for (const sub of subs) {
10285
- const dir = join7(root, sub);
11625
+ const dir = join9(root, sub);
10286
11626
  let files;
10287
11627
  try {
10288
- files = readdirSync4(dir);
11628
+ files = readdirSync5(dir);
10289
11629
  } catch {
10290
11630
  continue;
10291
11631
  }
10292
11632
  for (const f of files) {
10293
11633
  if (!f.endsWith(".jsonl"))
10294
11634
  continue;
10295
- const full = join7(dir, f);
11635
+ const full = join9(dir, f);
10296
11636
  if (existingBefore.has(full))
10297
11637
  continue;
10298
11638
  if (readJsonlCwd(full) === targetCwd)
@@ -10303,10 +11643,10 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
10303
11643
  }
10304
11644
  throw new TmuxRunError(`timeout waiting for claude to create the session transcript (cwd: ${targetCwd})`);
10305
11645
  }
10306
- function parseTranscript2(path5, defaultModel) {
11646
+ function parseTranscript2(path7, defaultModel) {
10307
11647
  let raw;
10308
11648
  try {
10309
- raw = readFileSync10(path5, "utf8");
11649
+ raw = readFileSync11(path7, "utf8");
10310
11650
  } catch {
10311
11651
  return { assembled: "", inputTokens: 0, outputTokens: 0, modelUsed: defaultModel, done: false };
10312
11652
  }
@@ -10366,7 +11706,7 @@ function cleanup(cwd, runId, tmuxBin, sessionName, transcriptPath) {
10366
11706
  }
10367
11707
  for (const f of [`system-${runId}.txt`, `user-${runId}.txt`, `run-${runId}.sh`]) {
10368
11708
  try {
10369
- unlinkSync3(join7(cwd, f));
11709
+ unlinkSync3(join9(cwd, f));
10370
11710
  } catch {
10371
11711
  }
10372
11712
  }
@@ -10381,7 +11721,7 @@ function shellEscape(s) {
10381
11721
  // ../entries/dist/llm-runner.js
10382
11722
  var RUNS_DIR = cclensPath("llm-runs");
10383
11723
  function ensureRunsDir() {
10384
- if (!existsSync7(RUNS_DIR)) {
11724
+ if (!existsSync9(RUNS_DIR)) {
10385
11725
  try {
10386
11726
  mkdirSync10(RUNS_DIR, { recursive: true });
10387
11727
  } catch {
@@ -10410,17 +11750,17 @@ function newRunId(kind) {
10410
11750
  return `${ts}_${kind}_${short}`;
10411
11751
  }
10412
11752
  function tracePath(runId) {
10413
- return join8(RUNS_DIR, `${runId}.jsonl`);
11753
+ return join10(RUNS_DIR, `${runId}.jsonl`);
10414
11754
  }
10415
- function safeAppend(path5, line) {
11755
+ function safeAppend(path7, line) {
10416
11756
  try {
10417
- appendFileSync3(path5, line);
11757
+ appendFileSync3(path7, line);
10418
11758
  } catch {
10419
11759
  }
10420
11760
  }
10421
- function safeWrite(path5, line) {
11761
+ function safeWrite(path7, line) {
10422
11762
  try {
10423
- writeFileSync9(path5, line);
11763
+ writeFileSync9(path7, line);
10424
11764
  } catch {
10425
11765
  }
10426
11766
  }
@@ -10498,7 +11838,7 @@ async function runClaudeSubprocess(args) {
10498
11838
  return runClaudePrintMode(args);
10499
11839
  }
10500
11840
  function runClaudePrintMode(args) {
10501
- return new Promise((resolve, reject) => {
11841
+ return new Promise((resolve3, reject) => {
10502
11842
  ensureRunsDir();
10503
11843
  const kind = detectKindFromSystemPrompt(args.systemPrompt);
10504
11844
  const runId = newRunId(kind);
@@ -10629,7 +11969,7 @@ ${args.reminder}` : args.userPrompt;
10629
11969
  reject(new Error(`claude exited ${code}: ${stderr.trim().slice(0, 300)}`));
10630
11970
  return;
10631
11971
  }
10632
- resolve({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
11972
+ resolve3({ content: buffer, input_tokens: inputTokens, output_tokens: outputTokens, model: modelUsed });
10633
11973
  });
10634
11974
  proc.on("error", (err) => {
10635
11975
  safeAppend(trace, JSON.stringify({ _meta: { type: "spawn_error", run_id: runId, ts: (/* @__PURE__ */ new Date()).toISOString(), error: err.message } }) + "\n");
@@ -10809,8 +12149,8 @@ function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt,
10809
12149
 
10810
12150
  // ../entries/dist/budget.js
10811
12151
  init_fs();
10812
- import { appendFileSync as appendFileSync4, readFileSync as readFileSync11, existsSync as existsSync8, mkdirSync as mkdirSync11 } from "node:fs";
10813
- import { dirname as dirname7 } from "node:path";
12152
+ import { appendFileSync as appendFileSync4, readFileSync as readFileSync12, existsSync as existsSync10, mkdirSync as mkdirSync11 } from "node:fs";
12153
+ import { dirname as dirname9 } from "node:path";
10814
12154
  var spendPathCached = null;
10815
12155
  function spendPath() {
10816
12156
  if (spendPathCached)
@@ -10820,17 +12160,17 @@ function spendPath() {
10820
12160
  }
10821
12161
  function appendSpend(record) {
10822
12162
  const p = spendPath();
10823
- mkdirSync11(dirname7(p), { recursive: true });
12163
+ mkdirSync11(dirname9(p), { recursive: true });
10824
12164
  appendFileSync4(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
10825
12165
  }
10826
12166
  function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
10827
12167
  const p = spendPath();
10828
- if (!existsSync8(p))
12168
+ if (!existsSync10(p))
10829
12169
  return 0;
10830
12170
  const year = now.getUTCFullYear();
10831
12171
  const month = now.getUTCMonth();
10832
12172
  let total = 0;
10833
- const raw = readFileSync11(p, "utf8");
12173
+ const raw = readFileSync12(p, "utf8");
10834
12174
  for (const line of raw.split("\n")) {
10835
12175
  if (!line)
10836
12176
  continue;
@@ -10852,7 +12192,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
10852
12192
 
10853
12193
  // ../entries/dist/pipeline-lock.js
10854
12194
  init_fs();
10855
- import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync9, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
12195
+ import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as readFileSync13, statSync as statSync5 } from "node:fs";
10856
12196
  var STALE_MS2 = 60 * 1e3;
10857
12197
  var HEARTBEAT_MS = 30 * 1e3;
10858
12198
  var lockPathCached = null;
@@ -10898,13 +12238,13 @@ function pidAlive(pid) {
10898
12238
  }
10899
12239
  function interactiveLockFresh(nowMs) {
10900
12240
  const p = lockPath();
10901
- if (!existsSync9(p))
12241
+ if (!existsSync11(p))
10902
12242
  return false;
10903
12243
  try {
10904
- const mtime = statSync3(p).mtimeMs;
12244
+ const mtime = statSync5(p).mtimeMs;
10905
12245
  if (nowMs - mtime > STALE_MS2)
10906
12246
  return false;
10907
- const pid = Number(readFileSync12(p, "utf8").trim());
12247
+ const pid = Number(readFileSync13(p, "utf8").trim());
10908
12248
  if (!Number.isFinite(pid) || pid <= 0)
10909
12249
  return false;
10910
12250
  if (!pidAlive(pid))
@@ -10919,7 +12259,7 @@ function interactiveLockFresh(nowMs) {
10919
12259
  var THIRTY_MIN_MS = 30 * 60 * 1e3;
10920
12260
 
10921
12261
  // ../entries/dist/settings.js
10922
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync12, existsSync as existsSync10 } from "node:fs";
12262
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, renameSync as renameSync5, chmodSync as chmodSync2, mkdirSync as mkdirSync12, existsSync as existsSync12 } from "node:fs";
10923
12263
  init_fs();
10924
12264
  var DEFAULT_SETTINGS = {
10925
12265
  ai_features: {
@@ -10951,10 +12291,10 @@ function fromDisk(d) {
10951
12291
  }
10952
12292
  function readSettings() {
10953
12293
  const p = settingsPath();
10954
- if (!existsSync10(p))
12294
+ if (!existsSync12(p))
10955
12295
  return DEFAULT_SETTINGS;
10956
12296
  try {
10957
- const raw = readFileSync13(p, "utf8");
12297
+ const raw = readFileSync14(p, "utf8");
10958
12298
  return fromDisk(JSON.parse(raw));
10959
12299
  } catch {
10960
12300
  return DEFAULT_SETTINGS;
@@ -11127,7 +12467,7 @@ function rollupOutcomeDay(entries) {
11127
12467
  return "exploratory";
11128
12468
  return "trivial";
11129
12469
  }
11130
- function truncate4(s, max) {
12470
+ function truncate5(s, max) {
11131
12471
  if (s.length <= max)
11132
12472
  return s;
11133
12473
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -11183,7 +12523,7 @@ function computeDaySignals(entries) {
11183
12523
  type,
11184
12524
  count: v.count,
11185
12525
  sample_description: v.sample_description,
11186
- sample_prompt_preview: truncate4(v.sample_prompt_preview, 200)
12526
+ sample_prompt_preview: truncate5(v.sample_prompt_preview, 200)
11187
12527
  }));
11188
12528
  const frameCounts = /* @__PURE__ */ new Map();
11189
12529
  for (const s of signalsList) {
@@ -11202,7 +12542,7 @@ function computeDaySignals(entries) {
11202
12542
  if ((e.first_user ?? "").length > 0)
11203
12543
  verbosity_distribution[s.verbosity] += 1;
11204
12544
  for (const ref of s.external_refs) {
11205
- external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate4(ref.preview, 200) });
12545
+ external_refs.push({ session_id: e.session_id, kind: ref.kind, preview: truncate5(ref.preview, 200) });
11206
12546
  }
11207
12547
  const ints = e.numbers.interrupts ?? 0;
11208
12548
  interrupts += ints;
@@ -12045,7 +13385,7 @@ function computeInteractionModes(entries) {
12045
13385
  date: entry.local_day,
12046
13386
  project_display: prettyProject3(entry.project),
12047
13387
  type: sa.type,
12048
- prompt_preview: truncate5(sa.prompt_preview, 200)
13388
+ prompt_preview: truncate6(sa.prompt_preview, 200)
12049
13389
  });
12050
13390
  }
12051
13391
  const skillExamples = [];
@@ -12062,7 +13402,7 @@ function computeInteractionModes(entries) {
12062
13402
  skillExamples.push({
12063
13403
  date: e.local_day,
12064
13404
  skill,
12065
- first_user_preview: truncate5(e.first_user, 200)
13405
+ first_user_preview: truncate6(e.first_user, 200)
12066
13406
  });
12067
13407
  }
12068
13408
  }
@@ -12079,7 +13419,7 @@ function computeInteractionModes(entries) {
12079
13419
  project_display: prettyProject3(longestTurnEntry.project),
12080
13420
  active_min: longestTurnEntry.numbers.active_min,
12081
13421
  top_tools: longestTurnEntry.top_tools.slice(0, 5),
12082
- first_user_preview: truncate5(longestTurnEntry.first_user, 200)
13422
+ first_user_preview: truncate6(longestTurnEntry.first_user, 200)
12083
13423
  } : null;
12084
13424
  const tools_per_turn = turn_count > 0 ? tools_total / turn_count : 0;
12085
13425
  const label = tools_per_turn < 5 ? "rapid" : tools_per_turn < 15 ? "mixed" : "batch";
@@ -12110,7 +13450,7 @@ function computeInteractionModes(entries) {
12110
13450
  }
12111
13451
  };
12112
13452
  }
12113
- function truncate5(s, max) {
13453
+ function truncate6(s, max) {
12114
13454
  if (s.length <= max)
12115
13455
  return s;
12116
13456
  return s.slice(0, max - 1).trimEnd() + "\u2026";
@@ -12167,7 +13507,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
12167
13507
  evidence_subagent = {
12168
13508
  type: sa.type,
12169
13509
  description: sa.description,
12170
- prompt_preview: truncate5(sa.prompt_preview, 200)
13510
+ prompt_preview: truncate6(sa.prompt_preview, 200)
12171
13511
  };
12172
13512
  }
12173
13513
  const project_display = repEntry ? prettyProject3(repEntry.project) : dd.projects[0]?.display_name ?? "";
@@ -12178,7 +13518,7 @@ function computeWorkingShapes(dayDigests, entriesByDay = /* @__PURE__ */ new Map
12178
13518
  outcome: dayOutcome.get(dd.key) ?? null,
12179
13519
  helpfulness: dayHelp.get(dd.key) ?? null,
12180
13520
  evidence_subagent,
12181
- evidence_first_user: repEntry?.first_user ? truncate5(repEntry.first_user, 200) : null,
13521
+ evidence_first_user: repEntry?.first_user ? truncate6(repEntry.first_user, 200) : null,
12182
13522
  day_signature: dd.day_signature ?? null
12183
13523
  };
12184
13524
  });
@@ -12253,7 +13593,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
12253
13593
  date: day,
12254
13594
  session_id: ref.session_id,
12255
13595
  ref_kind: ref.kind,
12256
- preview: truncate5(ref.preview, 200)
13596
+ preview: truncate6(ref.preview, 200)
12257
13597
  });
12258
13598
  }
12259
13599
  total_interrupts += signals.comm_style.steering.interrupts;
@@ -12325,7 +13665,7 @@ function computeInteractionGrammar(dayDigests, entriesByDay = /* @__PURE__ */ ne
12325
13665
  count: v.count,
12326
13666
  days: [...v.days].sort(),
12327
13667
  sample_description: v.sample_description,
12328
- sample_prompt_preview: truncate5(v.sample_prompt_preview, 200)
13668
+ sample_prompt_preview: truncate6(v.sample_prompt_preview, 200)
12329
13669
  })),
12330
13670
  threads,
12331
13671
  communication_style: {
@@ -12625,7 +13965,7 @@ var MonthDigestResponseSchema = external_exports.object({
12625
13965
  }).passthrough();
12626
13966
 
12627
13967
  // ../entries/dist/top-sessions.js
12628
- var IDLE_GAP_MS4 = 3 * 60 * 1e3;
13968
+ var IDLE_GAP_MS5 = 3 * 60 * 1e3;
12629
13969
  var LONG_AUTONOMOUS_MIN = 20;
12630
13970
  var N_USER = 150;
12631
13971
  var N_FIRST_AGENT = 150;
@@ -12656,7 +13996,7 @@ function activeMs(timestamps) {
12656
13996
  let active = 0;
12657
13997
  for (let i = 1; i < ts.length; i++) {
12658
13998
  const dt = ts[i] - ts[i - 1];
12659
- if (dt < IDLE_GAP_MS4)
13999
+ if (dt < IDLE_GAP_MS5)
12660
14000
  active += dt;
12661
14001
  }
12662
14002
  return active;
@@ -12830,7 +14170,7 @@ function buildSessionSlice(entry, events, daySignature) {
12830
14170
  let intEnd = sortedTs[0];
12831
14171
  for (let i = 1; i < sortedTs.length; i++) {
12832
14172
  const dt = sortedTs[i] - intEnd;
12833
- if (dt >= IDLE_GAP_MS4) {
14173
+ if (dt >= IDLE_GAP_MS5) {
12834
14174
  intervals.push({
12835
14175
  start_min: fmtMin(intStart - sliceStartMs),
12836
14176
  end_min: fmtMin(intEnd - sliceStartMs)
@@ -13711,14 +15051,14 @@ async function getUpdateAvailability() {
13711
15051
  if (isDevMode()) return null;
13712
15052
  const latest = await fetchLatestVersion();
13713
15053
  if (latest === null) return {
13714
- current: "0.11.2",
15054
+ current: "0.12.0",
13715
15055
  latest: null,
13716
15056
  updateAvailable: false
13717
15057
  };
13718
15058
  return {
13719
- current: "0.11.2",
15059
+ current: "0.12.0",
13720
15060
  latest,
13721
- updateAvailable: shouldUpdate("0.11.2", latest)
15061
+ updateAvailable: shouldUpdate("0.12.0", latest)
13722
15062
  };
13723
15063
  }
13724
15064
 
@@ -13728,7 +15068,7 @@ var DAEMON_LOG = cclensPath("daemon.log");
13728
15068
  var UPDATE_STATE = cclensPath("daemon-update.json");
13729
15069
  var WATCHDOG_INTERVAL_MS = 5 * 1e3;
13730
15070
  var AUTO_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
13731
- mkdirSync13(dirname8(USAGE_LOG3), { recursive: true });
15071
+ mkdirSync13(dirname10(USAGE_LOG3), { recursive: true });
13732
15072
  function log2(level, message) {
13733
15073
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
13734
15074
  `;
@@ -13770,7 +15110,7 @@ var perceptionHandle = setInterval(async () => {
13770
15110
  }, PERCEPTION_INTERVAL_MS);
13771
15111
  function readUpdateState() {
13772
15112
  try {
13773
- return JSON.parse(readFileSync14(UPDATE_STATE, "utf8"));
15113
+ return JSON.parse(readFileSync15(UPDATE_STATE, "utf8"));
13774
15114
  } catch {
13775
15115
  return null;
13776
15116
  }
@@ -13790,7 +15130,7 @@ function nextUpdateCheckFromState(nowMs) {
13790
15130
  }
13791
15131
  var nextUpdateCheckAtMs = nextUpdateCheckFromState(Date.now());
13792
15132
  function cliEntryPath() {
13793
- return join9(dirname8(fileURLToPath(import.meta.url)), "index.js");
15133
+ return join11(dirname10(fileURLToPath(import.meta.url)), "index.js");
13794
15134
  }
13795
15135
  async function runDaemonUpdateCheck() {
13796
15136
  if (process.env.FLEETLENS_DAEMON_AUTO_UPDATE === "0") {
@@ -13819,7 +15159,7 @@ async function runDaemonUpdateCheck() {
13819
15159
  return;
13820
15160
  }
13821
15161
  const entry = cliEntryPath();
13822
- if (!existsSync11(entry)) {
15162
+ if (!existsSync13(entry)) {
13823
15163
  log2("warn", `update check found ${availability.latest}, but CLI entry is missing at ${entry}`);
13824
15164
  return;
13825
15165
  }
@@ -13911,7 +15251,7 @@ function scheduleAfter(now, outcome) {
13911
15251
  nextPollAtMs = now + currentIntervalMs;
13912
15252
  }
13913
15253
  function sleep2(ms) {
13914
- return new Promise((resolve) => setTimeout(resolve, ms));
15254
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
13915
15255
  }
13916
15256
  async function runLoop() {
13917
15257
  while (true) {