fleetlens 0.2.9 → 0.4.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 (237) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +9 -0
  3. package/app/apps/web/.next/build-manifest.json +6 -6
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +62 -0
  6. package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +3 -3
  7. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/app/apps/web/.next/server/app/_not-found/page/build-manifest.json +3 -3
  15. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  16. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/app/apps/web/.next/server/app/api/ask/route.js +3 -2
  19. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/events/route.js +1 -0
  21. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/api/insights/months-index/route/app-paths-manifest.json +3 -0
  23. package/app/apps/web/.next/server/app/api/insights/months-index/route/build-manifest.json +9 -0
  24. package/app/apps/web/.next/server/app/api/insights/months-index/route/server-reference-manifest.json +4 -0
  25. package/app/apps/web/.next/server/app/api/insights/months-index/route.js +7 -0
  26. package/app/apps/web/.next/server/app/api/insights/months-index/route.js.map +5 -0
  27. package/app/apps/web/.next/server/app/api/insights/months-index/route.js.nft.json +1 -0
  28. package/app/apps/web/.next/server/app/api/insights/months-index/route_client-reference-manifest.js +3 -0
  29. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route/app-paths-manifest.json +3 -0
  30. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route/build-manifest.json +9 -0
  31. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route/server-reference-manifest.json +4 -0
  32. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route.js +6 -0
  33. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route.js.map +5 -0
  34. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route.js.nft.json +1 -0
  35. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route_client-reference-manifest.js +3 -0
  36. package/app/apps/web/.next/server/app/api/insights/route/app-paths-manifest.json +3 -0
  37. package/app/apps/web/.next/server/app/api/insights/route/build-manifest.json +9 -0
  38. package/app/apps/web/.next/server/app/api/insights/route/server-reference-manifest.json +4 -0
  39. package/app/apps/web/.next/server/app/api/insights/route.js +8 -0
  40. package/app/apps/web/.next/server/app/api/insights/route.js.map +5 -0
  41. package/app/apps/web/.next/server/app/api/insights/route.js.nft.json +1 -0
  42. package/app/apps/web/.next/server/app/api/insights/route_client-reference-manifest.js +3 -0
  43. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route/app-paths-manifest.json +3 -0
  44. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route/build-manifest.json +9 -0
  45. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route/server-reference-manifest.json +4 -0
  46. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route.js +6 -0
  47. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route.js.map +5 -0
  48. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route.js.nft.json +1 -0
  49. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route_client-reference-manifest.js +3 -0
  50. package/app/apps/web/.next/server/app/api/insights/saved/route/app-paths-manifest.json +3 -0
  51. package/app/apps/web/.next/server/app/api/insights/saved/route/build-manifest.json +9 -0
  52. package/app/apps/web/.next/server/app/api/insights/saved/route/server-reference-manifest.json +4 -0
  53. package/app/apps/web/.next/server/app/api/insights/saved/route.js +6 -0
  54. package/app/apps/web/.next/server/app/api/insights/saved/route.js.map +5 -0
  55. package/app/apps/web/.next/server/app/api/insights/saved/route.js.nft.json +1 -0
  56. package/app/apps/web/.next/server/app/api/insights/saved/route_client-reference-manifest.js +3 -0
  57. package/app/apps/web/.next/server/app/api/insights/weeks-index/route/app-paths-manifest.json +3 -0
  58. package/app/apps/web/.next/server/app/api/insights/weeks-index/route/build-manifest.json +9 -0
  59. package/app/apps/web/.next/server/app/api/insights/weeks-index/route/server-reference-manifest.json +4 -0
  60. package/app/apps/web/.next/server/app/api/insights/weeks-index/route.js +7 -0
  61. package/app/apps/web/.next/server/app/api/insights/weeks-index/route.js.map +5 -0
  62. package/app/apps/web/.next/server/app/api/insights/weeks-index/route.js.nft.json +1 -0
  63. package/app/apps/web/.next/server/app/api/insights/weeks-index/route_client-reference-manifest.js +3 -0
  64. package/app/apps/web/.next/server/app/insights/[key]/page/app-paths-manifest.json +3 -0
  65. package/app/apps/web/.next/server/app/insights/[key]/page/build-manifest.json +17 -0
  66. package/app/apps/web/.next/server/app/insights/[key]/page/next-font-manifest.json +6 -0
  67. package/app/apps/web/.next/server/app/insights/[key]/page/react-loadable-manifest.json +1 -0
  68. package/app/apps/web/.next/server/app/insights/[key]/page/server-reference-manifest.json +4 -0
  69. package/app/apps/web/.next/server/app/insights/[key]/page.js +16 -0
  70. package/app/apps/web/.next/server/app/insights/[key]/page.js.map +5 -0
  71. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -0
  72. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +3 -0
  73. package/app/apps/web/.next/server/app/insights/page/app-paths-manifest.json +3 -0
  74. package/app/apps/web/.next/server/app/insights/page/build-manifest.json +17 -0
  75. package/app/apps/web/.next/server/app/insights/page/next-font-manifest.json +6 -0
  76. package/app/apps/web/.next/server/app/insights/page/react-loadable-manifest.json +1 -0
  77. package/app/apps/web/.next/server/app/insights/page/server-reference-manifest.json +4 -0
  78. package/app/apps/web/.next/server/app/insights/page.js +16 -0
  79. package/app/apps/web/.next/server/app/insights/page.js.map +5 -0
  80. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -0
  81. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +3 -0
  82. package/app/apps/web/.next/server/app/insights/print/[key]/page/app-paths-manifest.json +3 -0
  83. package/app/apps/web/.next/server/app/insights/print/[key]/page/build-manifest.json +17 -0
  84. package/app/apps/web/.next/server/app/insights/print/[key]/page/next-font-manifest.json +6 -0
  85. package/app/apps/web/.next/server/app/insights/print/[key]/page/react-loadable-manifest.json +1 -0
  86. package/app/apps/web/.next/server/app/insights/print/[key]/page/server-reference-manifest.json +4 -0
  87. package/app/apps/web/.next/server/app/insights/print/[key]/page.js +16 -0
  88. package/app/apps/web/.next/server/app/insights/print/[key]/page.js.map +5 -0
  89. package/app/apps/web/.next/server/app/insights/print/[key]/page.js.nft.json +1 -0
  90. package/app/apps/web/.next/server/app/insights/print/[key]/page_client-reference-manifest.js +3 -0
  91. package/app/apps/web/.next/server/app/page/build-manifest.json +3 -3
  92. package/app/apps/web/.next/server/app/page.js +3 -3
  93. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  94. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/app/apps/web/.next/server/app/parallelism/page/build-manifest.json +3 -3
  96. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  97. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  98. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  99. package/app/apps/web/.next/server/app/projects/[slug]/page/build-manifest.json +3 -3
  100. package/app/apps/web/.next/server/app/projects/[slug]/page.js +2 -3
  101. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  102. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  103. package/app/apps/web/.next/server/app/projects/page/build-manifest.json +3 -3
  104. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  105. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  106. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  107. package/app/apps/web/.next/server/app/sessions/[id]/page/build-manifest.json +3 -3
  108. package/app/apps/web/.next/server/app/sessions/[id]/page.js +2 -2
  109. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  110. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  111. package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +3 -3
  112. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  113. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  114. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  115. package/app/apps/web/.next/server/app/usage/page/build-manifest.json +3 -3
  116. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  117. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  118. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  119. package/app/apps/web/.next/server/app-paths-manifest.json +9 -0
  120. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_months-index_route_actions_0~h~zci.js +3 -0
  121. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_saved_[key]_route_actions_0a5j9yw.js +3 -0
  122. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_weeks-index_route_actions_0hh-1c7.js +3 -0
  123. package/app/apps/web/.next/server/chunks/{[externals]__0rv9ct5._.js → [externals]__0_xztrf._.js} +2 -2
  124. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0.hmj0z._.js +3 -0
  125. package/app/apps/web/.next/server/chunks/[root-of-the-server]__01h_g-p._.js +3 -0
  126. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nh-z10._.js +3 -0
  127. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0op9af~._.js +1 -1
  128. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ri7q~r._.js +3 -0
  129. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0rr1~~x._.js +3 -0
  130. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0s0gv.0._.js +3 -0
  131. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_pdf_[key]_route_actions_0lu-lnx.js +3 -0
  132. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_route_actions_0h8l5lz.js +3 -0
  133. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_saved_route_actions_13rlblw.js +3 -0
  134. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_00t2xj6.js +68 -0
  135. package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0sgyieh._.js → node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js} +5 -5
  136. package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_0bc7ptm.js +3 -0
  137. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__00z7zlm._.js +3 -0
  138. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__08.d5ia._.js +12 -0
  139. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0b323~k._.js +8 -0
  140. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0euu8sa._.js +3 -0
  141. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0gio_ck._.js +3 -0
  142. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0gx-4qa._.js +3 -0
  143. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +3 -3
  144. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0z45d0g._.js +49 -0
  145. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__13itnvx._.js +3 -0
  146. package/app/apps/web/.next/server/chunks/ssr/_09n75_5._.js +1 -1
  147. package/app/apps/web/.next/server/chunks/ssr/_0r~__0v._.js +12 -0
  148. package/app/apps/web/.next/server/chunks/ssr/_0xies90._.js +1 -1
  149. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_insights_[key]_page_actions_0_z2~e_.js +3 -0
  150. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_insights_page_actions_0zjdm~n.js +3 -0
  151. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_insights_print_[key]_page_actions_0a853fw.js +3 -0
  152. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_06j3c.0._.js +1 -1
  153. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_insight-report_tsx_13q2a-c._.js +12 -0
  154. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_insights-view_tsx_11730ta._.js +6 -0
  155. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0-lmm4p._.js +3 -0
  156. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0nqz8dw._.js +3 -0
  157. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js +3 -0
  158. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0vz7_px.js +4 -0
  159. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10cdrh2.js +4 -0
  160. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_12n1lw_.js +4 -0
  161. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0-jvt~a._.js +1 -1
  162. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_analytics_0k4dgst.js +1 -1
  163. package/app/apps/web/.next/server/functions-config-manifest.json +7 -1
  164. package/app/apps/web/.next/server/middleware-build-manifest.js +6 -6
  165. package/app/apps/web/.next/server/pages/500.html +1 -1
  166. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  167. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  168. package/app/apps/web/.next/static/chunks/03.mv4y4.w-vb.js +6 -0
  169. package/app/apps/web/.next/static/chunks/03_7xtpi9sv4g.js +4 -0
  170. package/app/apps/web/.next/static/chunks/03g5dw5eskotd.js +6 -0
  171. package/app/apps/web/.next/static/chunks/063oimt30jeo9.js +10 -0
  172. package/app/apps/web/.next/static/chunks/08c1pcrocqq5s.js +6 -0
  173. package/app/apps/web/.next/static/chunks/0e-wrhqh4h3r5.js +2 -0
  174. package/app/apps/web/.next/static/chunks/0fc6dk2qtokwh.js +1 -0
  175. package/app/apps/web/.next/static/chunks/0i7er7_bfab36.js +10 -0
  176. package/app/apps/web/.next/static/chunks/0ipytyi01h0ta.js +1 -0
  177. package/app/apps/web/.next/static/chunks/137uw2-qbro07.js +31 -0
  178. package/app/apps/web/.next/static/chunks/{turbopack-17n9y8y0h4q9f.js → turbopack-169w-1._23ra_.js} +1 -1
  179. package/app/apps/web/app/api/insights/months-index/route.ts +80 -0
  180. package/app/apps/web/app/api/insights/pdf/[key]/route.ts +45 -0
  181. package/app/apps/web/app/api/insights/route.ts +508 -0
  182. package/app/apps/web/app/api/insights/saved/[key]/route.ts +11 -0
  183. package/app/apps/web/app/api/insights/saved/route.ts +9 -0
  184. package/app/apps/web/app/api/insights/weeks-index/route.ts +95 -0
  185. package/app/apps/web/app/insights/[key]/page.tsx +59 -0
  186. package/app/apps/web/app/insights/page.tsx +7 -0
  187. package/app/apps/web/app/insights/print/[key]/page.tsx +73 -0
  188. package/app/apps/web/app/layout.tsx +2 -0
  189. package/app/apps/web/app/projects/[slug]/page.tsx +2 -0
  190. package/app/apps/web/app/sessions/[id]/page.tsx +27 -1
  191. package/app/apps/web/app/sessions/[id]/session-view.tsx +372 -793
  192. package/app/apps/web/app/sessions/[id]/team-tab/adapter.ts +633 -0
  193. package/app/apps/web/app/sessions/[id]/team-tab/format.ts +20 -0
  194. package/app/apps/web/app/sessions/[id]/team-tab/team-minimap.tsx +682 -0
  195. package/app/apps/web/app/sessions/[id]/team-tab/team-tab-client.tsx +78 -0
  196. package/app/apps/web/app/sessions/[id]/team-tab/team-table.tsx +796 -0
  197. package/app/apps/web/app/sessions/[id]/team-tab/turn-drawer.tsx +805 -0
  198. package/app/apps/web/app/sessions/[id]/tool-cards.tsx +461 -0
  199. package/app/apps/web/app/sessions/[id]/turn-steps.tsx +306 -0
  200. package/app/apps/web/app/sessions/sessions-grid.tsx +36 -5
  201. package/app/apps/web/components/insight-report.tsx +1031 -0
  202. package/app/apps/web/components/insights-view.tsx +647 -0
  203. package/app/apps/web/components/live-refresher.tsx +11 -2
  204. package/app/apps/web/components/live-sessions-widget.tsx +20 -6
  205. package/app/apps/web/components/sidebar.tsx +8 -0
  206. package/app/apps/web/components/team-badge.tsx +90 -0
  207. package/app/apps/web/eslint.config.mjs +2 -0
  208. package/app/apps/web/lib/ai/chrome-pdf.ts +64 -0
  209. package/app/apps/web/lib/ai/insights-prompt.ts +71 -0
  210. package/app/apps/web/lib/ai/saved-reports.ts +83 -0
  211. package/app/apps/web/lib/date-range.ts +11 -2
  212. package/app/apps/web/lib/use-live-events.ts +7 -2
  213. package/app/apps/web/package.json +3 -2
  214. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  215. package/dist/daemon-worker.js +2765 -11
  216. package/dist/index.js +3047 -253
  217. package/package.json +5 -3
  218. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__08cr2u9._.js +0 -3
  219. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0b..h_8._.js +0 -3
  220. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0cdd~8n._.js +0 -3
  221. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ms7h0~._.js +0 -8
  222. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~a~v00._.js +0 -3
  223. package/app/apps/web/.next/server/chunks/ssr/_0zv~jv-._.js +0 -12
  224. package/app/apps/web/.next/server/chunks/ssr/_12dtryh._.js +0 -12
  225. package/app/apps/web/.next/server/chunks/ssr/node_modules_0i2xw~e._.js +0 -3
  226. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0.knjn.._.js +0 -3
  227. package/app/apps/web/.next/static/chunks/0257pdz1-imal.js +0 -2
  228. package/app/apps/web/.next/static/chunks/03fw7f2obcvq~.js +0 -6
  229. package/app/apps/web/.next/static/chunks/07oyxajlp3dvb.js +0 -6
  230. package/app/apps/web/.next/static/chunks/0a24elrqoqqep.js +0 -10
  231. package/app/apps/web/.next/static/chunks/0i4zodv86~~~i.js +0 -1
  232. package/app/apps/web/.next/static/chunks/0qmh881w772ef.js +0 -31
  233. package/app/apps/web/.next/static/chunks/0rbm60-qyshdm.js +0 -6
  234. package/app/apps/web/.next/static/chunks/0wdga7zbwvg70.js +0 -1
  235. /package/app/apps/web/.next/static/{2rEsiJssG30QsuNT0Lp9S → G_4ofKI9v9ARC_lb9JILZ}/_buildManifest.js +0 -0
  236. /package/app/apps/web/.next/static/{2rEsiJssG30QsuNT0Lp9S → G_4ofKI9v9ARC_lb9JILZ}/_clientMiddlewareManifest.js +0 -0
  237. /package/app/apps/web/.next/static/{2rEsiJssG30QsuNT0Lp9S → G_4ofKI9v9ARC_lb9JILZ}/_ssgManifest.js +0 -0
@@ -1,7 +1,2590 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __esm = (fn, res) => function __init() {
4
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
+ };
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+
11
+ // ../parser/dist/types.js
12
+ var init_types = __esm({
13
+ "../parser/dist/types.js"() {
14
+ "use strict";
15
+ }
16
+ });
17
+
18
+ // ../parser/dist/parser.js
19
+ function cleanText(s) {
20
+ return s.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
21
+ }
22
+ function truncate(s, n = 200) {
23
+ const one = cleanText(s);
24
+ return one.length > n ? one.slice(0, n - 1) + "\u2026" : one;
25
+ }
26
+ function classifyTeammateMessage(text) {
27
+ const m = text.match(TEAMMATE_MSG_RE);
28
+ if (!m)
29
+ return void 0;
30
+ const teammateId = m[1];
31
+ const body = m[2].trim();
32
+ let kind = "message";
33
+ if (body.startsWith("{")) {
34
+ try {
35
+ const parsed = JSON.parse(body);
36
+ if (parsed.type === "idle_notification")
37
+ kind = "idle-notification";
38
+ else if (parsed.type === "shutdown_request")
39
+ kind = "shutdown-request";
40
+ else if (parsed.type === "shutdown_approved")
41
+ kind = "shutdown-approved";
42
+ else if (parsed.type === "task_assignment")
43
+ kind = "task-assignment";
44
+ else if (parsed.type === "teammate_terminated")
45
+ kind = "teammate-terminated";
46
+ } catch {
47
+ }
48
+ }
49
+ return { teammateId, body, kind };
50
+ }
51
+ function extractUsage(u) {
52
+ if (!u || typeof u !== "object")
53
+ return void 0;
54
+ const r = u;
55
+ const toNum = (v) => typeof v === "number" ? v : 0;
56
+ return {
57
+ input: toNum(r.input_tokens),
58
+ output: toNum(r.output_tokens),
59
+ cacheRead: toNum(r.cache_read_input_tokens),
60
+ cacheWrite: toNum(r.cache_creation_input_tokens)
61
+ };
62
+ }
63
+ function toEvent(raw, index) {
64
+ if (!raw || typeof raw !== "object")
65
+ return null;
66
+ const r = raw;
67
+ const rawType = String(r.type ?? "");
68
+ const uuid = r.uuid;
69
+ const parentUuid = r.parentUuid ?? null;
70
+ const timestamp = r.timestamp;
71
+ if (rawType === "assistant") {
72
+ const msg = r.message ?? {};
73
+ const content = msg.content ?? [];
74
+ const usage = extractUsage(msg.usage);
75
+ const model = msg.model;
76
+ const requestId = r.requestId;
77
+ const messageId = msg.id;
78
+ const stopReason = msg.stop_reason;
79
+ const first = content[0];
80
+ let role = "agent";
81
+ let preview;
82
+ let toolName;
83
+ let toolUseId;
84
+ if (first?.type === "thinking") {
85
+ role = "agent-thinking";
86
+ preview = "[thinking] " + truncate(first.thinking, 140);
87
+ } else if (first?.type === "tool_use") {
88
+ role = "tool-call";
89
+ toolName = first.name;
90
+ toolUseId = first.id;
91
+ preview = `${first.name} ` + truncate(JSON.stringify(first.input ?? {}), 100);
92
+ } else if (first?.type === "text") {
93
+ preview = truncate(first.text, 200);
94
+ } else {
95
+ preview = `<assistant:${first?.type ?? "empty"}>`;
96
+ }
97
+ return {
98
+ index,
99
+ uuid,
100
+ parentUuid,
101
+ timestamp,
102
+ role,
103
+ rawType,
104
+ preview,
105
+ blocks: content,
106
+ usage,
107
+ model,
108
+ requestId,
109
+ messageId,
110
+ stopReason,
111
+ toolName,
112
+ toolUseId,
113
+ raw
114
+ };
115
+ }
116
+ if (rawType === "user") {
117
+ const msg = r.message ?? {};
118
+ const c = msg.content;
119
+ if (typeof c === "string") {
120
+ const tm = classifyTeammateMessage(c);
121
+ return {
122
+ index,
123
+ uuid,
124
+ parentUuid,
125
+ timestamp,
126
+ role: "user",
127
+ rawType,
128
+ preview: truncate(c, 200),
129
+ blocks: [{ type: "text", text: c }],
130
+ raw,
131
+ teammateMessage: tm
132
+ };
133
+ }
134
+ if (Array.isArray(c)) {
135
+ const first = c[0];
136
+ if (first?.type === "tool_result") {
137
+ const resultText = typeof first.content === "string" ? first.content : JSON.stringify(first.content);
138
+ return {
139
+ index,
140
+ uuid,
141
+ parentUuid,
142
+ timestamp,
143
+ role: "tool-result",
144
+ rawType,
145
+ preview: truncate(resultText, 200),
146
+ blocks: c,
147
+ toolUseId: first.tool_use_id,
148
+ toolResult: r.toolUseResult,
149
+ raw
150
+ };
151
+ }
152
+ const textBlock = c.find((b) => b?.type === "text");
153
+ const tm = textBlock ? classifyTeammateMessage(textBlock.text) : void 0;
154
+ return {
155
+ index,
156
+ uuid,
157
+ parentUuid,
158
+ timestamp,
159
+ role: "user",
160
+ rawType,
161
+ preview: textBlock ? truncate(textBlock.text, 200) : truncate(JSON.stringify(c), 200),
162
+ blocks: c,
163
+ raw,
164
+ teammateMessage: tm
165
+ };
166
+ }
167
+ }
168
+ if (rawType === "attachment") {
169
+ const a = r.attachment ?? {};
170
+ const at = String(a.type ?? "attachment");
171
+ let preview = `<attachment: ${at}>`;
172
+ if (typeof a.content === "string" && a.content.length > 0) {
173
+ preview = `[${at}] ` + truncate(a.content, 140);
174
+ } else if (Array.isArray(a.addedNames)) {
175
+ preview = `[${at}] +${a.addedNames.length} names`;
176
+ }
177
+ return {
178
+ index,
179
+ uuid,
180
+ parentUuid,
181
+ timestamp,
182
+ role: "system",
183
+ rawType,
184
+ preview,
185
+ blocks: [{ type: "text", text: JSON.stringify(a, null, 2) }],
186
+ attachmentType: at,
187
+ raw
188
+ };
189
+ }
190
+ return {
191
+ index,
192
+ uuid,
193
+ parentUuid,
194
+ timestamp,
195
+ role: "meta",
196
+ rawType: rawType || "unknown",
197
+ preview: `${rawType}: ${truncate(JSON.stringify(r), 140)}`,
198
+ blocks: [{ type: "text", text: JSON.stringify(r, null, 2) }],
199
+ raw
200
+ };
201
+ }
202
+ function parseTranscript(rawLines) {
203
+ const events = [];
204
+ for (let i = 0; i < rawLines.length; i++) {
205
+ const ev = toEvent(rawLines[i], i);
206
+ if (ev)
207
+ events.push(ev);
208
+ }
209
+ const timestamped = events.map((e) => e.timestamp ? Date.parse(e.timestamp) : void 0).filter((n) => typeof n === "number" && !Number.isNaN(n));
210
+ const startMs = timestamped.length ? Math.min(...timestamped) : void 0;
211
+ const endMs = timestamped.length ? Math.max(...timestamped) : void 0;
212
+ const firstTs = startMs !== void 0 ? new Date(startMs).toISOString() : void 0;
213
+ const lastTs = endMs !== void 0 ? new Date(endMs).toISOString() : void 0;
214
+ for (const ev of events) {
215
+ if (!ev.timestamp)
216
+ continue;
217
+ const ms = Date.parse(ev.timestamp);
218
+ if (Number.isNaN(ms))
219
+ continue;
220
+ if (startMs !== void 0)
221
+ ev.tOffsetMs = Math.max(0, ms - startMs);
222
+ }
223
+ const isConversational = (r) => r === "user" || r === "agent" || r === "agent-thinking" || r === "tool-call" || r === "tool-result";
224
+ const chrono = events.filter((e) => e.timestamp && isConversational(e.role)).map((e) => ({ e, ms: Date.parse(e.timestamp) })).filter((x) => !Number.isNaN(x.ms)).sort((a, b) => a.ms - b.ms);
225
+ for (let i = 1; i < chrono.length; i++) {
226
+ chrono[i].e.gapMs = Math.max(0, chrono[i].ms - chrono[i - 1].ms);
227
+ }
228
+ const IDLE_THRESHOLD_MS = 3 * 60 * 1e3;
229
+ const allChrono = events.filter((e) => e.timestamp).map((e) => Date.parse(e.timestamp)).filter((ms) => !Number.isNaN(ms)).sort((a, b) => a - b);
230
+ const activeSegments = [];
231
+ let airTimeMs = 0;
232
+ if (allChrono.length > 0) {
233
+ let segStart = allChrono[0];
234
+ let segEnd = allChrono[0];
235
+ for (let i = 1; i < allChrono.length; i++) {
236
+ const t = allChrono[i];
237
+ const gap = t - segEnd;
238
+ if (gap > IDLE_THRESHOLD_MS) {
239
+ activeSegments.push({ startMs: segStart, endMs: segEnd });
240
+ segStart = t;
241
+ } else if (gap > 0) {
242
+ airTimeMs += gap;
243
+ }
244
+ segEnd = t;
245
+ }
246
+ activeSegments.push({ startMs: segStart, endMs: segEnd });
247
+ }
248
+ const totalUsage = { ...BLANK_USAGE };
249
+ const seenMessageIds = /* @__PURE__ */ new Set();
250
+ let model;
251
+ let sessionId = "";
252
+ let cwd;
253
+ let gitBranch;
254
+ let teamName;
255
+ let agentName;
256
+ let hasTeamCreate = false;
257
+ let hasOutboundDispatch = false;
258
+ let toolCallCount = 0;
259
+ let turnCount = 0;
260
+ let firstUserPreview;
261
+ let lastAgentPreview;
262
+ let lastUserPreview;
263
+ let linesAdded = 0;
264
+ let linesRemoved = 0;
265
+ const filesEdited = /* @__PURE__ */ new Set();
266
+ for (const e of events) {
267
+ if (e.role === "tool-call") {
268
+ toolCallCount++;
269
+ const toolBlock = e.blocks.find((b) => b && b.type === "tool_use");
270
+ if (toolBlock) {
271
+ const input = toolBlock.input ?? {};
272
+ if (toolBlock.name === "TeamCreate")
273
+ hasTeamCreate = true;
274
+ if (toolBlock.name === "SendMessage") {
275
+ const to = typeof input.to === "string" ? input.to : "";
276
+ if (to && to !== "team-lead")
277
+ hasOutboundDispatch = true;
278
+ }
279
+ const fp = typeof input.file_path === "string" ? input.file_path : void 0;
280
+ if (toolBlock.name === "Edit" && fp) {
281
+ filesEdited.add(fp);
282
+ const oldStr = typeof input.old_string === "string" ? input.old_string : "";
283
+ const newStr = typeof input.new_string === "string" ? input.new_string : "";
284
+ const oldLines = oldStr ? oldStr.split("\n").length : 0;
285
+ const newLines = newStr ? newStr.split("\n").length : 0;
286
+ linesAdded += Math.max(0, newLines - oldLines);
287
+ linesRemoved += Math.max(0, oldLines - newLines);
288
+ } else if (toolBlock.name === "Write" && fp) {
289
+ filesEdited.add(fp);
290
+ const content = typeof input.content === "string" ? input.content : "";
291
+ linesAdded += content ? content.split("\n").length : 0;
292
+ }
293
+ }
294
+ }
295
+ if (e.role === "user") {
296
+ const rawMsg = e.raw?.message;
297
+ const rawContent = typeof rawMsg?.content === "string" ? rawMsg.content : "";
298
+ const isHidden = rawContent.startsWith("<command-name>") || rawContent.startsWith("<local-command-caveat>") || rawContent.startsWith("Base directory for this skill:") || rawContent.startsWith("<task-notification>");
299
+ const isTeamNoise = e.teammateMessage !== void 0 && !agentName;
300
+ if (!isHidden && !isTeamNoise) {
301
+ if (!firstUserPreview) {
302
+ firstUserPreview = e.teammateMessage ? e.teammateMessage.body : e.preview;
303
+ }
304
+ turnCount++;
305
+ lastUserPreview = e.preview;
306
+ }
307
+ }
308
+ if (e.role === "agent") {
309
+ lastAgentPreview = e.preview;
310
+ }
311
+ }
312
+ for (const r of rawLines) {
313
+ if (!r || typeof r !== "object")
314
+ continue;
315
+ const o = r;
316
+ if (typeof o.sessionId === "string" && !sessionId)
317
+ sessionId = o.sessionId;
318
+ if (typeof o.cwd === "string" && !cwd)
319
+ cwd = o.cwd;
320
+ if (typeof o.gitBranch === "string" && !gitBranch)
321
+ gitBranch = o.gitBranch;
322
+ if (typeof o.teamName === "string" && !teamName)
323
+ teamName = o.teamName;
324
+ if (typeof o.agentName === "string" && !agentName)
325
+ agentName = o.agentName;
326
+ if (o.type === "assistant") {
327
+ const m = o.message;
328
+ if (m) {
329
+ if (typeof m.model === "string" && !model)
330
+ model = m.model;
331
+ const mid = typeof m.id === "string" ? m.id : void 0;
332
+ const rid = typeof o.requestId === "string" ? o.requestId : void 0;
333
+ const dedupKey = mid != null ? `${mid}:${rid ?? ""}` : void 0;
334
+ if (dedupKey && seenMessageIds.has(dedupKey))
335
+ continue;
336
+ if (dedupKey)
337
+ seenMessageIds.add(dedupKey);
338
+ const u = extractUsage(m.usage);
339
+ if (u) {
340
+ totalUsage.input += u.input;
341
+ totalUsage.output += u.output;
342
+ totalUsage.cacheRead += u.cacheRead;
343
+ totalUsage.cacheWrite += u.cacheWrite;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ return {
349
+ events,
350
+ meta: {
351
+ sessionId,
352
+ firstTimestamp: firstTs,
353
+ lastTimestamp: lastTs,
354
+ durationMs: startMs !== void 0 && endMs !== void 0 ? endMs - startMs : void 0,
355
+ eventCount: events.length,
356
+ model,
357
+ cwd,
358
+ gitBranch,
359
+ totalUsage,
360
+ status: "idle",
361
+ firstUserPreview,
362
+ lastUserPreview,
363
+ lastAgentPreview,
364
+ toolCallCount,
365
+ turnCount,
366
+ airTimeMs,
367
+ activeSegments,
368
+ linesAdded,
369
+ linesRemoved,
370
+ filesEdited: filesEdited.size,
371
+ teamName,
372
+ agentName,
373
+ isTeamLead: teamName !== void 0 && agentName === void 0 && (hasTeamCreate || hasOutboundDispatch)
374
+ }
375
+ };
376
+ }
377
+ var BLANK_USAGE, TEAMMATE_MSG_RE;
378
+ var init_parser = __esm({
379
+ "../parser/dist/parser.js"() {
380
+ "use strict";
381
+ BLANK_USAGE = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
382
+ TEAMMATE_MSG_RE = /^\s*<teammate-message\s+teammate_id="([^"]+)"[^>]*>([\s\S]*?)<\/teammate-message>/;
383
+ }
384
+ });
385
+
386
+ // ../parser/dist/presentation.js
387
+ function rowPrimaryIndex(r) {
388
+ if (r.kind === "tool-group")
389
+ return r.events[0].index;
390
+ return r.event.index;
391
+ }
392
+ function buildMegaRows(rows) {
393
+ const out = [];
394
+ let buffer = [];
395
+ const flush = () => {
396
+ if (buffer.length === 0)
397
+ return;
398
+ let agentMessages = 0;
399
+ let toolCalls = 0;
400
+ let errors = 0;
401
+ let firstAgentIndex;
402
+ const toolMap = /* @__PURE__ */ new Map();
403
+ const seenMsgIds = /* @__PURE__ */ new Set();
404
+ const tokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
405
+ for (let i = 0; i < buffer.length; i++) {
406
+ const r = buffer[i];
407
+ if (r.kind === "agent") {
408
+ agentMessages++;
409
+ if (firstAgentIndex === void 0)
410
+ firstAgentIndex = i;
411
+ const msgId = r.event.messageId;
412
+ const usage = r.event.usage;
413
+ if (msgId && !seenMsgIds.has(msgId) && usage) {
414
+ seenMsgIds.add(msgId);
415
+ tokens.input += usage.input;
416
+ tokens.output += usage.output;
417
+ tokens.cacheRead += usage.cacheRead;
418
+ tokens.cacheWrite += usage.cacheWrite;
419
+ }
420
+ } else if (r.kind === "tool-group") {
421
+ toolCalls += r.count;
422
+ for (const t of r.toolNames) {
423
+ toolMap.set(t.name, (toolMap.get(t.name) ?? 0) + t.count);
424
+ }
425
+ } else if (r.kind === "error") {
426
+ errors++;
427
+ }
428
+ }
429
+ let finalAgentIndex;
430
+ for (let i = buffer.length - 1; i >= 0; i--) {
431
+ const r = buffer[i];
432
+ if (r.kind !== "agent")
433
+ continue;
434
+ const prev = buffer[i - 1];
435
+ if (prev?.kind === "task-notification")
436
+ continue;
437
+ finalAgentIndex = i;
438
+ break;
439
+ }
440
+ if (finalAgentIndex === void 0) {
441
+ for (let i = buffer.length - 1; i >= 0; i--) {
442
+ if (buffer[i].kind === "agent") {
443
+ finalAgentIndex = i;
444
+ break;
445
+ }
446
+ }
447
+ }
448
+ const firstAgentPreview = firstAgentIndex !== void 0 && buffer[firstAgentIndex].kind === "agent" ? buffer[firstAgentIndex].event.preview : void 0;
449
+ const finalAgentPreview = finalAgentIndex !== void 0 && buffer[finalAgentIndex].kind === "agent" ? buffer[finalAgentIndex].event.preview : void 0;
450
+ const toolNames = Array.from(toolMap.entries()).sort((a, b) => b[1] - a[1]).map(([name, count]) => ({ name, count }));
451
+ const first = buffer[0];
452
+ const last = buffer[buffer.length - 1];
453
+ const start = first.tOffsetMs;
454
+ const end = last.tOffsetMs;
455
+ const durationMs = start !== void 0 && end !== void 0 ? end - start : void 0;
456
+ out.push({
457
+ kind: "turn",
458
+ rows: buffer,
459
+ firstPrimaryIndex: rowPrimaryIndex(first),
460
+ tOffsetMs: start,
461
+ durationMs,
462
+ summary: {
463
+ agentMessages,
464
+ toolCalls,
465
+ errors,
466
+ firstAgentPreview,
467
+ finalAgentPreview,
468
+ firstAgentIndex,
469
+ finalAgentIndex,
470
+ toolNames,
471
+ totalTokens: tokens
472
+ }
473
+ });
474
+ buffer = [];
475
+ };
476
+ for (const r of rows) {
477
+ if (r.kind === "user" || r.kind === "interrupt") {
478
+ flush();
479
+ out.push(r);
480
+ } else {
481
+ buffer.push(r);
482
+ }
483
+ }
484
+ flush();
485
+ return out;
486
+ }
487
+ function parseTaskNotification(text) {
488
+ if (!TASK_NOTIFICATION_RE.test(text))
489
+ return null;
490
+ const pick = (tag) => {
491
+ const m = text.match(new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`));
492
+ return m ? m[1].trim() : void 0;
493
+ };
494
+ const statusRaw = (pick("status") ?? "").toLowerCase();
495
+ let status = "unknown";
496
+ if (statusRaw === "success" || statusRaw === "ok" || statusRaw === "done" || statusRaw === "completed")
497
+ status = "success";
498
+ else if (statusRaw === "failed" || statusRaw === "error" || statusRaw === "fail")
499
+ status = "failed";
500
+ else if (statusRaw === "running" || statusRaw === "in_progress")
501
+ status = "running";
502
+ return {
503
+ status,
504
+ summary: pick("summary") ?? "Background task notification",
505
+ taskId: pick("task-id"),
506
+ toolUseId: pick("tool-use-id"),
507
+ outputFile: pick("output-file")
508
+ };
509
+ }
510
+ function isAttachmentOrMeta(e) {
511
+ return e.role === "system" || e.role === "meta";
512
+ }
513
+ function firstTextOfBlocks(blocks) {
514
+ for (const b of blocks) {
515
+ if (b?.type === "text")
516
+ return b.text;
517
+ }
518
+ return void 0;
519
+ }
520
+ function hasZeroUsage(e) {
521
+ if (!e.usage)
522
+ return false;
523
+ const u = e.usage;
524
+ return u.input === 0 && u.output === 0 && u.cacheRead === 0 && u.cacheWrite === 0;
525
+ }
526
+ function buildPresentation(events) {
527
+ const byMessageId = /* @__PURE__ */ new Map();
528
+ for (const e of events) {
529
+ if (!e.messageId)
530
+ continue;
531
+ const list = byMessageId.get(e.messageId) ?? [];
532
+ list.push(e);
533
+ byMessageId.set(e.messageId, list);
534
+ }
535
+ const visible = events.filter((e) => {
536
+ if (isAttachmentOrMeta(e))
537
+ return false;
538
+ if (e.role === "tool-result")
539
+ return false;
540
+ if (e.role === "agent-thinking")
541
+ return false;
542
+ if (e.role === "user") {
543
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
544
+ if (SKILL_CONTENT_RE.test(txt))
545
+ return false;
546
+ }
547
+ return true;
548
+ });
549
+ const rows = [];
550
+ for (let i = 0; i < visible.length; i++) {
551
+ const e = visible[i];
552
+ if (e.role === "tool-call") {
553
+ const start = i;
554
+ while (i + 1 < visible.length && visible[i + 1].role === "tool-call") {
555
+ i++;
556
+ }
557
+ const group = visible.slice(start, i + 1);
558
+ const order = [];
559
+ const counts = /* @__PURE__ */ new Map();
560
+ for (const ev of group) {
561
+ const name = ev.toolName ?? "tool";
562
+ if (!counts.has(name))
563
+ order.push(name);
564
+ counts.set(name, (counts.get(name) ?? 0) + 1);
565
+ }
566
+ const toolNames = order.map((name) => ({
567
+ name,
568
+ count: counts.get(name) ?? 1
569
+ }));
570
+ rows.push({
571
+ kind: "tool-group",
572
+ toolNames,
573
+ count: group.length,
574
+ events: group,
575
+ tOffsetMs: group[0].tOffsetMs,
576
+ gapMs: group[0].gapMs
577
+ });
578
+ continue;
579
+ }
580
+ if (e.role === "user") {
581
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
582
+ const taskNotif = parseTaskNotification(txt);
583
+ if (taskNotif) {
584
+ rows.push({
585
+ kind: "task-notification",
586
+ event: e,
587
+ status: taskNotif.status,
588
+ summary: taskNotif.summary,
589
+ taskId: taskNotif.taskId,
590
+ toolUseId: taskNotif.toolUseId,
591
+ outputFile: taskNotif.outputFile,
592
+ tOffsetMs: e.tOffsetMs,
593
+ gapMs: e.gapMs
594
+ });
595
+ continue;
596
+ }
597
+ if (INTERRUPT_RE.test(txt)) {
598
+ rows.push({
599
+ kind: "interrupt",
600
+ event: e,
601
+ tOffsetMs: e.tOffsetMs,
602
+ gapMs: e.gapMs
603
+ });
604
+ continue;
605
+ }
606
+ const slashMatch = txt.match(SLASH_COMMAND_RE);
607
+ if (slashMatch) {
608
+ const cmd = slashMatch[1];
609
+ const args = (slashMatch[2] ?? "").trim();
610
+ const pretty = args ? `${cmd} ${args}` : cmd;
611
+ rows.push({
612
+ kind: "user",
613
+ event: e,
614
+ displayPreview: pretty,
615
+ displayBlocks: [{ type: "text", text: pretty }],
616
+ tOffsetMs: e.tOffsetMs,
617
+ gapMs: e.gapMs
618
+ });
619
+ continue;
620
+ }
621
+ rows.push({
622
+ kind: "user",
623
+ event: e,
624
+ tOffsetMs: e.tOffsetMs,
625
+ gapMs: e.gapMs
626
+ });
627
+ continue;
628
+ }
629
+ if (e.role === "agent") {
630
+ const txt = firstTextOfBlocks(e.blocks) ?? "";
631
+ if (hasZeroUsage(e)) {
632
+ rows.push({
633
+ kind: "model",
634
+ event: e,
635
+ tOffsetMs: e.tOffsetMs,
636
+ gapMs: e.gapMs
637
+ });
638
+ if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
639
+ rows.push({
640
+ kind: "error",
641
+ event: e,
642
+ message: txt,
643
+ tOffsetMs: e.tOffsetMs,
644
+ gapMs: 0
645
+ });
646
+ }
647
+ continue;
648
+ }
649
+ if (RATE_LIMIT_RE.test(txt) && txt.length < 500) {
650
+ rows.push({
651
+ kind: "error",
652
+ event: e,
653
+ message: txt,
654
+ tOffsetMs: e.tOffsetMs,
655
+ gapMs: e.gapMs
656
+ });
657
+ continue;
658
+ }
659
+ const grouped = e.messageId ? byMessageId.get(e.messageId) ?? [e] : [e];
660
+ rows.push({
661
+ kind: "agent",
662
+ event: e,
663
+ groupedEvents: grouped,
664
+ tOffsetMs: e.tOffsetMs,
665
+ gapMs: e.gapMs
666
+ });
667
+ continue;
668
+ }
669
+ }
670
+ return rows;
671
+ }
672
+ var INTERRUPT_RE, RATE_LIMIT_RE, SKILL_CONTENT_RE, SLASH_COMMAND_RE, TASK_NOTIFICATION_RE;
673
+ var init_presentation = __esm({
674
+ "../parser/dist/presentation.js"() {
675
+ "use strict";
676
+ INTERRUPT_RE = /\[request interrupted|interrupted by user/i;
677
+ RATE_LIMIT_RE = /rate.?limit|overloaded_error|api error|Error sending message/i;
678
+ SKILL_CONTENT_RE = /^Base directory for this skill:/;
679
+ SLASH_COMMAND_RE = /<command-name>(\/[^<\s]+)<\/command-name>(?:[\s\S]*?<command-args>([\s\S]*?)<\/command-args>)?/;
680
+ TASK_NOTIFICATION_RE = /<task-notification>/;
681
+ }
682
+ });
683
+
684
+ // ../parser/dist/analytics.js
685
+ function toLocalDay(ms) {
686
+ const d = new Date(ms);
687
+ const y = d.getFullYear();
688
+ const m = String(d.getMonth() + 1).padStart(2, "0");
689
+ const day = String(d.getDate()).padStart(2, "0");
690
+ return `${y}-${m}-${day}`;
691
+ }
692
+ function sessionDay(meta) {
693
+ if (!meta.firstTimestamp)
694
+ return void 0;
695
+ const ms = Date.parse(meta.firstTimestamp);
696
+ if (Number.isNaN(ms))
697
+ return void 0;
698
+ return toLocalDay(ms);
699
+ }
700
+ function findWorktreeMarker(projectName) {
701
+ for (const marker of WORKTREE_MARKERS) {
702
+ const idx = projectName.lastIndexOf(marker);
703
+ if (idx >= 0)
704
+ return { idx, marker };
705
+ }
706
+ return null;
707
+ }
708
+ function canonicalProjectName(projectName) {
709
+ const hit = findWorktreeMarker(projectName);
710
+ if (hit)
711
+ return projectName.slice(0, hit.idx);
712
+ return projectName;
713
+ }
714
+ function worktreeName(projectName) {
715
+ const hit = findWorktreeMarker(projectName);
716
+ if (!hit)
717
+ return null;
718
+ const tail = projectName.slice(hit.idx + hit.marker.length);
719
+ const slash = tail.indexOf("/");
720
+ return slash >= 0 ? tail.slice(0, slash) : tail;
721
+ }
722
+ function makeDay(date) {
723
+ return {
724
+ date,
725
+ sessions: 0,
726
+ toolCalls: 0,
727
+ turns: 0,
728
+ tokens: { ...BLANK_USAGE2 },
729
+ durationMs: 0,
730
+ airTimeMs: 0,
731
+ peakParallelism: 0
732
+ };
733
+ }
734
+ function daysBetween(startDay, endDay) {
735
+ const [ys, ms, ds] = startDay.split("-").map(Number);
736
+ const [ye, me, de] = endDay.split("-").map(Number);
737
+ const start = new Date(ys, ms - 1, ds);
738
+ const end = new Date(ye, me - 1, de);
739
+ const out = [];
740
+ for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
741
+ out.push(toLocalDay(d.getTime()));
742
+ }
743
+ return out;
744
+ }
745
+ function dailyActivity(sessions) {
746
+ if (sessions.length === 0)
747
+ return [];
748
+ const byDay = /* @__PURE__ */ new Map();
749
+ let minDay;
750
+ let maxDay;
751
+ const touchBucket = (day) => {
752
+ if (!minDay || day < minDay)
753
+ minDay = day;
754
+ if (!maxDay || day > maxDay)
755
+ maxDay = day;
756
+ const b = byDay.get(day) ?? makeDay(day);
757
+ byDay.set(day, b);
758
+ return b;
759
+ };
760
+ for (const s of sessions) {
761
+ const startDay = sessionDay(s);
762
+ if (startDay) {
763
+ const bucket = touchBucket(startDay);
764
+ bucket.toolCalls += s.toolCallCount ?? 0;
765
+ bucket.turns += s.turnCount ?? 0;
766
+ bucket.tokens.input += s.totalUsage.input;
767
+ bucket.tokens.output += s.totalUsage.output;
768
+ bucket.tokens.cacheRead += s.totalUsage.cacheRead;
769
+ bucket.tokens.cacheWrite += s.totalUsage.cacheWrite;
770
+ bucket.durationMs += s.durationMs ?? 0;
771
+ }
772
+ const segments = s.activeSegments;
773
+ if (segments && segments.length > 0) {
774
+ const touchedDays = /* @__PURE__ */ new Set();
775
+ for (const seg of segments) {
776
+ let cur = seg.startMs;
777
+ while (cur < seg.endMs) {
778
+ const day = toLocalDay(cur);
779
+ touchedDays.add(day);
780
+ const d = new Date(cur);
781
+ d.setHours(24, 0, 0, 0);
782
+ const dayEndMs = Math.min(d.getTime(), seg.endMs);
783
+ const bucket = touchBucket(day);
784
+ bucket.airTimeMs += dayEndMs - cur;
785
+ cur = dayEndMs;
786
+ }
787
+ }
788
+ for (const day of touchedDays) {
789
+ const bucket = touchBucket(day);
790
+ bucket.sessions++;
791
+ }
792
+ } else if (startDay) {
793
+ const bucket = touchBucket(startDay);
794
+ bucket.sessions++;
795
+ bucket.airTimeMs += s.airTimeMs ?? 0;
796
+ }
797
+ }
798
+ const range = minDay && maxDay ? daysBetween(minDay, maxDay) : [];
799
+ const out = [];
800
+ for (const day of range) {
801
+ out.push(byDay.get(day) ?? makeDay(day));
802
+ }
803
+ const parallelism = computeParallelism(sessions);
804
+ const byDayPar = /* @__PURE__ */ new Map();
805
+ for (const p of parallelism) {
806
+ const day = toLocalDay(p.atMs);
807
+ const cur = byDayPar.get(day) ?? 0;
808
+ if (p.active > cur)
809
+ byDayPar.set(day, p.active);
810
+ }
811
+ for (const b of out) {
812
+ b.peakParallelism = byDayPar.get(b.date) ?? 0;
813
+ }
814
+ return out;
815
+ }
816
+ function computeParallelism(sessions) {
817
+ const events = [];
818
+ for (const s of sessions) {
819
+ const segs = s.activeSegments && s.activeSegments.length > 0 ? s.activeSegments : s.firstTimestamp && s.lastTimestamp ? (() => {
820
+ const start = Date.parse(s.firstTimestamp);
821
+ const end = Date.parse(s.lastTimestamp);
822
+ if (Number.isNaN(start) || Number.isNaN(end) || end < start)
823
+ return [];
824
+ return [{ startMs: start, endMs: end }];
825
+ })() : [];
826
+ for (const seg of segs) {
827
+ events.push({ ms: seg.startMs, delta: 1, sessionId: s.id });
828
+ events.push({ ms: seg.endMs, delta: -1, sessionId: s.id });
829
+ }
830
+ }
831
+ events.sort((a, b) => a.ms - b.ms || b.delta - a.delta);
832
+ const segCount = /* @__PURE__ */ new Map();
833
+ const points = [];
834
+ for (const e of events) {
835
+ if (e.delta === 1) {
836
+ segCount.set(e.sessionId, (segCount.get(e.sessionId) ?? 0) + 1);
837
+ } else {
838
+ const n = (segCount.get(e.sessionId) ?? 0) - 1;
839
+ if (n <= 0)
840
+ segCount.delete(e.sessionId);
841
+ else
842
+ segCount.set(e.sessionId, n);
843
+ }
844
+ points.push({
845
+ atMs: e.ms,
846
+ active: segCount.size,
847
+ sessions: Array.from(segCount.keys()).slice(0, 10)
848
+ });
849
+ }
850
+ return points;
851
+ }
852
+ function detectParallelRuns(sessions, minActive = 2) {
853
+ const points = computeParallelism(sessions);
854
+ const runs = [];
855
+ let cur = null;
856
+ for (const p of points) {
857
+ if (p.active >= minActive) {
858
+ if (!cur) {
859
+ cur = { startMs: p.atMs, endMs: p.atMs, peak: p.active, sessions: [...p.sessions] };
860
+ } else {
861
+ cur.endMs = p.atMs;
862
+ if (p.active > cur.peak)
863
+ cur.peak = p.active;
864
+ for (const sid of p.sessions) {
865
+ if (!cur.sessions.includes(sid))
866
+ cur.sessions.push(sid);
867
+ }
868
+ }
869
+ } else if (cur) {
870
+ cur.endMs = p.atMs;
871
+ runs.push(cur);
872
+ cur = null;
873
+ }
874
+ }
875
+ if (cur)
876
+ runs.push(cur);
877
+ return runs;
878
+ }
879
+ function detectPrMarkers(session) {
880
+ const out = [];
881
+ const total = session.durationMs ?? 0;
882
+ for (const e of session.events) {
883
+ if (e.role !== "tool-call" || e.toolName !== "Bash")
884
+ continue;
885
+ const toolUseBlock = e.blocks.find((b) => b && "type" in b && b.type === "tool_use");
886
+ if (!toolUseBlock || toolUseBlock.type !== "tool_use")
887
+ continue;
888
+ const input = toolUseBlock.input;
889
+ const command = typeof input?.command === "string" ? input.command : "";
890
+ if (!PR_CREATE_RE.test(command))
891
+ continue;
892
+ const titleMatch = command.match(TITLE_FLAG_RE);
893
+ const numberMatch = command.match(PR_NUMBER_RE);
894
+ out.push({
895
+ sessionId: session.id,
896
+ timestamp: e.timestamp ?? session.firstTimestamp ?? "",
897
+ tOffsetMs: e.tOffsetMs,
898
+ positionInSession: total > 0 && e.tOffsetMs !== void 0 ? Math.min(1, Math.max(0, e.tOffsetMs / total)) : void 0,
899
+ command: command.slice(0, 300),
900
+ prNumber: numberMatch ? Number(numberMatch[1]) : void 0,
901
+ title: titleMatch?.[1]
902
+ });
903
+ }
904
+ return out;
905
+ }
906
+ function sessionAirTimeMs(events, idleThresholdMs = 3 * 60 * 1e3) {
907
+ let air = 0;
908
+ for (const e of events) {
909
+ if (e.gapMs === void 0)
910
+ continue;
911
+ if (e.gapMs <= idleThresholdMs)
912
+ air += e.gapMs;
913
+ }
914
+ return air;
915
+ }
916
+ function highLevelMetrics(sessions) {
917
+ const totals = {
918
+ sessionCount: sessions.length,
919
+ totalDurationMs: 0,
920
+ totalAirTimeMs: 0,
921
+ totalTokens: { ...BLANK_USAGE2 },
922
+ totalToolCalls: 0,
923
+ totalTurns: 0,
924
+ avgTurnsPerSession: 0,
925
+ avgDurationMs: 0,
926
+ totalLinesAdded: 0,
927
+ totalLinesRemoved: 0,
928
+ totalFilesEdited: 0
929
+ };
930
+ for (const s of sessions) {
931
+ totals.totalDurationMs += s.durationMs ?? 0;
932
+ totals.totalAirTimeMs += s.airTimeMs ?? s.durationMs ?? 0;
933
+ totals.totalTokens.input += s.totalUsage.input;
934
+ totals.totalTokens.output += s.totalUsage.output;
935
+ totals.totalTokens.cacheRead += s.totalUsage.cacheRead;
936
+ totals.totalTokens.cacheWrite += s.totalUsage.cacheWrite;
937
+ totals.totalToolCalls += s.toolCallCount ?? 0;
938
+ totals.totalTurns += s.turnCount ?? 0;
939
+ totals.totalLinesAdded += s.linesAdded ?? 0;
940
+ totals.totalLinesRemoved += s.linesRemoved ?? 0;
941
+ totals.totalFilesEdited += s.filesEdited ?? 0;
942
+ }
943
+ if (sessions.length > 0) {
944
+ totals.avgTurnsPerSession = totals.totalTurns / sessions.length;
945
+ totals.avgDurationMs = totals.totalDurationMs / sessions.length;
946
+ }
947
+ return totals;
948
+ }
949
+ function computeActiveSegments(events, idleThresholdMs = 3 * 60 * 1e3) {
950
+ const timed = events.filter((e) => e.timestamp).map((e) => Date.parse(e.timestamp)).filter((ms) => !Number.isNaN(ms)).sort((a, b) => a - b);
951
+ if (timed.length === 0)
952
+ return [];
953
+ const segments = [];
954
+ let segStart = timed[0];
955
+ let segEnd = timed[0];
956
+ for (let i = 1; i < timed.length; i++) {
957
+ const t = timed[i];
958
+ if (t - segEnd > idleThresholdMs) {
959
+ segments.push({ startMs: segStart, endMs: segEnd });
960
+ segStart = t;
961
+ }
962
+ segEnd = t;
963
+ }
964
+ segments.push({ startMs: segStart, endMs: segEnd });
965
+ return segments;
966
+ }
967
+ function buildGanttDay(sessions, date) {
968
+ const [y, m, d] = date.split("-").map(Number);
969
+ const dayStartMs = new Date(y, m - 1, d).getTime();
970
+ const dayEndMs = new Date(y, m - 1, d + 1).getTime();
971
+ const ganttSessions = [];
972
+ for (const s of sessions) {
973
+ const allSegments = computeActiveSegments(s.events);
974
+ const daySegments = allSegments.filter((seg) => seg.endMs > dayStartMs && seg.startMs < dayEndMs).map((seg) => ({
975
+ startMs: Math.max(seg.startMs, dayStartMs),
976
+ endMs: Math.min(seg.endMs, dayEndMs)
977
+ }));
978
+ if (daySegments.length === 0)
979
+ continue;
980
+ const startMs = Math.min(...daySegments.map((seg) => seg.startMs));
981
+ const endMs = Math.max(...daySegments.map((seg) => seg.endMs));
982
+ const activeMs = daySegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
983
+ ganttSessions.push({
984
+ id: s.id,
985
+ projectName: s.projectName,
986
+ projectDir: s.projectDir,
987
+ firstUserPreview: s.firstUserPreview,
988
+ lastAgentPreview: s.lastAgentPreview,
989
+ model: s.model,
990
+ segments: daySegments,
991
+ startMs,
992
+ endMs,
993
+ activeMs,
994
+ totalUsage: s.totalUsage
995
+ });
996
+ }
997
+ ganttSessions.sort((a, b) => a.startMs - b.startMs);
998
+ const evts = [];
999
+ for (const s of ganttSessions) {
1000
+ for (const seg of s.segments) {
1001
+ evts.push({ ms: seg.startMs, delta: 1 });
1002
+ evts.push({ ms: seg.endMs, delta: -1 });
1003
+ }
1004
+ }
1005
+ evts.sort((a, b) => a.ms - b.ms || b.delta - a.delta);
1006
+ let active = 0;
1007
+ let peak = 0;
1008
+ for (const e of evts) {
1009
+ active += e.delta;
1010
+ if (active > peak)
1011
+ peak = active;
1012
+ }
1013
+ return { date, sessions: ganttSessions, dayStartMs, dayEndMs, peakActiveParallelism: peak };
1014
+ }
1015
+ function detectBurstsCore(sessions, opts) {
1016
+ const minDurationMs = opts.minDurationMs ?? 6e4;
1017
+ const mergeGapMs = opts.mergeGapMs ?? 10 * 6e4;
1018
+ const minActive = opts.minActive ?? 2;
1019
+ const events = [];
1020
+ for (const s of sessions) {
1021
+ for (const seg of s.segments) {
1022
+ if (seg.endMs <= seg.startMs)
1023
+ continue;
1024
+ events.push({ ms: seg.startMs, delta: 1, sessionId: s.id });
1025
+ events.push({ ms: seg.endMs, delta: -1, sessionId: s.id });
1026
+ }
1027
+ }
1028
+ events.sort((a, b) => a.ms - b.ms || b.delta - a.delta);
1029
+ const segCount = /* @__PURE__ */ new Map();
1030
+ const rawRuns = [];
1031
+ let cur = null;
1032
+ for (const e of events) {
1033
+ if (e.delta === 1) {
1034
+ segCount.set(e.sessionId, (segCount.get(e.sessionId) ?? 0) + 1);
1035
+ } else {
1036
+ const n = (segCount.get(e.sessionId) ?? 0) - 1;
1037
+ if (n <= 0)
1038
+ segCount.delete(e.sessionId);
1039
+ else
1040
+ segCount.set(e.sessionId, n);
1041
+ }
1042
+ const active = segCount.size;
1043
+ if (active >= minActive) {
1044
+ if (!cur) {
1045
+ cur = {
1046
+ startMs: e.ms,
1047
+ endMs: e.ms,
1048
+ peak: active,
1049
+ sessionIds: new Set(segCount.keys())
1050
+ };
1051
+ } else {
1052
+ cur.endMs = e.ms;
1053
+ if (active > cur.peak)
1054
+ cur.peak = active;
1055
+ for (const sid of segCount.keys())
1056
+ cur.sessionIds.add(sid);
1057
+ }
1058
+ } else if (cur) {
1059
+ cur.endMs = e.ms;
1060
+ rawRuns.push(cur);
1061
+ cur = null;
1062
+ }
1063
+ }
1064
+ if (cur)
1065
+ rawRuns.push(cur);
1066
+ const significant = rawRuns.filter((r) => r.endMs - r.startMs >= minDurationMs);
1067
+ const merged = [];
1068
+ for (const r of significant) {
1069
+ const last = merged[merged.length - 1];
1070
+ if (last && r.startMs - last.endMs <= mergeGapMs) {
1071
+ last.endMs = r.endMs;
1072
+ if (r.peak > last.peak)
1073
+ last.peak = r.peak;
1074
+ for (const sid of r.sessionIds)
1075
+ last.sessionIds.add(sid);
1076
+ } else {
1077
+ merged.push({
1078
+ startMs: r.startMs,
1079
+ endMs: r.endMs,
1080
+ peak: r.peak,
1081
+ sessionIds: new Set(r.sessionIds)
1082
+ });
1083
+ }
1084
+ }
1085
+ const projectBySession = /* @__PURE__ */ new Map();
1086
+ for (const s of sessions)
1087
+ projectBySession.set(s.id, s.projectDir);
1088
+ return merged.map((r) => {
1089
+ const projectDirs = Array.from(new Set(Array.from(r.sessionIds).map((sid) => projectBySession.get(sid)).filter((p) => !!p)));
1090
+ return {
1091
+ startMs: r.startMs,
1092
+ endMs: r.endMs,
1093
+ peak: r.peak,
1094
+ sessionIds: Array.from(r.sessionIds),
1095
+ projectDirs,
1096
+ crossProject: projectDirs.length > 1
1097
+ };
1098
+ });
1099
+ }
1100
+ function computeParallelismBursts(gantt, opts = {}) {
1101
+ return detectBurstsCore(gantt.sessions, opts);
1102
+ }
1103
+ function computeBurstsFromSessions(sessions, opts = {}) {
1104
+ const input = [];
1105
+ for (const s of sessions) {
1106
+ if (!s.activeSegments || s.activeSegments.length === 0)
1107
+ continue;
1108
+ input.push({
1109
+ id: s.id,
1110
+ projectDir: s.projectDir,
1111
+ segments: s.activeSegments
1112
+ });
1113
+ }
1114
+ return detectBurstsCore(input, opts);
1115
+ }
1116
+ function summarizeBursts(bursts) {
1117
+ let peakConcurrent = 0;
1118
+ let totalParallelMs = 0;
1119
+ let crossProjectBurstCount = 0;
1120
+ const days = /* @__PURE__ */ new Set();
1121
+ for (const b of bursts) {
1122
+ if (b.peak > peakConcurrent)
1123
+ peakConcurrent = b.peak;
1124
+ totalParallelMs += b.endMs - b.startMs;
1125
+ if (b.crossProject)
1126
+ crossProjectBurstCount++;
1127
+ days.add(toLocalDay(b.startMs));
1128
+ }
1129
+ return {
1130
+ peakConcurrent,
1131
+ totalParallelMs,
1132
+ burstCount: bursts.length,
1133
+ crossProjectBurstCount,
1134
+ activeDayCount: days.size
1135
+ };
1136
+ }
1137
+ function groupByProject(sessions) {
1138
+ const map = /* @__PURE__ */ new Map();
1139
+ for (const s of sessions) {
1140
+ const canonical = canonicalProjectName(s.projectName);
1141
+ const key = canonical;
1142
+ let cur = map.get(key);
1143
+ if (!cur) {
1144
+ cur = {
1145
+ projectDir: canonical,
1146
+ projectName: canonical,
1147
+ sessions: [],
1148
+ rawProjectDirs: [],
1149
+ worktreeCount: 0,
1150
+ metrics: highLevelMetrics([]),
1151
+ lastActiveMs: void 0
1152
+ };
1153
+ map.set(key, cur);
1154
+ }
1155
+ cur.sessions.push(s);
1156
+ if (!cur.rawProjectDirs.includes(s.projectDir)) {
1157
+ cur.rawProjectDirs.push(s.projectDir);
1158
+ }
1159
+ const last = s.lastTimestamp ? Date.parse(s.lastTimestamp) : void 0;
1160
+ if (last && (!cur.lastActiveMs || last > cur.lastActiveMs)) {
1161
+ cur.lastActiveMs = last;
1162
+ }
1163
+ }
1164
+ for (const p of map.values()) {
1165
+ p.metrics = highLevelMetrics(p.sessions);
1166
+ const wtNames = /* @__PURE__ */ new Set();
1167
+ for (const s of p.sessions) {
1168
+ const wt = worktreeName(s.projectName);
1169
+ if (wt)
1170
+ wtNames.add(wt);
1171
+ }
1172
+ p.worktreeCount = wtNames.size;
1173
+ }
1174
+ return Array.from(map.values()).sort((a, b) => (b.lastActiveMs ?? 0) - (a.lastActiveMs ?? 0));
1175
+ }
1176
+ var BLANK_USAGE2, WORKTREE_MARKERS, PR_CREATE_RE, PR_NUMBER_RE, TITLE_FLAG_RE;
1177
+ var init_analytics = __esm({
1178
+ "../parser/dist/analytics.js"() {
1179
+ "use strict";
1180
+ BLANK_USAGE2 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
1181
+ WORKTREE_MARKERS = ["/.claude/worktrees/", "/.worktrees/"];
1182
+ PR_CREATE_RE = /gh\s+pr\s+create\b/i;
1183
+ PR_NUMBER_RE = /#(\d+)/;
1184
+ TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
1185
+ }
1186
+ });
1187
+
1188
+ // ../parser/dist/capsule.js
1189
+ function trunc(s, n) {
1190
+ if (!s)
1191
+ return "";
1192
+ const one = s.replace(/\s+/g, " ").trim();
1193
+ return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
1194
+ }
1195
+ function minimizeImages(s) {
1196
+ if (!s)
1197
+ return s;
1198
+ let counter = 0;
1199
+ return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
1200
+ }
1201
+ function firstNSentences(s, n = 3, hardCap = 500) {
1202
+ if (!s)
1203
+ return "";
1204
+ const flat = s.replace(/\s+/g, " ").trim();
1205
+ const out = flat.split(SENTENCE_SPLIT_RE).slice(0, n).join(" ").trim();
1206
+ return out.length > hardCap ? out.slice(0, hardCap - 1) + "\u2026" : out;
1207
+ }
1208
+ function median(xs) {
1209
+ if (xs.length === 0)
1210
+ return 0;
1211
+ const s = [...xs].sort((a, b) => a - b);
1212
+ const n = s.length;
1213
+ return n % 2 ? s[n >> 1] : (s[(n >> 1) - 1] + s[n >> 1]) / 2;
1214
+ }
1215
+ function p90(xs) {
1216
+ if (xs.length === 0)
1217
+ return 0;
1218
+ const s = [...xs].sort((a, b) => a - b);
1219
+ return s[Math.min(Math.floor(0.9 * s.length), s.length - 1)];
1220
+ }
1221
+ function bashVerb(cmd) {
1222
+ if (!cmd)
1223
+ return "?";
1224
+ let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
1225
+ if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
1226
+ const parts = cmd.split(/&&|;/, 3);
1227
+ if (parts.length >= 2)
1228
+ firstSeg = parts[1].trim();
1229
+ }
1230
+ const tokens = firstSeg.split(/\s+/).filter(Boolean);
1231
+ while (tokens.length && SHELL_NOISE.has(tokens[0]))
1232
+ tokens.shift();
1233
+ if (!tokens.length)
1234
+ return "?";
1235
+ let first = tokens[0];
1236
+ if (first.includes("=") && !first.startsWith("/")) {
1237
+ tokens.shift();
1238
+ if (!tokens.length)
1239
+ return "?";
1240
+ first = tokens[0];
1241
+ }
1242
+ if (first.includes("/") && !first.startsWith("./")) {
1243
+ first = first.replace(/\/+$/, "").split("/").pop();
1244
+ }
1245
+ return first.replace(/^["']|["']$/g, "") || "?";
1246
+ }
1247
+ function newTurn(startMs, userInput = "") {
1248
+ return {
1249
+ startMs,
1250
+ lastMs: startMs,
1251
+ eventTs: startMs !== void 0 ? [startMs] : [],
1252
+ userInput,
1253
+ firstToolsIfNoText: [],
1254
+ toolsTotal: 0,
1255
+ toolMix: /* @__PURE__ */ new Map(),
1256
+ bashVerbs: /* @__PURE__ */ new Map(),
1257
+ skills: /* @__PURE__ */ new Map(),
1258
+ subagents: [],
1259
+ taskOps: 0,
1260
+ toolErrors: 0,
1261
+ consecSameToolMax: 0,
1262
+ consec: 0,
1263
+ endedInInterrupt: false,
1264
+ models: /* @__PURE__ */ new Map(),
1265
+ tokensIn: 0,
1266
+ tokensOut: 0,
1267
+ tokensCacheR: 0,
1268
+ tokensCacheW: 0,
1269
+ seenMsgIds: /* @__PURE__ */ new Set()
1270
+ };
1271
+ }
1272
+ function closeTurn(t, closed) {
1273
+ const ts = [...t.eventTs].sort((a, b) => a - b);
1274
+ let active = 0;
1275
+ for (let i = 1; i < ts.length; i++) {
1276
+ const dt = ts[i] - ts[i - 1];
1277
+ if (dt < IDLE_GAP_MS)
1278
+ active += dt;
1279
+ }
1280
+ const wall = t.startMs !== void 0 && t.lastMs !== void 0 ? t.lastMs - t.startMs : 0;
1281
+ closed.push({
1282
+ startMs: t.startMs ?? 0,
1283
+ activeMs: active,
1284
+ wallMs: wall,
1285
+ userInput: t.userInput,
1286
+ firstAgentText: t.firstAgentText,
1287
+ lastAgentText: t.lastAgentText,
1288
+ firstToolsIfNoText: t.firstToolsIfNoText,
1289
+ toolsTotal: t.toolsTotal,
1290
+ toolMix: t.toolMix,
1291
+ bashVerbs: t.bashVerbs,
1292
+ skills: t.skills,
1293
+ subagents: t.subagents,
1294
+ taskOps: t.taskOps,
1295
+ toolErrors: t.toolErrors,
1296
+ consecSameToolMax: t.consecSameToolMax,
1297
+ endedInInterrupt: t.endedInInterrupt,
1298
+ models: t.models,
1299
+ totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW
1300
+ });
1301
+ }
1302
+ function firstTextBlock(blocks) {
1303
+ if (!Array.isArray(blocks))
1304
+ return "";
1305
+ for (const b of blocks) {
1306
+ if (b && typeof b === "object" && b.type === "text") {
1307
+ return String(b.text ?? "");
1308
+ }
1309
+ }
1310
+ return "";
1311
+ }
1312
+ function parseMs(iso) {
1313
+ if (!iso)
1314
+ return void 0;
1315
+ const ms = Date.parse(iso);
1316
+ return Number.isNaN(ms) ? void 0 : ms;
1317
+ }
1318
+ function buildCapsule(session, opts = {}) {
1319
+ const { compact = false } = opts;
1320
+ let firstUserSession;
1321
+ let finalAgentSession;
1322
+ const prTitles = [];
1323
+ let commits = 0;
1324
+ let pushes = 0;
1325
+ const modelTurnsGlobal = /* @__PURE__ */ new Map();
1326
+ let totalToolErrors = 0;
1327
+ let totalInterrupts = 0;
1328
+ let exitPlanCalls = 0;
1329
+ let taskOpsGlobal = 0;
1330
+ const closed = [];
1331
+ let cur;
1332
+ let sessionFirstTs;
1333
+ let sessionLastTs;
1334
+ const allEventTs = [];
1335
+ for (const ev of session.events) {
1336
+ const tsMs = parseMs(ev.timestamp);
1337
+ if (tsMs !== void 0) {
1338
+ if (sessionFirstTs === void 0 || tsMs < sessionFirstTs)
1339
+ sessionFirstTs = tsMs;
1340
+ if (sessionLastTs === void 0 || tsMs > sessionLastTs)
1341
+ sessionLastTs = tsMs;
1342
+ allEventTs.push(tsMs);
1343
+ }
1344
+ const rawType = ev.rawType;
1345
+ const raw = ev.raw;
1346
+ const msg = raw?.message ?? {};
1347
+ const content = msg.content;
1348
+ if (rawType === "user") {
1349
+ const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
1350
+ if (!isToolResultOnly) {
1351
+ const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
1352
+ if (cur) {
1353
+ if (text && INTERRUPT_RE2.test(text)) {
1354
+ cur.endedInInterrupt = true;
1355
+ totalInterrupts++;
1356
+ }
1357
+ closeTurn(cur, closed);
1358
+ }
1359
+ cur = newTurn(tsMs, text);
1360
+ if (!firstUserSession && text && !text.startsWith("<command-name>")) {
1361
+ firstUserSession = text;
1362
+ }
1363
+ } else {
1364
+ if (Array.isArray(content)) {
1365
+ for (const c of content) {
1366
+ if (c && typeof c === "object" && c.type === "tool_result" && c.is_error) {
1367
+ totalToolErrors++;
1368
+ if (cur)
1369
+ cur.toolErrors++;
1370
+ }
1371
+ }
1372
+ }
1373
+ if (cur && tsMs !== void 0) {
1374
+ cur.eventTs.push(tsMs);
1375
+ cur.lastMs = tsMs;
1376
+ }
1377
+ }
1378
+ } else if (rawType === "assistant") {
1379
+ if (cur && tsMs !== void 0) {
1380
+ cur.eventTs.push(tsMs);
1381
+ cur.lastMs = tsMs;
1382
+ }
1383
+ const model = msg.model;
1384
+ if (model) {
1385
+ modelTurnsGlobal.set(model, (modelTurnsGlobal.get(model) ?? 0) + 1);
1386
+ if (cur)
1387
+ cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
1388
+ }
1389
+ const msgId = msg.id;
1390
+ const usage = msg.usage;
1391
+ if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
1392
+ cur.seenMsgIds.add(msgId);
1393
+ cur.tokensIn += usage.input_tokens ?? 0;
1394
+ cur.tokensOut += usage.output_tokens ?? 0;
1395
+ cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
1396
+ cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
1397
+ }
1398
+ if (Array.isArray(content)) {
1399
+ for (const c of content) {
1400
+ if (!c || typeof c !== "object")
1401
+ continue;
1402
+ const ct = c;
1403
+ if (ct.type === "text" && ct.text) {
1404
+ finalAgentSession = ct.text;
1405
+ if (cur) {
1406
+ if (cur.firstAgentText === void 0)
1407
+ cur.firstAgentText = ct.text;
1408
+ cur.lastAgentText = ct.text;
1409
+ }
1410
+ } else if (ct.type === "tool_use") {
1411
+ const name = ct.name ?? "unknown";
1412
+ if (cur) {
1413
+ cur.toolsTotal++;
1414
+ cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
1415
+ if (cur.firstAgentText === void 0 && cur.firstToolsIfNoText.length < 3) {
1416
+ cur.firstToolsIfNoText.push(name);
1417
+ }
1418
+ if (name === cur.lastTool)
1419
+ cur.consec++;
1420
+ else {
1421
+ cur.lastTool = name;
1422
+ cur.consec = 1;
1423
+ }
1424
+ if (cur.consec > cur.consecSameToolMax)
1425
+ cur.consecSameToolMax = cur.consec;
1426
+ if (name === "Skill") {
1427
+ const sk = String(ct.input?.skill ?? "unknown");
1428
+ cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
1429
+ } else if (name === "ToolSearch") {
1430
+ const q = String(ct.input?.query ?? "");
1431
+ const key = `(ToolSearch: ${trunc(q, 60)})`;
1432
+ cur.skills.set(key, (cur.skills.get(key) ?? 0) + 1);
1433
+ }
1434
+ if (name === "Agent") {
1435
+ const inp = ct.input ?? {};
1436
+ cur.subagents.push({
1437
+ type: String(inp.subagent_type ?? "general-purpose"),
1438
+ model: typeof inp.model === "string" ? inp.model : void 0,
1439
+ description: trunc(String(inp.description ?? ""), 80),
1440
+ prompt_preview: trunc(String(inp.prompt ?? ""), 240),
1441
+ background: Boolean(inp.run_in_background)
1442
+ });
1443
+ }
1444
+ if (name === "ExitPlanMode")
1445
+ exitPlanCalls++;
1446
+ if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
1447
+ cur.taskOps++;
1448
+ taskOpsGlobal++;
1449
+ }
1450
+ }
1451
+ if (name === "Bash") {
1452
+ const cmd = String(ct.input?.command ?? "");
1453
+ if (cur)
1454
+ cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
1455
+ if (/\bgh\s+pr\s+create\b/.test(cmd)) {
1456
+ const m = cmd.match(PR_TITLE_RE);
1457
+ prTitles.push(m?.[1] ?? cmd.slice(0, 120));
1458
+ }
1459
+ if (/\bgit\s+commit\b/.test(cmd))
1460
+ commits++;
1461
+ if (/\bgit\s+push\b/.test(cmd))
1462
+ pushes++;
1463
+ }
1464
+ }
1465
+ }
1466
+ }
1467
+ }
1468
+ }
1469
+ if (cur)
1470
+ closeTurn(cur, closed);
1471
+ const tsSorted = [...allEventTs].sort((a, b) => a - b);
1472
+ let activeMsTotal = 0;
1473
+ for (let i = 1; i < tsSorted.length; i++) {
1474
+ const dt = tsSorted[i] - tsSorted[i - 1];
1475
+ if (dt < IDLE_GAP_MS)
1476
+ activeMsTotal += dt;
1477
+ }
1478
+ const turnActives = closed.map((t) => t.activeMs);
1479
+ const turnTools = closed.map((t) => t.toolsTotal);
1480
+ const turnTokens = closed.map((t) => t.totalTokens);
1481
+ const subagentTurns = closed.filter((t) => t.subagents.length > 0).length;
1482
+ const primaryModel = [...modelTurnsGlobal.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
1483
+ const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
1484
+ const longestTurnActive = Math.max(0, ...turnActives);
1485
+ const sessionSkills = /* @__PURE__ */ new Map();
1486
+ const sessionSubagents = [];
1487
+ for (const t of closed) {
1488
+ for (const [k, v] of t.skills)
1489
+ sessionSkills.set(k, (sessionSkills.get(k) ?? 0) + v);
1490
+ sessionSubagents.push(...t.subagents);
1491
+ }
1492
+ const skillCalls = [...sessionSkills.values()].reduce((a, b) => a + b, 0);
1493
+ const prs = prTitles.length;
1494
+ let outcome;
1495
+ if (prs > 0)
1496
+ outcome = "shipped";
1497
+ else if (commits > 0 && pushes > 0)
1498
+ outcome = "shipped-no-pr";
1499
+ else if (commits > 0)
1500
+ outcome = "partial";
1501
+ else if (activeMsTotal < 2 * 6e4 && closed.length < 3)
1502
+ outcome = "trivial";
1503
+ else
1504
+ outcome = "exploratory";
1505
+ const flags = [];
1506
+ if (totalInterrupts >= 3)
1507
+ flags.push("interrupt_heavy");
1508
+ if (totalToolErrors >= 20)
1509
+ flags.push("high_errors");
1510
+ if (consecMax >= 8)
1511
+ flags.push("loop_suspected");
1512
+ if (activeMsTotal < 5 * 6e4 && prs >= 1)
1513
+ flags.push("fast_ship");
1514
+ if (exitPlanCalls > 0)
1515
+ flags.push("plan_used");
1516
+ if (subagentTurns >= 3)
1517
+ flags.push("orchestrated");
1518
+ if (longestTurnActive >= 20 * 6e4 && totalInterrupts === 0)
1519
+ flags.push("long_autonomous");
1520
+ const maxTokens = Math.max(1, ...turnTokens);
1521
+ const maxTools = Math.max(1, ...turnTools);
1522
+ function significance(t) {
1523
+ return 0.5 * (t.totalTokens / maxTokens) + 0.5 * (t.toolsTotal / maxTools);
1524
+ }
1525
+ let substantive = closed.filter((t) => t.activeMs >= TURN_FLOOR_MS || t.toolsTotal >= 1);
1526
+ const droppedTrivial = closed.length - substantive.length;
1527
+ if (substantive.length > MAX_TURNS_IN_CAPSULE) {
1528
+ substantive = [...substantive].sort((a, b) => significance(b) - significance(a)).slice(0, MAX_TURNS_IN_CAPSULE);
1529
+ }
1530
+ substantive.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
1531
+ function renderTurn(t) {
1532
+ const tOffsetS = t.startMs && sessionFirstTs ? (t.startMs - sessionFirstTs) / 1e3 : 0;
1533
+ const topToolsArr = [];
1534
+ const topTools = [...t.toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
1535
+ for (const [name, count] of topTools) {
1536
+ if (name === "Bash" && t.bashVerbs.size > 0) {
1537
+ const sub = [...t.bashVerbs.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
1538
+ topToolsArr.push(`Bash\xD7${count} (${sub})`);
1539
+ } else {
1540
+ topToolsArr.push(count > 1 ? `${name}\xD7${count}` : name);
1541
+ }
1542
+ }
1543
+ const firstAgentRaw = t.firstAgentText ?? (t.firstToolsIfNoText.length > 0 ? `(tools only: ${t.firstToolsIfNoText.join(", ")})` : "");
1544
+ const firstTrunc = trunc(minimizeImages(firstAgentRaw), 260);
1545
+ const finalTrunc = firstNSentences(minimizeImages(t.lastAgentText ?? ""), 3, 500);
1546
+ const ended = t.endedInInterrupt ? "interrupt" : t.lastAgentText ? "text" : "tool-in-flight";
1547
+ const model = [...t.models.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
1548
+ const turn = {
1549
+ t_offset_min: Math.round(tOffsetS / 6) / 10,
1550
+ active_min: Math.round(t.activeMs / 6e3) / 10,
1551
+ wall_min: Math.round(t.wallMs / 6e3) / 10,
1552
+ user_input: trunc(minimizeImages(t.userInput), 300),
1553
+ tools_total: t.toolsTotal,
1554
+ top_tools: topToolsArr,
1555
+ tokens: t.totalTokens,
1556
+ errors: t.toolErrors,
1557
+ consec_same_tool_max: t.consecSameToolMax,
1558
+ ended,
1559
+ model
1560
+ };
1561
+ if (firstTrunc && finalTrunc && firstTrunc === finalTrunc) {
1562
+ turn.agent = firstTrunc;
1563
+ } else if (firstAgentRaw && t.lastAgentText && t.firstAgentText === t.lastAgentText) {
1564
+ turn.agent = finalTrunc;
1565
+ } else {
1566
+ turn.first_agent = firstTrunc;
1567
+ turn.final_agent = finalTrunc;
1568
+ }
1569
+ if (t.skills.size > 0)
1570
+ turn.skills = Object.fromEntries(t.skills);
1571
+ if (t.subagents.length > 0)
1572
+ turn.subagents = t.subagents;
1573
+ return turn;
1574
+ }
1575
+ const capsule = {
1576
+ session_id: session.id,
1577
+ project: session.projectName,
1578
+ start_iso: session.firstTimestamp,
1579
+ end_iso: session.lastTimestamp,
1580
+ outcome,
1581
+ flags,
1582
+ primary_model: primaryModel,
1583
+ model_mix: Object.fromEntries(modelTurnsGlobal),
1584
+ numbers: {
1585
+ active_min: Math.round(activeMsTotal / 6e3) / 10,
1586
+ turn_count: closed.length,
1587
+ longest_turn_min: Math.round(longestTurnActive / 6e3) / 10,
1588
+ median_turn_min: Math.round(median(turnActives) / 6e3) / 10,
1589
+ p90_turn_min: Math.round(p90(turnActives) / 6e3) / 10,
1590
+ tools_total: turnTools.reduce((a, b) => a + b, 0),
1591
+ tokens_total: turnTokens.reduce((a, b) => a + b, 0),
1592
+ subagent_turns: subagentTurns,
1593
+ subagent_calls: sessionSubagents.length,
1594
+ skill_calls: skillCalls,
1595
+ task_ops: taskOpsGlobal,
1596
+ interrupts: totalInterrupts,
1597
+ tool_errors: totalToolErrors,
1598
+ consec_same_tool_max: consecMax,
1599
+ exit_plan_calls: exitPlanCalls,
1600
+ prs,
1601
+ commits,
1602
+ pushes
1603
+ },
1604
+ pr_titles: prTitles,
1605
+ first_user: trunc(minimizeImages(firstUserSession ?? ""), 400),
1606
+ final_agent: trunc(minimizeImages(finalAgentSession ?? ""), 400)
1607
+ };
1608
+ if (sessionSkills.size > 0)
1609
+ capsule.skills = Object.fromEntries(sessionSkills);
1610
+ if (sessionSubagents.length > 0)
1611
+ capsule.subagents = sessionSubagents;
1612
+ if (!compact) {
1613
+ capsule.numbers.turns_in_capsule = substantive.length;
1614
+ capsule.numbers.trivial_turns_dropped = droppedTrivial;
1615
+ capsule.turns = substantive.map(renderTurn);
1616
+ }
1617
+ return capsule;
1618
+ }
1619
+ var IDLE_GAP_MS, MAX_TURNS_IN_CAPSULE, TURN_FLOOR_MS, INTERRUPT_RE2, SENTENCE_SPLIT_RE, IMAGE_BLOCK_RE, PR_TITLE_RE, BASH_CHAIN_SPLIT_RE, SHELL_NOISE;
1620
+ var init_capsule = __esm({
1621
+ "../parser/dist/capsule.js"() {
1622
+ "use strict";
1623
+ IDLE_GAP_MS = 3 * 60 * 1e3;
1624
+ MAX_TURNS_IN_CAPSULE = 25;
1625
+ TURN_FLOOR_MS = 15 * 1e3;
1626
+ INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
1627
+ SENTENCE_SPLIT_RE = /(?<=[.!?])\s+(?=[A-Z[`])/;
1628
+ IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
1629
+ PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
1630
+ BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
1631
+ SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
1632
+ }
1633
+ });
1634
+
1635
+ // ../parser/dist/aggregate.js
1636
+ function isoDay(d) {
1637
+ return toLocalDay(d.getTime());
1638
+ }
1639
+ function rangeDays(start, end) {
1640
+ const out = [];
1641
+ const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
1642
+ const stop = new Date(end.getFullYear(), end.getMonth(), end.getDate());
1643
+ while (cur.getTime() <= stop.getTime()) {
1644
+ out.push(new Date(cur));
1645
+ cur.setDate(cur.getDate() + 1);
1646
+ }
1647
+ return out;
1648
+ }
1649
+ function prettyProjectName(p) {
1650
+ const cleaned = p.replace(/^\/Users\/[^/]+\//, "");
1651
+ return cleaned || p;
1652
+ }
1653
+ function buildPeriodBundle(caps, opts) {
1654
+ const { period, trivial_dropped, sessions_total } = opts;
1655
+ const days = rangeDays(period.start, period.end);
1656
+ const dayKeys = new Map(days.map((d) => [isoDay(d), d]));
1657
+ const dayAgent = /* @__PURE__ */ new Map();
1658
+ const daySessions = /* @__PURE__ */ new Map();
1659
+ for (const k of dayKeys.keys()) {
1660
+ dayAgent.set(k, 0);
1661
+ daySessions.set(k, 0);
1662
+ }
1663
+ for (const c of caps) {
1664
+ if (!c.start_iso)
1665
+ continue;
1666
+ const d = isoDay(new Date(c.start_iso));
1667
+ if (!dayAgent.has(d))
1668
+ continue;
1669
+ dayAgent.set(d, (dayAgent.get(d) ?? 0) + c.numbers.active_min);
1670
+ daySessions.set(d, (daySessions.get(d) ?? 0) + 1);
1671
+ }
1672
+ const by_day = days.map((d) => {
1673
+ const key = isoDay(d);
1674
+ return {
1675
+ date: key,
1676
+ day_name: DAY_NAMES[d.getDay()],
1677
+ agent_min: Math.round((dayAgent.get(key) ?? 0) * 10) / 10,
1678
+ sessions: daySessions.get(key) ?? 0
1679
+ };
1680
+ });
1681
+ const projectAccum = /* @__PURE__ */ new Map();
1682
+ for (const c of caps) {
1683
+ const canon = canonicalProjectName(c.project ?? "unknown");
1684
+ const b = projectAccum.get(canon) ?? { agent_min: 0, prs: 0, commits: 0 };
1685
+ b.agent_min += c.numbers.active_min;
1686
+ b.prs += c.numbers.prs;
1687
+ b.commits += c.numbers.commits;
1688
+ projectAccum.set(canon, b);
1689
+ }
1690
+ const totalProjectMin = [...projectAccum.values()].reduce((a, b) => a + b.agent_min, 0);
1691
+ const project_shares = [...projectAccum.entries()].map(([name, b]) => ({
1692
+ name,
1693
+ display_name: prettyProjectName(name),
1694
+ agent_min: Math.round(b.agent_min * 10) / 10,
1695
+ share_pct: totalProjectMin === 0 ? 0 : Math.round(b.agent_min / totalProjectMin * 100),
1696
+ prs: b.prs,
1697
+ commits: b.commits
1698
+ })).sort((a, b) => b.agent_min - a.agent_min);
1699
+ const shipped_prs = [];
1700
+ for (const c of caps) {
1701
+ for (const title of c.pr_titles) {
1702
+ shipped_prs.push({
1703
+ title,
1704
+ project: prettyProjectName(canonicalProjectName(c.project ?? "")),
1705
+ session_id: c.session_id,
1706
+ start_iso: c.start_iso ?? "",
1707
+ active_min: c.numbers.active_min,
1708
+ commits: c.numbers.commits,
1709
+ subagents: c.numbers.subagent_calls,
1710
+ flags: c.flags
1711
+ });
1712
+ }
1713
+ }
1714
+ const flags_count = {};
1715
+ const skills_total = {};
1716
+ const totals = { agent_min: 0, commits: 0, pushes: 0, prs: 0, subagent_calls: 0, tool_errors: 0 };
1717
+ for (const c of caps) {
1718
+ for (const f of c.flags)
1719
+ flags_count[f] = (flags_count[f] ?? 0) + 1;
1720
+ if (c.skills) {
1721
+ for (const [k, v] of Object.entries(c.skills)) {
1722
+ skills_total[k] = (skills_total[k] ?? 0) + v;
1723
+ }
1724
+ }
1725
+ totals.agent_min += c.numbers.active_min;
1726
+ totals.commits += c.numbers.commits;
1727
+ totals.pushes += c.numbers.pushes;
1728
+ totals.prs += c.numbers.prs;
1729
+ totals.subagent_calls += c.numbers.subagent_calls;
1730
+ totals.tool_errors += c.numbers.tool_errors;
1731
+ }
1732
+ totals.agent_min = Math.round(totals.agent_min * 10) / 10;
1733
+ const longest = [...caps].sort((a, b) => b.numbers.active_min - a.numbers.active_min)[0];
1734
+ const shipped = caps.filter((c) => c.numbers.prs >= 1);
1735
+ const fastest = shipped.sort((a, b) => a.numbers.active_min - b.numbers.active_min)[0];
1736
+ const mostErr = [...caps].sort((a, b) => b.numbers.tool_errors - a.numbers.tool_errors)[0];
1737
+ const wandered = [...caps].filter((c) => c.outcome === "exploratory" && c.numbers.prs === 0 && c.numbers.commits === 0 && c.numbers.pushes === 0 && c.numbers.active_min >= 10).sort((a, b) => b.numbers.active_min - a.numbers.active_min)[0];
1738
+ const outliers = {};
1739
+ if (longest && longest.numbers.active_min > 0) {
1740
+ outliers.longest_run = {
1741
+ session_id: longest.session_id,
1742
+ project: prettyProjectName(canonicalProjectName(longest.project ?? "")),
1743
+ active_min: longest.numbers.active_min,
1744
+ flags: longest.flags
1745
+ };
1746
+ }
1747
+ if (fastest) {
1748
+ outliers.fastest_ship = {
1749
+ session_id: fastest.session_id,
1750
+ project: prettyProjectName(canonicalProjectName(fastest.project ?? "")),
1751
+ active_min: fastest.numbers.active_min,
1752
+ pr_title: fastest.pr_titles[0] ?? ""
1753
+ };
1754
+ }
1755
+ if (mostErr && mostErr.numbers.tool_errors > 0) {
1756
+ outliers.most_errors = {
1757
+ session_id: mostErr.session_id,
1758
+ project: prettyProjectName(canonicalProjectName(mostErr.project ?? "")),
1759
+ active_min: mostErr.numbers.active_min,
1760
+ tool_errors: mostErr.numbers.tool_errors
1761
+ };
1762
+ }
1763
+ if (wandered) {
1764
+ outliers.wandered = {
1765
+ session_id: wandered.session_id,
1766
+ project: prettyProjectName(canonicalProjectName(wandered.project ?? "")),
1767
+ active_min: wandered.numbers.active_min
1768
+ };
1769
+ }
1770
+ const label = period.range_type === "month" ? period.start.toLocaleDateString("en-US", { month: "long", year: "numeric" }) : `${period.start.toLocaleDateString("en-US", { month: "short", day: "numeric" })} \u2014 ${period.end.toLocaleDateString("en-US", { month: "short", day: "numeric" })}`;
1771
+ return {
1772
+ period: {
1773
+ start: isoDay(period.start),
1774
+ end: isoDay(period.end),
1775
+ label,
1776
+ day_count: days.length,
1777
+ range_type: period.range_type ?? "custom"
1778
+ },
1779
+ counts: { sessions_total, substantive: caps.length, trivial_dropped },
1780
+ totals,
1781
+ by_day,
1782
+ project_shares,
1783
+ shipped_prs,
1784
+ flags_count,
1785
+ skills_total,
1786
+ outliers
1787
+ };
1788
+ }
1789
+ function calendarWeek(ref = /* @__PURE__ */ new Date()) {
1790
+ const d = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate());
1791
+ const dow = d.getDay();
1792
+ const daysSinceMon = (dow + 6) % 7;
1793
+ const monday = new Date(d);
1794
+ monday.setDate(d.getDate() - daysSinceMon);
1795
+ const sunday = new Date(monday);
1796
+ sunday.setDate(monday.getDate() + 6);
1797
+ return { start: monday, end: sunday };
1798
+ }
1799
+ function priorCalendarWeek(ref = /* @__PURE__ */ new Date()) {
1800
+ const cur = calendarWeek(ref);
1801
+ const prevEnd = new Date(cur.start);
1802
+ prevEnd.setDate(cur.start.getDate() - 1);
1803
+ const prevStart = new Date(prevEnd);
1804
+ prevStart.setDate(prevEnd.getDate() - 6);
1805
+ return { start: prevStart, end: prevEnd };
1806
+ }
1807
+ function last4CompletedWeeks(ref = /* @__PURE__ */ new Date()) {
1808
+ const { end } = priorCalendarWeek(ref);
1809
+ const start = new Date(end);
1810
+ start.setDate(end.getDate() - 27);
1811
+ return { start, end };
1812
+ }
1813
+ function calendarMonth(ref = /* @__PURE__ */ new Date()) {
1814
+ const start = new Date(ref.getFullYear(), ref.getMonth(), 1);
1815
+ const end = new Date(ref.getFullYear(), ref.getMonth() + 1, 0);
1816
+ return { start, end };
1817
+ }
1818
+ function priorCalendarMonth(ref = /* @__PURE__ */ new Date()) {
1819
+ const start = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
1820
+ const end = new Date(ref.getFullYear(), ref.getMonth(), 0);
1821
+ return { start, end };
1822
+ }
1823
+ function aggregateConcurrency(bursts, period) {
1824
+ const days = rangeDays(period.start, period.end);
1825
+ const byDayKey = /* @__PURE__ */ new Map();
1826
+ for (const d of days)
1827
+ byDayKey.set(isoDay(d), { peak: 0, has_cross_project: false });
1828
+ for (const b of bursts) {
1829
+ const dayKey = isoDay(new Date(b.startMs));
1830
+ const cur = byDayKey.get(dayKey);
1831
+ if (!cur)
1832
+ continue;
1833
+ if (b.peak > cur.peak)
1834
+ cur.peak = b.peak;
1835
+ if (b.crossProject)
1836
+ cur.has_cross_project = true;
1837
+ }
1838
+ const by_day = days.map((d) => {
1839
+ const k = isoDay(d);
1840
+ const v = byDayKey.get(k);
1841
+ return { date: k, peak: v.peak, has_cross_project: v.has_cross_project };
1842
+ });
1843
+ let peak = 0;
1844
+ let peak_day;
1845
+ let multi_agent_days = 0;
1846
+ let cross_project_days = 0;
1847
+ for (const d of by_day) {
1848
+ if (d.peak > peak) {
1849
+ peak = d.peak;
1850
+ peak_day = d.date;
1851
+ }
1852
+ if (d.peak >= 3)
1853
+ multi_agent_days++;
1854
+ if (d.has_cross_project)
1855
+ cross_project_days++;
1856
+ }
1857
+ return { by_day, peak, peak_day, multi_agent_days, cross_project_days };
1858
+ }
1859
+ var DAY_NAMES;
1860
+ var init_aggregate = __esm({
1861
+ "../parser/dist/aggregate.js"() {
1862
+ "use strict";
1863
+ init_analytics();
1864
+ DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1865
+ }
1866
+ });
1867
+
1868
+ // ../parser/dist/team.js
1869
+ function groupByTeam(sessions, details) {
1870
+ const byTeam = /* @__PURE__ */ new Map();
1871
+ for (const s of sessions) {
1872
+ if (!s.teamName)
1873
+ continue;
1874
+ const arr = byTeam.get(s.teamName) ?? [];
1875
+ arr.push(s);
1876
+ byTeam.set(s.teamName, arr);
1877
+ }
1878
+ const views = [];
1879
+ for (const [teamName, group] of byTeam) {
1880
+ const view = buildTeamView(teamName, group, details);
1881
+ if (view)
1882
+ views.push(view);
1883
+ }
1884
+ return views;
1885
+ }
1886
+ function buildTeamView(teamName, group, details) {
1887
+ const candidates = group.filter((s) => s.isTeamLead);
1888
+ if (candidates.length === 0)
1889
+ return null;
1890
+ let lead;
1891
+ for (const c of candidates) {
1892
+ const d = details.get(c.sessionId);
1893
+ if (d && containsTeamCreate(d.events)) {
1894
+ lead = c;
1895
+ break;
1896
+ }
1897
+ }
1898
+ if (!lead) {
1899
+ const sorted = [...candidates].sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
1900
+ lead = sorted[0];
1901
+ }
1902
+ const agentNameBySessionId = /* @__PURE__ */ new Map();
1903
+ for (const s of group)
1904
+ agentNameBySessionId.set(s.sessionId, s.agentName);
1905
+ const members = group.filter((s) => s.sessionId !== lead.sessionId).sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
1906
+ const sessionIdByAgentName = /* @__PURE__ */ new Map();
1907
+ for (const m of members) {
1908
+ if (m.agentName)
1909
+ sessionIdByAgentName.set(m.agentName, m.sessionId);
1910
+ }
1911
+ const messages = [];
1912
+ for (const s of group) {
1913
+ const d = details.get(s.sessionId);
1914
+ if (!d)
1915
+ continue;
1916
+ const senderId = s.sessionId;
1917
+ const senderTeammateId = s.agentName ?? "team-lead";
1918
+ for (const ev of d.events) {
1919
+ const msg = extractSendMessage(ev);
1920
+ if (!msg)
1921
+ continue;
1922
+ const toTeammateId = msg.to;
1923
+ const toSessionId = toTeammateId === "team-lead" ? lead.sessionId : sessionIdByAgentName.get(toTeammateId) ?? "";
1924
+ messages.push({
1925
+ tsMs: ev.timestamp ? Date.parse(ev.timestamp) : 0,
1926
+ fromSessionId: senderId,
1927
+ fromTeammateId: senderTeammateId,
1928
+ toSessionId,
1929
+ toTeammateId,
1930
+ body: msg.body,
1931
+ kind: "message"
1932
+ });
1933
+ }
1934
+ }
1935
+ messages.sort((a, b) => a.tsMs - b.tsMs);
1936
+ const allTs = [];
1937
+ for (const s of group) {
1938
+ if (s.firstTimestamp)
1939
+ allTs.push(Date.parse(s.firstTimestamp));
1940
+ if (s.lastTimestamp)
1941
+ allTs.push(Date.parse(s.lastTimestamp));
1942
+ }
1943
+ const firstEventMs = allTs.length ? Math.min(...allTs) : 0;
1944
+ const lastEventMs = allTs.length ? Math.max(...allTs) : 0;
1945
+ return {
1946
+ teamName,
1947
+ leadSessionId: lead.sessionId,
1948
+ memberSessionIds: members.map((m) => m.sessionId),
1949
+ agentNameBySessionId,
1950
+ messages,
1951
+ firstEventMs,
1952
+ lastEventMs
1953
+ };
1954
+ }
1955
+ function containsTeamCreate(events) {
1956
+ for (const ev of events) {
1957
+ if (ev.role !== "tool-call")
1958
+ continue;
1959
+ if (ev.toolName === "TeamCreate")
1960
+ return true;
1961
+ }
1962
+ return false;
1963
+ }
1964
+ function extractSendMessage(ev) {
1965
+ if (ev.role !== "tool-call" || ev.toolName !== "SendMessage")
1966
+ return null;
1967
+ for (const b of ev.blocks) {
1968
+ if (!b || typeof b !== "object")
1969
+ continue;
1970
+ if (b.type !== "tool_use")
1971
+ continue;
1972
+ const input = b.input ?? {};
1973
+ const to = typeof input.to === "string" ? input.to : "";
1974
+ const body = typeof input.message === "string" ? input.message : "";
1975
+ if (!to)
1976
+ return null;
1977
+ return { to, body };
1978
+ }
1979
+ return null;
1980
+ }
1981
+ var init_team = __esm({
1982
+ "../parser/dist/team.js"() {
1983
+ "use strict";
1984
+ }
1985
+ });
1986
+
1987
+ // ../parser/dist/index.js
1988
+ var dist_exports = {};
1989
+ __export(dist_exports, {
1990
+ aggregateConcurrency: () => aggregateConcurrency,
1991
+ buildCapsule: () => buildCapsule,
1992
+ buildGanttDay: () => buildGanttDay,
1993
+ buildMegaRows: () => buildMegaRows,
1994
+ buildPeriodBundle: () => buildPeriodBundle,
1995
+ buildPresentation: () => buildPresentation,
1996
+ calendarMonth: () => calendarMonth,
1997
+ calendarWeek: () => calendarWeek,
1998
+ canonicalProjectName: () => canonicalProjectName,
1999
+ computeActiveSegments: () => computeActiveSegments,
2000
+ computeBurstsFromSessions: () => computeBurstsFromSessions,
2001
+ computeParallelism: () => computeParallelism,
2002
+ computeParallelismBursts: () => computeParallelismBursts,
2003
+ dailyActivity: () => dailyActivity,
2004
+ daysBetween: () => daysBetween,
2005
+ detectParallelRuns: () => detectParallelRuns,
2006
+ detectPrMarkers: () => detectPrMarkers,
2007
+ groupByProject: () => groupByProject,
2008
+ groupByTeam: () => groupByTeam,
2009
+ highLevelMetrics: () => highLevelMetrics,
2010
+ last4CompletedWeeks: () => last4CompletedWeeks,
2011
+ parseTranscript: () => parseTranscript,
2012
+ priorCalendarMonth: () => priorCalendarMonth,
2013
+ priorCalendarWeek: () => priorCalendarWeek,
2014
+ rowPrimaryIndex: () => rowPrimaryIndex,
2015
+ sessionAirTimeMs: () => sessionAirTimeMs,
2016
+ sessionDay: () => sessionDay,
2017
+ summarizeBursts: () => summarizeBursts,
2018
+ toLocalDay: () => toLocalDay,
2019
+ worktreeName: () => worktreeName
2020
+ });
2021
+ var init_dist = __esm({
2022
+ "../parser/dist/index.js"() {
2023
+ "use strict";
2024
+ init_types();
2025
+ init_parser();
2026
+ init_presentation();
2027
+ init_analytics();
2028
+ init_capsule();
2029
+ init_aggregate();
2030
+ init_team();
2031
+ }
2032
+ });
2033
+
2034
+ // ../parser/dist/fs.js
2035
+ var fs_exports = {};
2036
+ __export(fs_exports, {
2037
+ DEFAULT_ROOT: () => DEFAULT_ROOT,
2038
+ cacheStats: () => cacheStats,
2039
+ clearCaches: () => clearCaches,
2040
+ decodeProjectName: () => decodeProjectName,
2041
+ findTeamLead: () => findTeamLead,
2042
+ getSession: () => getSession,
2043
+ invalidateFile: () => invalidateFile,
2044
+ listProjects: () => listProjects,
2045
+ listSessions: () => listSessions,
2046
+ loadTeamForSession: () => loadTeamForSession,
2047
+ loadUsageByDay: () => loadUsageByDay,
2048
+ readJsonlFile: () => readJsonlFile,
2049
+ sessionIdFromFileName: () => sessionIdFromFileName,
2050
+ walkJsonlFiles: () => walkJsonlFiles
2051
+ });
2052
+ import { promises as fs } from "node:fs";
2053
+ import os from "node:os";
2054
+ import path from "node:path";
2055
+ function decodeProjectName(dir) {
2056
+ if (!dir.startsWith("-"))
2057
+ return dir;
2058
+ return "/" + dir.slice(1).replace(/-/g, "/");
2059
+ }
2060
+ async function readJsonlFile(filePath) {
2061
+ const raw = await fs.readFile(filePath, "utf8");
2062
+ const out = [];
2063
+ for (const line of raw.split("\n")) {
2064
+ const trimmed = line.trim();
2065
+ if (!trimmed)
2066
+ continue;
2067
+ try {
2068
+ out.push(JSON.parse(trimmed));
2069
+ } catch {
2070
+ }
2071
+ }
2072
+ return out;
2073
+ }
2074
+ function cacheStats() {
2075
+ return { metaEntries: metaCache.size, detailEntries: detailCache.size };
2076
+ }
2077
+ function clearCaches() {
2078
+ metaCache.clear();
2079
+ detailCache.clear();
2080
+ fileListCache = null;
2081
+ }
2082
+ function invalidateFile(fullPath) {
2083
+ metaCache.delete(fullPath);
2084
+ detailCache.delete(fullPath);
2085
+ fileListCache = null;
2086
+ }
2087
+ async function walkJsonlFiles(root = DEFAULT_ROOT) {
2088
+ if (fileListCache && fileListCache.root === root && Date.now() - fileListCache.capturedAtMs < FILE_LIST_TTL_MS) {
2089
+ return fileListCache.files;
2090
+ }
2091
+ let topEntries;
2092
+ try {
2093
+ topEntries = await fs.readdir(root, { withFileTypes: true });
2094
+ } catch {
2095
+ return [];
2096
+ }
2097
+ const projectDirs = topEntries.filter((e) => e.isDirectory()).map((e) => e.name);
2098
+ const perProject = await Promise.all(projectDirs.map(async (projectDir) => {
2099
+ const projectPath = path.join(root, projectDir);
2100
+ let files;
2101
+ try {
2102
+ files = await fs.readdir(projectPath);
2103
+ } catch {
2104
+ return [];
2105
+ }
2106
+ const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
2107
+ const refs = await Promise.all(jsonlFiles.map(async (f) => {
2108
+ const fullPath = path.join(projectPath, f);
2109
+ try {
2110
+ const stat = await fs.stat(fullPath);
2111
+ return {
2112
+ projectDir,
2113
+ fileName: f,
2114
+ fullPath,
2115
+ mtimeMs: stat.mtimeMs,
2116
+ sizeBytes: stat.size
2117
+ };
2118
+ } catch {
2119
+ return null;
2120
+ }
2121
+ }));
2122
+ return refs.filter((r) => r !== null);
2123
+ }));
2124
+ const all = perProject.flat();
2125
+ fileListCache = { files: all, capturedAtMs: Date.now(), root };
2126
+ return all;
2127
+ }
2128
+ function sessionIdFromFileName(fileName) {
2129
+ return fileName.replace(/\.jsonl$/i, "");
2130
+ }
2131
+ function aggregateUsageFromLines(lines, seenKeys, usage) {
2132
+ for (const raw of lines) {
2133
+ if (!raw || typeof raw !== "object")
2134
+ continue;
2135
+ const r = raw;
2136
+ if (r.type !== "assistant")
2137
+ continue;
2138
+ const m = r.message;
2139
+ if (!m)
2140
+ continue;
2141
+ const mid = typeof m.id === "string" ? m.id : void 0;
2142
+ const rid = typeof r.requestId === "string" ? r.requestId : void 0;
2143
+ const key = mid != null && rid != null ? `${mid}:${rid}` : void 0;
2144
+ if (key && seenKeys.has(key))
2145
+ continue;
2146
+ if (key)
2147
+ seenKeys.add(key);
2148
+ const u = m.usage;
2149
+ if (u) {
2150
+ const toNum = (v) => typeof v === "number" ? v : 0;
2151
+ usage.input += toNum(u.input_tokens);
2152
+ usage.output += toNum(u.output_tokens);
2153
+ usage.cacheRead += toNum(u.cache_read_input_tokens);
2154
+ usage.cacheWrite += toNum(u.cache_creation_input_tokens);
2155
+ }
2156
+ }
2157
+ }
2158
+ async function computeSessionUsageWithSubagents(parentLines, projectDirPath, sessionId) {
2159
+ const usage = { ...BLANK_USAGE3 };
2160
+ const seenKeys = /* @__PURE__ */ new Set();
2161
+ aggregateUsageFromLines(parentLines, seenKeys, usage);
2162
+ const subagentsDir = path.join(projectDirPath, sessionId, "subagents");
2163
+ let entries;
2164
+ try {
2165
+ entries = await fs.readdir(subagentsDir);
2166
+ } catch {
2167
+ return usage;
2168
+ }
2169
+ const jsonlFiles = entries.filter((f) => f.endsWith(".jsonl"));
2170
+ if (jsonlFiles.length === 0)
2171
+ return usage;
2172
+ await Promise.all(jsonlFiles.map(async (f) => {
2173
+ try {
2174
+ const lines = await readJsonlFile(path.join(subagentsDir, f));
2175
+ aggregateUsageFromLines(lines, seenKeys, usage);
2176
+ } catch {
2177
+ }
2178
+ }));
2179
+ return usage;
2180
+ }
2181
+ async function getCachedMeta(f) {
2182
+ const cached = metaCache.get(f.fullPath);
2183
+ if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
2184
+ return cached.meta;
2185
+ }
2186
+ try {
2187
+ const rawLines = await readJsonlFile(f.fullPath);
2188
+ const { meta } = parseTranscript(rawLines);
2189
+ const sessionId = sessionIdFromFileName(f.fileName);
2190
+ const projectDirPath = path.dirname(f.fullPath);
2191
+ meta.totalUsage = await computeSessionUsageWithSubagents(rawLines, projectDirPath, sessionId);
2192
+ const full = {
2193
+ ...meta,
2194
+ id: sessionId,
2195
+ filePath: f.fullPath,
2196
+ projectDir: f.projectDir,
2197
+ projectName: meta.cwd ?? decodeProjectName(f.projectDir)
2198
+ };
2199
+ metaCache.set(f.fullPath, { meta: full, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
2200
+ return full;
2201
+ } catch {
2202
+ return null;
2203
+ }
2204
+ }
2205
+ async function getCachedDetail(f) {
2206
+ const cached = detailCache.get(f.fullPath);
2207
+ if (cached && cached.mtimeMs === f.mtimeMs && cached.sizeBytes === f.sizeBytes) {
2208
+ return cached.detail;
2209
+ }
2210
+ try {
2211
+ const rawLines = await readJsonlFile(f.fullPath);
2212
+ const { meta, events } = parseTranscript(rawLines);
2213
+ const detail = {
2214
+ ...meta,
2215
+ id: sessionIdFromFileName(f.fileName),
2216
+ filePath: f.fullPath,
2217
+ projectDir: f.projectDir,
2218
+ projectName: meta.cwd ?? decodeProjectName(f.projectDir),
2219
+ events
2220
+ };
2221
+ detailCache.set(f.fullPath, { detail, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
2222
+ const metaOnly = { ...detail };
2223
+ delete metaOnly.events;
2224
+ metaCache.set(f.fullPath, { meta: metaOnly, mtimeMs: f.mtimeMs, sizeBytes: f.sizeBytes });
2225
+ return detail;
2226
+ } catch {
2227
+ return null;
2228
+ }
2229
+ }
2230
+ async function listSessions(opts = {}) {
2231
+ const { root = DEFAULT_ROOT, limit = 500, projectDir } = opts;
2232
+ let files = await walkJsonlFiles(root);
2233
+ if (projectDir)
2234
+ files = files.filter((f) => f.projectDir === projectDir);
2235
+ files.sort((a, b) => b.mtimeMs - a.mtimeMs);
2236
+ const sliced = files.slice(0, limit);
2237
+ const metas = await Promise.all(sliced.map((f) => getCachedMeta(f)));
2238
+ return metas.filter((m) => m !== null);
2239
+ }
2240
+ async function getSession(id, opts = {}) {
2241
+ const { root = DEFAULT_ROOT } = opts;
2242
+ const files = await walkJsonlFiles(root);
2243
+ const hit = files.find((f) => sessionIdFromFileName(f.fileName) === id);
2244
+ if (!hit)
2245
+ return null;
2246
+ const detail = await getCachedDetail(hit);
2247
+ if (!detail)
2248
+ return detail;
2249
+ const sessionStartMs = detail.firstTimestamp ? Date.parse(detail.firstTimestamp) : void 0;
2250
+ const subagentsDir = path.join(path.dirname(hit.fullPath), sessionIdFromFileName(hit.fileName), "subagents");
2251
+ const subagents = await loadSubagents(subagentsDir, sessionStartMs, detail.events);
2252
+ return { ...detail, subagents };
2253
+ }
2254
+ function summarizeSubagentLines(lines) {
2255
+ let startMs;
2256
+ let endMs;
2257
+ const totalUsage = { ...BLANK_USAGE3 };
2258
+ const seenMessageIds = /* @__PURE__ */ new Set();
2259
+ let finalText;
2260
+ let model;
2261
+ let eventCount = 0;
2262
+ let assistantMessageCount = 0;
2263
+ let toolCallCount = 0;
2264
+ const toolCounts = /* @__PURE__ */ new Map();
2265
+ let initialPrompt;
2266
+ for (const raw of lines) {
2267
+ if (!raw || typeof raw !== "object")
2268
+ continue;
2269
+ const r = raw;
2270
+ eventCount++;
2271
+ const ts = typeof r.timestamp === "string" ? Date.parse(r.timestamp) : NaN;
2272
+ if (!Number.isNaN(ts)) {
2273
+ if (startMs === void 0 || ts < startMs)
2274
+ startMs = ts;
2275
+ if (endMs === void 0 || ts > endMs)
2276
+ endMs = ts;
2277
+ }
2278
+ if (initialPrompt === void 0 && r.type === "user" && (r.parentUuid === null || r.parentUuid === void 0)) {
2279
+ const msg = r.message;
2280
+ const content = msg?.content;
2281
+ if (typeof content === "string") {
2282
+ initialPrompt = content;
2283
+ } else if (Array.isArray(content)) {
2284
+ const txt = content.find((b) => b && typeof b === "object" && b.type === "text");
2285
+ if (txt?.text)
2286
+ initialPrompt = txt.text;
2287
+ }
2288
+ }
2289
+ if (r.type === "assistant") {
2290
+ const m = r.message;
2291
+ if (!m)
2292
+ continue;
2293
+ if (!model && typeof m.model === "string")
2294
+ model = m.model;
2295
+ const mid = typeof m.id === "string" ? m.id : void 0;
2296
+ const rid = typeof r.requestId === "string" ? r.requestId : void 0;
2297
+ const dedupKey = mid != null && rid != null ? `${mid}:${rid}` : void 0;
2298
+ const fresh = dedupKey ? !seenMessageIds.has(dedupKey) : true;
2299
+ if (dedupKey && fresh)
2300
+ seenMessageIds.add(dedupKey);
2301
+ if (fresh) {
2302
+ assistantMessageCount++;
2303
+ const u = m.usage;
2304
+ if (u) {
2305
+ const toNum = (v) => typeof v === "number" ? v : 0;
2306
+ totalUsage.input += toNum(u.input_tokens);
2307
+ totalUsage.output += toNum(u.output_tokens);
2308
+ totalUsage.cacheRead += toNum(u.cache_read_input_tokens);
2309
+ totalUsage.cacheWrite += toNum(u.cache_creation_input_tokens);
2310
+ }
2311
+ }
2312
+ const content = m.content;
2313
+ if (Array.isArray(content)) {
2314
+ for (const block of content) {
2315
+ if (!block || typeof block !== "object")
2316
+ continue;
2317
+ if (block.type === "text" && typeof block.text === "string") {
2318
+ finalText = block.text;
2319
+ } else if (block.type === "tool_use" && typeof block.name === "string") {
2320
+ toolCallCount++;
2321
+ toolCounts.set(block.name, (toolCounts.get(block.name) ?? 0) + 1);
2322
+ }
2323
+ }
2324
+ }
2325
+ }
2326
+ }
2327
+ const toolCalls = Array.from(toolCounts.entries()).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
2328
+ const finalPreview = finalText !== void 0 ? finalText.replace(/\s+/g, " ").trim().slice(0, 240) : void 0;
2329
+ return {
2330
+ startMs,
2331
+ endMs,
2332
+ totalUsage,
2333
+ eventCount,
2334
+ finalPreview,
2335
+ finalText,
2336
+ model,
2337
+ toolCalls,
2338
+ toolCallCount,
2339
+ assistantMessageCount,
2340
+ initialPrompt
2341
+ };
2342
+ }
2343
+ async function loadSubagents(dir, sessionStartMs, parentEvents) {
2344
+ let entries;
2345
+ try {
2346
+ entries = await fs.readdir(dir);
2347
+ } catch {
2348
+ return [];
2349
+ }
2350
+ const byDesc = /* @__PURE__ */ new Map();
2351
+ const dispatchesByTs = [];
2352
+ for (const e of parentEvents) {
2353
+ if (e.role !== "tool-call" || e.toolName !== "Agent")
2354
+ continue;
2355
+ for (const b of e.blocks) {
2356
+ if (b?.type !== "tool_use" || b.name !== "Agent")
2357
+ continue;
2358
+ const input = b.input ?? {};
2359
+ const desc = typeof input.description === "string" ? input.description : void 0;
2360
+ const tsMs = e.timestamp ? Date.parse(e.timestamp) : void 0;
2361
+ const ref = {
2362
+ toolUseId: b.id,
2363
+ parentUuid: e.uuid ?? "",
2364
+ runInBackground: input.run_in_background === true,
2365
+ prompt: typeof input.prompt === "string" ? input.prompt : void 0,
2366
+ tsMs
2367
+ };
2368
+ if (desc)
2369
+ byDesc.set(desc, ref);
2370
+ if (tsMs !== void 0)
2371
+ dispatchesByTs.push(ref);
2372
+ }
2373
+ }
2374
+ dispatchesByTs.sort((a, b) => (a.tsMs ?? 0) - (b.tsMs ?? 0));
2375
+ function matchByTime(startMs) {
2376
+ if (startMs === void 0)
2377
+ return void 0;
2378
+ const TOLERANCE_MS = 2e3;
2379
+ let best;
2380
+ let bestDelta = Infinity;
2381
+ for (const ref of dispatchesByTs) {
2382
+ if (ref.tsMs === void 0)
2383
+ continue;
2384
+ const delta = Math.abs(startMs - ref.tsMs);
2385
+ if (delta < bestDelta && delta <= TOLERANCE_MS) {
2386
+ best = ref;
2387
+ bestDelta = delta;
2388
+ }
2389
+ }
2390
+ return best;
2391
+ }
2392
+ const jsonlFiles = entries.filter((f) => f.startsWith("agent-") && f.endsWith(".jsonl"));
2393
+ const runs = await Promise.all(jsonlFiles.map(async (f) => {
2394
+ const agentId = f.replace(/^agent-/, "").replace(/\.jsonl$/, "");
2395
+ const jsonlPath = path.join(dir, f);
2396
+ const metaPath = path.join(dir, `agent-${agentId}.meta.json`);
2397
+ let meta = {};
2398
+ try {
2399
+ const raw = await fs.readFile(metaPath, "utf8");
2400
+ meta = JSON.parse(raw);
2401
+ } catch {
2402
+ }
2403
+ let lines;
2404
+ try {
2405
+ lines = await readJsonlFile(jsonlPath);
2406
+ } catch {
2407
+ return null;
2408
+ }
2409
+ const summary = summarizeSubagentLines(lines);
2410
+ const metaDesc = meta.description && meta.description.trim().length > 0 ? meta.description : void 0;
2411
+ const parentRef = metaDesc ? byDesc.get(metaDesc) : matchByTime(summary.startMs);
2412
+ const description = metaDesc ?? (summary.initialPrompt ? summary.initialPrompt.replace(/\s+/g, " ").trim().slice(0, 80) : "(no description)");
2413
+ const prompt = parentRef?.prompt ?? summary.initialPrompt;
2414
+ const startTOffsetMs = sessionStartMs !== void 0 && summary.startMs !== void 0 ? Math.max(0, summary.startMs - sessionStartMs) : void 0;
2415
+ const endTOffsetMs = sessionStartMs !== void 0 && summary.endMs !== void 0 ? Math.max(0, summary.endMs - sessionStartMs) : void 0;
2416
+ const durationMs = summary.startMs !== void 0 && summary.endMs !== void 0 ? summary.endMs - summary.startMs : void 0;
2417
+ return {
2418
+ agentId,
2419
+ agentType: meta.agentType ?? "unknown",
2420
+ description,
2421
+ startMs: summary.startMs,
2422
+ endMs: summary.endMs,
2423
+ durationMs,
2424
+ startTOffsetMs,
2425
+ endTOffsetMs,
2426
+ eventCount: summary.eventCount,
2427
+ totalUsage: summary.totalUsage,
2428
+ parentUuid: parentRef?.parentUuid,
2429
+ parentToolUseId: parentRef?.toolUseId,
2430
+ runInBackground: parentRef?.runInBackground,
2431
+ prompt,
2432
+ finalPreview: summary.finalPreview,
2433
+ finalText: summary.finalText,
2434
+ model: summary.model,
2435
+ toolCalls: summary.toolCalls,
2436
+ toolCallCount: summary.toolCallCount,
2437
+ assistantMessageCount: summary.assistantMessageCount
2438
+ };
2439
+ }));
2440
+ return runs.filter((r) => r !== null).sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
2441
+ }
2442
+ async function listProjects(root = DEFAULT_ROOT) {
2443
+ const files = await walkJsonlFiles(root);
2444
+ const byRawDir = /* @__PURE__ */ new Map();
2445
+ for (const f of files) {
2446
+ const cur = byRawDir.get(f.projectDir) ?? { count: 0, lastActiveMs: 0 };
2447
+ cur.count++;
2448
+ if (f.mtimeMs > cur.lastActiveMs)
2449
+ cur.lastActiveMs = f.mtimeMs;
2450
+ byRawDir.set(f.projectDir, cur);
2451
+ }
2452
+ const cwdForProject = (dir) => {
2453
+ for (const [, entry] of metaCache.entries()) {
2454
+ if (entry.meta.projectDir === dir && entry.meta.cwd) {
2455
+ return entry.meta.cwd;
2456
+ }
2457
+ }
2458
+ return void 0;
2459
+ };
2460
+ const canonicalMap = /* @__PURE__ */ new Map();
2461
+ for (const [rawDir, { count, lastActiveMs }] of byRawDir) {
2462
+ const cwdOrDecoded = cwdForProject(rawDir) ?? decodeProjectName(rawDir);
2463
+ const canonical = canonicalProjectName(cwdOrDecoded);
2464
+ const wt = worktreeName(cwdOrDecoded);
2465
+ const agg = canonicalMap.get(canonical) ?? {
2466
+ canonicalName: canonical,
2467
+ count: 0,
2468
+ lastActiveMs: 0,
2469
+ worktreeBranches: /* @__PURE__ */ new Set()
2470
+ };
2471
+ agg.count += count;
2472
+ if (lastActiveMs > agg.lastActiveMs)
2473
+ agg.lastActiveMs = lastActiveMs;
2474
+ if (wt)
2475
+ agg.worktreeBranches.add(wt);
2476
+ canonicalMap.set(canonical, agg);
2477
+ }
2478
+ return Array.from(canonicalMap.values()).map((a) => ({
2479
+ projectDir: a.canonicalName,
2480
+ projectName: a.canonicalName,
2481
+ sessionCount: a.count,
2482
+ lastActiveMs: a.lastActiveMs,
2483
+ worktreeCount: a.worktreeBranches.size
2484
+ })).sort((a, b) => b.lastActiveMs - a.lastActiveMs);
2485
+ }
2486
+ async function loadTeamForSession(sessionId, opts = {}) {
2487
+ const { root = DEFAULT_ROOT } = opts;
2488
+ const all = await listSessions({ root });
2489
+ const self = all.find((s) => s.sessionId === sessionId);
2490
+ if (!self || !self.teamName)
2491
+ return null;
2492
+ const teamName = self.teamName;
2493
+ const candidates = all.filter((s) => s.teamName === teamName && !s.filePath.includes("/subagents/"));
2494
+ const loaded = await Promise.all(candidates.map((c) => getSession(c.sessionId, { root })));
2495
+ const details = /* @__PURE__ */ new Map();
2496
+ candidates.forEach((c, i) => {
2497
+ const d = loaded[i];
2498
+ if (d)
2499
+ details.set(c.sessionId, d);
2500
+ });
2501
+ const views = groupByTeam(candidates, details);
2502
+ const view = views.find((v) => v.teamName === teamName);
2503
+ if (!view)
2504
+ return null;
2505
+ return { view, details };
2506
+ }
2507
+ async function findTeamLead(sessionId, opts = {}) {
2508
+ const { root = DEFAULT_ROOT } = opts;
2509
+ const all = await listSessions({ root });
2510
+ const self = all.find((s) => s.sessionId === sessionId);
2511
+ if (!self || !self.teamName || !self.agentName)
2512
+ return null;
2513
+ if (self.isTeamLead)
2514
+ return null;
2515
+ const candidates = all.filter((s) => s.teamName === self.teamName && s.isTeamLead && !s.filePath.includes("/subagents/"));
2516
+ if (candidates.length === 0)
2517
+ return null;
2518
+ return {
2519
+ leadSessionId: candidates[0].sessionId,
2520
+ teamName: self.teamName,
2521
+ agentName: self.agentName
2522
+ };
2523
+ }
2524
+ async function loadUsageByDay(start, end) {
2525
+ let raw;
2526
+ try {
2527
+ raw = await fs.readFile(USAGE_LOG, "utf8");
2528
+ } catch {
2529
+ return { by_day: [] };
2530
+ }
2531
+ const startMs = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime();
2532
+ const endMs = new Date(end.getFullYear(), end.getMonth(), end.getDate(), 23, 59, 59, 999).getTime();
2533
+ const byDay = /* @__PURE__ */ new Map();
2534
+ for (const line of raw.split("\n")) {
2535
+ const trimmed = line.trim();
2536
+ if (!trimmed)
2537
+ continue;
2538
+ try {
2539
+ const snap = JSON.parse(trimmed);
2540
+ if (!snap.captured_at)
2541
+ continue;
2542
+ const ms = Date.parse(snap.captured_at);
2543
+ if (Number.isNaN(ms))
2544
+ continue;
2545
+ if (ms < startMs)
2546
+ continue;
2547
+ if (ms > endMs)
2548
+ break;
2549
+ const key = toLocalDay(ms);
2550
+ const util = snap.five_hour?.utilization ?? 0;
2551
+ const cur2 = byDay.get(key) ?? 0;
2552
+ if (util > cur2)
2553
+ byDay.set(key, util);
2554
+ } catch {
2555
+ }
2556
+ }
2557
+ const out = [];
2558
+ const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
2559
+ const stop = new Date(end.getFullYear(), end.getMonth(), end.getDate());
2560
+ while (cur.getTime() <= stop.getTime()) {
2561
+ const k = toLocalDay(cur.getTime());
2562
+ out.push({ date: k, peak_util_pct: byDay.get(k) ?? 0 });
2563
+ cur.setDate(cur.getDate() + 1);
2564
+ }
2565
+ return { by_day: out };
2566
+ }
2567
+ var DEFAULT_ROOT, metaCache, detailCache, fileListCache, FILE_LIST_TTL_MS, BLANK_USAGE3, USAGE_LOG;
2568
+ var init_fs = __esm({
2569
+ "../parser/dist/fs.js"() {
2570
+ "use strict";
2571
+ init_parser();
2572
+ init_analytics();
2573
+ init_team();
2574
+ DEFAULT_ROOT = path.join(os.homedir(), ".claude", "projects");
2575
+ metaCache = /* @__PURE__ */ new Map();
2576
+ detailCache = /* @__PURE__ */ new Map();
2577
+ fileListCache = null;
2578
+ FILE_LIST_TTL_MS = 1e3;
2579
+ BLANK_USAGE3 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
2580
+ USAGE_LOG = path.join(os.homedir(), ".cclens", "usage.jsonl");
2581
+ }
2582
+ });
2583
+
1
2584
  // src/daemon-worker.ts
2
- import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
3
- import { dirname as dirname2, join as join2 } from "node:path";
4
- import { homedir as homedir2 } from "node:os";
2585
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync4 } from "node:fs";
2586
+ import { dirname as dirname3, join as join4 } from "node:path";
2587
+ import { homedir as homedir4 } from "node:os";
5
2588
 
6
2589
  // src/usage/token.ts
7
2590
  import { execFileSync } from "node:child_process";
@@ -31,9 +2614,9 @@ function readFromCredentialsFile() {
31
2614
  join(homedir(), ".claude", ".credentials.json"),
32
2615
  join(homedir(), ".config", "claude", "credentials.json")
33
2616
  ];
34
- for (const path of candidates) {
2617
+ for (const path2 of candidates) {
35
2618
  try {
36
- const blob = readFileSync(path, "utf8");
2619
+ const blob = readFileSync(path2, "utf8");
37
2620
  const creds = extractCredentials(blob);
38
2621
  if (creds) return creds;
39
2622
  } catch {
@@ -156,17 +2739,187 @@ function nextIntervalMs(current, outcome) {
156
2739
  return Math.min(Math.max(current, BASE_INTERVAL_MS) * 2, MAX_INTERVAL_MS);
157
2740
  }
158
2741
 
2742
+ // src/team/config.ts
2743
+ import { readFileSync as readFileSync3, writeFileSync, unlinkSync, mkdirSync as mkdirSync2 } from "node:fs";
2744
+ import { join as join2 } from "node:path";
2745
+ import { homedir as homedir2 } from "node:os";
2746
+ var DEFAULT_DIR = join2(homedir2(), ".cclens");
2747
+ var CONFIG_FILE = "team.json";
2748
+ function readTeamConfig(dir = DEFAULT_DIR) {
2749
+ try {
2750
+ return JSON.parse(readFileSync3(join2(dir, CONFIG_FILE), "utf8"));
2751
+ } catch {
2752
+ return null;
2753
+ }
2754
+ }
2755
+ function writeTeamConfig(config, dir = DEFAULT_DIR) {
2756
+ mkdirSync2(dir, { recursive: true });
2757
+ writeFileSync(join2(dir, CONFIG_FILE), JSON.stringify(config, null, 2), { mode: 384 });
2758
+ }
2759
+
2760
+ // src/team/push.ts
2761
+ init_dist();
2762
+ import { randomUUID } from "node:crypto";
2763
+ function bucketToRollup(b) {
2764
+ return {
2765
+ day: b.date,
2766
+ agentTimeMs: b.airTimeMs,
2767
+ sessions: b.sessions,
2768
+ toolCalls: b.toolCalls,
2769
+ turns: b.turns,
2770
+ tokens: { ...b.tokens }
2771
+ };
2772
+ }
2773
+ function buildRollupsForRange(sessions, sinceDay) {
2774
+ const buckets = dailyActivity(sessions);
2775
+ const startCounts = /* @__PURE__ */ new Map();
2776
+ for (const s of sessions) {
2777
+ const d = sessionDay(s);
2778
+ if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
2779
+ }
2780
+ return buckets.filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({
2781
+ ...bucketToRollup(b),
2782
+ sessions: startCounts.get(b.date) ?? 0
2783
+ }));
2784
+ }
2785
+ function buildIngestPayload(rollup) {
2786
+ return {
2787
+ ingestId: randomUUID(),
2788
+ observedAt: (/* @__PURE__ */ new Date()).toISOString(),
2789
+ dailyRollup: rollup
2790
+ };
2791
+ }
2792
+ async function pushToTeamServer(config, payload) {
2793
+ const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
2794
+ method: "POST",
2795
+ headers: {
2796
+ "Content-Type": "application/json",
2797
+ Authorization: `Bearer ${config.bearerToken}`
2798
+ },
2799
+ body: JSON.stringify(payload)
2800
+ });
2801
+ const body = await res.json().catch(() => null);
2802
+ return { ok: res.ok, status: res.status, body };
2803
+ }
2804
+
2805
+ // src/team/queue.ts
2806
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2, statSync, mkdirSync as mkdirSync3 } from "node:fs";
2807
+ import { join as join3, dirname as dirname2 } from "node:path";
2808
+ import { homedir as homedir3 } from "node:os";
2809
+ var QUEUE_PATH = join3(homedir3(), ".cclens", "ingest-queue.jsonl");
2810
+ var MAX_SIZE_BYTES = 10 * 1024 * 1024;
2811
+ var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
2812
+ function enqueuePayload(payload, queuePath = QUEUE_PATH) {
2813
+ mkdirSync3(dirname2(queuePath), { recursive: true });
2814
+ const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
2815
+ appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
2816
+ try {
2817
+ const { size } = statSync(queuePath);
2818
+ if (size > MAX_SIZE_BYTES) {
2819
+ pruneOldest(queuePath);
2820
+ }
2821
+ } catch {
2822
+ }
2823
+ }
2824
+ function dequeuePayloads(queuePath = QUEUE_PATH) {
2825
+ let lines;
2826
+ try {
2827
+ lines = readFileSync4(queuePath, "utf8").trim().split("\n").filter(Boolean);
2828
+ } catch {
2829
+ return [];
2830
+ }
2831
+ const now = Date.now();
2832
+ const valid = [];
2833
+ for (const line of lines) {
2834
+ try {
2835
+ const entry = JSON.parse(line);
2836
+ if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
2837
+ valid.push(entry);
2838
+ }
2839
+ } catch {
2840
+ }
2841
+ }
2842
+ writeFileSync2(queuePath, "", "utf8");
2843
+ return valid.map((e) => e.payload);
2844
+ }
2845
+ function pruneOldest(queuePath) {
2846
+ let lines;
2847
+ try {
2848
+ lines = readFileSync4(queuePath, "utf8").trim().split("\n").filter(Boolean);
2849
+ } catch {
2850
+ return;
2851
+ }
2852
+ const keep = lines.slice(Math.floor(lines.length / 2));
2853
+ writeFileSync2(queuePath, keep.join("\n") + "\n", "utf8");
2854
+ }
2855
+
2856
+ // src/team/sync.ts
2857
+ var noopLog = () => {
2858
+ };
2859
+ async function runTeamSync(log2 = noopLog) {
2860
+ const config = readTeamConfig();
2861
+ if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
2862
+ try {
2863
+ const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
2864
+ const { toLocalDay: toLocalDay2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
2865
+ const today = toLocalDay2(Date.now());
2866
+ const sessions = await listSessions2({ limit: 1e4 });
2867
+ const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
2868
+ if (rollups.length === 0) {
2869
+ log2("info", "team push: nothing to sync");
2870
+ return { paired: true, pushed: 0, queued: 0, queuedDrained: 0 };
2871
+ }
2872
+ let pushed = 0;
2873
+ let queued = 0;
2874
+ let failedDay;
2875
+ for (const rollup of rollups) {
2876
+ const payload = buildIngestPayload(rollup);
2877
+ const result = await pushToTeamServer(config, payload);
2878
+ if (!result.ok) {
2879
+ log2("warn", `team push failed on ${rollup.day} (${result.status}); queueing`);
2880
+ enqueuePayload(payload);
2881
+ queued++;
2882
+ failedDay = rollup.day;
2883
+ break;
2884
+ }
2885
+ pushed++;
2886
+ }
2887
+ const nextConfig = { ...config, lastSyncedDay: today };
2888
+ if (pushed > 0 || !failedDay) writeTeamConfig(nextConfig);
2889
+ let queuedDrained = 0;
2890
+ if (!failedDay) {
2891
+ const backlog = dequeuePayloads();
2892
+ for (let i = 0; i < backlog.length; i++) {
2893
+ const qResult = await pushToTeamServer(config, backlog[i]);
2894
+ if (!qResult.ok) {
2895
+ for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
2896
+ break;
2897
+ }
2898
+ queuedDrained++;
2899
+ }
2900
+ }
2901
+ if (pushed > 0) {
2902
+ log2("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
2903
+ }
2904
+ return { paired: true, pushed, queued, queuedDrained, failedDay };
2905
+ } catch (err) {
2906
+ const message = err.message;
2907
+ log2("warn", `team push error: ${message}`);
2908
+ return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
2909
+ }
2910
+ }
2911
+
159
2912
  // src/daemon-worker.ts
160
- var STATE_DIR = join2(homedir2(), ".cclens");
161
- var USAGE_LOG = join2(STATE_DIR, "usage.jsonl");
162
- var DAEMON_LOG = join2(STATE_DIR, "daemon.log");
2913
+ var STATE_DIR = join4(homedir4(), ".cclens");
2914
+ var USAGE_LOG2 = join4(STATE_DIR, "usage.jsonl");
2915
+ var DAEMON_LOG = join4(STATE_DIR, "daemon.log");
163
2916
  var WATCHDOG_INTERVAL_MS = 5 * 1e3;
164
- mkdirSync2(dirname2(USAGE_LOG), { recursive: true });
2917
+ mkdirSync4(dirname3(USAGE_LOG2), { recursive: true });
165
2918
  function log(level, message) {
166
2919
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
167
2920
  `;
168
2921
  try {
169
- appendFileSync2(DAEMON_LOG, line, "utf8");
2922
+ appendFileSync3(DAEMON_LOG, line, "utf8");
170
2923
  } catch {
171
2924
  }
172
2925
  }
@@ -176,7 +2929,7 @@ var waitingForRefresh = false;
176
2929
  async function tick() {
177
2930
  try {
178
2931
  const snapshot = await fetchUsage();
179
- appendSnapshot(USAGE_LOG, snapshot);
2932
+ appendSnapshot(USAGE_LOG2, snapshot);
180
2933
  log(
181
2934
  "info",
182
2935
  `snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
@@ -233,6 +2986,7 @@ async function runLoop() {
233
2986
  const outcome = await tick();
234
2987
  scheduleAfter(Date.now(), outcome);
235
2988
  }
2989
+ await runTeamSync(log);
236
2990
  }
237
2991
  await sleep(WATCHDOG_INTERVAL_MS);
238
2992
  }