fleetlens 0.4.1 → 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 +119 -0
  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 +1937 -979
  340. package/dist/index.js +11210 -1877
  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/08c1pcrocqq5s.js +0 -6
  396. package/app/apps/web/.next/static/chunks/0e-wrhqh4h3r5.js +0 -2
  397. package/app/apps/web/.next/static/chunks/0fc6dk2qtokwh.js +0 -1
  398. package/app/apps/web/.next/static/chunks/0i7er7_bfab36.js +0 -10
  399. package/app/apps/web/.next/static/chunks/0ipytyi01h0ta.js +0 -1
  400. package/app/apps/web/.next/static/chunks/0lsznd4_avzhv.js +0 -6
  401. package/app/apps/web/.next/static/chunks/0lty3v_i9mde7.js +0 -1
  402. package/app/apps/web/.next/static/chunks/0u-n8fz-ms.ee.js +0 -6
  403. package/app/apps/web/.next/static/chunks/0vekjjj4pidlz.css +0 -1
  404. package/app/apps/web/.next/static/chunks/137uw2-qbro07.js +0 -31
  405. package/app/apps/web/.next/static/chunks/15w5_bct86ut6.js +0 -10
  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/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_buildManifest.js +0 -0
  439. /package/app/apps/web/.next/static/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_clientMiddlewareManifest.js +0 -0
  440. /package/app/apps/web/.next/static/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_ssgManifest.js +0 -0
@@ -0,0 +1,513 @@
1
+ "use client";
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+
5
+ type ActiveRun = { pid: number; kind: string; model: string; elapsed_s: number; cpu_time: string };
6
+
7
+ type TraceSummary = {
8
+ run_id: string;
9
+ kind: string;
10
+ model: string;
11
+ pid: number | null;
12
+ started_at: string;
13
+ ended_at: string | null;
14
+ elapsed_ms: number | null;
15
+ exit_code: number | null;
16
+ status: "running" | "ok" | "error" | "unknown";
17
+ content_chars: number | null;
18
+ output_tokens: number | null;
19
+ };
20
+
21
+ type RunsResponse = {
22
+ generated_at: string;
23
+ active: ActiveRun[];
24
+ totals: { ops: number; input_tokens: number; output_tokens: number; cost_usd: number };
25
+ by_kind: Record<string, { ops: number; input_tokens: number; output_tokens: number; cost_usd: number }>;
26
+ traces: TraceSummary[];
27
+ };
28
+
29
+ type StreamEvent =
30
+ | { sseType: "connected"; data: { run_id: string; path: string } }
31
+ | { sseType: "meta"; data: { _meta: { type: string; [k: string]: unknown } } }
32
+ | { sseType: "event"; data: Record<string, unknown> }
33
+ | { sseType: "done"; data: { reason: string } }
34
+ | { sseType: "raw"; data: { line: string } }
35
+ | { sseType: "stream_error"; data: { message: string } };
36
+
37
+ function fmtElapsed(ms: number | null): string {
38
+ if (ms == null) return "—";
39
+ const s = Math.round(ms / 100) / 10;
40
+ if (s < 60) return `${s.toFixed(1)}s`;
41
+ const m = Math.floor(s / 60);
42
+ const r = (s - m * 60).toFixed(0);
43
+ return `${m}m${r.padStart(2, "0")}s`;
44
+ }
45
+
46
+ function fmtAge(iso: string): string {
47
+ if (!iso) return "—";
48
+ const ms = Date.now() - new Date(iso).getTime();
49
+ if (ms < 0) return "now";
50
+ const s = Math.floor(ms / 1000);
51
+ if (s < 60) return `${s}s ago`;
52
+ const m = Math.floor(s / 60);
53
+ if (m < 60) return `${m}m ago`;
54
+ const h = Math.floor(m / 60);
55
+ if (h < 24) return `${h}h ago`;
56
+ return `${Math.floor(h / 24)}d ago`;
57
+ }
58
+
59
+ const STATUS_BADGE: Record<TraceSummary["status"], { bg: string; fg: string; label: string }> = {
60
+ running: { bg: "rgba(59, 130, 246, 0.15)", fg: "#3b82f6", label: "running" },
61
+ ok: { bg: "rgba(34, 197, 94, 0.15)", fg: "#16a34a", label: "ok" },
62
+ error: { bg: "rgba(239, 68, 68, 0.15)", fg: "#dc2626", label: "error" },
63
+ unknown: { bg: "rgba(148, 163, 184, 0.15)", fg: "#64748b", label: "?" },
64
+ };
65
+
66
+ const KIND_COLOR: Record<string, string> = {
67
+ week_digest: "#a855f7",
68
+ month_digest: "#ec4899",
69
+ day_digest: "#06b6d4",
70
+ top_session: "#f59e0b",
71
+ entry_enrich: "#10b981",
72
+ unknown: "#94a3b8",
73
+ };
74
+
75
+ export function RunsLiveBoard() {
76
+ const [data, setData] = useState<RunsResponse | null>(null);
77
+ const [selected, setSelected] = useState<string | null>(null);
78
+ const [error, setError] = useState<string | null>(null);
79
+
80
+ // Poll the snapshot every 3s while the page is open.
81
+ useEffect(() => {
82
+ let cancelled = false;
83
+ async function tick() {
84
+ try {
85
+ const r = await fetch("/api/runs?since=24h&trace_limit=40", { cache: "no-store" });
86
+ if (!r.ok) throw new Error(`/api/runs ${r.status}`);
87
+ const j = (await r.json()) as RunsResponse;
88
+ if (!cancelled) {
89
+ setData(j);
90
+ setError(null);
91
+ // Auto-select latest if nothing selected yet.
92
+ if (!cancelled && !selected && j.traces.length > 0) {
93
+ setSelected(j.traces[0]!.run_id);
94
+ }
95
+ }
96
+ } catch (e) {
97
+ if (!cancelled) setError((e as Error).message);
98
+ }
99
+ }
100
+ tick();
101
+ const id = setInterval(tick, 3000);
102
+ return () => { cancelled = true; clearInterval(id); };
103
+ }, [selected]);
104
+
105
+ return (
106
+ <div style={{ display: "grid", gridTemplateColumns: "minmax(380px, 1fr) minmax(0, 2.4fr)", gap: 16 }}>
107
+ <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
108
+ <SnapshotPanel data={data} error={error} />
109
+ <RunsList traces={data?.traces ?? []} active={data?.active ?? []} selected={selected} onSelect={setSelected} />
110
+ </div>
111
+ <div>
112
+ {selected ? <RunDetail runId={selected} /> : <Placeholder text="Select a run to see its prompt + live events." />}
113
+ </div>
114
+ </div>
115
+ );
116
+ }
117
+
118
+ function Card({ children, padding = 14 }: { children: React.ReactNode; padding?: number }) {
119
+ return (
120
+ <div
121
+ style={{
122
+ background: "var(--af-card)",
123
+ border: "1px solid var(--af-border)",
124
+ borderRadius: 10,
125
+ padding,
126
+ }}
127
+ >
128
+ {children}
129
+ </div>
130
+ );
131
+ }
132
+
133
+ function Placeholder({ text }: { text: string }) {
134
+ return (
135
+ <Card>
136
+ <div style={{ color: "var(--af-text-tertiary)", fontSize: 13, textAlign: "center", padding: "60px 0" }}>{text}</div>
137
+ </Card>
138
+ );
139
+ }
140
+
141
+ function SnapshotPanel({ data, error }: { data: RunsResponse | null; error: string | null }) {
142
+ if (error) {
143
+ return <Card><div style={{ color: "#dc2626", fontSize: 12 }}>error: {error}</div></Card>;
144
+ }
145
+ if (!data) return <Card><div style={{ color: "var(--af-text-tertiary)", fontSize: 12 }}>loading…</div></Card>;
146
+ const { active, totals, by_kind } = data;
147
+ return (
148
+ <Card>
149
+ <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 8 }}>
150
+ <strong style={{ fontSize: 13 }}>{active.length === 0 ? "no active runs" : `${active.length} running`}</strong>
151
+ <span style={{ fontSize: 11, color: "var(--af-text-tertiary)" }}>{totals.ops} completed · last 24h</span>
152
+ </div>
153
+ {active.length > 0 && (
154
+ <ul style={{ listStyle: "none", padding: 0, margin: "0 0 10px 0", display: "flex", flexDirection: "column", gap: 4 }}>
155
+ {active.map(r => (
156
+ <li key={r.pid} style={{ fontSize: 12, fontFamily: "var(--font-mono, ui-monospace)" }}>
157
+ <KindDot kind={r.kind} /> pid {r.pid} · {r.kind} · {r.model} · {r.elapsed_s}s
158
+ </li>
159
+ ))}
160
+ </ul>
161
+ )}
162
+ <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
163
+ {Object.entries(by_kind).map(([k, v]) => (
164
+ <span key={k} style={{ fontSize: 11, padding: "2px 8px", background: "var(--af-bg-soft, rgba(0,0,0,0.04))", borderRadius: 6, color: "var(--af-text-secondary)" }}>
165
+ <KindDot kind={k} /> {k}: {v.ops}
166
+ </span>
167
+ ))}
168
+ </div>
169
+ </Card>
170
+ );
171
+ }
172
+
173
+ function KindDot({ kind }: { kind: string }) {
174
+ return <span style={{ display: "inline-block", width: 8, height: 8, borderRadius: "50%", background: KIND_COLOR[kind] ?? KIND_COLOR.unknown, marginRight: 6, verticalAlign: "middle" }} />;
175
+ }
176
+
177
+ function RunsList({
178
+ traces, active, selected, onSelect,
179
+ }: {
180
+ traces: TraceSummary[];
181
+ active: ActiveRun[];
182
+ selected: string | null;
183
+ onSelect: (id: string) => void;
184
+ }) {
185
+ const activePids = useMemo(() => new Set(active.map(a => a.pid)), [active]);
186
+ return (
187
+ <Card padding={0}>
188
+ <div style={{ padding: "10px 14px", borderBottom: "1px solid var(--af-border)" }}>
189
+ <strong style={{ fontSize: 13 }}>Recent runs</strong>
190
+ <span style={{ fontSize: 11, color: "var(--af-text-tertiary)", marginLeft: 8 }}>{traces.length}</span>
191
+ </div>
192
+ <ul style={{ listStyle: "none", padding: 0, margin: 0, maxHeight: 600, overflow: "auto" }}>
193
+ {traces.map(t => {
194
+ const isActive = t.pid != null && activePids.has(t.pid);
195
+ const status: TraceSummary["status"] = isActive ? "running" : t.status;
196
+ const badge = STATUS_BADGE[status];
197
+ const isSelected = selected === t.run_id;
198
+ return (
199
+ <li
200
+ key={t.run_id}
201
+ onClick={() => onSelect(t.run_id)}
202
+ style={{
203
+ padding: "10px 14px",
204
+ cursor: "pointer",
205
+ borderBottom: "1px solid var(--af-border)",
206
+ background: isSelected ? "var(--af-bg-soft, rgba(59,130,246,0.08))" : "transparent",
207
+ }}
208
+ >
209
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 }}>
210
+ <div style={{ minWidth: 0, flex: 1 }}>
211
+ <div style={{ fontSize: 12, fontWeight: 500, display: "flex", alignItems: "center", gap: 6 }}>
212
+ <KindDot kind={t.kind} />
213
+ <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
214
+ {t.kind} · {t.model}
215
+ </span>
216
+ </div>
217
+ <div style={{ fontSize: 10, color: "var(--af-text-tertiary)", marginTop: 2, fontFamily: "var(--font-mono, ui-monospace)" }}>
218
+ {t.run_id.slice(0, 19)}…{t.run_id.slice(-8)}
219
+ </div>
220
+ </div>
221
+ <span style={{ fontSize: 10, padding: "2px 7px", borderRadius: 5, background: badge.bg, color: badge.fg, fontWeight: 600 }}>
222
+ {badge.label}
223
+ </span>
224
+ </div>
225
+ <div style={{ display: "flex", justifyContent: "space-between", marginTop: 4, fontSize: 10, color: "var(--af-text-tertiary)" }}>
226
+ <span>{t.started_at ? fmtAge(t.started_at) : "—"}</span>
227
+ <span>
228
+ {t.elapsed_ms != null ? fmtElapsed(t.elapsed_ms) : "…"}
229
+ {t.output_tokens != null ? ` · ${t.output_tokens.toLocaleString()} out` : ""}
230
+ </span>
231
+ </div>
232
+ </li>
233
+ );
234
+ })}
235
+ </ul>
236
+ </Card>
237
+ );
238
+ }
239
+
240
+ function RunDetail({ runId }: { runId: string }) {
241
+ const [events, setEvents] = useState<StreamEvent[]>([]);
242
+ const [open, setOpen] = useState(false);
243
+ const [closed, setClosed] = useState<string | null>(null);
244
+ const eventsBoxRef = useRef<HTMLDivElement>(null);
245
+ const [showPrompts, setShowPrompts] = useState(false);
246
+
247
+ // Reset + reconnect when the user picks a different run.
248
+ useEffect(() => {
249
+ setEvents([]);
250
+ setOpen(false);
251
+ setClosed(null);
252
+ setShowPrompts(false);
253
+ const es = new EventSource(`/api/runs/${runId}/stream`);
254
+ // EventSource has built-in `open` and `error` DOM events that fire with no
255
+ // `data`. Server-side we deliberately use `connected` / `stream_error` for
256
+ // our payloads to avoid collision; this listener handles the DOM open.
257
+ es.onopen = () => setOpen(true);
258
+ const safeParse = (raw: unknown): unknown => {
259
+ if (typeof raw !== "string" || raw === "") return null;
260
+ try { return JSON.parse(raw); } catch { return null; }
261
+ };
262
+ es.addEventListener("connected", e => {
263
+ const d = safeParse((e as MessageEvent).data) as { run_id: string; path: string } | null;
264
+ if (d) setEvents(prev => [...prev, { sseType: "connected", data: d }]);
265
+ });
266
+ es.addEventListener("meta", e => {
267
+ const d = safeParse((e as MessageEvent).data) as { _meta: { type: string; [k: string]: unknown } } | null;
268
+ if (d) setEvents(prev => [...prev, { sseType: "meta", data: d }]);
269
+ });
270
+ es.addEventListener("event", e => {
271
+ const d = safeParse((e as MessageEvent).data) as Record<string, unknown> | null;
272
+ if (d) setEvents(prev => [...prev, { sseType: "event", data: d }]);
273
+ });
274
+ es.addEventListener("done", e => {
275
+ const d = safeParse((e as MessageEvent).data) as { reason: string } | null;
276
+ if (d) {
277
+ setEvents(prev => [...prev, { sseType: "done", data: d }]);
278
+ setClosed(d.reason);
279
+ }
280
+ es.close();
281
+ });
282
+ es.addEventListener("stream_error", e => {
283
+ const d = safeParse((e as MessageEvent).data) as { message: string } | null;
284
+ if (d) setEvents(prev => [...prev, { sseType: "stream_error", data: d }]);
285
+ });
286
+ es.onerror = () => {
287
+ // EventSource auto-retries on transport errors; mark as closed only if
288
+ // we never managed to open the connection in the first place.
289
+ setOpen(currentOpen => {
290
+ if (!currentOpen) setClosed("connect-error");
291
+ return currentOpen;
292
+ });
293
+ };
294
+ return () => { es.close(); };
295
+ }, [runId]);
296
+
297
+ // Auto-scroll to bottom when new events arrive.
298
+ useEffect(() => {
299
+ if (eventsBoxRef.current) eventsBoxRef.current.scrollTop = eventsBoxRef.current.scrollHeight;
300
+ }, [events.length]);
301
+
302
+ // Pull out the payload meta for the prompt panel.
303
+ const payloadEvent = events.find(e => e.sseType === "meta" && (e.data as { _meta: { type: string } })._meta.type === "payload");
304
+ const startEvent = events.find(e => e.sseType === "meta" && (e.data as { _meta: { type: string } })._meta.type === "start");
305
+ const endEvent = events.find(e => e.sseType === "meta" && (e.data as { _meta: { type: string } })._meta.type === "end");
306
+ const payload = payloadEvent ? (payloadEvent.data as { _meta: { system_prompt: string; user_prompt: string; reminder: string | null } })._meta : null;
307
+ const startMeta = startEvent ? (startEvent.data as { _meta: { kind: string; model: string; ts: string } })._meta : null;
308
+ const endMeta = endEvent ? (endEvent.data as { _meta: { elapsed_ms: number; exit_code: number; content_chars: number; output_tokens: number; input_tokens: number; stderr_tail: string | null } })._meta : null;
309
+
310
+ // Compute live token counts from streamed assistant events.
311
+ const liveTokens = useMemo(() => {
312
+ let inT = 0, outT = 0, cacheCreate = 0, cacheRead = 0, contentChars = 0;
313
+ for (const ev of events) {
314
+ if (ev.sseType !== "event") continue;
315
+ const obj = ev.data as { type?: string; message?: { content?: Array<{ type?: string; text?: string }>; usage?: { input_tokens?: number; output_tokens?: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number } } };
316
+ if (obj.type === "assistant" && obj.message) {
317
+ const u = obj.message.usage;
318
+ if (u) {
319
+ inT = u.input_tokens ?? inT;
320
+ outT = u.output_tokens ?? outT;
321
+ cacheCreate = u.cache_creation_input_tokens ?? cacheCreate;
322
+ cacheRead = u.cache_read_input_tokens ?? cacheRead;
323
+ }
324
+ const content = obj.message.content;
325
+ if (Array.isArray(content)) {
326
+ for (const blk of content) {
327
+ if (blk.type === "text" && typeof blk.text === "string") contentChars += blk.text.length;
328
+ }
329
+ }
330
+ }
331
+ }
332
+ return { inT, outT, cacheCreate, cacheRead, contentChars };
333
+ }, [events]);
334
+
335
+ return (
336
+ <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
337
+ {/* Header */}
338
+ <Card>
339
+ <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }}>
340
+ <div>
341
+ <div style={{ fontSize: 13, fontWeight: 600 }}>
342
+ {startMeta ? (
343
+ <>
344
+ <KindDot kind={startMeta.kind} /> {startMeta.kind} · <span style={{ color: "var(--af-text-secondary)" }}>{startMeta.model}</span>
345
+ </>
346
+ ) : (
347
+ "loading…"
348
+ )}
349
+ </div>
350
+ <div style={{ fontSize: 10, color: "var(--af-text-tertiary)", fontFamily: "var(--font-mono, ui-monospace)", marginTop: 3 }}>
351
+ {runId}
352
+ </div>
353
+ </div>
354
+ <div style={{ fontSize: 11, color: "var(--af-text-tertiary)", textAlign: "right" }}>
355
+ {open && !closed && <span style={{ color: "#3b82f6" }}>● live</span>}
356
+ {closed && <span style={{ color: closed === "end" ? "#16a34a" : "#dc2626" }}>● {closed}</span>}
357
+ </div>
358
+ </div>
359
+ <div style={{ marginTop: 8, fontSize: 11, display: "flex", gap: 14, flexWrap: "wrap", color: "var(--af-text-secondary)" }}>
360
+ <span>in: <strong>{liveTokens.inT.toLocaleString()}</strong></span>
361
+ <span>out: <strong>{liveTokens.outT.toLocaleString()}</strong></span>
362
+ <span>cache_create: <strong>{liveTokens.cacheCreate.toLocaleString()}</strong></span>
363
+ <span>cache_read: <strong>{liveTokens.cacheRead.toLocaleString()}</strong></span>
364
+ <span>chars: <strong>{liveTokens.contentChars.toLocaleString()}</strong></span>
365
+ {endMeta && <span>elapsed: <strong>{fmtElapsed(endMeta.elapsed_ms)}</strong></span>}
366
+ {endMeta && endMeta.exit_code !== 0 && <span style={{ color: "#dc2626" }}>exit: {endMeta.exit_code}</span>}
367
+ </div>
368
+ {endMeta && endMeta.stderr_tail && (
369
+ <pre style={{ marginTop: 8, padding: 8, background: "rgba(239, 68, 68, 0.08)", border: "1px solid rgba(239,68,68,0.2)", borderRadius: 6, fontSize: 11, whiteSpace: "pre-wrap", color: "#dc2626", maxHeight: 120, overflow: "auto" }}>
370
+ {endMeta.stderr_tail}
371
+ </pre>
372
+ )}
373
+ </Card>
374
+
375
+ {/* Prompt panel — collapsed by default, this is what was actually sent */}
376
+ <Card>
377
+ <button
378
+ onClick={() => setShowPrompts(s => !s)}
379
+ style={{ background: "transparent", border: "none", padding: 0, cursor: "pointer", color: "var(--af-text-secondary)", fontSize: 12, display: "flex", alignItems: "center", gap: 6 }}
380
+ >
381
+ {showPrompts ? "▾" : "▸"} Prompts ({payload ? `${payload.system_prompt.length + payload.user_prompt.length + (payload.reminder?.length ?? 0)} chars` : "—"})
382
+ </button>
383
+ {showPrompts && payload && (
384
+ <div style={{ marginTop: 10, display: "flex", flexDirection: "column", gap: 10 }}>
385
+ <PromptBlock label="System prompt" content={payload.system_prompt} />
386
+ <PromptBlock label="User prompt" content={payload.user_prompt} />
387
+ {payload.reminder && <PromptBlock label="Reminder (retry)" content={payload.reminder} />}
388
+ </div>
389
+ )}
390
+ </Card>
391
+
392
+ {/* Streamed events */}
393
+ <Card>
394
+ <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 8 }}>
395
+ <strong style={{ fontSize: 13 }}>Stream events</strong>
396
+ <span style={{ fontSize: 11, color: "var(--af-text-tertiary)" }}>{events.length} msgs</span>
397
+ </div>
398
+ <div
399
+ ref={eventsBoxRef}
400
+ style={{
401
+ maxHeight: 480,
402
+ overflow: "auto",
403
+ background: "var(--af-bg-soft, rgba(0,0,0,0.04))",
404
+ borderRadius: 6,
405
+ padding: 8,
406
+ fontSize: 11,
407
+ fontFamily: "var(--font-mono, ui-monospace)",
408
+ display: "flex",
409
+ flexDirection: "column",
410
+ gap: 6,
411
+ }}
412
+ >
413
+ {events.map((ev, i) => <EventRow key={i} ev={ev} />)}
414
+ </div>
415
+ </Card>
416
+ </div>
417
+ );
418
+ }
419
+
420
+ function PromptBlock({ label, content }: { label: string; content: string }) {
421
+ return (
422
+ <div>
423
+ <div style={{ fontSize: 11, color: "var(--af-text-tertiary)", marginBottom: 4 }}>
424
+ {label} · {content.length.toLocaleString()} chars
425
+ </div>
426
+ <pre style={{ margin: 0, padding: 8, background: "var(--af-bg-soft, rgba(0,0,0,0.04))", borderRadius: 6, fontSize: 11, lineHeight: 1.5, whiteSpace: "pre-wrap", maxHeight: 280, overflow: "auto" }}>
427
+ {content}
428
+ </pre>
429
+ </div>
430
+ );
431
+ }
432
+
433
+ function EventRow({ ev }: { ev: StreamEvent }) {
434
+ if (ev.sseType === "connected") {
435
+ const d = ev.data;
436
+ return <Row tone="info"><b>connected</b> {d.run_id} ({d.path})</Row>;
437
+ }
438
+ if (ev.sseType === "done") {
439
+ return <Row tone="info"><b>done</b> reason={ev.data.reason}</Row>;
440
+ }
441
+ if (ev.sseType === "raw") {
442
+ return <Row tone="warn"><b>raw</b> {ev.data.line.slice(0, 200)}</Row>;
443
+ }
444
+ if (ev.sseType === "stream_error") {
445
+ return <Row tone="error"><b>stream_error</b> {ev.data.message}</Row>;
446
+ }
447
+ if (ev.sseType === "meta") {
448
+ const meta = ev.data._meta;
449
+ return <Row tone="meta"><b>{meta.type}</b> {summarizeMeta(meta)}</Row>;
450
+ }
451
+ // event from claude stream-json
452
+ const obj = ev.data as { type?: string; message?: { content?: Array<{ type?: string; text?: string; thinking?: string }>; model?: string }; rate_limit_info?: { status?: string; rateLimitType?: string; resetsAt?: number } };
453
+ if (obj.type === "system") {
454
+ return <Row tone="info"><b>system.init</b> tools={(obj as unknown as { tools?: unknown[] }).tools?.length ?? 0} mcp={(obj as unknown as { mcp_servers?: unknown[] }).mcp_servers?.length ?? 0}</Row>;
455
+ }
456
+ if (obj.type === "assistant" && obj.message?.content) {
457
+ const blocks = obj.message.content;
458
+ const summary = blocks.map(b => b.type === "text" ? `text(${(b.text ?? "").length})` : b.type === "thinking" ? `thinking(${(b.thinking ?? "").length})` : b.type ?? "?").join("+");
459
+ return <Row tone="ok"><b>assistant</b> {summary}</Row>;
460
+ }
461
+ if (obj.type === "rate_limit_event") {
462
+ const r = obj.rate_limit_info;
463
+ return <Row tone="warn"><b>rate_limit</b> status={r?.status} type={r?.rateLimitType} {r?.resetsAt ? `resets=${new Date(r.resetsAt * 1000).toLocaleTimeString()}` : ""}</Row>;
464
+ }
465
+ if (obj.type === "result") {
466
+ return <Row tone="ok"><b>result</b> {summarizeResult(obj as unknown as Record<string, unknown>)}</Row>;
467
+ }
468
+ return <Row tone="default"><b>{obj.type ?? "?"}</b> {JSON.stringify(obj).slice(0, 160)}</Row>;
469
+ }
470
+
471
+ function Row({ children, tone }: { children: React.ReactNode; tone: "info" | "ok" | "warn" | "error" | "meta" | "default" }) {
472
+ const colors: Record<typeof tone, string> = {
473
+ info: "var(--af-text-secondary)",
474
+ ok: "#16a34a",
475
+ warn: "#d97706",
476
+ error: "#dc2626",
477
+ meta: "#a855f7",
478
+ default: "var(--af-text-secondary)",
479
+ };
480
+ return <div style={{ color: colors[tone], lineHeight: 1.5 }}>{children}</div>;
481
+ }
482
+
483
+ function summarizeMeta(meta: { type: string; [k: string]: unknown }): string {
484
+ const { type, ...rest } = meta;
485
+ if (type === "payload") {
486
+ const m = rest as { system_prompt?: string; user_prompt?: string; reminder?: string | null };
487
+ return `system=${m.system_prompt?.length ?? 0}c user=${m.user_prompt?.length ?? 0}c reminder=${m.reminder?.length ?? 0}c`;
488
+ }
489
+ if (type === "spawned") return `pid=${(rest as { pid?: number }).pid}`;
490
+ if (type === "start") {
491
+ const m = rest as { kind?: string; model?: string; user_prompt_chars?: number };
492
+ return `${m.kind} ${m.model} user=${m.user_prompt_chars}c`;
493
+ }
494
+ if (type === "end") {
495
+ const m = rest as { elapsed_ms?: number; exit_code?: number; output_tokens?: number; content_chars?: number };
496
+ return `elapsed=${m.elapsed_ms}ms exit=${m.exit_code} out=${m.output_tokens}t chars=${m.content_chars}`;
497
+ }
498
+ return JSON.stringify(rest).slice(0, 200);
499
+ }
500
+
501
+ function summarizeResult(r: Record<string, unknown>): string {
502
+ const usage = r.usage as { input_tokens?: number; output_tokens?: number; cache_creation_input_tokens?: number; cache_read_input_tokens?: number } | undefined;
503
+ const cost = r.total_cost_usd as number | undefined;
504
+ const dur = r.duration_ms as number | undefined;
505
+ const parts: string[] = [];
506
+ if (dur != null) parts.push(`${dur}ms`);
507
+ if (usage?.input_tokens != null) parts.push(`in=${usage.input_tokens}`);
508
+ if (usage?.output_tokens != null) parts.push(`out=${usage.output_tokens}`);
509
+ if (usage?.cache_creation_input_tokens) parts.push(`cache_create=${usage.cache_creation_input_tokens.toLocaleString()}`);
510
+ if (usage?.cache_read_input_tokens) parts.push(`cache_read=${usage.cache_read_input_tokens.toLocaleString()}`);
511
+ if (cost != null) parts.push(`cost=$${cost.toFixed(4)}`);
512
+ return parts.join(" ");
513
+ }
@@ -14,6 +14,9 @@ import {
14
14
  Activity,
15
15
  Gauge,
16
16
  Lightbulb,
17
+ Radio,
18
+ Settings,
19
+ CalendarDays,
17
20
  } from "lucide-react";
18
21
  import { formatRelative, prettyProjectName } from "@/lib/format";
19
22
  import { ThemeToggle } from "@/components/theme-toggle";
@@ -181,11 +184,17 @@ export function Sidebar({
181
184
  Projects
182
185
  </NavLink>
183
186
  <NavLink
184
- href="/parallelism"
185
- active={pathname === "/parallelism"}
186
- icon={<GitBranch size={15} />}
187
+ href="/day"
188
+ active={
189
+ pathname === "/day" ||
190
+ pathname.startsWith("/day/") ||
191
+ pathname === "/digest" ||
192
+ pathname.startsWith("/digest/") ||
193
+ pathname === "/parallelism"
194
+ }
195
+ icon={<CalendarDays size={15} />}
187
196
  >
188
- Timeline
197
+ Day
189
198
  </NavLink>
190
199
  <NavLink
191
200
  href="/usage"
@@ -201,6 +210,13 @@ export function Sidebar({
201
210
  >
202
211
  Insights
203
212
  </NavLink>
213
+ <NavLink
214
+ href="/runs"
215
+ active={pathname === "/runs" || pathname.startsWith("/runs/")}
216
+ icon={<Radio size={15} />}
217
+ >
218
+ Runs
219
+ </NavLink>
204
220
  </nav>
205
221
 
206
222
  {/* Projects search + list */}
@@ -267,6 +283,24 @@ export function Sidebar({
267
283
  <span style={{ flex: 1, minWidth: 0 }}>
268
284
  {totalSessions} sessions · {projects.length} projects
269
285
  </span>
286
+ <Link
287
+ href="/settings"
288
+ aria-label="Settings"
289
+ title="Settings"
290
+ style={{
291
+ display: "inline-flex",
292
+ alignItems: "center",
293
+ justifyContent: "center",
294
+ width: 24,
295
+ height: 24,
296
+ borderRadius: 6,
297
+ color: pathname === "/settings" ? "var(--af-text)" : "var(--af-text-tertiary)",
298
+ background: pathname === "/settings" ? "var(--af-surface-hover)" : "transparent",
299
+ textDecoration: "none",
300
+ }}
301
+ >
302
+ <Settings size={14} />
303
+ </Link>
270
304
  <ThemeToggle />
271
305
  </div>
272
306
  </aside>
@@ -76,6 +76,7 @@ export function UsageChartRange({
76
76
  endMs,
77
77
  windowMs,
78
78
  colorVar,
79
+ predictedSeries,
79
80
  }: {
80
81
  snapshots: UsageSnapshot[];
81
82
  seriesKey: SeriesKey;
@@ -84,6 +85,10 @@ export function UsageChartRange({
84
85
  /** Window duration in ms — 5h or 7d depending on seriesKey */
85
86
  windowMs: number;
86
87
  colorVar: string;
88
+ /** JSONL-derived utilization estimates (one per snapshot timestamp).
89
+ * Drawn per cycle as a dashed overlay so historical cycles can be
90
+ * compared against the prediction. */
91
+ predictedSeries?: { capturedAt: number; util: number }[];
87
92
  }) {
88
93
  const width = 1280;
89
94
  const height = 320;
@@ -100,12 +105,17 @@ export function UsageChartRange({
100
105
  }
101
106
  if (valid.length === 0) return null;
102
107
 
103
- // 2. Group into cycles by resets_at. All snapshots sharing the same
104
- // resets_at belong to the same rolling window.
105
- const byReset = new Map<string, typeof valid>();
108
+ // 2. Group into cycles by resets_at. Anthropic jitters resets_at by
109
+ // milliseconds across snapshots in the same cycle, so we hour-round
110
+ // the bucket key — without this, "complete cycles" inflates from
111
+ // ~4 to ~2000 over a 30-day window.
112
+ const HOUR = 3_600_000;
113
+ const byReset = new Map<number, typeof valid>();
106
114
  for (const p of valid) {
107
115
  if (!p.resetsAt) continue;
108
- const key = p.resetsAt;
116
+ const ms = new Date(p.resetsAt).getTime();
117
+ if (Number.isNaN(ms)) continue;
118
+ const key = Math.round(ms / HOUR) * HOUR;
109
119
  let bucket = byReset.get(key);
110
120
  if (!bucket) {
111
121
  bucket = [];
@@ -115,8 +125,7 @@ export function UsageChartRange({
115
125
  }
116
126
 
117
127
  const cycles: CycleData[] = [];
118
- for (const [resetsAtIso, points] of byReset) {
119
- const resetsAtMs = new Date(resetsAtIso).getTime();
128
+ for (const [resetsAtMs, points] of byReset) {
120
129
  const cycleStartMs = resetsAtMs - windowMs;
121
130
  // Sort points by time
122
131
  points.sort((a, b) => a.t - b.t);
@@ -439,6 +448,49 @@ export function UsageChartRange({
439
448
  );
440
449
  })}
441
450
 
451
+ {/* Predicted overlay — single continuous path across the visible
452
+ * range, broken when pred drops >= 50pp (= cycle reset). One path
453
+ * covers daemon-backed AND cold-start periods uniformly, so a
454
+ * 30D / 90D zoomout shows the prediction over its whole span. */}
455
+ {(() => {
456
+ const pred = (predictedSeries ?? [])
457
+ .filter((p) => p.capturedAt >= startMs && p.capturedAt <= endMs)
458
+ .sort((a, b) => a.capturedAt - b.capturedAt);
459
+ if (pred.length < 2) return null;
460
+ const parts: string[] = [];
461
+ let inSeg = false;
462
+ let prevUtil = pred[0]!.util;
463
+ for (let i = 0; i < pred.length; i++) {
464
+ const p = pred[i]!;
465
+ const u = Math.max(0, Math.min(100, p.util));
466
+ const remaining = 100 - u;
467
+ const drop = prevUtil - u;
468
+ const isReset = i > 0 && drop >= 50;
469
+ // Predicted series is sampled at 30-min granularity by the
470
+ // calibrator (vs 5-min for daemon snapshots), so the snapshot
471
+ // gap threshold rejects every adjacent pair as a "gap" and the
472
+ // line never connects. Use 60 min instead — anything beyond
473
+ // that is a genuine missing range.
474
+ const isGap = i > 0 && p.capturedAt - pred[i - 1]!.capturedAt > 60 * 60 * 1000;
475
+ const cmd = !inSeg || isReset || isGap ? "M" : "L";
476
+ parts.push(`${cmd} ${xScale(p.capturedAt).toFixed(1)} ${yScale(remaining).toFixed(1)}`);
477
+ inSeg = true;
478
+ prevUtil = u;
479
+ }
480
+ return (
481
+ <path
482
+ d={parts.join(" ")}
483
+ fill="none"
484
+ stroke="#ff7a00"
485
+ strokeOpacity="0.85"
486
+ strokeWidth="1.5"
487
+ strokeLinecap="round"
488
+ strokeLinejoin="round"
489
+ strokeDasharray="5 4"
490
+ />
491
+ );
492
+ })()}
493
+
442
494
  {/* Current-time vertical marker */}
443
495
  {Date.now() >= startMs && Date.now() <= endMs && (
444
496
  <line