fleetlens 0.4.0 → 0.6.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 (440) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +16 -7
  3. package/app/apps/web/.next/build-manifest.json +7 -7
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/required-server-files.json +2 -1
  6. package/app/apps/web/.next/routes-manifest.json +86 -24
  7. package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +4 -4
  8. package/app/apps/web/.next/server/app/_global-error/page.js +1 -1
  9. package/app/apps/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  10. package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.rsc +7 -7
  13. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  14. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  15. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  16. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  17. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/app/apps/web/.next/server/app/_not-found/page/build-manifest.json +4 -4
  19. package/app/apps/web/.next/server/app/_not-found/page.js +5 -4
  20. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  21. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  22. package/app/apps/web/.next/server/app/api/digest/day/[date]/route/app-paths-manifest.json +3 -0
  23. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +9 -0
  24. package/app/apps/web/.next/server/app/api/{insights/saved/[key] → digest/day/[date]}/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/digest/day/[date]/route_client-reference-manifest.js +3 -0
  26. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route/app-paths-manifest.json +3 -0
  27. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +9 -0
  28. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -0
  29. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route_client-reference-manifest.js +3 -0
  30. package/app/apps/web/.next/server/app/api/digest/month-index/route/app-paths-manifest.json +3 -0
  31. package/app/apps/web/.next/server/app/api/digest/month-index/route.js +7 -0
  32. package/app/apps/web/.next/server/app/api/{insights/months-index → digest/month-index}/route.js.nft.json +1 -1
  33. package/app/apps/web/.next/server/app/api/{insights/pdf/[key] → digest/month-index}/route_client-reference-manifest.js +1 -1
  34. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route/app-paths-manifest.json +3 -0
  35. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +9 -0
  36. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -0
  37. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route_client-reference-manifest.js +3 -0
  38. package/app/apps/web/.next/server/app/api/digest/week-index/route/app-paths-manifest.json +3 -0
  39. package/app/apps/web/.next/server/app/api/digest/week-index/route.js +7 -0
  40. package/app/apps/web/.next/server/app/api/{insights/weeks-index → digest/week-index}/route.js.nft.json +1 -1
  41. package/app/apps/web/.next/server/app/api/digest/week-index/route_client-reference-manifest.js +3 -0
  42. package/app/apps/web/.next/server/app/api/jobs/route/app-paths-manifest.json +3 -0
  43. package/app/apps/web/.next/server/app/api/jobs/route.js +6 -0
  44. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -0
  45. package/app/apps/web/.next/server/app/api/jobs/route_client-reference-manifest.js +3 -0
  46. package/app/apps/web/.next/server/app/api/runs/[runId]/route/app-paths-manifest.json +3 -0
  47. package/app/apps/web/.next/server/app/api/runs/[runId]/route/build-manifest.json +9 -0
  48. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js +6 -0
  49. package/app/apps/web/.next/server/app/api/{insights/saved → runs/[runId]}/route.js.nft.json +1 -1
  50. package/app/apps/web/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +3 -0
  51. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route/app-paths-manifest.json +3 -0
  52. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route/build-manifest.json +9 -0
  53. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route/server-reference-manifest.json +4 -0
  54. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js +6 -0
  55. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.map +5 -0
  56. package/app/apps/web/.next/server/app/api/{insights/pdf/[key] → runs/[runId]/stream}/route.js.nft.json +1 -1
  57. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route_client-reference-manifest.js +3 -0
  58. package/app/apps/web/.next/server/app/api/runs/route/app-paths-manifest.json +3 -0
  59. package/app/apps/web/.next/server/app/api/runs/route/build-manifest.json +9 -0
  60. package/app/apps/web/.next/server/app/api/runs/route/server-reference-manifest.json +4 -0
  61. package/app/apps/web/.next/server/app/api/runs/route.js +6 -0
  62. package/app/apps/web/.next/server/app/api/runs/route.js.map +5 -0
  63. package/app/apps/web/.next/server/app/api/{insights → runs}/route.js.nft.json +1 -1
  64. package/app/apps/web/.next/server/app/api/runs/route_client-reference-manifest.js +3 -0
  65. package/app/apps/web/.next/server/app/api/settings/route/app-paths-manifest.json +3 -0
  66. package/app/apps/web/.next/server/app/api/settings/route/build-manifest.json +9 -0
  67. package/app/apps/web/.next/server/app/api/settings/route/server-reference-manifest.json +4 -0
  68. package/app/apps/web/.next/server/app/api/settings/route.js +10 -0
  69. package/app/apps/web/.next/server/app/api/settings/route.js.map +5 -0
  70. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -0
  71. package/app/apps/web/.next/server/app/api/{insights → settings}/route_client-reference-manifest.js +1 -1
  72. package/app/apps/web/.next/server/app/day/[date]/page/app-paths-manifest.json +3 -0
  73. package/app/apps/web/.next/server/app/{insights/print/[key] → day/[date]}/page/build-manifest.json +4 -4
  74. package/app/apps/web/.next/server/app/day/[date]/page/server-reference-manifest.json +4 -0
  75. package/app/apps/web/.next/server/app/day/[date]/page.js +18 -0
  76. package/app/apps/web/.next/server/app/day/[date]/page.js.map +5 -0
  77. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -0
  78. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +3 -0
  79. package/app/apps/web/.next/server/app/day/page/app-paths-manifest.json +3 -0
  80. package/app/apps/web/.next/server/app/day/page/build-manifest.json +17 -0
  81. package/app/apps/web/.next/server/app/day/page/next-font-manifest.json +6 -0
  82. package/app/apps/web/.next/server/app/day/page/react-loadable-manifest.json +1 -0
  83. package/app/apps/web/.next/server/app/day/page/server-reference-manifest.json +4 -0
  84. package/app/apps/web/.next/server/app/{insights/print/[key] → day}/page.js +11 -10
  85. package/app/apps/web/.next/server/app/day/page.js.map +5 -0
  86. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -0
  87. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +3 -0
  88. package/app/apps/web/.next/server/app/digest/[date]/page/app-paths-manifest.json +3 -0
  89. package/app/apps/web/.next/server/app/digest/[date]/page/build-manifest.json +17 -0
  90. package/app/apps/web/.next/server/app/digest/[date]/page/next-font-manifest.json +6 -0
  91. package/app/apps/web/.next/server/app/digest/[date]/page/react-loadable-manifest.json +1 -0
  92. package/app/apps/web/.next/server/app/digest/[date]/page/server-reference-manifest.json +4 -0
  93. package/app/apps/web/.next/server/app/digest/[date]/page.js +17 -0
  94. package/app/apps/web/.next/server/app/digest/[date]/page.js.map +5 -0
  95. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -0
  96. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +3 -0
  97. package/app/apps/web/.next/server/app/digest/page/app-paths-manifest.json +3 -0
  98. package/app/apps/web/.next/server/app/digest/page/build-manifest.json +17 -0
  99. package/app/apps/web/.next/server/app/digest/page/next-font-manifest.json +6 -0
  100. package/app/apps/web/.next/server/app/digest/page/react-loadable-manifest.json +1 -0
  101. package/app/apps/web/.next/server/app/digest/page/server-reference-manifest.json +4 -0
  102. package/app/apps/web/.next/server/app/digest/page.js +17 -0
  103. package/app/apps/web/.next/server/app/digest/page.js.map +5 -0
  104. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -0
  105. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +3 -0
  106. package/app/apps/web/.next/server/app/insights/[key]/page/build-manifest.json +4 -4
  107. package/app/apps/web/.next/server/app/insights/[key]/page.js +7 -5
  108. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  109. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  110. package/app/apps/web/.next/server/app/insights/page/build-manifest.json +4 -4
  111. package/app/apps/web/.next/server/app/insights/page.js +7 -5
  112. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  113. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  114. package/app/apps/web/.next/server/app/page/build-manifest.json +4 -4
  115. package/app/apps/web/.next/server/app/page.js +7 -6
  116. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  117. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  118. package/app/apps/web/.next/server/app/parallelism/page/build-manifest.json +4 -4
  119. package/app/apps/web/.next/server/app/parallelism/page.js +6 -5
  120. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  121. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  122. package/app/apps/web/.next/server/app/projects/[slug]/page/build-manifest.json +4 -4
  123. package/app/apps/web/.next/server/app/projects/[slug]/page.js +6 -5
  124. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  125. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  126. package/app/apps/web/.next/server/app/projects/page/build-manifest.json +4 -4
  127. package/app/apps/web/.next/server/app/projects/page.js +5 -4
  128. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  129. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  130. package/app/apps/web/.next/server/app/runs/page/app-paths-manifest.json +3 -0
  131. package/app/apps/web/.next/server/app/runs/page/build-manifest.json +17 -0
  132. package/app/apps/web/.next/server/app/runs/page/next-font-manifest.json +6 -0
  133. package/app/apps/web/.next/server/app/runs/page/react-loadable-manifest.json +1 -0
  134. package/app/apps/web/.next/server/app/runs/page/server-reference-manifest.json +4 -0
  135. package/app/apps/web/.next/server/app/runs/page.js +17 -0
  136. package/app/apps/web/.next/server/app/runs/page.js.map +5 -0
  137. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -0
  138. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +3 -0
  139. package/app/apps/web/.next/server/app/sessions/[id]/page/build-manifest.json +4 -4
  140. package/app/apps/web/.next/server/app/sessions/[id]/page.js +5 -4
  141. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  142. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  143. package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +4 -4
  144. package/app/apps/web/.next/server/app/sessions/page.js +5 -4
  145. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  146. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  147. package/app/apps/web/.next/server/app/settings/page/app-paths-manifest.json +3 -0
  148. package/app/apps/web/.next/server/app/settings/page/build-manifest.json +17 -0
  149. package/app/apps/web/.next/server/app/settings/page/next-font-manifest.json +6 -0
  150. package/app/apps/web/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
  151. package/app/apps/web/.next/server/app/settings/page/server-reference-manifest.json +4 -0
  152. package/app/apps/web/.next/server/app/settings/page.js +18 -0
  153. package/app/apps/web/.next/server/app/settings/page.js.map +5 -0
  154. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -0
  155. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +3 -0
  156. package/app/apps/web/.next/server/app/usage/page/build-manifest.json +4 -4
  157. package/app/apps/web/.next/server/app/usage/page.js +6 -5
  158. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  159. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  160. package/app/apps/web/.next/server/app-paths-manifest.json +16 -7
  161. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_digest_week_[startDate]_route_actions_0klqqsh.js +3 -0
  162. package/app/apps/web/.next/server/chunks/09og__next-internal_server_app_api_digest_month_[yearMonth]_route_actions_0i4ielu.js +3 -0
  163. package/app/apps/web/.next/server/chunks/[externals]__0o43ejh._.js +3 -0
  164. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0-vgrh1._.js +3 -0
  165. package/app/apps/web/.next/server/chunks/[root-of-the-server]__00n3f9y._.js +3 -0
  166. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02zukjw._.js +3 -0
  167. package/app/apps/web/.next/server/chunks/[root-of-the-server]__067~im1._.js +6 -0
  168. package/app/apps/web/.next/server/chunks/[root-of-the-server]__06b4cj-._.js +3 -0
  169. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_8zh-i._.js +9 -0
  170. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e8n_x4._.js +3 -0
  171. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ej_p3s._.js +9 -0
  172. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jprdks._.js +9 -0
  173. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_digest_day_[date]_route_actions_09sxie5.js +3 -0
  174. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_digest_month-index_route_actions_0wldpbu.js +3 -0
  175. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_digest_week-index_route_actions_12c2wj2.js +3 -0
  176. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_jobs_route_actions_08kx~8-.js +3 -0
  177. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_runs_[runId]_route_actions_0dfcghc.js +3 -0
  178. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_runs_[runId]_stream_route_actions_0.8e8-k.js +3 -0
  179. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_runs_route_actions_0xyx82f.js +3 -0
  180. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_settings_route_actions_0.xv7sx.js +3 -0
  181. package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +49 -0
  182. package/app/apps/web/.next/server/chunks/packages_0e7q9u_._.js +3 -0
  183. package/app/apps/web/.next/server/chunks/packages_0n6p2ot._.js +3 -0
  184. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +379 -0
  185. package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_0bc7ptm.js +1 -1
  186. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0-1_njm._.js +3 -0
  187. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0-94ji~._.js +3 -0
  188. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__13xpdx0._.js → [root-of-the-server]__03uoeiw._.js} +2 -2
  189. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0_6.inc._.js +1 -1
  190. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0d~2~le._.js +8 -0
  191. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0gio_ck._.js +1 -1
  192. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__08.d5ia._.js → [root-of-the-server]__0jdyefm._.js} +10 -10
  193. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ln9jsw._.js +3 -0
  194. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0z45d0g._.js → [root-of-the-server]__0lz4a~g._.js} +2 -48
  195. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o1_g.~._.js +3 -0
  196. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +3 -0
  197. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0rvzdnx._.js +3 -0
  198. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0euu8sa._.js → [root-of-the-server]__0u39afl._.js} +2 -2
  199. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0u5d7de._.js +12 -0
  200. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +3 -3
  201. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0wwa6gh._.js +3 -0
  202. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0xy1qim._.js +3 -0
  203. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~_8vff._.js +3 -0
  204. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__10srpu0._.js +3 -0
  205. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__11v.eqw._.js +1 -1
  206. package/app/apps/web/.next/server/chunks/ssr/_02s1n-x._.js +8 -0
  207. package/app/apps/web/.next/server/chunks/ssr/_0fjbf--._.js +3 -0
  208. package/app/apps/web/.next/server/chunks/ssr/_0jw2sm7._.js +3 -0
  209. package/app/apps/web/.next/server/chunks/ssr/_0wj750k._.js +3 -0
  210. package/app/apps/web/.next/server/chunks/ssr/_0woap-3._.js +1 -1
  211. package/app/apps/web/.next/server/chunks/ssr/{_0xies90._.js → _0xu2pvc._.js} +2 -2
  212. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_day_[date]_page_actions_0u2kxb..js +3 -0
  213. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_day_page_actions_1042maj.js +3 -0
  214. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_digest_[date]_page_actions_0olu967.js +3 -0
  215. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_digest_page_actions_0bl.eil.js +3 -0
  216. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_runs_page_actions_0485qy7.js +3 -0
  217. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_settings_page_actions_0f6epye.js +3 -0
  218. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +3 -0
  219. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_settings_ai-features-form_tsx_01p699p._.js +3 -0
  220. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0b-4-4j._.js +1 -1
  221. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.js +6 -0
  222. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +16 -0
  223. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0xs658s._.js +10 -0
  224. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_runs-live-board_tsx_0r6dkdq._.js +3 -0
  225. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_usage-charts-dashboard_tsx_0bgr_uv._.js +1 -1
  226. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_usage-data_ts_0xtv99n._.js +3 -0
  227. package/app/apps/web/.next/server/chunks/ssr/node_modules_0i2xw~e._.js +3 -0
  228. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_0e1izl_._.js +1 -1
  229. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0d3d4wn.js +4 -0
  230. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0edz1ko.js +4 -0
  231. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0eq.n-n.js +4 -0
  232. package/app/apps/web/.next/server/chunks/ssr/{node_modules_next_dist_esm_build_templates_app-page_0vz7_px.js → node_modules_next_dist_esm_build_templates_app-page_0hcvt-s.js} +3 -3
  233. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0nd5zu0.js +4 -0
  234. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_11j1qy0.js +4 -0
  235. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_12n1lw_.js +1 -1
  236. package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +3 -0
  237. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0-jvt~a._.js +1 -1
  238. package/app/apps/web/.next/server/functions-config-manifest.json +11 -6
  239. package/app/apps/web/.next/server/middleware-build-manifest.js +7 -7
  240. package/app/apps/web/.next/server/pages/500.html +1 -1
  241. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  242. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  243. package/app/apps/web/.next/static/chunks/0-yc2_pjxwwuq.js +1 -0
  244. package/app/apps/web/.next/static/chunks/0257pdz1-imal.js +2 -0
  245. package/app/apps/web/.next/static/chunks/05m.oz3kmf1p7.js +1 -0
  246. package/app/apps/web/.next/static/chunks/{0pqt~8bl3ukh4.js → 073081qv4uyn8.js} +1 -1
  247. package/app/apps/web/.next/static/chunks/{0018dy-m-kusj.js → 07lbm-sdh9c2j.js} +1 -1
  248. package/app/apps/web/.next/static/chunks/0858bgw2i4vob.js +1 -0
  249. package/app/apps/web/.next/static/chunks/0_7303jr0sf0w.js +10 -0
  250. package/app/apps/web/.next/static/chunks/0byz8fqw.exef.js +1 -0
  251. package/app/apps/web/.next/static/chunks/0coa1800xqgfj.js +1 -0
  252. package/app/apps/web/.next/static/chunks/0fs3gxea5h0s3.css +1 -0
  253. package/app/apps/web/.next/static/chunks/0k1psab.bybh9.js +14 -0
  254. package/app/apps/web/.next/static/chunks/0k9pksi_i.odf.js +1 -0
  255. package/app/apps/web/.next/static/chunks/0m62vx92~0_0d.js +1 -0
  256. package/app/apps/web/.next/static/chunks/0mc8aa8z.0-5l.js +8 -0
  257. package/app/apps/web/.next/static/chunks/0mykrrv810ra-.js +31 -0
  258. package/app/apps/web/.next/static/chunks/0o9ayt8sknubd.js +1 -0
  259. package/app/apps/web/.next/static/chunks/0osdgl2.9cl1q.js +6 -0
  260. package/app/apps/web/.next/static/chunks/0xfu09fn_358k.js +4 -0
  261. package/app/apps/web/.next/static/chunks/171za22fv684..js +6 -0
  262. package/app/apps/web/.next/static/chunks/17u2ww8ya5dv1.js +6 -0
  263. package/app/apps/web/.next/static/chunks/{turbopack-169w-1._23ra_.js → turbopack-0_p36yduv.waj.js} +1 -1
  264. package/app/apps/web/app/api/digest/day/[date]/route.ts +152 -0
  265. package/app/apps/web/app/api/digest/month/[yearMonth]/route.ts +147 -0
  266. package/app/apps/web/app/api/digest/month-index/route.ts +103 -0
  267. package/app/apps/web/app/api/digest/week/[startDate]/route.ts +149 -0
  268. package/app/apps/web/app/api/{insights/weeks-index → digest/week-index}/route.ts +45 -33
  269. package/app/apps/web/app/api/jobs/route.ts +8 -0
  270. package/app/apps/web/app/api/runs/[runId]/route.ts +64 -0
  271. package/app/apps/web/app/api/runs/[runId]/stream/route.ts +154 -0
  272. package/app/apps/web/app/api/runs/route.ts +266 -0
  273. package/app/apps/web/app/api/settings/route.ts +35 -0
  274. package/app/apps/web/app/day/[date]/day-view.tsx +193 -0
  275. package/app/apps/web/app/day/[date]/page.tsx +175 -0
  276. package/app/apps/web/app/day/page.tsx +8 -0
  277. package/app/apps/web/app/digest/[date]/page.tsx +15 -0
  278. package/app/apps/web/app/digest/page.tsx +7 -0
  279. package/app/apps/web/app/insights/[key]/page.tsx +106 -48
  280. package/app/apps/web/app/insights/page.tsx +133 -3
  281. package/app/apps/web/app/layout.tsx +23 -3
  282. package/app/apps/web/app/page.tsx +33 -4
  283. package/app/apps/web/app/parallelism/gantt-chart.tsx +67 -1
  284. package/app/apps/web/app/parallelism/page.tsx +8 -278
  285. package/app/apps/web/app/projects/[slug]/page.tsx +153 -0
  286. package/app/apps/web/app/projects/page.tsx +49 -4
  287. package/app/apps/web/app/projects/projects-view.tsx +79 -68
  288. package/app/apps/web/app/runs/page.tsx +21 -0
  289. package/app/apps/web/app/sessions/[id]/page.tsx +12 -1
  290. package/app/apps/web/app/sessions/[id]/session-view.tsx +569 -21
  291. package/app/apps/web/app/sessions/page.tsx +19 -3
  292. package/app/apps/web/app/sessions/sessions-grid.tsx +172 -131
  293. package/app/apps/web/app/settings/ai-features-form.tsx +45 -0
  294. package/app/apps/web/app/settings/page.tsx +20 -0
  295. package/app/apps/web/app/usage/page.tsx +45 -3
  296. package/app/apps/web/components/auto-generate-yesterday.tsx +96 -0
  297. package/app/apps/web/components/backfill-drawer.tsx +309 -0
  298. package/app/apps/web/components/dashboard-view.tsx +4 -2
  299. package/app/apps/web/components/date-nav.tsx +385 -0
  300. package/app/apps/web/components/day-digest-view.tsx +180 -0
  301. package/app/apps/web/components/day-digest.tsx +489 -0
  302. package/app/apps/web/components/flag-chip.tsx +54 -0
  303. package/app/apps/web/components/goal-bar.tsx +63 -0
  304. package/app/apps/web/components/heatmap.tsx +78 -1
  305. package/app/apps/web/components/helpfulness-sparkline.tsx +82 -0
  306. package/app/apps/web/components/insights-history.tsx +211 -0
  307. package/app/apps/web/components/insights-top-bar.tsx +216 -0
  308. package/app/apps/web/components/job-queue-widget.tsx +274 -0
  309. package/app/apps/web/components/live-sessions-widget.tsx +38 -3
  310. package/app/apps/web/components/month-digest-view.tsx +138 -0
  311. package/app/apps/web/components/month-digest.tsx +216 -0
  312. package/app/apps/web/components/outcome-mix-row.tsx +37 -0
  313. package/app/apps/web/components/outcome-pill.tsx +101 -0
  314. package/app/apps/web/components/previous-cycles-trend.tsx +155 -0
  315. package/app/apps/web/components/recent-days-panel.tsx +83 -0
  316. package/app/apps/web/components/runs-live-board.tsx +513 -0
  317. package/app/apps/web/components/sidebar.tsx +38 -4
  318. package/app/apps/web/components/usage-chart-range.tsx +58 -6
  319. package/app/apps/web/components/usage-chart.tsx +94 -7
  320. package/app/apps/web/components/usage-charts-dashboard.tsx +20 -1
  321. package/app/apps/web/components/week-digest-view.tsx +226 -0
  322. package/app/apps/web/components/week-digest.tsx +2128 -0
  323. package/app/apps/web/components/yesterday-hero.tsx +110 -0
  324. package/app/apps/web/lib/auto-week-fire.ts +37 -0
  325. package/app/apps/web/lib/calibration-data.ts +171 -0
  326. package/app/apps/web/lib/entries-index.ts +174 -0
  327. package/app/apps/web/lib/entries.ts +72 -0
  328. package/app/apps/web/lib/inflight-coalesce.test.ts +43 -0
  329. package/app/apps/web/lib/inflight-coalesce.ts +19 -0
  330. package/app/apps/web/lib/jobs.ts +170 -0
  331. package/app/apps/web/lib/usage-data.ts +37 -0
  332. package/app/apps/web/lib/validate-settings.test.ts +25 -0
  333. package/app/apps/web/lib/validate-settings.ts +9 -0
  334. package/app/apps/web/next.config.ts +1 -1
  335. package/app/apps/web/package.json +4 -2
  336. package/app/apps/web/server.js +1 -1
  337. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  338. package/app/apps/web/vitest.config.ts +9 -0
  339. package/dist/daemon-worker.js +1758 -713
  340. package/dist/index.js +11308 -1888
  341. package/package.json +4 -1
  342. package/app/apps/web/.next/server/app/api/insights/months-index/route/app-paths-manifest.json +0 -3
  343. package/app/apps/web/.next/server/app/api/insights/months-index/route.js +0 -7
  344. package/app/apps/web/.next/server/app/api/insights/months-index/route_client-reference-manifest.js +0 -3
  345. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route/app-paths-manifest.json +0 -3
  346. package/app/apps/web/.next/server/app/api/insights/pdf/[key]/route.js +0 -6
  347. package/app/apps/web/.next/server/app/api/insights/route/app-paths-manifest.json +0 -3
  348. package/app/apps/web/.next/server/app/api/insights/route.js +0 -8
  349. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route/app-paths-manifest.json +0 -3
  350. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route.js +0 -6
  351. package/app/apps/web/.next/server/app/api/insights/saved/[key]/route_client-reference-manifest.js +0 -3
  352. package/app/apps/web/.next/server/app/api/insights/saved/route/app-paths-manifest.json +0 -3
  353. package/app/apps/web/.next/server/app/api/insights/saved/route.js +0 -6
  354. package/app/apps/web/.next/server/app/api/insights/saved/route_client-reference-manifest.js +0 -3
  355. package/app/apps/web/.next/server/app/api/insights/weeks-index/route/app-paths-manifest.json +0 -3
  356. package/app/apps/web/.next/server/app/api/insights/weeks-index/route.js +0 -7
  357. package/app/apps/web/.next/server/app/api/insights/weeks-index/route_client-reference-manifest.js +0 -3
  358. package/app/apps/web/.next/server/app/insights/print/[key]/page/app-paths-manifest.json +0 -3
  359. package/app/apps/web/.next/server/app/insights/print/[key]/page.js.nft.json +0 -1
  360. package/app/apps/web/.next/server/app/insights/print/[key]/page_client-reference-manifest.js +0 -3
  361. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_months-index_route_actions_0~h~zci.js +0 -3
  362. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_saved_[key]_route_actions_0a5j9yw.js +0 -3
  363. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_insights_weeks-index_route_actions_0hh-1c7.js +0 -3
  364. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0.hmj0z._.js +0 -3
  365. package/app/apps/web/.next/server/chunks/[root-of-the-server]__01h_g-p._.js +0 -3
  366. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0nh-z10._.js +0 -3
  367. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ri7q~r._.js +0 -3
  368. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0rr1~~x._.js +0 -3
  369. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0s0gv.0._.js +0 -3
  370. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_pdf_[key]_route_actions_0lu-lnx.js +0 -3
  371. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_route_actions_0h8l5lz.js +0 -3
  372. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_insights_saved_route_actions_13rlblw.js +0 -3
  373. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_00t2xj6.js +0 -68
  374. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0.tsxpj._.js +0 -3
  375. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__00z7zlm._.js +0 -3
  376. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__08pen0k._.js +0 -3
  377. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0b323~k._.js +0 -8
  378. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0gx-4qa._.js +0 -3
  379. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0wv27wt._.js +0 -3
  380. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__13itnvx._.js +0 -3
  381. package/app/apps/web/.next/server/chunks/ssr/_09n75_5._.js +0 -8
  382. package/app/apps/web/.next/server/chunks/ssr/_0r~__0v._.js +0 -12
  383. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_insights_print_[key]_page_actions_0a853fw.js +0 -3
  384. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_parallelism_08vub.0._.js +0 -3
  385. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_06j3c.0._.js +0 -8
  386. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0bbo_pd._.js +0 -8
  387. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_insight-report_tsx_13q2a-c._.js +0 -12
  388. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_insights-view_tsx_11730ta._.js +0 -6
  389. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0nqz8dw._.js +0 -3
  390. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_0t-hj0x._.js +0 -3
  391. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_analytics_0k4dgst.js +0 -3
  392. package/app/apps/web/.next/static/chunks/028_pmk40b69_.js +0 -6
  393. package/app/apps/web/.next/static/chunks/03.mv4y4.w-vb.js +0 -6
  394. package/app/apps/web/.next/static/chunks/03_7xtpi9sv4g.js +0 -4
  395. package/app/apps/web/.next/static/chunks/03g5dw5eskotd.js +0 -6
  396. package/app/apps/web/.next/static/chunks/063oimt30jeo9.js +0 -10
  397. package/app/apps/web/.next/static/chunks/08c1pcrocqq5s.js +0 -6
  398. package/app/apps/web/.next/static/chunks/0e-wrhqh4h3r5.js +0 -2
  399. package/app/apps/web/.next/static/chunks/0fc6dk2qtokwh.js +0 -1
  400. package/app/apps/web/.next/static/chunks/0i7er7_bfab36.js +0 -10
  401. package/app/apps/web/.next/static/chunks/0ipytyi01h0ta.js +0 -1
  402. package/app/apps/web/.next/static/chunks/0lsznd4_avzhv.js +0 -6
  403. package/app/apps/web/.next/static/chunks/0lty3v_i9mde7.js +0 -1
  404. package/app/apps/web/.next/static/chunks/0vekjjj4pidlz.css +0 -1
  405. package/app/apps/web/.next/static/chunks/137uw2-qbro07.js +0 -31
  406. package/app/apps/web/app/api/insights/months-index/route.ts +0 -80
  407. package/app/apps/web/app/api/insights/pdf/[key]/route.ts +0 -45
  408. package/app/apps/web/app/api/insights/route.ts +0 -508
  409. package/app/apps/web/app/api/insights/saved/[key]/route.ts +0 -11
  410. package/app/apps/web/app/api/insights/saved/route.ts +0 -9
  411. package/app/apps/web/app/insights/print/[key]/page.tsx +0 -73
  412. package/app/apps/web/components/insight-report.tsx +0 -1031
  413. package/app/apps/web/components/insights-view.tsx +0 -647
  414. package/app/apps/web/lib/ai/insights-prompt.ts +0 -71
  415. package/app/apps/web/lib/ai/saved-reports.ts +0 -83
  416. /package/app/apps/web/.next/server/app/api/{insights/months-index → digest/day/[date]}/route/build-manifest.json +0 -0
  417. /package/app/apps/web/.next/server/app/api/{insights/months-index → digest/day/[date]}/route/server-reference-manifest.json +0 -0
  418. /package/app/apps/web/.next/server/app/api/{insights/months-index → digest/day/[date]}/route.js.map +0 -0
  419. /package/app/apps/web/.next/server/app/api/{insights → digest/month/[yearMonth]}/route/build-manifest.json +0 -0
  420. /package/app/apps/web/.next/server/app/api/{insights → digest/month/[yearMonth]}/route/server-reference-manifest.json +0 -0
  421. /package/app/apps/web/.next/server/app/api/{insights → digest/month/[yearMonth]}/route.js.map +0 -0
  422. /package/app/apps/web/.next/server/app/api/{insights/pdf/[key] → digest/month-index}/route/build-manifest.json +0 -0
  423. /package/app/apps/web/.next/server/app/api/{insights/pdf/[key] → digest/month-index}/route/server-reference-manifest.json +0 -0
  424. /package/app/apps/web/.next/server/app/api/{insights/pdf/[key] → digest/month-index}/route.js.map +0 -0
  425. /package/app/apps/web/.next/server/app/api/{insights/saved → digest/week/[startDate]}/route/build-manifest.json +0 -0
  426. /package/app/apps/web/.next/server/app/api/{insights/saved → digest/week/[startDate]}/route/server-reference-manifest.json +0 -0
  427. /package/app/apps/web/.next/server/app/api/{insights/saved → digest/week/[startDate]}/route.js.map +0 -0
  428. /package/app/apps/web/.next/server/app/api/{insights/saved/[key] → digest/week-index}/route/build-manifest.json +0 -0
  429. /package/app/apps/web/.next/server/app/api/{insights/saved/[key] → digest/week-index}/route/server-reference-manifest.json +0 -0
  430. /package/app/apps/web/.next/server/app/api/{insights/saved/[key] → digest/week-index}/route.js.map +0 -0
  431. /package/app/apps/web/.next/server/app/api/{insights/weeks-index → jobs}/route/build-manifest.json +0 -0
  432. /package/app/apps/web/.next/server/app/api/{insights/weeks-index → jobs}/route/server-reference-manifest.json +0 -0
  433. /package/app/apps/web/.next/server/app/api/{insights/weeks-index → jobs}/route.js.map +0 -0
  434. /package/app/apps/web/.next/server/app/{insights/print/[key]/page → api/runs/[runId]/route}/server-reference-manifest.json +0 -0
  435. /package/app/apps/web/.next/server/app/{insights/print/[key]/page.js.map → api/runs/[runId]/route.js.map} +0 -0
  436. /package/app/apps/web/.next/server/app/{insights/print/[key] → day/[date]}/page/next-font-manifest.json +0 -0
  437. /package/app/apps/web/.next/server/app/{insights/print/[key] → day/[date]}/page/react-loadable-manifest.json +0 -0
  438. /package/app/apps/web/.next/static/{G_4ofKI9v9ARC_lb9JILZ → 8tTWPvUhtatZEoocIhamh}/_buildManifest.js +0 -0
  439. /package/app/apps/web/.next/static/{G_4ofKI9v9ARC_lb9JILZ → 8tTWPvUhtatZEoocIhamh}/_clientMiddlewareManifest.js +0 -0
  440. /package/app/apps/web/.next/static/{G_4ofKI9v9ARC_lb9JILZ → 8tTWPvUhtatZEoocIhamh}/_ssgManifest.js +0 -0
@@ -245,6 +245,91 @@ function parseTranscript(rawLines) {
245
245
  }
246
246
  activeSegments.push({ startMs: segStart, endMs: segEnd });
247
247
  }
248
+ const COLD_GAP_MS = 5 * 60 * 1e3;
249
+ const COLD_MIN_WRITE = 1e3;
250
+ const COLD_MIN_RATIO = 0.3;
251
+ const buckets = /* @__PURE__ */ new Map();
252
+ const compactBoundaries = [];
253
+ for (const e of events) {
254
+ if (!e.timestamp)
255
+ continue;
256
+ const ms = Date.parse(e.timestamp);
257
+ if (Number.isNaN(ms))
258
+ continue;
259
+ if (e.rawType === "assistant" && e.usage && e.messageId) {
260
+ const b = buckets.get(e.messageId);
261
+ if (b) {
262
+ b.siblings.push(e);
263
+ if (ms < b.ms) {
264
+ b.rep = e;
265
+ b.ms = ms;
266
+ }
267
+ } else {
268
+ buckets.set(e.messageId, { rep: e, ms, siblings: [e] });
269
+ }
270
+ } else if (e.rawType === "system") {
271
+ const r = e.raw;
272
+ if (r?.subtype !== "compact_boundary")
273
+ continue;
274
+ const meta = r.compactMetadata ?? {};
275
+ compactBoundaries.push({
276
+ ms,
277
+ trigger: meta.trigger === "manual" ? "manual" : "auto",
278
+ preTokens: typeof meta.preTokens === "number" ? meta.preTokens : 0
279
+ });
280
+ }
281
+ }
282
+ const assistantChrono = [...buckets.values()].sort((a, b) => a.ms - b.ms);
283
+ compactBoundaries.sort((a, b) => a.ms - b.ms);
284
+ let coldResumeCount = 0;
285
+ let cacheRebuildTokens = 0;
286
+ const applyFlag = (bucket, flag) => {
287
+ for (const sib of bucket.siblings)
288
+ sib.coldResume = flag;
289
+ coldResumeCount++;
290
+ cacheRebuildTokens += flag.writeTokens;
291
+ };
292
+ const flagged = /* @__PURE__ */ new Set();
293
+ for (const b of compactBoundaries) {
294
+ const after = assistantChrono.find((a) => a.ms > b.ms && (a.rep.usage?.cacheWrite ?? 0) >= COLD_MIN_WRITE);
295
+ if (!after)
296
+ continue;
297
+ let prevMs;
298
+ for (let i = assistantChrono.length - 1; i >= 0; i--) {
299
+ if (assistantChrono[i].ms <= b.ms) {
300
+ prevMs = assistantChrono[i].ms;
301
+ break;
302
+ }
303
+ }
304
+ const u = after.rep.usage;
305
+ const denom = u.cacheWrite + u.cacheRead;
306
+ applyFlag(after, {
307
+ trigger: "compact",
308
+ gapMs: prevMs !== void 0 ? after.ms - prevMs : 0,
309
+ writeTokens: u.cacheWrite,
310
+ writeRatio: denom > 0 ? u.cacheWrite / denom : 0,
311
+ compact: { trigger: b.trigger, preTokens: b.preTokens }
312
+ });
313
+ flagged.add(after.rep.messageId);
314
+ }
315
+ for (let i = 1; i < assistantChrono.length; i++) {
316
+ const prev = assistantChrono[i - 1];
317
+ const cur = assistantChrono[i];
318
+ if (flagged.has(cur.rep.messageId))
319
+ continue;
320
+ const u = cur.rep.usage;
321
+ const denom = u.cacheWrite + u.cacheRead;
322
+ const writeRatio = denom > 0 ? u.cacheWrite / denom : 0;
323
+ const gapMs = cur.ms - prev.ms;
324
+ if (gapMs > COLD_GAP_MS && u.cacheWrite >= COLD_MIN_WRITE && writeRatio >= COLD_MIN_RATIO) {
325
+ applyFlag(cur, {
326
+ trigger: "idle",
327
+ gapMs,
328
+ writeTokens: u.cacheWrite,
329
+ writeRatio
330
+ });
331
+ }
332
+ }
248
333
  const totalUsage = { ...BLANK_USAGE };
249
334
  const seenMessageIds = /* @__PURE__ */ new Set();
250
335
  let model;
@@ -368,6 +453,8 @@ function parseTranscript(rawLines) {
368
453
  linesAdded,
369
454
  linesRemoved,
370
455
  filesEdited: filesEdited.size,
456
+ coldResumeCount,
457
+ cacheRebuildTokens,
371
458
  teamName,
372
459
  agentName,
373
460
  isTeamLead: teamName !== void 0 && agentName === void 0 && (hasTeamCreate || hasOutboundDispatch)
@@ -706,16 +793,18 @@ function findWorktreeMarker(projectName) {
706
793
  return null;
707
794
  }
708
795
  function canonicalProjectName(projectName) {
709
- const hit = findWorktreeMarker(projectName);
796
+ const normalized = projectName.replace(/\/{2,}/g, "/");
797
+ const hit = findWorktreeMarker(normalized);
710
798
  if (hit)
711
- return projectName.slice(0, hit.idx);
712
- return projectName;
799
+ return normalized.slice(0, hit.idx);
800
+ return normalized;
713
801
  }
714
802
  function worktreeName(projectName) {
715
- const hit = findWorktreeMarker(projectName);
803
+ const normalized = projectName.replace(/\/{2,}/g, "/");
804
+ const hit = findWorktreeMarker(normalized);
716
805
  if (!hit)
717
806
  return null;
718
- const tail = projectName.slice(hit.idx + hit.marker.length);
807
+ const tail = normalized.slice(hit.idx + hit.marker.length);
719
808
  const slash = tail.indexOf("/");
720
809
  return slash >= 0 ? tail.slice(0, slash) : tail;
721
810
  }
@@ -1178,690 +1267,290 @@ var init_analytics = __esm({
1178
1267
  "../parser/dist/analytics.js"() {
1179
1268
  "use strict";
1180
1269
  BLANK_USAGE2 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
1181
- WORKTREE_MARKERS = ["/.claude/worktrees/", "/.worktrees/"];
1270
+ WORKTREE_MARKERS = [
1271
+ "/.claude/worktrees/",
1272
+ "/.worktrees/",
1273
+ "/claude/worktrees/"
1274
+ ];
1182
1275
  PR_CREATE_RE = /gh\s+pr\s+create\b/i;
1183
1276
  PR_NUMBER_RE = /#(\d+)/;
1184
1277
  TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
1185
1278
  }
1186
1279
  });
1187
1280
 
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";
1281
+ // ../parser/dist/aggregate.js
1282
+ function isoDay(d) {
1283
+ return toLocalDay(d.getTime());
1194
1284
  }
1195
- function minimizeImages(s) {
1196
- if (!s)
1197
- return s;
1198
- let counter = 0;
1199
- return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
1285
+ function rangeDays(start, end) {
1286
+ const out = [];
1287
+ const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
1288
+ const stop = new Date(end.getFullYear(), end.getMonth(), end.getDate());
1289
+ while (cur.getTime() <= stop.getTime()) {
1290
+ out.push(new Date(cur));
1291
+ cur.setDate(cur.getDate() + 1);
1292
+ }
1293
+ return out;
1200
1294
  }
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;
1295
+ function calendarWeek(ref = /* @__PURE__ */ new Date()) {
1296
+ const d = new Date(ref.getFullYear(), ref.getMonth(), ref.getDate());
1297
+ const dow = d.getDay();
1298
+ const daysSinceMon = (dow + 6) % 7;
1299
+ const monday = new Date(d);
1300
+ monday.setDate(d.getDate() - daysSinceMon);
1301
+ const sunday = new Date(monday);
1302
+ sunday.setDate(monday.getDate() + 6);
1303
+ return { start: monday, end: sunday };
1207
1304
  }
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;
1305
+ function priorCalendarWeek(ref = /* @__PURE__ */ new Date()) {
1306
+ const cur = calendarWeek(ref);
1307
+ const prevEnd = new Date(cur.start);
1308
+ prevEnd.setDate(cur.start.getDate() - 1);
1309
+ const prevStart = new Date(prevEnd);
1310
+ prevStart.setDate(prevEnd.getDate() - 6);
1311
+ return { start: prevStart, end: prevEnd };
1214
1312
  }
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)];
1313
+ function last4CompletedWeeks(ref = /* @__PURE__ */ new Date()) {
1314
+ const { end } = priorCalendarWeek(ref);
1315
+ const start = new Date(end);
1316
+ start.setDate(end.getDate() - 27);
1317
+ return { start, end };
1220
1318
  }
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();
1319
+ function calendarMonth(ref = /* @__PURE__ */ new Date()) {
1320
+ const start = new Date(ref.getFullYear(), ref.getMonth(), 1);
1321
+ const end = new Date(ref.getFullYear(), ref.getMonth() + 1, 0);
1322
+ return { start, end };
1323
+ }
1324
+ function priorCalendarMonth(ref = /* @__PURE__ */ new Date()) {
1325
+ const start = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
1326
+ const end = new Date(ref.getFullYear(), ref.getMonth(), 0);
1327
+ return { start, end };
1328
+ }
1329
+ function aggregateConcurrency(bursts, period) {
1330
+ const days = rangeDays(period.start, period.end);
1331
+ const byDayKey = /* @__PURE__ */ new Map();
1332
+ for (const d of days)
1333
+ byDayKey.set(isoDay(d), { peak: 0, has_cross_project: false });
1334
+ for (const b of bursts) {
1335
+ const dayKey = isoDay(new Date(b.startMs));
1336
+ const cur = byDayKey.get(dayKey);
1337
+ if (!cur)
1338
+ continue;
1339
+ if (b.peak > cur.peak)
1340
+ cur.peak = b.peak;
1341
+ if (b.crossProject)
1342
+ cur.has_cross_project = true;
1229
1343
  }
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];
1344
+ const by_day = days.map((d) => {
1345
+ const k = isoDay(d);
1346
+ const v = byDayKey.get(k);
1347
+ return { date: k, peak: v.peak, has_cross_project: v.has_cross_project };
1348
+ });
1349
+ let peak = 0;
1350
+ let peak_day;
1351
+ let multi_agent_days = 0;
1352
+ let cross_project_days = 0;
1353
+ for (const d of by_day) {
1354
+ if (d.peak > peak) {
1355
+ peak = d.peak;
1356
+ peak_day = d.date;
1357
+ }
1358
+ if (d.peak >= 3)
1359
+ multi_agent_days++;
1360
+ if (d.has_cross_project)
1361
+ cross_project_days++;
1241
1362
  }
1242
- if (first.includes("/") && !first.startsWith("./")) {
1243
- first = first.replace(/\/+$/, "").split("/").pop();
1363
+ return { by_day, peak, peak_day, multi_agent_days, cross_project_days };
1364
+ }
1365
+ var init_aggregate = __esm({
1366
+ "../parser/dist/aggregate.js"() {
1367
+ "use strict";
1368
+ init_analytics();
1244
1369
  }
1245
- return first.replace(/^["']|["']$/g, "") || "?";
1370
+ });
1371
+
1372
+ // ../parser/dist/calibration.js
1373
+ function modelFamily(modelStr) {
1374
+ const m = (modelStr ?? "").toLowerCase();
1375
+ if (!m || m === "<synthetic>")
1376
+ return null;
1377
+ if (m.includes("haiku"))
1378
+ return "haiku";
1379
+ if (m.includes("opus"))
1380
+ return "opus";
1381
+ if (m.includes("sonnet"))
1382
+ return "sonnet";
1383
+ return null;
1246
1384
  }
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
- };
1385
+ function eventDollars(ev) {
1386
+ const p = PRICES[ev.family];
1387
+ return (ev.input * p.input + ev.output * p.output + ev.cacheRead * p.cacheRead + ev.cache_1h * p.cache_1h + ev.cache_5m * p.cache_5m) / 1e6;
1271
1388
  }
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;
1389
+ function dollarsInWindow(events, startMs, endMs) {
1390
+ let total = 0;
1391
+ for (const ev of events) {
1392
+ const t = Date.parse(ev.ts);
1393
+ if (t < startMs)
1394
+ continue;
1395
+ if (t >= endMs)
1396
+ break;
1397
+ total += eventDollars(ev);
1279
1398
  }
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
- });
1399
+ return total;
1301
1400
  }
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 ?? "");
1401
+ function predictUtilization(events, startMs, endMs, ratePerPct) {
1402
+ if (ratePerPct <= 0)
1403
+ return 0;
1404
+ return dollarsInWindow(events, startMs, endMs) / ratePerPct;
1405
+ }
1406
+ function buildSpendIndex(events) {
1407
+ const sorted = events.map((e) => ({ e, t: Date.parse(e.ts) })).filter((x) => !Number.isNaN(x.t)).sort((a, b) => a.t - b.t);
1408
+ const ts = new Float64Array(sorted.length);
1409
+ const cum = new Float64Array(sorted.length + 1);
1410
+ let acc = 0;
1411
+ for (let i = 0; i < sorted.length; i++) {
1412
+ ts[i] = sorted[i].t;
1413
+ cum[i] = acc;
1414
+ acc += eventDollars(sorted[i].e);
1415
+ }
1416
+ cum[sorted.length] = acc;
1417
+ return { ts, cum };
1418
+ }
1419
+ function lowerBound(arr, target, hi) {
1420
+ let lo = 0;
1421
+ while (lo < hi) {
1422
+ const mid = lo + hi >>> 1;
1423
+ if (arr[mid] < target)
1424
+ lo = mid + 1;
1425
+ else
1426
+ hi = mid;
1427
+ }
1428
+ return lo;
1429
+ }
1430
+ function dollarsInRange(idx, startMs, endMs) {
1431
+ if (endMs <= startMs)
1432
+ return 0;
1433
+ const n = idx.ts.length;
1434
+ const lo = lowerBound(idx.ts, startMs, n);
1435
+ const hi = lowerBound(idx.ts, endMs, n);
1436
+ return idx.cum[hi] - idx.cum[lo];
1437
+ }
1438
+ function groupSnapsByCycle(snapshots, pick) {
1439
+ const out = /* @__PURE__ */ new Map();
1440
+ for (const s of snapshots) {
1441
+ if (!s.captured_at)
1442
+ continue;
1443
+ const w = pick(s);
1444
+ if (!w?.resets_at || typeof w.utilization !== "number")
1445
+ continue;
1446
+ const ts = Date.parse(s.captured_at);
1447
+ const reset = Date.parse(w.resets_at);
1448
+ if (Number.isNaN(ts) || Number.isNaN(reset))
1449
+ continue;
1450
+ const k = Math.round(reset / HOUR_MS) * HOUR_MS;
1451
+ const arr = out.get(k) ?? [];
1452
+ arr.push({ ts, pct: w.utilization });
1453
+ out.set(k, arr);
1454
+ }
1455
+ for (const arr of out.values())
1456
+ arr.sort((a, b) => a.ts - b.ts);
1457
+ return out;
1458
+ }
1459
+ function collectSnapPairRates(snapsByCycle, spend, opts) {
1460
+ const minTravel = opts?.minTravelPct ?? 1;
1461
+ const minDollars = opts?.minDollars ?? 1;
1462
+ const maxGap = opts?.maxGapMs ?? 24 * HOUR_MS;
1463
+ const out = [];
1464
+ for (const [cycleEnd, arr] of snapsByCycle) {
1465
+ for (let i = 1; i < arr.length; i++) {
1466
+ const a = arr[i - 1];
1467
+ const b = arr[i];
1468
+ const gap = b.ts - a.ts;
1469
+ const travel = b.pct - a.pct;
1470
+ if (gap <= 0 || gap > maxGap)
1471
+ continue;
1472
+ if (travel < minTravel)
1473
+ continue;
1474
+ const dollars = dollarsInRange(spend, a.ts, b.ts);
1475
+ if (dollars < minDollars)
1476
+ continue;
1477
+ out.push({ cycleEndMs: cycleEnd, rate: dollars / travel, travel, dollars, gapMs: gap });
1308
1478
  }
1309
1479
  }
1310
- return "";
1480
+ return out;
1311
1481
  }
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;
1482
+ function userSoloRate(pairs, percentile = 0.9) {
1483
+ if (pairs.length < 3)
1484
+ return null;
1485
+ const sorted = pairs.map((p) => p.rate).sort((a, b) => a - b);
1486
+ const idx = Math.min(Math.floor(sorted.length * percentile), sorted.length - 1);
1487
+ return sorted[idx];
1488
+ }
1489
+ function predictAnchored(spend, cycleSnaps, forwardRate, cycleEndMs, cycleHours, t) {
1490
+ const cycleStart = cycleEndMs - cycleHours * HOUR_MS;
1491
+ if (forwardRate <= 0)
1492
+ forwardRate = 1;
1493
+ if (cycleSnaps.length === 0) {
1494
+ return Math.max(0, dollarsInRange(spend, cycleStart, t) / forwardRate);
1495
+ }
1496
+ let aIdx = -1;
1497
+ for (let i = 0; i < cycleSnaps.length; i++) {
1498
+ if (cycleSnaps[i].ts <= t)
1499
+ aIdx = i;
1500
+ else
1501
+ break;
1502
+ }
1503
+ if (aIdx >= 0 && aIdx + 1 < cycleSnaps.length) {
1504
+ const a = cycleSnaps[aIdx];
1505
+ const b = cycleSnaps[aIdx + 1];
1506
+ if (b.pct === a.pct)
1507
+ return a.pct;
1508
+ const totalDollars2 = dollarsInRange(spend, a.ts, b.ts);
1509
+ if (totalDollars2 <= 0) {
1510
+ const frac = (t - a.ts) / (b.ts - a.ts);
1511
+ return a.pct + (b.pct - a.pct) * frac;
1512
+ }
1513
+ const tDollars2 = dollarsInRange(spend, a.ts, t);
1514
+ return a.pct + (b.pct - a.pct) * (tDollars2 / totalDollars2);
1515
+ }
1516
+ if (aIdx >= 0) {
1517
+ const last = cycleSnaps[aIdx];
1518
+ return last.pct + dollarsInRange(spend, last.ts, t) / forwardRate;
1519
+ }
1520
+ const first = cycleSnaps[0];
1521
+ if (t <= cycleStart)
1522
+ return 0;
1523
+ const totalDollars = dollarsInRange(spend, cycleStart, first.ts);
1524
+ if (totalDollars <= 0) {
1525
+ const frac = (t - cycleStart) / (first.ts - cycleStart);
1526
+ return first.pct * frac;
1527
+ }
1528
+ const tDollars = dollarsInRange(spend, cycleStart, t);
1529
+ return first.pct * (tDollars / totalDollars);
1317
1530
  }
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"() {
1531
+ var PRICES, RATE_PER_PCT_7D, RATE_PER_PCT_5H, RATE_PER_PCT, HOUR_MS;
1532
+ var init_calibration = __esm({
1533
+ "../parser/dist/calibration.js"() {
1622
1534
  "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
1535
+ PRICES = {
1536
+ sonnet: { input: 3, output: 15, cacheRead: 0.3, cache_1h: 3.75, cache_5m: 3.75 },
1537
+ opus: { input: 5, output: 25, cacheRead: 0.5, cache_1h: 6.25, cache_5m: 6.25 },
1538
+ haiku: { input: 1, output: 5, cacheRead: 0.1, cache_1h: 1.25, cache_5m: 1.25 }
1745
1539
  };
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
1540
+ RATE_PER_PCT_7D = {
1541
+ "pro": 1.2,
1542
+ "pro-max": 2.4,
1543
+ "pro-max-20x": 12,
1544
+ "custom": 12
1761
1545
  };
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
1546
+ RATE_PER_PCT_5H = {
1547
+ "pro": 0.2,
1548
+ "pro-max": 0.4,
1549
+ "pro-max-20x": 2,
1550
+ "custom": 2
1768
1551
  };
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"];
1552
+ RATE_PER_PCT = RATE_PER_PCT_7D;
1553
+ HOUR_MS = 36e5;
1865
1554
  }
1866
1555
  });
1867
1556
 
@@ -1987,15 +1676,19 @@ var init_team = __esm({
1987
1676
  // ../parser/dist/index.js
1988
1677
  var dist_exports = {};
1989
1678
  __export(dist_exports, {
1679
+ PRICES: () => PRICES,
1680
+ RATE_PER_PCT: () => RATE_PER_PCT,
1681
+ RATE_PER_PCT_5H: () => RATE_PER_PCT_5H,
1682
+ RATE_PER_PCT_7D: () => RATE_PER_PCT_7D,
1990
1683
  aggregateConcurrency: () => aggregateConcurrency,
1991
- buildCapsule: () => buildCapsule,
1992
1684
  buildGanttDay: () => buildGanttDay,
1993
1685
  buildMegaRows: () => buildMegaRows,
1994
- buildPeriodBundle: () => buildPeriodBundle,
1995
1686
  buildPresentation: () => buildPresentation,
1687
+ buildSpendIndex: () => buildSpendIndex,
1996
1688
  calendarMonth: () => calendarMonth,
1997
1689
  calendarWeek: () => calendarWeek,
1998
1690
  canonicalProjectName: () => canonicalProjectName,
1691
+ collectSnapPairRates: () => collectSnapPairRates,
1999
1692
  computeActiveSegments: () => computeActiveSegments,
2000
1693
  computeBurstsFromSessions: () => computeBurstsFromSessions,
2001
1694
  computeParallelism: () => computeParallelism,
@@ -2004,11 +1697,18 @@ __export(dist_exports, {
2004
1697
  daysBetween: () => daysBetween,
2005
1698
  detectParallelRuns: () => detectParallelRuns,
2006
1699
  detectPrMarkers: () => detectPrMarkers,
1700
+ dollarsInRange: () => dollarsInRange,
1701
+ dollarsInWindow: () => dollarsInWindow,
1702
+ eventDollars: () => eventDollars,
2007
1703
  groupByProject: () => groupByProject,
2008
1704
  groupByTeam: () => groupByTeam,
1705
+ groupSnapsByCycle: () => groupSnapsByCycle,
2009
1706
  highLevelMetrics: () => highLevelMetrics,
2010
1707
  last4CompletedWeeks: () => last4CompletedWeeks,
1708
+ modelFamily: () => modelFamily,
2011
1709
  parseTranscript: () => parseTranscript,
1710
+ predictAnchored: () => predictAnchored,
1711
+ predictUtilization: () => predictUtilization,
2012
1712
  priorCalendarMonth: () => priorCalendarMonth,
2013
1713
  priorCalendarWeek: () => priorCalendarWeek,
2014
1714
  rowPrimaryIndex: () => rowPrimaryIndex,
@@ -2016,6 +1716,7 @@ __export(dist_exports, {
2016
1716
  sessionDay: () => sessionDay,
2017
1717
  summarizeBursts: () => summarizeBursts,
2018
1718
  toLocalDay: () => toLocalDay,
1719
+ userSoloRate: () => userSoloRate,
2019
1720
  worktreeName: () => worktreeName
2020
1721
  });
2021
1722
  var init_dist = __esm({
@@ -2025,8 +1726,8 @@ var init_dist = __esm({
2025
1726
  init_parser();
2026
1727
  init_presentation();
2027
1728
  init_analytics();
2028
- init_capsule();
2029
1729
  init_aggregate();
1730
+ init_calibration();
2030
1731
  init_team();
2031
1732
  }
2032
1733
  });
@@ -2035,6 +1736,7 @@ var init_dist = __esm({
2035
1736
  var fs_exports = {};
2036
1737
  __export(fs_exports, {
2037
1738
  DEFAULT_ROOT: () => DEFAULT_ROOT,
1739
+ buildCalibrationCurve: () => buildCalibrationCurve,
2038
1740
  cacheStats: () => cacheStats,
2039
1741
  clearCaches: () => clearCaches,
2040
1742
  decodeProjectName: () => decodeProjectName,
@@ -2043,6 +1745,9 @@ __export(fs_exports, {
2043
1745
  invalidateFile: () => invalidateFile,
2044
1746
  listProjects: () => listProjects,
2045
1747
  listSessions: () => listSessions,
1748
+ loadCalibrationCurve: () => loadCalibrationCurve,
1749
+ loadCalibrationEvents: () => loadCalibrationEvents,
1750
+ loadCalibrationSnapshots: () => loadCalibrationSnapshots,
2046
1751
  loadTeamForSession: () => loadTeamForSession,
2047
1752
  loadUsageByDay: () => loadUsageByDay,
2048
1753
  readJsonlFile: () => readJsonlFile,
@@ -2564,27 +2269,232 @@ async function loadUsageByDay(start, end) {
2564
2269
  }
2565
2270
  return { by_day: out };
2566
2271
  }
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");
2272
+ async function loadCalibrationEvents(root = DEFAULT_ROOT) {
2273
+ const files = await walkJsonlFiles(root);
2274
+ const byId = /* @__PURE__ */ new Map();
2275
+ for (const f of files) {
2276
+ let lines;
2277
+ try {
2278
+ lines = await readJsonlFile(f.fullPath);
2279
+ } catch {
2280
+ continue;
2281
+ }
2282
+ for (const raw of lines) {
2283
+ if (!raw || typeof raw !== "object")
2284
+ continue;
2285
+ const r = raw;
2286
+ if (r.type !== "assistant")
2287
+ continue;
2288
+ const ts = typeof r.timestamp === "string" ? r.timestamp : void 0;
2289
+ if (!ts)
2290
+ continue;
2291
+ const m = r.message;
2292
+ if (!m)
2293
+ continue;
2294
+ const u = m.usage;
2295
+ if (!u)
2296
+ continue;
2297
+ const family = modelFamily(typeof m.model === "string" ? m.model : void 0);
2298
+ if (family === null)
2299
+ continue;
2300
+ const mid = typeof m.id === "string" ? m.id : `__noid_${f.fullPath}_${ts}`;
2301
+ const cc = u.cache_creation ?? {};
2302
+ const toNum = (v) => typeof v === "number" ? v : 0;
2303
+ byId.set(mid, {
2304
+ ts,
2305
+ family,
2306
+ input: toNum(u.input_tokens),
2307
+ output: toNum(u.output_tokens),
2308
+ cacheRead: toNum(u.cache_read_input_tokens),
2309
+ cache_1h: toNum(cc.ephemeral_1h_input_tokens),
2310
+ cache_5m: toNum(cc.ephemeral_5m_input_tokens)
2311
+ });
2312
+ }
2581
2313
  }
2582
- });
2314
+ const events = Array.from(byId.values());
2315
+ events.sort((a, b) => a.ts.localeCompare(b.ts));
2316
+ return events;
2317
+ }
2318
+ async function loadCalibrationSnapshots() {
2319
+ let raw;
2320
+ try {
2321
+ raw = await fs.readFile(USAGE_LOG, "utf8");
2322
+ } catch {
2323
+ return [];
2324
+ }
2325
+ const out = [];
2326
+ for (const line of raw.split("\n")) {
2327
+ const t = line.trim();
2328
+ if (!t)
2329
+ continue;
2330
+ try {
2331
+ const s = JSON.parse(t);
2332
+ if (s.captured_at)
2333
+ out.push(s);
2334
+ } catch {
2335
+ }
2336
+ }
2337
+ out.sort((a, b) => (a.captured_at ?? "").localeCompare(b.captured_at ?? ""));
2338
+ return out;
2339
+ }
2340
+ function inferResetsAt(ts, snapResets, cycleHours) {
2341
+ if (snapResets.length === 0)
2342
+ return null;
2343
+ for (const [snapMs, resetMs] of snapResets) {
2344
+ if (snapMs >= ts && resetMs > ts) {
2345
+ let r = resetMs;
2346
+ const cycleMs2 = cycleHours * 36e5;
2347
+ while (r - cycleMs2 > ts)
2348
+ r -= cycleMs2;
2349
+ return r;
2350
+ }
2351
+ }
2352
+ let last = snapResets[snapResets.length - 1][1];
2353
+ const cycleMs = cycleHours * 36e5;
2354
+ while (last < ts)
2355
+ last += cycleMs;
2356
+ return last;
2357
+ }
2358
+ function buildCalibrationCurve(events, snapshots, tier = "pro-max-20x", granularityMin = 30) {
2359
+ if (snapshots.length === 0 || events.length === 0)
2360
+ return null;
2361
+ const spend = buildSpendIndex(events);
2362
+ const tierDefault7d = RATE_PER_PCT_7D[tier];
2363
+ const tierDefault5h = RATE_PER_PCT_5H[tier];
2364
+ const snapsByCycle5h = groupSnapsByCycle(snapshots, (s) => s.five_hour);
2365
+ const snapsByCycle7d = groupSnapsByCycle(snapshots, (s) => s.seven_day);
2366
+ const pairs7d = collectSnapPairRates(snapsByCycle7d, spend, {
2367
+ minTravelPct: 1,
2368
+ minDollars: 10,
2369
+ maxGapMs: 24 * 36e5
2370
+ });
2371
+ const pairs5h = collectSnapPairRates(snapsByCycle5h, spend, {
2372
+ minTravelPct: 1,
2373
+ minDollars: 1,
2374
+ maxGapMs: 2 * 36e5
2375
+ });
2376
+ const fwdRate7d = userSoloRate(pairs7d, 0.9) ?? tierDefault7d;
2377
+ const fwdRate5h = userSoloRate(pairs5h, 0.9) ?? tierDefault5h;
2378
+ const cycles7d = new Set(pairs7d.map((p) => p.cycleEndMs)).size;
2379
+ const cycles5h = new Set(pairs5h.map((p) => p.cycleEndMs)).size;
2380
+ const snap5h = [];
2381
+ const snap7d = [];
2382
+ for (const s of snapshots) {
2383
+ const ms = Date.parse(s.captured_at);
2384
+ if (Number.isNaN(ms))
2385
+ continue;
2386
+ if (s.five_hour?.resets_at) {
2387
+ const r = Date.parse(s.five_hour.resets_at);
2388
+ if (!Number.isNaN(r))
2389
+ snap5h.push([ms, r]);
2390
+ }
2391
+ if (s.seven_day?.resets_at) {
2392
+ const r = Date.parse(s.seven_day.resets_at);
2393
+ if (!Number.isNaN(r))
2394
+ snap7d.push([ms, r]);
2395
+ }
2396
+ }
2397
+ const HOUR = 36e5;
2398
+ const realByMinute = /* @__PURE__ */ new Map();
2399
+ for (const s of snapshots) {
2400
+ const ms = Date.parse(s.captured_at);
2401
+ if (Number.isNaN(ms))
2402
+ continue;
2403
+ const k = Math.floor(ms / 6e4);
2404
+ realByMinute.set(k, [
2405
+ s.five_hour?.utilization ?? null,
2406
+ s.seven_day?.utilization ?? null
2407
+ ]);
2408
+ }
2409
+ const firstSnapMs = Date.parse(snapshots[0].captured_at);
2410
+ const lastSnapMs = Date.parse(snapshots[snapshots.length - 1].captured_at);
2411
+ const firstEventMs = Date.parse(events[0].ts);
2412
+ const rangeStart = Math.max(firstEventMs, firstSnapMs - 14 * 864e5);
2413
+ const rangeEnd = lastSnapMs;
2414
+ const stepMs = granularityMin * 6e4;
2415
+ const curve = [];
2416
+ let cur = rangeStart;
2417
+ while (cur <= rangeEnd) {
2418
+ let real5 = null;
2419
+ let real7 = null;
2420
+ for (let off = 0; off < granularityMin; off++) {
2421
+ const k = Math.floor((cur + off * 6e4) / 6e4);
2422
+ const r = realByMinute.get(k);
2423
+ if (r) {
2424
+ real5 = r[0];
2425
+ real7 = r[1];
2426
+ break;
2427
+ }
2428
+ }
2429
+ const r5 = inferResetsAt(cur, snap5h, 5);
2430
+ const r7 = inferResetsAt(cur, snap7d, 168);
2431
+ const r5k = r5 != null ? Math.round(r5 / HOUR) * HOUR : null;
2432
+ const r7k = r7 != null ? Math.round(r7 / HOUR) * HOUR : null;
2433
+ const cycle5Snaps = r5k != null ? snapsByCycle5h.get(r5k) ?? [] : [];
2434
+ const cycle7Snaps = r7k != null ? snapsByCycle7d.get(r7k) ?? [] : [];
2435
+ const cycleEnd5 = r5k ?? cur + 5 * HOUR;
2436
+ const cycleEnd7 = r7k ?? cur + 168 * HOUR;
2437
+ const p5 = predictAnchored(spend, cycle5Snaps, fwdRate5h, cycleEnd5, 5, cur);
2438
+ const p7 = predictAnchored(spend, cycle7Snaps, fwdRate7d, cycleEnd7, 168, cur);
2439
+ curve.push({
2440
+ ts: new Date(cur).toISOString(),
2441
+ real_5h: real5,
2442
+ pred_5h: Math.max(0, Math.min(200, p5)),
2443
+ real_7d: real7,
2444
+ pred_7d: Math.max(0, Math.min(200, p7)),
2445
+ cycle_end_5h: r5k != null ? new Date(r5k).toISOString() : null,
2446
+ cycle_end_7d: r7k != null ? new Date(r7k).toISOString() : null
2447
+ });
2448
+ cur += stepMs;
2449
+ }
2450
+ const rateSource7d = pairs7d.length >= 3 ? "user_calibrated" : "tier_default";
2451
+ const rateSource5h = pairs5h.length >= 3 ? "user_calibrated" : "tier_default";
2452
+ return {
2453
+ model: `anchored-${tier}`,
2454
+ tier,
2455
+ rate_per_pct: fwdRate7d,
2456
+ rate_per_pct_5h: fwdRate5h,
2457
+ rate_per_pct_7d: fwdRate7d,
2458
+ rate_source_5h: rateSource5h,
2459
+ rate_source_7d: rateSource7d,
2460
+ cycles_used_5h: cycles5h,
2461
+ cycles_used_7d: cycles7d,
2462
+ granularity_min: granularityMin,
2463
+ curve,
2464
+ first_snapshot_ts: snapshots[0].captured_at ?? null,
2465
+ real_count: curve.filter((c) => c.real_7d !== null).length,
2466
+ total_count: curve.length
2467
+ };
2468
+ }
2469
+ async function loadCalibrationCurve(tier = "pro-max-20x", root = DEFAULT_ROOT, granularityMin = 30) {
2470
+ const [events, snapshots] = await Promise.all([
2471
+ loadCalibrationEvents(root),
2472
+ loadCalibrationSnapshots()
2473
+ ]);
2474
+ return buildCalibrationCurve(events, snapshots, tier, granularityMin);
2475
+ }
2476
+ var DEFAULT_ROOT, metaCache, detailCache, fileListCache, FILE_LIST_TTL_MS, BLANK_USAGE3, USAGE_LOG;
2477
+ var init_fs = __esm({
2478
+ "../parser/dist/fs.js"() {
2479
+ "use strict";
2480
+ init_parser();
2481
+ init_analytics();
2482
+ init_team();
2483
+ init_calibration();
2484
+ DEFAULT_ROOT = path.join(os.homedir(), ".claude", "projects");
2485
+ metaCache = /* @__PURE__ */ new Map();
2486
+ detailCache = /* @__PURE__ */ new Map();
2487
+ fileListCache = null;
2488
+ FILE_LIST_TTL_MS = 1e3;
2489
+ BLANK_USAGE3 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
2490
+ USAGE_LOG = path.join(os.homedir(), ".cclens", "usage.jsonl");
2491
+ }
2492
+ });
2583
2493
 
2584
2494
  // src/daemon-worker.ts
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";
2495
+ import { appendFileSync as appendFileSync3, mkdirSync as mkdirSync7 } from "node:fs";
2496
+ import { dirname as dirname6, join as join8 } from "node:path";
2497
+ import { homedir as homedir8 } from "node:os";
2588
2498
 
2589
2499
  // src/usage/token.ts
2590
2500
  import { execFileSync } from "node:child_process";
@@ -2729,6 +2639,23 @@ function appendSnapshot(filePath, snapshot) {
2729
2639
  mkdirSync(dirname(filePath), { recursive: true });
2730
2640
  appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
2731
2641
  }
2642
+ function readSnapshots(filePath) {
2643
+ if (!existsSync(filePath)) return [];
2644
+ const content = readFileSync2(filePath, "utf8");
2645
+ const snapshots = [];
2646
+ for (const line of content.split("\n")) {
2647
+ if (!line.trim()) continue;
2648
+ try {
2649
+ snapshots.push(JSON.parse(line));
2650
+ } catch {
2651
+ }
2652
+ }
2653
+ return snapshots;
2654
+ }
2655
+ function latestSnapshot(filePath) {
2656
+ const all = readSnapshots(filePath);
2657
+ return all.length > 0 ? all[all.length - 1] : null;
2658
+ }
2732
2659
 
2733
2660
  // src/usage/backoff.ts
2734
2661
  var BASE_INTERVAL_MS = 5 * 60 * 1e3;
@@ -2739,6 +2666,10 @@ function nextIntervalMs(current, outcome) {
2739
2666
  return Math.min(Math.max(current, BASE_INTERVAL_MS) * 2, MAX_INTERVAL_MS);
2740
2667
  }
2741
2668
 
2669
+ // src/team/sync.ts
2670
+ import { homedir as homedir4 } from "node:os";
2671
+ import { join as join4 } from "node:path";
2672
+
2742
2673
  // src/team/config.ts
2743
2674
  import { readFileSync as readFileSync3, writeFileSync, unlinkSync, mkdirSync as mkdirSync2 } from "node:fs";
2744
2675
  import { join as join2 } from "node:path";
@@ -2760,6 +2691,30 @@ function writeTeamConfig(config, dir = DEFAULT_DIR) {
2760
2691
  // src/team/push.ts
2761
2692
  init_dist();
2762
2693
  import { randomUUID } from "node:crypto";
2694
+ var SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
2695
+ function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
2696
+ const raw = latestSnapshot(filePath);
2697
+ if (!raw) return null;
2698
+ const capturedMs = Date.parse(raw.captured_at);
2699
+ if (Number.isNaN(capturedMs)) return null;
2700
+ if (nowMs - capturedMs > SNAPSHOT_FRESHNESS_MS) return null;
2701
+ const toWire = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
2702
+ return {
2703
+ capturedAt: raw.captured_at,
2704
+ fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
2705
+ sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
2706
+ sevenDayOpus: toWire(raw.seven_day_opus),
2707
+ sevenDaySonnet: toWire(raw.seven_day_sonnet),
2708
+ sevenDayOauthApps: toWire(raw.seven_day_oauth_apps),
2709
+ sevenDayCowork: toWire(raw.seven_day_cowork),
2710
+ extraUsage: raw.extra_usage ? {
2711
+ isEnabled: raw.extra_usage.is_enabled,
2712
+ monthlyLimitUsd: raw.extra_usage.monthly_limit,
2713
+ usedCreditsUsd: raw.extra_usage.used_credits,
2714
+ utilization: raw.extra_usage.utilization
2715
+ } : null
2716
+ };
2717
+ }
2763
2718
  function bucketToRollup(b) {
2764
2719
  return {
2765
2720
  day: b.date,
@@ -2782,11 +2737,14 @@ function buildRollupsForRange(sessions, sinceDay) {
2782
2737
  sessions: startCounts.get(b.date) ?? 0
2783
2738
  }));
2784
2739
  }
2785
- function buildIngestPayload(rollup) {
2740
+ function buildIngestPayload(rollup, usageSnapshot, planTier, cyclePeaks) {
2786
2741
  return {
2787
2742
  ingestId: randomUUID(),
2788
2743
  observedAt: (/* @__PURE__ */ new Date()).toISOString(),
2789
- dailyRollup: rollup
2744
+ ...rollup ? { dailyRollup: rollup } : {},
2745
+ ...usageSnapshot ? { usageSnapshot } : {},
2746
+ ...planTier ? { planTier } : {},
2747
+ ...cyclePeaks ? { cyclePeaks } : {}
2790
2748
  };
2791
2749
  }
2792
2750
  async function pushToTeamServer(config, payload) {
@@ -2853,30 +2811,141 @@ function pruneOldest(queuePath) {
2853
2811
  writeFileSync2(queuePath, keep.join("\n") + "\n", "utf8");
2854
2812
  }
2855
2813
 
2814
+ // src/usage/profile.ts
2815
+ import { existsSync as existsSync2, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
2816
+ import { dirname as dirname3 } from "node:path";
2817
+ var PROFILE_ENDPOINT = "https://api.anthropic.com/api/oauth/profile";
2818
+ var BETA_HEADER2 = "oauth-2025-04-20";
2819
+ var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
2820
+ function mapRateLimitTier(rateLimitTier) {
2821
+ switch (rateLimitTier) {
2822
+ case "default_claude_pro":
2823
+ return "pro";
2824
+ case "default_claude_max":
2825
+ case "default_claude_max_5x":
2826
+ return "pro-max";
2827
+ case "default_claude_max_20x":
2828
+ return "pro-max-20x";
2829
+ default:
2830
+ return "custom";
2831
+ }
2832
+ }
2833
+ async function fetchProfile(nowMs = Date.now()) {
2834
+ const creds = readOAuthCredentials();
2835
+ if (!creds) return null;
2836
+ if (creds.expiresAt - 6e4 <= nowMs) return null;
2837
+ let res;
2838
+ try {
2839
+ res = await fetch(PROFILE_ENDPOINT, {
2840
+ headers: {
2841
+ Authorization: `Bearer ${creds.accessToken}`,
2842
+ "anthropic-beta": BETA_HEADER2
2843
+ }
2844
+ });
2845
+ } catch {
2846
+ return null;
2847
+ }
2848
+ if (!res.ok) return null;
2849
+ const body = await res.json().catch(() => null);
2850
+ if (!body) return null;
2851
+ const rateLimitTier = body.organization?.rate_limit_tier ?? null;
2852
+ return {
2853
+ planTier: mapRateLimitTier(rateLimitTier),
2854
+ rateLimitTier,
2855
+ organizationType: body.organization?.organization_type ?? null
2856
+ };
2857
+ }
2858
+ function readCachedProfile(cachePath) {
2859
+ if (!existsSync2(cachePath)) return null;
2860
+ try {
2861
+ const raw = JSON.parse(readFileSync5(cachePath, "utf8"));
2862
+ if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
2863
+ return raw;
2864
+ } catch {
2865
+ return null;
2866
+ }
2867
+ }
2868
+ function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
2869
+ mkdirSync4(dirname3(cachePath), { recursive: true });
2870
+ writeFileSync3(
2871
+ cachePath,
2872
+ JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
2873
+ { mode: 384 }
2874
+ );
2875
+ }
2876
+ async function getPlanTier(cachePath, nowMs = Date.now(), fetcher = fetchProfile) {
2877
+ const cached = readCachedProfile(cachePath);
2878
+ if (cached && nowMs - cached.fetchedAtMs < PROFILE_CACHE_TTL_MS) {
2879
+ return cached.profile.planTier;
2880
+ }
2881
+ const fresh = await fetcher(nowMs);
2882
+ if (!fresh) {
2883
+ return cached?.profile.planTier ?? null;
2884
+ }
2885
+ writeCachedProfile(cachePath, fresh, nowMs);
2886
+ return fresh.planTier;
2887
+ }
2888
+
2856
2889
  // src/team/sync.ts
2890
+ var USAGE_LOG2 = join4(homedir4(), ".cclens", "usage.jsonl");
2891
+ var PROFILE_CACHE = join4(homedir4(), ".cclens", "profile.json");
2857
2892
  var noopLog = () => {
2858
2893
  };
2859
- async function runTeamSync(log2 = noopLog) {
2860
- const config = readTeamConfig();
2894
+ async function runTeamSync(log3 = noopLog, configOverride) {
2895
+ const config = configOverride === void 0 ? readTeamConfig() : configOverride;
2861
2896
  if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
2862
2897
  try {
2863
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
2898
+ const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
2864
2899
  const { toLocalDay: toLocalDay2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
2865
2900
  const today = toLocalDay2(Date.now());
2866
2901
  const sessions = await listSessions2({ limit: 1e4 });
2867
2902
  const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
2903
+ const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
2904
+ const planTier = await getPlanTier(PROFILE_CACHE).catch(() => null) ?? void 0;
2905
+ const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
2868
2906
  if (rollups.length === 0) {
2869
- log2("info", "team push: nothing to sync");
2870
- return { paired: true, pushed: 0, queued: 0, queuedDrained: 0 };
2907
+ const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
2908
+ if (!hasLiveData) {
2909
+ log3("info", "team push: nothing to sync");
2910
+ return { paired: true, pushed: 0, queued: 0, queuedDrained: 0 };
2911
+ }
2912
+ const payload = buildIngestPayload(void 0, usageSnapshot, planTier, cyclePeaks);
2913
+ const result = await pushToTeamServer(config, payload);
2914
+ if (!result.ok) {
2915
+ log3("warn", `team push (live-only) failed (${result.status}); queueing`);
2916
+ enqueuePayload(payload);
2917
+ return { paired: true, pushed: 0, queued: 1, queuedDrained: 0 };
2918
+ }
2919
+ let queuedDrained2 = 0;
2920
+ const backlog = dequeuePayloads();
2921
+ for (let i = 0; i < backlog.length; i++) {
2922
+ const qResult = await pushToTeamServer(config, backlog[i]);
2923
+ if (!qResult.ok) {
2924
+ for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
2925
+ break;
2926
+ }
2927
+ queuedDrained2++;
2928
+ }
2929
+ log3("info", `team push ok: live-only (no new daily activity)` + (queuedDrained2 ? `, ${queuedDrained2} queued retried` : ""));
2930
+ return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2 };
2871
2931
  }
2872
2932
  let pushed = 0;
2873
2933
  let queued = 0;
2874
2934
  let failedDay;
2875
- for (const rollup of rollups) {
2876
- const payload = buildIngestPayload(rollup);
2935
+ for (let i = 0; i < rollups.length; i++) {
2936
+ const rollup = rollups[i];
2937
+ const isLatest = i === rollups.length - 1;
2938
+ const payload = buildIngestPayload(
2939
+ rollup,
2940
+ isLatest ? usageSnapshot : void 0,
2941
+ planTier,
2942
+ // Same rationale as usageSnapshot — current cycle data only on the
2943
+ // latest rollup so older days don't get tagged with today's peaks.
2944
+ isLatest ? cyclePeaks : void 0
2945
+ );
2877
2946
  const result = await pushToTeamServer(config, payload);
2878
2947
  if (!result.ok) {
2879
- log2("warn", `team push failed on ${rollup.day} (${result.status}); queueing`);
2948
+ log3("warn", `team push failed on ${rollup.day} (${result.status}); queueing`);
2880
2949
  enqueuePayload(payload);
2881
2950
  queued++;
2882
2951
  failedDay = rollup.day;
@@ -2899,23 +2968,982 @@ async function runTeamSync(log2 = noopLog) {
2899
2968
  }
2900
2969
  }
2901
2970
  if (pushed > 0) {
2902
- log2("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
2971
+ log3("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
2903
2972
  }
2904
2973
  return { paired: true, pushed, queued, queuedDrained, failedDay };
2905
2974
  } catch (err) {
2906
2975
  const message = err.message;
2907
- log2("warn", `team push error: ${message}`);
2976
+ log3("warn", `team push error: ${message}`);
2908
2977
  return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
2909
2978
  }
2910
2979
  }
2980
+ async function buildCyclePeaksForPush(planTier, loadCurve) {
2981
+ const validTiers = ["pro", "pro-max", "pro-max-20x", "custom"];
2982
+ const tier = validTiers.find((t) => t === planTier) ?? "pro-max-20x";
2983
+ const dump = await loadCurve(tier).catch(() => null);
2984
+ if (!dump || dump.curve.length === 0) return void 0;
2985
+ const HOUR = 36e5;
2986
+ const nowMs = Date.now();
2987
+ const peaksFor = (cycleKey, realKey, predKey, maxCycles) => {
2988
+ const byCycle = /* @__PURE__ */ new Map();
2989
+ for (const p of dump.curve) {
2990
+ const k = p[cycleKey];
2991
+ if (!k) continue;
2992
+ const ms = Date.parse(k);
2993
+ if (Number.isNaN(ms)) continue;
2994
+ const bucket = Math.round(ms / HOUR) * HOUR;
2995
+ const arr = byCycle.get(bucket) ?? [];
2996
+ arr.push(p);
2997
+ byCycle.set(bucket, arr);
2998
+ }
2999
+ const out = [];
3000
+ for (const [endMs, points] of Array.from(byCycle.entries()).sort((a, b) => a[0] - b[0])) {
3001
+ let peak = 0;
3002
+ let source = "predicted";
3003
+ for (const p of points) {
3004
+ const r = p[realKey];
3005
+ if (typeof r === "number" && r > peak) {
3006
+ peak = r;
3007
+ source = "real";
3008
+ }
3009
+ const v = p[predKey] ?? 0;
3010
+ if (v > peak) {
3011
+ peak = v;
3012
+ source = "predicted";
3013
+ }
3014
+ }
3015
+ out.push({
3016
+ endsAt: new Date(endMs).toISOString(),
3017
+ peakPct: Math.round(peak * 10) / 10,
3018
+ source,
3019
+ current: endMs > nowMs
3020
+ });
3021
+ }
3022
+ return out.slice(-maxCycles);
3023
+ };
3024
+ return {
3025
+ fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
3026
+ sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 12)
3027
+ };
3028
+ }
3029
+
3030
+ // src/perception/worker.ts
3031
+ init_dist();
3032
+ import { statSync as statSync2, readFileSync as readFileSync8 } from "node:fs";
3033
+ import { basename, dirname as dirname5 } from "node:path";
3034
+
3035
+ // ../entries/dist/types.js
3036
+ var CURRENT_ENTRY_SCHEMA_VERSION = 2;
3037
+ function pendingEnrichment() {
3038
+ return {
3039
+ status: "pending",
3040
+ generated_at: null,
3041
+ model: null,
3042
+ cost_usd: null,
3043
+ error: null,
3044
+ brief_summary: null,
3045
+ underlying_goal: null,
3046
+ friction_detail: null,
3047
+ user_instructions: [],
3048
+ outcome: null,
3049
+ claude_helpfulness: null,
3050
+ goal_categories: {},
3051
+ retry_count: 0
3052
+ };
3053
+ }
3054
+ function skippedTrivialEnrichment(generatedAt) {
3055
+ return {
3056
+ status: "skipped_trivial",
3057
+ generated_at: generatedAt,
3058
+ model: null,
3059
+ cost_usd: null,
3060
+ error: null,
3061
+ brief_summary: null,
3062
+ underlying_goal: null,
3063
+ friction_detail: null,
3064
+ user_instructions: [],
3065
+ outcome: "trivial",
3066
+ claude_helpfulness: null,
3067
+ goal_categories: {},
3068
+ retry_count: 0
3069
+ };
3070
+ }
3071
+ function entryKey(sessionId, localDay) {
3072
+ return `${sessionId}__${localDay}`;
3073
+ }
3074
+
3075
+ // ../entries/dist/signals.js
3076
+ var TEAMMATE_RE = /^<teammate-message\b/;
3077
+ var SKILL_LOAD_RE = /^Base directory for this skill:/;
3078
+ var SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
3079
+ function classifyUserInputSource(text) {
3080
+ if (!text)
3081
+ return "human";
3082
+ if (TEAMMATE_RE.test(text))
3083
+ return "teammate";
3084
+ if (SKILL_LOAD_RE.test(text))
3085
+ return "skill_load";
3086
+ if (SLASH_COMMAND_RE2.test(text))
3087
+ return "slash_command";
3088
+ return "human";
3089
+ }
3090
+ var HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
3091
+ var SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
3092
+ var DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
3093
+ var FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
3094
+ function countSatisfactionSignals(text) {
3095
+ if (!text)
3096
+ return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
3097
+ return {
3098
+ happy: (text.match(HAPPY_RE) ?? []).length,
3099
+ satisfied: (text.match(SATISFIED_RE) ?? []).length,
3100
+ dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
3101
+ frustrated: (text.match(FRUSTRATED_RE) ?? []).length
3102
+ };
3103
+ }
3104
+ var INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
3105
+ function extractUserInstructions(text) {
3106
+ if (!text)
3107
+ return [];
3108
+ const out = [];
3109
+ const seen = /* @__PURE__ */ new Set();
3110
+ let m;
3111
+ while ((m = INSTRUCTION_RE.exec(text)) !== null) {
3112
+ const phrase = m[1].trim().replace(/\s+/g, " ");
3113
+ const lc = phrase.toLowerCase();
3114
+ if (seen.has(lc))
3115
+ continue;
3116
+ seen.add(lc);
3117
+ out.push(phrase);
3118
+ if (out.length >= 5)
3119
+ break;
3120
+ }
3121
+ return out;
3122
+ }
3123
+ function classifySubagentRole(sa) {
3124
+ const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
3125
+ if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
3126
+ return "reviewer";
3127
+ }
3128
+ if (/\b(implement|build chunk|build task|initialize|create the|add the)\b/.test(text)) {
3129
+ return "implementer";
3130
+ }
3131
+ if (/\bexplore\b|\binventory\b|\bmap\b.*\b(codebase|repo|branch)\b|\baudit\b.*coverage/.test(text)) {
3132
+ return "explorer";
3133
+ }
3134
+ if (/\b(investigate|research|reverse[- ]engineer|study|analy[sz]e|look up|tell me|brief me on|find out)\b/.test(text)) {
3135
+ return "researcher";
3136
+ }
3137
+ if (/\b(env(?:ironment)? setup|configure|setup|bootstrap)\b/.test(text)) {
3138
+ return "env-setup";
3139
+ }
3140
+ if (/\b(polish|cleanup|fix|refactor)\b/.test(text)) {
3141
+ return "polish";
3142
+ }
3143
+ return "other";
3144
+ }
3145
+ function inferWorkingShape(entry) {
3146
+ if (entry.numbers.turn_count < 2)
3147
+ return null;
3148
+ const subagents = entry.subagents ?? [];
3149
+ const roles = subagents.map(classifySubagentRole);
3150
+ const reviewerCount = roles.filter((r) => r === "reviewer").length;
3151
+ const implementerCount = roles.filter((r) => r === "implementer").length;
3152
+ const explorerOrResearcher = roles.filter((r) => r === "explorer" || r === "researcher").length;
3153
+ const hasBackground = subagents.some((sa) => sa.background);
3154
+ if (implementerCount >= 2) {
3155
+ const chunkRefs = subagents.filter((sa) => /\b(chunk|task)\s*\d+/i.test(`${sa.description} ${sa.prompt_preview}`));
3156
+ if (chunkRefs.length >= 2)
3157
+ return "chunk-implementation";
3158
+ }
3159
+ if (reviewerCount >= 3 && implementerCount === 0) {
3160
+ const lensSigs = /* @__PURE__ */ new Set();
3161
+ for (let i = 0; i < subagents.length; i++) {
3162
+ if (roles[i] !== "reviewer")
3163
+ continue;
3164
+ const sig = subagents[i].description.toLowerCase().replace(/[^a-z0-9 ]/g, "").split(/\s+/).filter((w) => w.length > 2).slice(0, 3).join("|");
3165
+ lensSigs.add(sig);
3166
+ }
3167
+ if (lensSigs.size >= 3)
3168
+ return "reviewer-triad";
3169
+ }
3170
+ if (reviewerCount >= 2)
3171
+ return "spec-review-loop";
3172
+ if (explorerOrResearcher >= 2 || explorerOrResearcher >= 1 && implementerCount >= 1) {
3173
+ return "research-then-build";
3174
+ }
3175
+ if (hasBackground && subagents.length >= 1)
3176
+ return "background-coordinated";
3177
+ if (subagents.length === 0) {
3178
+ const first = (entry.first_user || "").trim().toLowerCase();
3179
+ if (/^continue\b/.test(first) || first === "continue.")
3180
+ return "solo-continuation";
3181
+ const skills = Object.keys(entry.skills ?? {});
3182
+ if (skills.some((s) => /brainstorm|writing-plans|brainstorming/i.test(s)))
3183
+ return "solo-design";
3184
+ return "solo-build";
3185
+ }
3186
+ return "solo-build";
3187
+ }
3188
+ function detectPromptFrames(text) {
3189
+ if (!text)
3190
+ return [];
3191
+ const out = [];
3192
+ if (/<teammate-message\b/i.test(text))
3193
+ out.push("teammate");
3194
+ if (/<task-notification\b/i.test(text))
3195
+ out.push("task-notification");
3196
+ if (/<local-command-caveat\b/i.test(text))
3197
+ out.push("local-command-caveat");
3198
+ if (/<command-(message|name)\b/i.test(text))
3199
+ out.push("slash-command");
3200
+ if (/\[Image #\d+\]/.test(text))
3201
+ out.push("image-attached");
3202
+ if (/\b(here'?s the (handoff|handover) prompt|session-close summary|wrap-up summary|all wrapped up|all captured\.)\b/i.test(text) || /^##? (Wrap-up|Session conclusion|Handoff)\b/m.test(text) || /^# Handoff:/m.test(text)) {
3203
+ out.push("handoff-prose");
3204
+ }
3205
+ return out;
3206
+ }
3207
+ var EXTERNAL_REF_PATTERNS = [
3208
+ { kind: "linear-kip", re: /\bKIP-\d+\b/i },
3209
+ { kind: "github-issue-pr", re: /\b(issue|pr|pull request)\s*#\d+\b|github\.com\/[^\s]+\/(issues|pull)\/\d+/i },
3210
+ { kind: "branch-ref", re: /\b(branch|feat|fix|chore|refactor)[/:]\s*[\w./-]+|on `[\w./-]+`/i },
3211
+ { kind: "url", re: /https?:\/\/\S+/ }
3212
+ ];
3213
+ function detectExternalRef(text) {
3214
+ if (!text)
3215
+ return null;
3216
+ if (/<teammate-message|<task-notification|<local-command-caveat/i.test(text.slice(0, 400)))
3217
+ return null;
3218
+ for (const { kind, re } of EXTERNAL_REF_PATTERNS) {
3219
+ const m = text.match(re);
3220
+ if (m)
3221
+ return { kind, preview: text.slice(0, 160) };
3222
+ }
3223
+ return null;
3224
+ }
3225
+ function bucketVerbosity(n) {
3226
+ if (n < 100)
3227
+ return "short";
3228
+ if (n < 500)
3229
+ return "medium";
3230
+ if (n < 2e3)
3231
+ return "long";
3232
+ return "very_long";
3233
+ }
3234
+ function computeEntrySignals(entry) {
3235
+ const fu = entry.first_user || "";
3236
+ const verbosity = bucketVerbosity(fu.length);
3237
+ const ext = detectExternalRef(fu);
3238
+ const external_refs = ext ? [ext] : [];
3239
+ const skillNames = Object.keys(entry.skills ?? {});
3240
+ const brainstorm_warmup = skillNames.some((s) => /brainstorm|writing-plans/i.test(s));
3241
+ const fuTrim = fu.trim().toLowerCase();
3242
+ const frames = detectPromptFrames(fu);
3243
+ const continuation_kind = /^continue\b/.test(fuTrim) || fuTrim === "continue." ? "literal-continue" : frames.includes("handoff-prose") ? "handoff-prose" : "none";
3244
+ return {
3245
+ working_shape: inferWorkingShape(entry),
3246
+ prompt_frames: frames,
3247
+ subagent_roles: (entry.subagents ?? []).map(classifySubagentRole),
3248
+ verbosity,
3249
+ external_refs,
3250
+ brainstorm_warmup,
3251
+ continuation_kind
3252
+ };
3253
+ }
3254
+
3255
+ // ../entries/dist/trivial.js
3256
+ function isTrivial(n) {
3257
+ return n.active_min < 1 && n.turn_count < 3 && n.tools_total === 0;
3258
+ }
3259
+
3260
+ // ../entries/dist/build.js
3261
+ init_analytics();
3262
+ var IDLE_GAP_MS = 3 * 60 * 1e3;
3263
+ var INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
3264
+ var PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
3265
+ var BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
3266
+ var SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
3267
+ var IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
3268
+ function parseMs(iso) {
3269
+ if (!iso)
3270
+ return void 0;
3271
+ const ms = Date.parse(iso);
3272
+ return Number.isNaN(ms) ? void 0 : ms;
3273
+ }
3274
+ function trunc(s, n) {
3275
+ if (!s)
3276
+ return "";
3277
+ const one = s.replace(/\s+/g, " ").trim();
3278
+ return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
3279
+ }
3280
+ function minimizeImages(s) {
3281
+ if (!s)
3282
+ return s;
3283
+ let counter = 0;
3284
+ return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
3285
+ }
3286
+ function bashVerb(cmd) {
3287
+ if (!cmd)
3288
+ return "?";
3289
+ let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
3290
+ if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
3291
+ const parts = cmd.split(/&&|;/, 3);
3292
+ if (parts.length >= 2)
3293
+ firstSeg = parts[1].trim();
3294
+ }
3295
+ const tokens = firstSeg.split(/\s+/).filter(Boolean);
3296
+ while (tokens.length && SHELL_NOISE.has(tokens[0]))
3297
+ tokens.shift();
3298
+ if (!tokens.length)
3299
+ return "?";
3300
+ let first = tokens[0];
3301
+ if (first.includes("=") && !first.startsWith("/")) {
3302
+ tokens.shift();
3303
+ if (!tokens.length)
3304
+ return "?";
3305
+ first = tokens[0];
3306
+ }
3307
+ if (first.includes("/") && !first.startsWith("./")) {
3308
+ first = first.replace(/\/+$/, "").split("/").pop();
3309
+ }
3310
+ return first.replace(/^["']|["']$/g, "") || "?";
3311
+ }
3312
+ function firstTextBlock(blocks) {
3313
+ if (!Array.isArray(blocks))
3314
+ return "";
3315
+ for (const b of blocks) {
3316
+ if (b && typeof b === "object" && b.type === "text") {
3317
+ return String(b.text ?? "");
3318
+ }
3319
+ }
3320
+ return "";
3321
+ }
3322
+ function blockText(blocks) {
3323
+ if (!blocks)
3324
+ return "";
3325
+ return blocks.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ");
3326
+ }
3327
+ function groupEventsByLocalDay(events) {
3328
+ const byDay = /* @__PURE__ */ new Map();
3329
+ for (const ev of events) {
3330
+ if (!ev.timestamp)
3331
+ continue;
3332
+ const ms = Date.parse(ev.timestamp);
3333
+ if (Number.isNaN(ms))
3334
+ continue;
3335
+ const day = toLocalDay(ms);
3336
+ let bucket = byDay.get(day);
3337
+ if (!bucket) {
3338
+ bucket = [];
3339
+ byDay.set(day, bucket);
3340
+ }
3341
+ bucket.push(ev);
3342
+ }
3343
+ return byDay;
3344
+ }
3345
+ function newTurn(startMs, userInput = "") {
3346
+ return {
3347
+ startMs,
3348
+ lastMs: startMs,
3349
+ eventTs: startMs !== void 0 ? [startMs] : [],
3350
+ userInput,
3351
+ toolsTotal: 0,
3352
+ toolMix: /* @__PURE__ */ new Map(),
3353
+ bashVerbs: /* @__PURE__ */ new Map(),
3354
+ skills: /* @__PURE__ */ new Map(),
3355
+ subagents: [],
3356
+ taskOps: 0,
3357
+ toolErrors: 0,
3358
+ consecSameToolMax: 0,
3359
+ consec: 0,
3360
+ endedInInterrupt: false,
3361
+ models: /* @__PURE__ */ new Map(),
3362
+ tokensIn: 0,
3363
+ tokensOut: 0,
3364
+ tokensCacheR: 0,
3365
+ tokensCacheW: 0,
3366
+ seenMsgIds: /* @__PURE__ */ new Set()
3367
+ };
3368
+ }
3369
+ function closeTurn(t, closed, cwd) {
3370
+ const ts = [...t.eventTs].sort((a, b) => a - b);
3371
+ let active = 0;
3372
+ for (let i = 1; i < ts.length; i++) {
3373
+ const dt = ts[i] - ts[i - 1];
3374
+ if (dt < IDLE_GAP_MS)
3375
+ active += dt;
3376
+ }
3377
+ closed.push({
3378
+ startMs: t.startMs ?? 0,
3379
+ activeMs: active,
3380
+ userInput: t.userInput,
3381
+ firstAgentText: t.firstAgentText,
3382
+ lastAgentText: t.lastAgentText,
3383
+ toolsTotal: t.toolsTotal,
3384
+ toolMix: t.toolMix,
3385
+ bashVerbs: t.bashVerbs,
3386
+ skills: t.skills,
3387
+ subagents: t.subagents,
3388
+ taskOps: t.taskOps,
3389
+ toolErrors: t.toolErrors,
3390
+ consecSameToolMax: t.consecSameToolMax,
3391
+ endedInInterrupt: t.endedInInterrupt,
3392
+ models: t.models,
3393
+ totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
3394
+ cwd
3395
+ });
3396
+ }
3397
+ function aggregateDay(dayEvents, _sessionFallbackProject) {
3398
+ const prTitles = [];
3399
+ let commits = 0;
3400
+ let pushes = 0;
3401
+ const modelTurns = /* @__PURE__ */ new Map();
3402
+ let totalToolErrors = 0;
3403
+ let totalInterrupts = 0;
3404
+ let exitPlanCalls = 0;
3405
+ let taskOpsTotal = 0;
3406
+ let firstUser;
3407
+ let finalAgent;
3408
+ const cwdCounts = /* @__PURE__ */ new Map();
3409
+ const closed = [];
3410
+ let cur;
3411
+ const allTs = [];
3412
+ let lastEventCwd;
3413
+ for (const ev of dayEvents) {
3414
+ const tsMs = parseMs(ev.timestamp);
3415
+ if (tsMs !== void 0)
3416
+ allTs.push(tsMs);
3417
+ const rawType = ev.rawType;
3418
+ const raw = ev.raw;
3419
+ const msg = raw?.message ?? {};
3420
+ const content = msg.content;
3421
+ const eventCwd = raw?.cwd;
3422
+ if (eventCwd)
3423
+ lastEventCwd = eventCwd;
3424
+ if (rawType === "user") {
3425
+ const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
3426
+ if (!isToolResultOnly) {
3427
+ const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
3428
+ if (cur) {
3429
+ if (text && INTERRUPT_RE2.test(text)) {
3430
+ cur.endedInInterrupt = true;
3431
+ totalInterrupts++;
3432
+ }
3433
+ closeTurn(cur, closed, eventCwd);
3434
+ }
3435
+ cur = newTurn(tsMs, text);
3436
+ if (!firstUser && text && !text.startsWith("<command-name>")) {
3437
+ firstUser = text;
3438
+ }
3439
+ } else {
3440
+ if (Array.isArray(content)) {
3441
+ for (const c of content) {
3442
+ if (c && typeof c === "object" && c.type === "tool_result" && c.is_error) {
3443
+ totalToolErrors++;
3444
+ if (cur)
3445
+ cur.toolErrors++;
3446
+ }
3447
+ }
3448
+ }
3449
+ if (cur && tsMs !== void 0) {
3450
+ cur.eventTs.push(tsMs);
3451
+ cur.lastMs = tsMs;
3452
+ }
3453
+ }
3454
+ } else if (rawType === "assistant") {
3455
+ if (cur && tsMs !== void 0) {
3456
+ cur.eventTs.push(tsMs);
3457
+ cur.lastMs = tsMs;
3458
+ }
3459
+ const model = msg.model;
3460
+ if (model) {
3461
+ modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
3462
+ if (cur)
3463
+ cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
3464
+ }
3465
+ const msgId = msg.id;
3466
+ const usage = msg.usage;
3467
+ if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
3468
+ cur.seenMsgIds.add(msgId);
3469
+ cur.tokensIn += usage.input_tokens ?? 0;
3470
+ cur.tokensOut += usage.output_tokens ?? 0;
3471
+ cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
3472
+ cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
3473
+ }
3474
+ if (Array.isArray(content)) {
3475
+ for (const c of content) {
3476
+ if (!c || typeof c !== "object")
3477
+ continue;
3478
+ const ct = c;
3479
+ if (ct.type === "text" && ct.text) {
3480
+ finalAgent = ct.text;
3481
+ if (cur) {
3482
+ if (cur.firstAgentText === void 0)
3483
+ cur.firstAgentText = ct.text;
3484
+ cur.lastAgentText = ct.text;
3485
+ }
3486
+ } else if (ct.type === "tool_use") {
3487
+ const name = ct.name ?? "unknown";
3488
+ if (cur) {
3489
+ cur.toolsTotal++;
3490
+ cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
3491
+ if (name === cur.lastTool)
3492
+ cur.consec++;
3493
+ else {
3494
+ cur.lastTool = name;
3495
+ cur.consec = 1;
3496
+ }
3497
+ if (cur.consec > cur.consecSameToolMax)
3498
+ cur.consecSameToolMax = cur.consec;
3499
+ if (name === "Skill") {
3500
+ const sk = String(ct.input?.skill ?? "unknown");
3501
+ cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
3502
+ } else if (name === "ToolSearch") {
3503
+ const q = String(ct.input?.query ?? "");
3504
+ const key = `(ToolSearch: ${trunc(q, 60)})`;
3505
+ cur.skills.set(key, (cur.skills.get(key) ?? 0) + 1);
3506
+ }
3507
+ if (name === "Agent") {
3508
+ const inp = ct.input ?? {};
3509
+ cur.subagents.push({
3510
+ type: String(inp.subagent_type ?? "general-purpose"),
3511
+ description: trunc(String(inp.description ?? ""), 80),
3512
+ prompt_preview: trunc(String(inp.prompt ?? ""), 240),
3513
+ background: Boolean(inp.run_in_background)
3514
+ });
3515
+ }
3516
+ if (name === "ExitPlanMode")
3517
+ exitPlanCalls++;
3518
+ if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
3519
+ cur.taskOps++;
3520
+ taskOpsTotal++;
3521
+ }
3522
+ }
3523
+ if (name === "Bash") {
3524
+ const cmd = String(ct.input?.command ?? "");
3525
+ if (cur)
3526
+ cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
3527
+ if (/\bgh\s+pr\s+create\b/.test(cmd)) {
3528
+ const m = cmd.match(PR_TITLE_RE);
3529
+ prTitles.push(m?.[1] ?? cmd.slice(0, 120));
3530
+ }
3531
+ if (/\bgit\s+commit\b/.test(cmd))
3532
+ commits++;
3533
+ if (/\bgit\s+push\b/.test(cmd))
3534
+ pushes++;
3535
+ }
3536
+ if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
3537
+ cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
3538
+ }
3539
+ }
3540
+ }
3541
+ }
3542
+ }
3543
+ }
3544
+ if (cur)
3545
+ closeTurn(cur, closed, lastEventCwd);
3546
+ const tsSorted = [...allTs].sort((a, b) => a - b);
3547
+ let activeMs = 0;
3548
+ for (let i = 1; i < tsSorted.length; i++) {
3549
+ const dt = tsSorted[i] - tsSorted[i - 1];
3550
+ if (dt < IDLE_GAP_MS)
3551
+ activeMs += dt;
3552
+ }
3553
+ const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
3554
+ const start_iso = tsSortedStrings[0] ?? "";
3555
+ const end_iso = tsSortedStrings[tsSortedStrings.length - 1] ?? "";
3556
+ return {
3557
+ closed,
3558
+ firstUser,
3559
+ finalAgent,
3560
+ prTitles,
3561
+ commits,
3562
+ pushes,
3563
+ modelTurns,
3564
+ totalToolErrors,
3565
+ totalInterrupts,
3566
+ exitPlanCalls,
3567
+ taskOpsTotal,
3568
+ cwdCounts,
3569
+ start_iso,
3570
+ end_iso,
3571
+ activeMs
3572
+ };
3573
+ }
3574
+ function buildTopTools(closed) {
3575
+ const toolMix = /* @__PURE__ */ new Map();
3576
+ const bashVerbsAll = /* @__PURE__ */ new Map();
3577
+ for (const t of closed) {
3578
+ for (const [k, v] of t.toolMix)
3579
+ toolMix.set(k, (toolMix.get(k) ?? 0) + v);
3580
+ for (const [k, v] of t.bashVerbs)
3581
+ bashVerbsAll.set(k, (bashVerbsAll.get(k) ?? 0) + v);
3582
+ }
3583
+ const top = [...toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
3584
+ return top.map(([name, count]) => {
3585
+ if (name === "Bash" && bashVerbsAll.size > 0) {
3586
+ const sub = [...bashVerbsAll.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
3587
+ return `Bash\xD7${count} (${sub})`;
3588
+ }
3589
+ return count > 1 ? `${name}\xD7${count}` : name;
3590
+ });
3591
+ }
3592
+ function buildEntries(sessionDetail) {
3593
+ const byDay = groupEventsByLocalDay(sessionDetail.events);
3594
+ const entries = [];
3595
+ const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
3596
+ const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
3597
+ for (const [local_day, dayEvents] of byDay) {
3598
+ if (dayEvents.length === 0)
3599
+ continue;
3600
+ const agg = aggregateDay(dayEvents, sessionFallbackProject);
3601
+ const { closed } = agg;
3602
+ const turnCount = closed.length;
3603
+ const toolsTotal = closed.reduce((s, t) => s + t.toolsTotal, 0);
3604
+ const tokensTotal = closed.reduce((s, t) => s + t.totalTokens, 0);
3605
+ const subagentCalls = closed.reduce((s, t) => s + t.subagents.length, 0);
3606
+ const skillsMap = /* @__PURE__ */ new Map();
3607
+ for (const t of closed) {
3608
+ for (const [k, v] of t.skills)
3609
+ skillsMap.set(k, (skillsMap.get(k) ?? 0) + v);
3610
+ }
3611
+ const skillCalls = [...skillsMap.values()].reduce((a, b) => a + b, 0);
3612
+ const taskOps = agg.taskOpsTotal;
3613
+ const interrupts = agg.totalInterrupts;
3614
+ const toolErrors = agg.totalToolErrors;
3615
+ const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
3616
+ const exitPlanCalls = agg.exitPlanCalls;
3617
+ const prs = agg.prTitles.length;
3618
+ const commits = agg.commits;
3619
+ const pushes = agg.pushes;
3620
+ const activeMin = Math.round(agg.activeMs / 6e3) / 10;
3621
+ const numbers = {
3622
+ active_min: activeMin,
3623
+ turn_count: turnCount,
3624
+ tools_total: toolsTotal,
3625
+ subagent_calls: subagentCalls,
3626
+ skill_calls: skillCalls,
3627
+ task_ops: taskOps,
3628
+ interrupts,
3629
+ tool_errors: toolErrors,
3630
+ consec_same_tool_max: consecMax,
3631
+ exit_plan_calls: exitPlanCalls,
3632
+ prs,
3633
+ commits,
3634
+ pushes,
3635
+ tokens_total: tokensTotal
3636
+ };
3637
+ const longestTurnActiveMs = Math.max(0, ...closed.map((t) => t.activeMs));
3638
+ const flags = [];
3639
+ if (interrupts >= 3)
3640
+ flags.push("interrupt_heavy");
3641
+ if (toolErrors >= 20)
3642
+ flags.push("high_errors");
3643
+ if (consecMax >= 8)
3644
+ flags.push("loop_suspected");
3645
+ if (agg.activeMs < 5 * 6e4 && prs >= 1)
3646
+ flags.push("fast_ship");
3647
+ if (exitPlanCalls > 0)
3648
+ flags.push("plan_used");
3649
+ const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
3650
+ if (subagentTurns >= 3)
3651
+ flags.push("orchestrated");
3652
+ if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
3653
+ flags.push("long_autonomous");
3654
+ const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
3655
+ const modelMix = Object.fromEntries(agg.modelTurns);
3656
+ const firstUserRaw = agg.firstUser ?? "";
3657
+ const first_user = trunc(minimizeImages(firstUserRaw), 400);
3658
+ const finalAgentRaw = agg.finalAgent ?? "";
3659
+ const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
3660
+ let project = sessionFallbackProject;
3661
+ if (agg.cwdCounts.size > 0) {
3662
+ const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
3663
+ project = canonicalProjectName(best[0]);
3664
+ }
3665
+ const top_tools = buildTopTools(closed);
3666
+ const skills = Object.fromEntries(skillsMap);
3667
+ const subagents = closed.flatMap((t) => t.subagents);
3668
+ const humanText = dayEvents.filter((ev) => {
3669
+ if (ev.rawType !== "user")
3670
+ return false;
3671
+ const raw = ev.raw;
3672
+ const content = raw?.message?.content;
3673
+ const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
3674
+ if (isToolResultOnly)
3675
+ return false;
3676
+ const text = blockText(ev.blocks) || ev.preview || "";
3677
+ return classifyUserInputSource(text) === "human";
3678
+ }).map((ev) => blockText(ev.blocks) || ev.preview || "").join("\n");
3679
+ const satisfaction_signals = countSatisfactionSignals(humanText);
3680
+ const user_input_sources = { human: 0, teammate: 0, skill_load: 0, slash_command: 0 };
3681
+ for (const ev of dayEvents) {
3682
+ if (ev.rawType !== "user")
3683
+ continue;
3684
+ const raw = ev.raw;
3685
+ const content = raw?.message?.content;
3686
+ const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
3687
+ if (isToolResultOnly)
3688
+ continue;
3689
+ const text = blockText(ev.blocks) || ev.preview || "";
3690
+ const src = classifyUserInputSource(text);
3691
+ user_input_sources[src]++;
3692
+ }
3693
+ const pendingUserInstructions = extractUserInstructions(humanText).slice(0, 5);
3694
+ const trivial = isTrivial(numbers);
3695
+ const entry = {
3696
+ version: CURRENT_ENTRY_SCHEMA_VERSION,
3697
+ session_id: sessionDetail.id,
3698
+ local_day,
3699
+ project,
3700
+ start_iso: agg.start_iso,
3701
+ end_iso: agg.end_iso,
3702
+ numbers,
3703
+ flags,
3704
+ primary_model: primaryModel,
3705
+ model_mix: modelMix,
3706
+ first_user,
3707
+ final_agent,
3708
+ pr_titles: agg.prTitles,
3709
+ top_tools,
3710
+ skills,
3711
+ subagents,
3712
+ satisfaction_signals,
3713
+ user_input_sources,
3714
+ enrichment: trivial ? skippedTrivialEnrichment(generatedAt) : { ...pendingEnrichment(), user_instructions: pendingUserInstructions },
3715
+ generated_at: generatedAt,
3716
+ source_jsonl: sessionDetail.filePath ?? "",
3717
+ source_checkpoint: { byte_offset: 0, last_event_ts: agg.end_iso || null }
3718
+ };
3719
+ entry.signals = computeEntrySignals(entry);
3720
+ entries.push(entry);
3721
+ }
3722
+ entries.sort((a, b) => a.local_day.localeCompare(b.local_day));
3723
+ return entries;
3724
+ }
3725
+
3726
+ // ../entries/dist/fs.js
3727
+ import { readFileSync as readFileSync6, readdirSync, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, renameSync, existsSync as existsSync3 } from "node:fs";
3728
+ import { randomBytes } from "node:crypto";
3729
+ import { join as join5 } from "node:path";
3730
+ import { homedir as homedir5 } from "node:os";
3731
+
3732
+ // ../entries/dist/digest-fs.js
3733
+ var TODAY_TTL_MS = 10 * 60 * 1e3;
3734
+ var CURRENT_WEEK_TTL_MS = 10 * 60 * 1e3;
3735
+ var CURRENT_MONTH_TTL_MS = 10 * 60 * 1e3;
3736
+
3737
+ // ../entries/dist/fs.js
3738
+ var entriesDirCached = null;
3739
+ function entriesDir() {
3740
+ if (entriesDirCached)
3741
+ return entriesDirCached;
3742
+ const envOverride = process.env.CCLENS_ENTRIES_DIR;
3743
+ entriesDirCached = envOverride ?? join5(homedir5(), ".cclens", "entries");
3744
+ return entriesDirCached;
3745
+ }
3746
+ function pathFor(sessionId, localDay) {
3747
+ return join5(entriesDir(), `${entryKey(sessionId, localDay)}.json`);
3748
+ }
3749
+ function tmpPathFor(final) {
3750
+ return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
3751
+ }
3752
+ var PRESERVABLE_STATUSES = /* @__PURE__ */ new Set([
3753
+ "done",
3754
+ "skipped_trivial"
3755
+ ]);
3756
+ function enrichmentInputHash(e) {
3757
+ return [
3758
+ e.numbers.active_min,
3759
+ e.numbers.turn_count,
3760
+ e.numbers.tools_total,
3761
+ e.numbers.subagent_calls,
3762
+ e.numbers.skill_calls,
3763
+ e.numbers.exit_plan_calls,
3764
+ e.numbers.interrupts,
3765
+ e.numbers.prs,
3766
+ e.flags.slice().sort().join(","),
3767
+ e.pr_titles.length,
3768
+ e.end_iso
3769
+ ].join("|");
3770
+ }
3771
+ function writeEntryPreservingEnrichment(fresh) {
3772
+ const dir = entriesDir();
3773
+ mkdirSync5(dir, { recursive: true });
3774
+ const final = pathFor(fresh.session_id, fresh.local_day);
3775
+ const existing = readEntry(fresh.session_id, fresh.local_day);
3776
+ let toWrite = fresh;
3777
+ if (existing && PRESERVABLE_STATUSES.has(existing.enrichment.status) && enrichmentInputHash(existing) === enrichmentInputHash(fresh)) {
3778
+ toWrite = { ...fresh, enrichment: existing.enrichment };
3779
+ }
3780
+ const tmp = tmpPathFor(final);
3781
+ writeFileSync4(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
3782
+ renameSync(tmp, final);
3783
+ }
3784
+ function readEntry(sessionId, localDay) {
3785
+ const p = pathFor(sessionId, localDay);
3786
+ if (!existsSync3(p))
3787
+ return null;
3788
+ const raw = readFileSync6(p, "utf8");
3789
+ return JSON.parse(raw);
3790
+ }
3791
+
3792
+ // src/perception/state.ts
3793
+ import { existsSync as existsSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, renameSync as renameSync2 } from "node:fs";
3794
+ import { dirname as dirname4, join as join6 } from "node:path";
3795
+ import { homedir as homedir6 } from "node:os";
3796
+ var STALE_MS = 15 * 60 * 1e3;
3797
+ var pathCached = null;
3798
+ function statePath() {
3799
+ if (pathCached) return pathCached;
3800
+ pathCached = join6(homedir6(), ".cclens", "perception-state.json");
3801
+ return pathCached;
3802
+ }
3803
+ function readState() {
3804
+ const p = statePath();
3805
+ if (!existsSync4(p)) {
3806
+ return {
3807
+ sweep_in_progress: false,
3808
+ last_sweep_started_at: null,
3809
+ last_sweep_completed_at: null,
3810
+ file_checkpoints: {}
3811
+ };
3812
+ }
3813
+ const raw = readFileSync7(p, "utf8");
3814
+ return JSON.parse(raw);
3815
+ }
3816
+ function writeState(s) {
3817
+ const p = statePath();
3818
+ mkdirSync6(dirname4(p), { recursive: true });
3819
+ const tmp = `${p}.tmp`;
3820
+ writeFileSync5(tmp, JSON.stringify(s, null, 2), "utf8");
3821
+ renameSync2(tmp, p);
3822
+ }
3823
+ function markSweepStart() {
3824
+ const s = readState();
3825
+ s.sweep_in_progress = true;
3826
+ s.last_sweep_started_at = (/* @__PURE__ */ new Date()).toISOString();
3827
+ writeState(s);
3828
+ }
3829
+ function markSweepEnd() {
3830
+ const s = readState();
3831
+ s.sweep_in_progress = false;
3832
+ s.last_sweep_completed_at = (/* @__PURE__ */ new Date()).toISOString();
3833
+ writeState(s);
3834
+ }
3835
+ function isSweepStale() {
3836
+ const s = readState();
3837
+ if (!s.sweep_in_progress) return false;
3838
+ if (!s.last_sweep_started_at) return true;
3839
+ const age = Date.now() - Date.parse(s.last_sweep_started_at);
3840
+ return age > STALE_MS;
3841
+ }
3842
+ function updateCheckpoint(jsonlPath, cp) {
3843
+ const s = readState();
3844
+ s.file_checkpoints[jsonlPath] = cp;
3845
+ writeState(s);
3846
+ }
3847
+
3848
+ // src/perception/scan.ts
3849
+ import { readdirSync as readdirSync2 } from "node:fs";
3850
+ import { join as join7 } from "node:path";
3851
+ import { homedir as homedir7 } from "node:os";
3852
+ async function listAllSessionJsonls(root) {
3853
+ const projectsRoot = root ?? join7(homedir7(), ".claude", "projects");
3854
+ const out = [];
3855
+ try {
3856
+ for (const project of readdirSync2(projectsRoot)) {
3857
+ const dir = join7(projectsRoot, project);
3858
+ try {
3859
+ for (const f of readdirSync2(dir)) {
3860
+ if (f.endsWith(".jsonl")) out.push(join7(dir, f));
3861
+ }
3862
+ } catch {
3863
+ }
3864
+ }
3865
+ } catch {
3866
+ }
3867
+ return out;
3868
+ }
3869
+
3870
+ // src/perception/worker.ts
3871
+ function log(msg) {
3872
+ console.error(`[perception] ${msg}`);
3873
+ }
3874
+ function decodeProjectDirName(projectDir) {
3875
+ if (projectDir.startsWith("-")) {
3876
+ return "/" + projectDir.slice(1).replace(/-/g, "/");
3877
+ }
3878
+ return projectDir.replace(/-/g, "/");
3879
+ }
3880
+ async function runPerceptionSweep(opts = {}) {
3881
+ const state = readState();
3882
+ if (state.sweep_in_progress && !isSweepStale()) {
3883
+ return { sessionsProcessed: 0, entriesWritten: 0, errors: 0 };
3884
+ }
3885
+ markSweepStart();
3886
+ let sessions = 0;
3887
+ let entries = 0;
3888
+ let errors = 0;
3889
+ try {
3890
+ const files = await listAllSessionJsonls(opts.projectsRoot);
3891
+ for (const f of files) {
3892
+ try {
3893
+ const stat = statSync2(f);
3894
+ const prev = state.file_checkpoints[f];
3895
+ if (prev && prev.byte_offset >= stat.size) continue;
3896
+ const raw = readFileSync8(f, "utf8");
3897
+ const rawLines = raw.split("\n").filter(Boolean).map((l) => {
3898
+ try {
3899
+ return JSON.parse(l);
3900
+ } catch {
3901
+ return null;
3902
+ }
3903
+ }).filter((x) => x !== null);
3904
+ if (rawLines.length === 0) continue;
3905
+ const { meta, events } = parseTranscript(rawLines);
3906
+ const fileName = basename(f);
3907
+ const projectDir = basename(dirname5(f));
3908
+ const sessionId = fileName.replace(/\.jsonl$/, "");
3909
+ const sd = {
3910
+ ...meta,
3911
+ id: sessionId,
3912
+ filePath: f,
3913
+ projectDir,
3914
+ projectName: meta.cwd ?? decodeProjectDirName(projectDir),
3915
+ events
3916
+ };
3917
+ const built = buildEntries(sd);
3918
+ for (const e of built) {
3919
+ e.source_checkpoint.byte_offset = stat.size;
3920
+ writeEntryPreservingEnrichment(e);
3921
+ entries++;
3922
+ }
3923
+ updateCheckpoint(f, {
3924
+ byte_offset: stat.size,
3925
+ last_event_ts: built.at(-1)?.end_iso ?? null,
3926
+ affects_days: built.map((e) => e.local_day)
3927
+ });
3928
+ sessions++;
3929
+ } catch (err) {
3930
+ errors++;
3931
+ log(`skipped ${f}: ${err.message}`);
3932
+ }
3933
+ }
3934
+ } finally {
3935
+ markSweepEnd();
3936
+ }
3937
+ return { sessionsProcessed: sessions, entriesWritten: entries, errors };
3938
+ }
2911
3939
 
2912
3940
  // src/daemon-worker.ts
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");
3941
+ var STATE_DIR = join8(homedir8(), ".cclens");
3942
+ var USAGE_LOG3 = join8(STATE_DIR, "usage.jsonl");
3943
+ var DAEMON_LOG = join8(STATE_DIR, "daemon.log");
2916
3944
  var WATCHDOG_INTERVAL_MS = 5 * 1e3;
2917
- mkdirSync4(dirname3(USAGE_LOG2), { recursive: true });
2918
- function log(level, message) {
3945
+ mkdirSync7(dirname6(USAGE_LOG3), { recursive: true });
3946
+ function log2(level, message) {
2919
3947
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
2920
3948
  `;
2921
3949
  try {
@@ -2926,21 +3954,37 @@ function log(level, message) {
2926
3954
  var nextPollAtMs = 0;
2927
3955
  var currentIntervalMs = BASE_INTERVAL_MS;
2928
3956
  var waitingForRefresh = false;
3957
+ var PERCEPTION_INTERVAL_MS = 5 * 60 * 1e3;
3958
+ var perceptionInFlight = false;
3959
+ var perceptionHandle = setInterval(async () => {
3960
+ if (perceptionInFlight) return;
3961
+ perceptionInFlight = true;
3962
+ try {
3963
+ const { sessionsProcessed, entriesWritten, errors } = await runPerceptionSweep();
3964
+ if (sessionsProcessed > 0 || errors > 0) {
3965
+ log2("info", `perception sweep: ${sessionsProcessed} sessions, ${entriesWritten} entries, ${errors} errors`);
3966
+ }
3967
+ } catch (err) {
3968
+ log2("error", `perception sweep failed: ${err.message}`);
3969
+ } finally {
3970
+ perceptionInFlight = false;
3971
+ }
3972
+ }, PERCEPTION_INTERVAL_MS);
2929
3973
  async function tick() {
2930
3974
  try {
2931
3975
  const snapshot = await fetchUsage();
2932
- appendSnapshot(USAGE_LOG2, snapshot);
2933
- log(
3976
+ appendSnapshot(USAGE_LOG3, snapshot);
3977
+ log2(
2934
3978
  "info",
2935
3979
  `snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
2936
3980
  );
2937
3981
  return "success";
2938
3982
  } catch (err) {
2939
3983
  if (err instanceof UsageApiError) {
2940
- log("warn", `poll failed (${err.code}): ${err.message}`);
3984
+ log2("warn", `poll failed (${err.code}): ${err.message}`);
2941
3985
  return err.code === "network" ? "network" : "auth";
2942
3986
  }
2943
- log("error", `unexpected error: ${err.stack ?? err}`);
3987
+ log2("error", `unexpected error: ${err.stack ?? err}`);
2944
3988
  return "auth";
2945
3989
  }
2946
3990
  }
@@ -2948,7 +3992,7 @@ function scheduleAfter(now, outcome) {
2948
3992
  const prev = currentIntervalMs;
2949
3993
  currentIntervalMs = nextIntervalMs(currentIntervalMs, outcome);
2950
3994
  if (currentIntervalMs !== prev) {
2951
- log(
3995
+ log2(
2952
3996
  "info",
2953
3997
  `poll interval ${outcome === "success" ? "reset" : "backoff"} to ${currentIntervalMs / 1e3}s`
2954
3998
  );
@@ -2963,40 +4007,41 @@ async function runLoop() {
2963
4007
  const now = Date.now();
2964
4008
  if (now >= nextPollAtMs) {
2965
4009
  if (nextPollAtMs > 0 && now - nextPollAtMs > currentIntervalMs) {
2966
- log(
4010
+ log2(
2967
4011
  "info",
2968
4012
  `wake-from-sleep catch-up: ${Math.round((now - nextPollAtMs + currentIntervalMs) / 1e3)}s since last poll`
2969
4013
  );
2970
4014
  }
2971
4015
  const creds = readOAuthCredentials();
2972
4016
  if (!creds) {
2973
- log("warn", "no Claude Code OAuth token found; waiting");
4017
+ log2("warn", "no Claude Code OAuth token found; waiting");
2974
4018
  waitingForRefresh = true;
2975
4019
  nextPollAtMs = now + BASE_INTERVAL_MS;
2976
4020
  } else if (!isUsable(creds, now)) {
2977
4021
  if (!waitingForRefresh) {
2978
- log("info", "token expired; waiting for Claude Code to refresh it");
4022
+ log2("info", "token expired; waiting for Claude Code to refresh it");
2979
4023
  waitingForRefresh = true;
2980
4024
  }
2981
4025
  } else {
2982
4026
  if (waitingForRefresh) {
2983
- log("info", "token refreshed; resuming polls");
4027
+ log2("info", "token refreshed; resuming polls");
2984
4028
  waitingForRefresh = false;
2985
4029
  }
2986
4030
  const outcome = await tick();
2987
4031
  scheduleAfter(Date.now(), outcome);
2988
4032
  }
2989
- await runTeamSync(log);
4033
+ await runTeamSync(log2);
2990
4034
  }
2991
4035
  await sleep(WATCHDOG_INTERVAL_MS);
2992
4036
  }
2993
4037
  }
2994
- log(
4038
+ log2(
2995
4039
  "info",
2996
4040
  `daemon started (pid=${process.pid}, interval=${BASE_INTERVAL_MS / 1e3}s, watchdog=${WATCHDOG_INTERVAL_MS / 1e3}s)`
2997
4041
  );
2998
4042
  void runLoop();
2999
4043
  process.on("SIGTERM", () => {
3000
- log("info", `daemon stopping (pid=${process.pid})`);
4044
+ clearInterval(perceptionHandle);
4045
+ log2("info", `daemon stopping (pid=${process.pid})`);
3001
4046
  process.exit(0);
3002
4047
  });