fleetlens 0.15.7 → 0.16.1

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 (316) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/app-path-routes-manifest.json +9 -0
  3. package/app/apps/web/.next/build-manifest.json +3 -3
  4. package/app/apps/web/.next/prerender-manifest.json +3 -3
  5. package/app/apps/web/.next/routes-manifest.json +62 -0
  6. package/app/apps/web/.next/server/app/_global-error/page.js +1 -1
  7. package/app/apps/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  9. package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
  10. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  16. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  17. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  18. package/app/apps/web/.next/server/app/agent/page/app-paths-manifest.json +3 -0
  19. package/app/apps/web/.next/server/app/agent/page/build-manifest.json +17 -0
  20. package/app/apps/web/.next/server/app/agent/page/next-font-manifest.json +6 -0
  21. package/app/apps/web/.next/server/app/agent/page/react-loadable-manifest.json +1 -0
  22. package/app/apps/web/.next/server/app/agent/page/server-reference-manifest.json +4 -0
  23. package/app/apps/web/.next/server/app/agent/page.js +18 -0
  24. package/app/apps/web/.next/server/app/agent/page.js.map +5 -0
  25. package/app/apps/web/.next/server/app/agent/page.js.nft.json +1 -0
  26. package/app/apps/web/.next/server/app/agent/page_client-reference-manifest.js +3 -0
  27. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/app-paths-manifest.json +3 -0
  28. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/build-manifest.json +9 -0
  29. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/server-reference-manifest.json +4 -0
  30. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js +7 -0
  31. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js.map +5 -0
  32. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js.nft.json +1 -0
  33. package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route_client-reference-manifest.js +3 -0
  34. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/app-paths-manifest.json +3 -0
  35. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/build-manifest.json +9 -0
  36. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/server-reference-manifest.json +4 -0
  37. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js +9 -0
  38. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.map +5 -0
  39. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.nft.json +1 -0
  40. package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route_client-reference-manifest.js +3 -0
  41. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/app-paths-manifest.json +3 -0
  42. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/build-manifest.json +9 -0
  43. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/server-reference-manifest.json +4 -0
  44. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js +7 -0
  45. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js.map +5 -0
  46. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js.nft.json +1 -0
  47. package/app/apps/web/.next/server/app/api/agent/chats/[id]/route_client-reference-manifest.js +3 -0
  48. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/app-paths-manifest.json +3 -0
  49. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/build-manifest.json +9 -0
  50. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/server-reference-manifest.json +4 -0
  51. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js +7 -0
  52. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js.map +5 -0
  53. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js.nft.json +1 -0
  54. package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route_client-reference-manifest.js +3 -0
  55. package/app/apps/web/.next/server/app/api/agent/chats/route/app-paths-manifest.json +3 -0
  56. package/app/apps/web/.next/server/app/api/agent/chats/route/build-manifest.json +9 -0
  57. package/app/apps/web/.next/server/app/api/agent/chats/route/server-reference-manifest.json +4 -0
  58. package/app/apps/web/.next/server/app/api/agent/chats/route.js +7 -0
  59. package/app/apps/web/.next/server/app/api/agent/chats/route.js.map +5 -0
  60. package/app/apps/web/.next/server/app/api/agent/chats/route.js.nft.json +1 -0
  61. package/app/apps/web/.next/server/app/api/agent/chats/route_client-reference-manifest.js +3 -0
  62. package/app/apps/web/.next/server/app/api/agent/index/route/app-paths-manifest.json +3 -0
  63. package/app/apps/web/.next/server/app/api/agent/index/route/build-manifest.json +9 -0
  64. package/app/apps/web/.next/server/app/api/agent/index/route/server-reference-manifest.json +4 -0
  65. package/app/apps/web/.next/server/app/api/agent/index/route.js +9 -0
  66. package/app/apps/web/.next/server/app/api/agent/index/route.js.map +5 -0
  67. package/app/apps/web/.next/server/app/api/agent/index/route.js.nft.json +1 -0
  68. package/app/apps/web/.next/server/app/api/agent/index/route_client-reference-manifest.js +3 -0
  69. package/app/apps/web/.next/server/app/api/agent/mcp/route/app-paths-manifest.json +3 -0
  70. package/app/apps/web/.next/server/app/api/agent/mcp/route/build-manifest.json +9 -0
  71. package/app/apps/web/.next/server/app/api/agent/mcp/route/server-reference-manifest.json +4 -0
  72. package/app/apps/web/.next/server/app/api/agent/mcp/route.js +10 -0
  73. package/app/apps/web/.next/server/app/api/agent/mcp/route.js.map +5 -0
  74. package/app/apps/web/.next/server/app/api/agent/mcp/route.js.nft.json +1 -0
  75. package/app/apps/web/.next/server/app/api/agent/mcp/route_client-reference-manifest.js +3 -0
  76. package/app/apps/web/.next/server/app/api/agent/suggestions/route/app-paths-manifest.json +3 -0
  77. package/app/apps/web/.next/server/app/api/agent/suggestions/route/build-manifest.json +9 -0
  78. package/app/apps/web/.next/server/app/api/agent/suggestions/route/server-reference-manifest.json +4 -0
  79. package/app/apps/web/.next/server/app/api/agent/suggestions/route.js +12 -0
  80. package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.map +5 -0
  81. package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.nft.json +1 -0
  82. package/app/apps/web/.next/server/app/api/agent/suggestions/route_client-reference-manifest.js +3 -0
  83. package/app/apps/web/.next/server/app/api/ask/route.js +3 -4
  84. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  85. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +4 -4
  86. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +4 -4
  88. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  89. package/app/apps/web/.next/server/app/api/digest/month-index/route.js +2 -2
  90. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  91. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +4 -4
  92. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  93. package/app/apps/web/.next/server/app/api/digest/week-index/route.js +2 -2
  94. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  95. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  96. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  97. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +3 -3
  98. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  99. package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.nft.json +1 -1
  100. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  101. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  102. package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
  103. package/app/apps/web/.next/server/app/api/settings/credentials/route.js +5 -5
  104. package/app/apps/web/.next/server/app/api/settings/credentials/route.js.nft.json +1 -1
  105. package/app/apps/web/.next/server/app/api/settings/route.js +3 -3
  106. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  107. package/app/apps/web/.next/server/app/api/team/autostart/route.js.nft.json +1 -1
  108. package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.nft.json +1 -1
  109. package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.nft.json +1 -1
  110. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
  111. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  112. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  113. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  114. package/app/apps/web/.next/server/app/day/[date]/page.js +2 -2
  115. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  116. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  117. package/app/apps/web/.next/server/app/day/page.js +1 -1
  118. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  119. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  120. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  121. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  122. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  123. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  124. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  125. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  126. package/app/apps/web/.next/server/app/insights/[key]/page.js +2 -2
  127. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  128. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  129. package/app/apps/web/.next/server/app/insights/page.js +2 -2
  130. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  131. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  132. package/app/apps/web/.next/server/app/page.js +2 -2
  133. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  134. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  135. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  136. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  137. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  138. package/app/apps/web/.next/server/app/print/[key]/page.js +2 -2
  139. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  140. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  141. package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
  142. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  143. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  144. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  145. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  146. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  147. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  148. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  149. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  150. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  151. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  152. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  153. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  154. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  155. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  156. package/app/apps/web/.next/server/app/settings/page.js +2 -2
  157. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  158. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  159. package/app/apps/web/.next/server/app/team/onboarding/page.js +1 -1
  160. package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -1
  161. package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +1 -1
  162. package/app/apps/web/.next/server/app/team/page.js +1 -1
  163. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  164. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  165. package/app/apps/web/.next/server/app/usage/page.js +3 -2
  166. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  167. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  168. package/app/apps/web/.next/server/app-paths-manifest.json +9 -0
  169. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_agent_chats_[id]_events_route_actions_0fkgl_r.js +3 -0
  170. package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_agent_chats_[id]_stop_route_actions_036fon_.js +3 -0
  171. package/app/apps/web/.next/server/chunks/09og__next-internal_server_app_api_agent_chats_[id]_messages_route_actions_0ubwh4q.js +3 -0
  172. package/app/apps/web/.next/server/chunks/[root-of-the-server]__01~cgp8._.js +3 -0
  173. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02faosk._.js +30 -0
  174. package/app/apps/web/.next/server/chunks/[root-of-the-server]__02puo0u._.js +3 -0
  175. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0367vbv._.js +3 -0
  176. package/app/apps/web/.next/server/chunks/[root-of-the-server]__04g-b-m._.js +9 -0
  177. package/app/apps/web/.next/server/chunks/[root-of-the-server]__06xto5z._.js +9 -0
  178. package/app/apps/web/.next/server/chunks/[root-of-the-server]__096_llw._.js +3 -0
  179. package/app/apps/web/.next/server/chunks/[root-of-the-server]__09cgmam._.js +28 -0
  180. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_5tf-2._.js +21 -0
  181. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gvw6dk._.js +3 -0
  182. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mx_9mb._.js +5 -0
  183. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0pirsh0._.js +9 -0
  184. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0sbhe0s._.js +3 -0
  185. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0tk8p3d._.js +28 -0
  186. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0u6pamj._.js +28 -0
  187. package/app/apps/web/.next/server/chunks/[root-of-the-server]__123tfcb._.js +3 -0
  188. package/app/apps/web/.next/server/chunks/[root-of-the-server]__12jomao._.js +28 -0
  189. package/app/apps/web/.next/server/chunks/_132c.4p._.js +3 -0
  190. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_chats_[id]_route_actions_0b95y8h.js +3 -0
  191. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_chats_route_actions_07b4w1x.js +3 -0
  192. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_index_route_actions_03~ylvr.js +3 -0
  193. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_mcp_route_actions_0-b30h..js +3 -0
  194. package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_suggestions_route_actions_0r_5s~k.js +3 -0
  195. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0sr4wr_.js +13 -0
  196. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0z_xi9k.js +16 -0
  197. package/app/apps/web/.next/server/chunks/{packages_0o6.gpk._.js → packages_entries_dist_0-0s5ui._.js} +2 -2
  198. package/app/apps/web/.next/server/chunks/packages_entries_dist_0~0d7ku._.js +3 -0
  199. package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +8 -8
  200. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  201. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  202. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0e.gxk3._.js → [root-of-the-server]__0.wm54e._.js} +2 -2
  203. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__04il6m2._.js → [root-of-the-server]__04qodwp._.js} +2 -2
  204. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0k1x-c9._.js → [root-of-the-server]__07~kk2~._.js} +2 -2
  205. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__03uoeiw._.js → [root-of-the-server]__0_78i1t._.js} +2 -2
  206. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0amklf0._.js +3 -0
  207. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0nr2~y1._.js → [root-of-the-server]__0d5y90m._.js} +2 -2
  208. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0f3q8gt._.js +3 -0
  209. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__03svi~4._.js → [root-of-the-server]__0iv286q._.js} +2 -2
  210. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0j683am._.js +3 -0
  211. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0rvzdnx._.js → [root-of-the-server]__0kml6jt._.js} +2 -2
  212. package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__08l86e-._.js → [root-of-the-server]__0lmftab._.js} +2 -2
  213. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0vo.r~s._.js +3 -0
  214. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~8.fgs._.js +3 -0
  215. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__11xp-1u._.js +3 -0
  216. package/app/apps/web/.next/server/chunks/ssr/_0k8yioi._.js +1 -1
  217. package/app/apps/web/.next/server/chunks/ssr/{_13jdm1g._.js → _0~r5ica._.js} +2 -2
  218. package/app/apps/web/.next/server/chunks/ssr/apps_web_0epqfet._.js +3 -0
  219. package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_agent_page_actions_0tuled1.js +3 -0
  220. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
  221. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_[id]_session-view_tsx_0-9t5~.._.js +12 -0
  222. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0x014hn._.js +2 -2
  223. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_agent-chat_tsx_0xbtx2n._.js +5 -0
  224. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_usage-charts-dashboard_tsx_0bgr_uv._.js +1 -1
  225. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  226. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  227. package/app/apps/web/.next/server/chunks/ssr/{node_modules_0yrkyef._.js → node_modules_next_dist_0t-hj0x._.js} +2 -2
  228. package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0~g07z5.js +4 -0
  229. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
  230. package/app/apps/web/.next/server/functions-config-manifest.json +8 -0
  231. package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
  232. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  233. package/app/apps/web/.next/server/pages/500.html +1 -1
  234. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  235. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  236. package/app/apps/web/.next/static/chunks/04feoajklmrl3.js +3 -0
  237. package/app/apps/web/.next/static/chunks/07bzlfv97o1ig.js +10 -0
  238. package/app/apps/web/.next/static/chunks/0at6brbse8wn2.js +14 -0
  239. package/app/apps/web/.next/static/chunks/0d~7p_ye7v7_5.css +1 -0
  240. package/app/apps/web/.next/static/chunks/0iiqpl6d_3i5~.js +1 -0
  241. package/app/apps/web/.next/static/chunks/0qwgm81o9o~um.js +1 -0
  242. package/app/apps/web/.next/static/chunks/{0_0~~ipv90vm3.js → 0rrcj195v1nd..js} +1 -1
  243. package/app/apps/web/.next/static/chunks/{0y3448zrint0k.js → 0~rycwo36m~c3.js} +1 -1
  244. package/app/apps/web/.next/static/chunks/12zmbd3mlcbn_.js +1 -0
  245. package/app/apps/web/.next/static/chunks/16bapkvp5.x37.js +1 -0
  246. package/app/apps/web/app/agent/page.tsx +12 -0
  247. package/app/apps/web/app/api/agent/chats/[id]/events/route.ts +86 -0
  248. package/app/apps/web/app/api/agent/chats/[id]/messages/route.ts +47 -0
  249. package/app/apps/web/app/api/agent/chats/[id]/route.ts +19 -0
  250. package/app/apps/web/app/api/agent/chats/[id]/stop/route.ts +10 -0
  251. package/app/apps/web/app/api/agent/chats/route.ts +26 -0
  252. package/app/apps/web/app/api/agent/index/route.ts +56 -0
  253. package/app/apps/web/app/api/agent/mcp/route.ts +31 -0
  254. package/app/apps/web/app/api/agent/suggestions/route.ts +150 -0
  255. package/app/apps/web/app/usage/page.tsx +166 -2
  256. package/app/apps/web/components/agent-badge.tsx +3 -0
  257. package/app/apps/web/components/agent-chat.tsx +940 -0
  258. package/app/apps/web/components/agent-icon.tsx +42 -4
  259. package/app/apps/web/components/menubar-install-banner.tsx +2 -1
  260. package/app/apps/web/components/sidebar.tsx +8 -21
  261. package/app/apps/web/components/usage-charts-dashboard.tsx +9 -1
  262. package/app/apps/web/lib/agent/chat-model.ts +125 -0
  263. package/app/apps/web/lib/agent/chat-store.test.ts +125 -0
  264. package/app/apps/web/lib/agent/chat-store.ts +59 -0
  265. package/app/apps/web/lib/agent/extract.test.ts +141 -0
  266. package/app/apps/web/lib/agent/extract.ts +75 -0
  267. package/app/apps/web/lib/agent/index-store.ts +206 -0
  268. package/app/apps/web/lib/agent/links.test.ts +27 -0
  269. package/app/apps/web/lib/agent/links.ts +10 -0
  270. package/app/apps/web/lib/agent/mcp.test.ts +83 -0
  271. package/app/apps/web/lib/agent/mcp.ts +85 -0
  272. package/app/apps/web/lib/agent/prompt.test.ts +38 -0
  273. package/app/apps/web/lib/agent/prompt.ts +44 -0
  274. package/app/apps/web/lib/agent/run.ts +426 -0
  275. package/app/apps/web/lib/agent/search.test.ts +111 -0
  276. package/app/apps/web/lib/agent/search.ts +144 -0
  277. package/app/apps/web/lib/agent/suggestions.test.ts +156 -0
  278. package/app/apps/web/lib/agent/suggestions.ts +167 -0
  279. package/app/apps/web/lib/agent/tools.test.ts +98 -0
  280. package/app/apps/web/lib/agent/tools.ts +176 -0
  281. package/app/apps/web/lib/agent/types.ts +50 -0
  282. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  283. package/app/apps/web/package.json +1 -1
  284. package/app/apps/web/public/agent-icons/grok.svg +8 -0
  285. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  286. package/app/apps/web/vitest.config.ts +4 -0
  287. package/dist/daemon-worker.js +1042 -110
  288. package/dist/index.js +1101 -154
  289. package/menubar/FleetlensMenubar.app/Contents/Info.plist +2 -2
  290. package/menubar/FleetlensMenubar.app/Contents/MacOS/FleetlensMenubar +0 -0
  291. package/menubar/FleetlensMenubar.app/Contents/Resources/grok.svg +8 -0
  292. package/menubar/FleetlensMenubar.app/Contents/_CodeSignature/CodeResources +11 -0
  293. package/package.json +1 -1
  294. package/app/apps/web/.next/server/chunks/[root-of-the-server]__03ewxxu._.js +0 -9
  295. package/app/apps/web/.next/server/chunks/[root-of-the-server]__03rmbz6._.js +0 -3
  296. package/app/apps/web/.next/server/chunks/[root-of-the-server]__05z0pcw._.js +0 -3
  297. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0acikq.._.js +0 -9
  298. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b5r~-1._.js +0 -3
  299. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jbc7tz._.js +0 -3
  300. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ow1kn1._.js +0 -9
  301. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0t7i-hy._.js +0 -3
  302. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~xt99s._.js +0 -3
  303. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +0 -21
  304. package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +0 -3
  305. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0g-tomj._.js +0 -3
  306. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +0 -3
  307. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +0 -12
  308. package/app/apps/web/.next/static/chunks/02xxpexzk3rf-.js +0 -1
  309. package/app/apps/web/.next/static/chunks/044mtrzh7d7va.js +0 -10
  310. package/app/apps/web/.next/static/chunks/05-k9oryi9wz1.js +0 -14
  311. package/app/apps/web/.next/static/chunks/05b0dn.r00hq1.js +0 -1
  312. package/app/apps/web/.next/static/chunks/0t2on1dhydyb2.css +0 -1
  313. package/app/apps/web/.next/static/chunks/0tliv91mhmrzn.js +0 -1
  314. /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → d1mwRtSMG-R7wb8o9KxM3}/_buildManifest.js +0 -0
  315. /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → d1mwRtSMG-R7wb8o9KxM3}/_clientMiddlewareManifest.js +0 -0
  316. /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → d1mwRtSMG-R7wb8o9KxM3}/_ssgManifest.js +0 -0
@@ -9,17 +9,55 @@ type Props = {
9
9
  title?: string;
10
10
  };
11
11
 
12
- /** Single-glyph agent indicator: ✦ for Claude, ◆ for Codex (or whatever
13
- * the registered metadata declares), painted in the agent's accent color.
14
- * Returns null for unknown / undefined agents. */
12
+ /**
13
+ * Grok mark path data from OpenUsage ProviderIcons/grok.svg (MIT)
14
+ * https://github.com/robinebers/openusage
15
+ * viewBox 0 0 527.27 578.68
16
+ */
17
+ const GROK_MARK_PATH =
18
+ "M274.67 360.75 L527.27 0 L405.19 0 L213.64 273.58 Z M122.08 578.68 L183.12 491.51 L122.08 404.34 L0 578.68 Z M274.67 578.68 L396.75 578.68 L122.08 186.40 L0 186.40 Z M527.27 43.59 L427.27 186.40 L437.27 578.68 L517.27 578.68 Z";
19
+
20
+ /** Single-glyph agent indicator painted in the agent's accent color.
21
+ * Grok uses the OpenUsage provider mark; other agents use iconChar. */
15
22
  export function AgentIcon({ agent, size = 11, title }: Props) {
16
23
  if (!agent) return null;
17
24
  const meta = getAgentMetadata(agent);
18
25
  if (!meta) return null;
26
+ const label = title ?? meta.displayName;
27
+
28
+ if (agent === "grok") {
29
+ return (
30
+ <span
31
+ aria-hidden
32
+ title={label}
33
+ style={{
34
+ display: "inline-flex",
35
+ alignItems: "center",
36
+ justifyContent: "center",
37
+ flexShrink: 0,
38
+ width: size + 2,
39
+ height: size + 2,
40
+ color: meta.accentColor,
41
+ }}
42
+ >
43
+ <svg
44
+ width={size}
45
+ height={size}
46
+ viewBox="0 0 527.27 578.68"
47
+ fill="currentColor"
48
+ aria-hidden
49
+ style={{ display: "block" }}
50
+ >
51
+ <path d={GROK_MARK_PATH} />
52
+ </svg>
53
+ </span>
54
+ );
55
+ }
56
+
19
57
  return (
20
58
  <span
21
59
  aria-hidden
22
- title={title ?? meta.displayName}
60
+ title={label}
23
61
  style={{
24
62
  display: "inline-flex",
25
63
  alignItems: "center",
@@ -83,7 +83,8 @@ export function MenubarInstallBanner() {
83
83
  }}
84
84
  >
85
85
  <span style={{ flex: 1 }}>
86
- Get live Claude Code / Codex / Z.ai usage in your menu bar —{" "}
86
+ Get live Claude Code / Codex / Z.ai plan usage in your menu bar (Grok
87
+ sessions live on the dashboard) —{" "}
87
88
  <strong>install the Fleetlens widget</strong>.
88
89
  </span>
89
90
  <button
@@ -12,12 +12,10 @@ import {
12
12
  PinOff,
13
13
  Search,
14
14
  Activity,
15
- Gauge,
16
15
  Lightbulb,
17
- Radio,
18
16
  Settings,
19
17
  CalendarDays,
20
- Users,
18
+ Sparkles,
21
19
  } from "lucide-react";
22
20
  import { formatRelative, prettyProjectName } from "@/lib/format";
23
21
  import { ThemeToggle } from "@/components/theme-toggle";
@@ -204,13 +202,6 @@ export function Sidebar({
204
202
  >
205
203
  Day
206
204
  </NavLink>
207
- <NavLink
208
- href="/usage"
209
- active={pathname === "/usage"}
210
- icon={<Gauge size={15} />}
211
- >
212
- Usage
213
- </NavLink>
214
205
  <NavLink
215
206
  href="/insights"
216
207
  active={pathname === "/insights"}
@@ -218,19 +209,15 @@ export function Sidebar({
218
209
  >
219
210
  Insights
220
211
  </NavLink>
212
+ {/* Usage and Team live in the sidebar widgets below (both already
213
+ link to their pages); Runs is reachable from the Agent
214
+ header. Keeping the nav to the primary destinations. */}
221
215
  <NavLink
222
- href="/runs"
223
- active={pathname === "/runs" || pathname.startsWith("/runs/")}
224
- icon={<Radio size={15} />}
225
- >
226
- Runs
227
- </NavLink>
228
- <NavLink
229
- href="/team"
230
- active={pathname === "/team" || pathname.startsWith("/team/")}
231
- icon={<Users size={15} />}
216
+ href="/agent"
217
+ active={pathname === "/agent" || pathname === "/runs" || pathname.startsWith("/runs/")}
218
+ icon={<Sparkles size={15} />}
232
219
  >
233
- Team
220
+ Agent
234
221
  </NavLink>
235
222
  </nav>
236
223
 
@@ -48,9 +48,14 @@ const SONNET_WINDOW: WindowConfig = {
48
48
  export function UsageChartsDashboard({
49
49
  snapshots,
50
50
  predicted,
51
+ /** Override default 5h/7d window labels (e.g. Grok context window). */
52
+ windows,
53
+ hideSonnet,
51
54
  }: {
52
55
  snapshots: UsageSnapshot[];
53
56
  predicted?: PredictedSeriesByKey;
57
+ windows?: WindowConfig[];
58
+ hideSonnet?: boolean;
54
59
  }) {
55
60
  const [expanded, setExpanded] = useState<WindowConfig | null>(null);
56
61
  const emptyPredicted: PredictedSeriesByKey = {
@@ -59,6 +64,7 @@ export function UsageChartsDashboard({
59
64
  seven_day_sonnet: [],
60
65
  };
61
66
  const pred = predicted ?? emptyPredicted;
67
+ const mainWindows = windows ?? MAIN_WINDOWS;
62
68
 
63
69
  // Close on Escape.
64
70
  useEffect(() => {
@@ -82,7 +88,7 @@ export function UsageChartsDashboard({
82
88
 
83
89
  return (
84
90
  <>
85
- {MAIN_WINDOWS.map((w) => (
91
+ {mainWindows.map((w) => (
86
92
  <section
87
93
  key={w.key}
88
94
  style={{ display: "flex", flexDirection: "column", gap: 6 }}
@@ -99,6 +105,7 @@ export function UsageChartsDashboard({
99
105
  </section>
100
106
  ))}
101
107
 
108
+ {!hideSonnet && (
102
109
  <OptionalChart storageKey="cclens:usage:show-sonnet" label="Sonnet 7d utilization">
103
110
  <section style={{ display: "flex", flexDirection: "column", gap: 6 }}>
104
111
  <SectionLabel>{SONNET_WINDOW.label}</SectionLabel>
@@ -112,6 +119,7 @@ export function UsageChartsDashboard({
112
119
  />
113
120
  </section>
114
121
  </OptionalChart>
122
+ )}
115
123
 
116
124
  {expanded && (
117
125
  <ExpandedModal
@@ -0,0 +1,125 @@
1
+
2
+ export type TextSegment = { kind: "text"; text: string };
3
+ export type ToolSegment = {
4
+ kind: "tool";
5
+ id: string;
6
+ name: string;
7
+ input: unknown;
8
+ brief?: string;
9
+ isError?: boolean;
10
+ done: boolean;
11
+ };
12
+ export type Segment = TextSegment | ToolSegment;
13
+
14
+ export type StoredMessage =
15
+ | { role: "user"; text: string }
16
+ | { role: "assistant"; segments: Segment[] };
17
+
18
+ export type ChatStatus = "idle" | "running" | "error";
19
+
20
+ export type Chat = {
21
+ version: 1;
22
+ id: string;
23
+ title: string;
24
+ created_at: string;
25
+ updated_at: string;
26
+ status: ChatStatus;
27
+ /** Highest run-event seq folded into this chat — SSE clients resume after it. */
28
+ lastSeq: number;
29
+ messages: StoredMessage[];
30
+ };
31
+
32
+ export type ChatListItem = {
33
+ id: string;
34
+ title: string;
35
+ updated_at: string;
36
+ status: ChatStatus;
37
+ messageCount: number;
38
+ };
39
+
40
+ /** Normalized event emitted by a run. seq is per-run, starting at 1. */
41
+ export type RunEvent =
42
+ | { seq: number; type: "delta"; text: string }
43
+ | { seq: number; type: "tool"; id: string; name: string; input: unknown }
44
+ | { seq: number; type: "tool_result"; id: string; name: string; brief: string; isError: boolean }
45
+ | { seq: number; type: "done"; totalTokens?: number }
46
+ | { seq: number; type: "error"; message: string };
47
+
48
+ /** Omit<union, K> collapses to the common keys; this distributes instead so
49
+ * emit sites can pass any variant sans seq. */
50
+ export type RunEventBody = RunEvent extends infer E ? (E extends RunEvent ? Omit<E, "seq"> : never) : never;
51
+
52
+ const TITLE_MAX = 80;
53
+
54
+ export function chatTitle(firstUserText: string): string {
55
+ return firstUserText.replace(/\s+/g, " ").trim().slice(0, TITLE_MAX);
56
+ }
57
+
58
+ export function createChat(): Chat {
59
+ const now = new Date().toISOString();
60
+ return {
61
+ version: 1,
62
+ id: crypto.randomUUID(),
63
+ title: "New conversation",
64
+ created_at: now,
65
+ updated_at: now,
66
+ status: "idle",
67
+ lastSeq: 0,
68
+ messages: [],
69
+ };
70
+ }
71
+
72
+ export function newAssistantMessage(): StoredMessage {
73
+ return { role: "assistant", segments: [] };
74
+ }
75
+
76
+ /** Fold one run event into the chat. Pure; the same reducer runs on the
77
+ * server (persistence) and could run client-side, so restore and live
78
+ * rendering can never drift apart. Events at or below lastSeq are replays
79
+ * and ignored. */
80
+ export function applyRunEvent(chat: Chat, event: RunEvent): Chat {
81
+ if (event.seq <= chat.lastSeq) return chat;
82
+ const messages = [...chat.messages];
83
+ let last = messages[messages.length - 1];
84
+ if (!last || last.role !== "assistant") {
85
+ last = newAssistantMessage();
86
+ messages.push(last);
87
+ }
88
+ const assistant = { ...last, segments: [...(last as { segments: Segment[] }).segments] } as Extract<
89
+ StoredMessage,
90
+ { role: "assistant" }
91
+ >;
92
+ messages[messages.length - 1] = assistant;
93
+
94
+ let status: ChatStatus = chat.status;
95
+ switch (event.type) {
96
+ case "delta": {
97
+ const tail = assistant.segments[assistant.segments.length - 1];
98
+ if (tail?.kind === "text") {
99
+ assistant.segments[assistant.segments.length - 1] = { kind: "text", text: tail.text + event.text };
100
+ } else {
101
+ assistant.segments.push({ kind: "text", text: event.text });
102
+ }
103
+ break;
104
+ }
105
+ case "tool":
106
+ assistant.segments.push({ kind: "tool", id: event.id, name: event.name, input: event.input, done: false });
107
+ break;
108
+ case "tool_result":
109
+ assistant.segments = assistant.segments.map((s) =>
110
+ s.kind === "tool" && s.id === event.id ? { ...s, brief: event.brief, isError: event.isError, done: true } : s,
111
+ );
112
+ break;
113
+ case "done":
114
+ status = "idle";
115
+ assistant.segments = assistant.segments.map((s) => (s.kind === "tool" && !s.done ? { ...s, done: true } : s));
116
+ break;
117
+ case "error":
118
+ status = "error";
119
+ assistant.segments = assistant.segments.map((s) => (s.kind === "tool" && !s.done ? { ...s, done: true } : s));
120
+ assistant.segments.push({ kind: "text", text: `\n\n**Error:** ${event.message}` });
121
+ break;
122
+ }
123
+
124
+ return { ...chat, messages, status, lastSeq: event.seq, updated_at: new Date().toISOString() };
125
+ }
@@ -0,0 +1,125 @@
1
+ import { afterAll, beforeAll, describe, expect, test } from "vitest";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ applyRunEvent,
7
+ chatTitle,
8
+ createChat,
9
+ deleteChat,
10
+ listChats,
11
+ newAssistantMessage,
12
+ readChat,
13
+ writeChat,
14
+ type Chat,
15
+ type RunEvent,
16
+ type RunEventBody,
17
+ } from "./chat-store";
18
+
19
+ let dir: string;
20
+ let prevHome: string | undefined;
21
+
22
+ beforeAll(() => {
23
+ dir = mkdtempSync(join(tmpdir(), "cclens-chat-test-"));
24
+ prevHome = process.env.CCLENS_HOME;
25
+ process.env.CCLENS_HOME = dir;
26
+ });
27
+
28
+ afterAll(() => {
29
+ if (prevHome === undefined) delete process.env.CCLENS_HOME;
30
+ else process.env.CCLENS_HOME = prevHome;
31
+ rmSync(dir, { recursive: true, force: true });
32
+ });
33
+
34
+ function running(): Chat {
35
+ const chat = createChat();
36
+ chat.messages.push({ role: "user", text: "find my daemon work" });
37
+ chat.messages.push(newAssistantMessage());
38
+ chat.status = "running";
39
+ return chat;
40
+ }
41
+
42
+ const ev = (e: RunEventBody, seq = 1): RunEvent => ({ seq, ...e }) as RunEvent;
43
+
44
+ describe("applyRunEvent", () => {
45
+ test("delta appends to the trailing text segment, creating one if needed", () => {
46
+ let chat = running();
47
+ chat = applyRunEvent(chat, ev({ type: "delta", text: "Hello" }));
48
+ chat = applyRunEvent(chat, ev({ type: "delta", text: " world" }, 2));
49
+ const last = chat.messages[chat.messages.length - 1]!;
50
+ expect(last.role).toBe("assistant");
51
+ expect(last.role === "assistant" && last.segments).toEqual([{ kind: "text", text: "Hello world" }]);
52
+ });
53
+
54
+ test("tool events add tool segments; results resolve them by id", () => {
55
+ let chat = running();
56
+ chat = applyRunEvent(chat, ev({ type: "delta", text: "Searching. " }));
57
+ chat = applyRunEvent(chat, ev({ type: "tool", id: "t1", name: "search_sessions", input: { query: "x" } }, 2));
58
+ chat = applyRunEvent(chat, ev({ type: "tool_result", id: "t1", name: "search_sessions", brief: "3 matching sessions", isError: false }, 3));
59
+ chat = applyRunEvent(chat, ev({ type: "delta", text: "Found it." }, 4));
60
+ const last = chat.messages[chat.messages.length - 1]!;
61
+ expect(last.role === "assistant" && last.segments).toEqual([
62
+ { kind: "text", text: "Searching. " },
63
+ { kind: "tool", id: "t1", name: "search_sessions", input: { query: "x" }, brief: "3 matching sessions", isError: false, done: true },
64
+ { kind: "text", text: "Found it." },
65
+ ]);
66
+ });
67
+
68
+ test("done marks the chat idle and finalizes dangling tools", () => {
69
+ let chat = running();
70
+ chat = applyRunEvent(chat, ev({ type: "tool", id: "t1", name: "get_session", input: {} }));
71
+ chat = applyRunEvent(chat, ev({ type: "done", totalTokens: 123 }, 2));
72
+ expect(chat.status).toBe("idle");
73
+ const last = chat.messages[chat.messages.length - 1]!;
74
+ expect(last.role === "assistant" && last.segments[0]).toMatchObject({ kind: "tool", done: true });
75
+ expect(chat.lastSeq).toBe(2);
76
+ });
77
+
78
+ test("error appends an error text segment and marks the chat error", () => {
79
+ let chat = running();
80
+ chat = applyRunEvent(chat, ev({ type: "error", message: "timed out" }));
81
+ expect(chat.status).toBe("error");
82
+ const last = chat.messages[chat.messages.length - 1]!;
83
+ expect(last.role === "assistant" && last.segments.some((s) => s.kind === "text" && s.text.includes("timed out"))).toBe(true);
84
+ });
85
+
86
+ test("stale events (seq <= lastSeq) are ignored", () => {
87
+ let chat = running();
88
+ chat = applyRunEvent(chat, ev({ type: "delta", text: "a" }, 5));
89
+ chat = applyRunEvent(chat, ev({ type: "delta", text: "b" }, 5));
90
+ const last = chat.messages[chat.messages.length - 1]!;
91
+ expect(last.role === "assistant" && last.segments).toEqual([{ kind: "text", text: "a" }]);
92
+ });
93
+ });
94
+
95
+ describe("chatTitle", () => {
96
+ test("derives from the first user message, collapsed and clamped", () => {
97
+ expect(chatTitle(" find my\ndaemon work ")).toBe("find my daemon work");
98
+ expect(chatTitle("x".repeat(200)).length).toBeLessThanOrEqual(80);
99
+ });
100
+ });
101
+
102
+ describe("fs round-trip", () => {
103
+ test("create, write, read, list, delete", () => {
104
+ const a = createChat();
105
+ a.messages.push({ role: "user", text: "first chat" });
106
+ a.title = chatTitle("first chat");
107
+ writeChat(a);
108
+ const b = createChat();
109
+ b.messages.push({ role: "user", text: "second chat" });
110
+ b.title = chatTitle("second chat");
111
+ b.updated_at = new Date(Date.now() + 1000).toISOString();
112
+ writeChat(b);
113
+
114
+ expect(readChat(a.id)?.messages[0]).toEqual({ role: "user", text: "first chat" });
115
+ expect(readChat("nope")).toBeNull();
116
+
117
+ const listed = listChats();
118
+ expect(listed.map((c) => c.id)).toEqual([b.id, a.id]); // newest first
119
+ expect(listed[0]).toMatchObject({ title: "second chat", status: "idle", messageCount: 1 });
120
+
121
+ deleteChat(a.id);
122
+ expect(readChat(a.id)).toBeNull();
123
+ expect(listChats().map((c) => c.id)).toEqual([b.id]);
124
+ });
125
+ });
@@ -0,0 +1,59 @@
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { cclensPath } from "@claude-lens/parser/fs";
4
+ import type { Chat, ChatListItem } from "./chat-model";
5
+
6
+ export * from "./chat-model";
7
+
8
+ /* ── persistence: one JSON file per conversation ───────────────────── */
9
+
10
+ function chatsDir(): string {
11
+ return cclensPath("agent-chats");
12
+ }
13
+
14
+ function chatPath(id: string): string {
15
+ return join(chatsDir(), `${id}.json`);
16
+ }
17
+
18
+ export function writeChat(chat: Chat): void {
19
+ mkdirSync(chatsDir(), { recursive: true });
20
+ const final = chatPath(chat.id);
21
+ const tmp = `${final}.tmp.${process.pid}`;
22
+ writeFileSync(tmp, JSON.stringify(chat), "utf8");
23
+ renameSync(tmp, final);
24
+ }
25
+
26
+ export function readChat(id: string): Chat | null {
27
+ // Ids come from URLs — resolve strictly inside the chats dir.
28
+ if (!/^[a-zA-Z0-9-]+$/.test(id)) return null;
29
+ try {
30
+ const chat = JSON.parse(readFileSync(chatPath(id), "utf8")) as Chat;
31
+ return chat.version === 1 ? chat : null;
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+
37
+ export function listChats(): ChatListItem[] {
38
+ if (!existsSync(chatsDir())) return [];
39
+ const out: ChatListItem[] = [];
40
+ for (const f of readdirSync(chatsDir())) {
41
+ if (!f.endsWith(".json")) continue;
42
+ const chat = readChat(f.slice(0, -".json".length));
43
+ if (!chat) continue;
44
+ out.push({
45
+ id: chat.id,
46
+ title: chat.title,
47
+ updated_at: chat.updated_at,
48
+ status: chat.status,
49
+ messageCount: chat.messages.length,
50
+ });
51
+ }
52
+ out.sort((a, b) => b.updated_at.localeCompare(a.updated_at));
53
+ return out;
54
+ }
55
+
56
+ export function deleteChat(id: string): void {
57
+ if (!/^[a-zA-Z0-9-]+$/.test(id)) return;
58
+ rmSync(chatPath(id), { force: true });
59
+ }
@@ -0,0 +1,141 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import type { SessionDetail, SessionEvent } from "@claude-lens/parser";
3
+ import { extractIndexDoc } from "./extract";
4
+
5
+ function ev(partial: Partial<SessionEvent> & { role: SessionEvent["role"] }): SessionEvent {
6
+ return {
7
+ index: 0,
8
+ rawType: partial.role,
9
+ preview: "",
10
+ blocks: [],
11
+ raw: null,
12
+ ...partial,
13
+ };
14
+ }
15
+
16
+ function session(events: SessionEvent[], meta: Partial<SessionDetail> = {}): SessionDetail {
17
+ return {
18
+ id: "abc-123",
19
+ filePath: "/tmp/abc-123.jsonl",
20
+ projectName: "/Users/me/Repo/foo",
21
+ projectDir: "-Users-me-Repo-foo",
22
+ sessionId: "abc-123",
23
+ eventCount: events.length,
24
+ totalUsage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
25
+ status: "idle",
26
+ firstTimestamp: "2026-07-01T10:00:00.000Z",
27
+ lastTimestamp: "2026-07-01T11:00:00.000Z",
28
+ events,
29
+ ...meta,
30
+ } as SessionDetail;
31
+ }
32
+
33
+ const FILE = { mtimeMs: 1000, sizeBytes: 2000 };
34
+
35
+ describe("extractIndexDoc", () => {
36
+ test("extracts user and agent text blocks as chunks", () => {
37
+ const doc = extractIndexDoc(
38
+ session([
39
+ ev({ index: 0, role: "user", blocks: [{ type: "text", text: "fix the daemon crash" }] }),
40
+ ev({ index: 1, role: "agent", blocks: [{ type: "text", text: "Found the crash in pid.ts" }] }),
41
+ ev({ index: 2, role: "tool-call", blocks: [{ type: "tool_use", id: "t1", name: "Bash", input: { command: "ls secret" } }] }),
42
+ ev({ index: 3, role: "agent-thinking", blocks: [{ type: "thinking", thinking: "hmm private" }] }),
43
+ ]),
44
+ FILE,
45
+ );
46
+ expect(doc.chunks).toEqual([
47
+ { role: "user", text: "fix the daemon crash", idx: 0 },
48
+ { role: "agent", text: "Found the crash in pid.ts", idx: 1 },
49
+ ]);
50
+ });
51
+
52
+ test("fills metadata: canonical project, start day, title, agent default", () => {
53
+ const doc = extractIndexDoc(
54
+ session(
55
+ [ev({ index: 0, role: "user", blocks: [{ type: "text", text: "hello world" }] })],
56
+ { projectName: "/Users/me/Repo/foo/.worktrees/kip-1", firstUserPreview: "hello world" },
57
+ ),
58
+ FILE,
59
+ );
60
+ expect(doc.sessionId).toBe("abc-123");
61
+ expect(doc.agent).toBe("claude-code");
62
+ expect(doc.project).toBe("/Users/me/Repo/foo");
63
+ expect(doc.repoName).toBeUndefined();
64
+ expect(doc.day).toBe("2026-07-01");
65
+ expect(doc.title).toBe("hello world");
66
+ expect(doc.mtimeMs).toBe(1000);
67
+ expect(doc.sizeBytes).toBe(2000);
68
+ });
69
+
70
+ test("skips command wrappers, skill loads, and teammate deliveries", () => {
71
+ const doc = extractIndexDoc(
72
+ session([
73
+ ev({ index: 0, role: "user", blocks: [{ type: "text", text: "<command-name>/clear</command-name>" }] }),
74
+ ev({ index: 1, role: "user", blocks: [{ type: "text", text: "Base directory for this skill: /x\n# Skill" }] }),
75
+ ev({
76
+ index: 2,
77
+ role: "user",
78
+ blocks: [{ type: "text", text: "ignore me" }],
79
+ teammateMessage: { teammateId: "t", body: "ignore me", kind: "message" },
80
+ }),
81
+ ev({ index: 3, role: "user", blocks: [{ type: "text", text: "real question" }] }),
82
+ ]),
83
+ FILE,
84
+ );
85
+ expect(doc.chunks).toEqual([{ role: "user", text: "real question", idx: 3 }]);
86
+ });
87
+
88
+ test("strips system-reminder spans inside user text", () => {
89
+ const doc = extractIndexDoc(
90
+ session([
91
+ ev({
92
+ index: 0,
93
+ role: "user",
94
+ blocks: [{ type: "text", text: "before <system-reminder>injected</system-reminder> after" }],
95
+ }),
96
+ ]),
97
+ FILE,
98
+ );
99
+ expect(doc.chunks[0]!.text).toBe("before after");
100
+ });
101
+
102
+ test("caps chunk length and total text, marking truncation", () => {
103
+ const long = "x".repeat(10_000);
104
+ const events: SessionEvent[] = [];
105
+ for (let i = 0; i < 200; i++) {
106
+ events.push(ev({ index: i, role: "agent", blocks: [{ type: "text", text: long }] }));
107
+ }
108
+ const doc = extractIndexDoc(session(events), FILE);
109
+ expect(Math.max(...doc.chunks.map((c) => c.text.length))).toBeLessThanOrEqual(4000);
110
+ const total = doc.chunks.reduce((s, c) => s + c.text.length, 0);
111
+ expect(total).toBeLessThanOrEqual(300_000);
112
+ expect(doc.truncated).toBe(true);
113
+ });
114
+
115
+ test("drops empty and whitespace-only text", () => {
116
+ const doc = extractIndexDoc(
117
+ session([
118
+ ev({ index: 0, role: "user", blocks: [{ type: "text", text: " " }] }),
119
+ ev({ index: 1, role: "agent", blocks: [{ type: "text", text: "" }] }),
120
+ ]),
121
+ FILE,
122
+ );
123
+ expect(doc.chunks).toEqual([]);
124
+ });
125
+
126
+ test("carries the git repo name when the session has one", () => {
127
+ const doc = extractIndexDoc(
128
+ session([ev({ index: 0, role: "user", blocks: [{ type: "text", text: "hi" }] })], { repoName: "fleetlens" }),
129
+ FILE,
130
+ );
131
+ expect(doc.repoName).toBe("fleetlens");
132
+ });
133
+
134
+ test("title falls back to first user chunk when firstUserPreview is absent", () => {
135
+ const doc = extractIndexDoc(
136
+ session([ev({ index: 0, role: "user", blocks: [{ type: "text", text: "the actual ask" }] })]),
137
+ FILE,
138
+ );
139
+ expect(doc.title).toBe("the actual ask");
140
+ });
141
+ });