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
@@ -0,0 +1,940 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Agent — full-page chat over the local session history.
5
+ *
6
+ * Conversations are server-persisted (~/.cclens/agent-chats) and runs
7
+ * are detached from the page: send a message, navigate away, and the agent
8
+ * keeps working — the conversation list shows it running, and reopening
9
+ * replays everything you missed. Live updates arrive over SSE and are
10
+ * folded in with the same reducer the server uses to persist, so restored
11
+ * and live renders can't drift.
12
+ */
13
+
14
+ import React, { useCallback, useEffect, useRef, useState } from "react";
15
+ import ReactMarkdown from "react-markdown";
16
+ import remarkGfm from "remark-gfm";
17
+ import { classifyHref } from "@/lib/agent/links";
18
+ import {
19
+ applyRunEvent,
20
+ chatTitle,
21
+ type Chat,
22
+ type ChatListItem,
23
+ type RunEvent,
24
+ type Segment,
25
+ type ToolSegment,
26
+ } from "@/lib/agent/chat-model";
27
+ import { formatRelative } from "@/lib/format";
28
+ import {
29
+ CalendarDays,
30
+ Check,
31
+ ClipboardCopy,
32
+ FileText,
33
+ FolderGit2,
34
+ Layers,
35
+ List,
36
+ Loader2,
37
+ MessageSquarePlus,
38
+ Radio,
39
+ RefreshCw,
40
+ Search,
41
+ Send,
42
+ SendHorizonal,
43
+ Sparkles,
44
+ Square,
45
+ X,
46
+ } from "lucide-react";
47
+
48
+ type Suggestion = { label: string; category: "recap" | "find" | "synthesize" | "handoff" };
49
+
50
+ const FALLBACK_SUGGESTIONS: Suggestion[] = [
51
+ { label: "What did I work on yesterday?", category: "recap" },
52
+ { label: "Find the sessions behind my most recent fix", category: "find" },
53
+ { label: "Which projects did I touch this week, and what happened in each?", category: "synthesize" },
54
+ { label: "Draft a prompt to continue my most recent feature work, with full context", category: "handoff" },
55
+ ];
56
+
57
+ const SUGGESTION_ICONS: Record<Suggestion["category"], React.ReactNode> = {
58
+ recap: <CalendarDays size={13} />,
59
+ find: <Search size={13} />,
60
+ synthesize: <Layers size={13} />,
61
+ handoff: <SendHorizonal size={13} />,
62
+ };
63
+
64
+ const SUGGESTION_REFETCH_MS = 25_000;
65
+
66
+ const TOOL_ICONS: Record<string, React.ReactNode> = {
67
+ search_sessions: <Search size={12} />,
68
+ get_session: <FileText size={12} />,
69
+ list_sessions: <List size={12} />,
70
+ list_projects: <FolderGit2 size={12} />,
71
+ };
72
+
73
+ type IndexState = {
74
+ sessions: number;
75
+ building: boolean;
76
+ progress?: { built: number; total: number };
77
+ };
78
+
79
+ function toolLabel(seg: ToolSegment): string {
80
+ const input = (seg.input ?? {}) as Record<string, unknown>;
81
+ if (typeof input.query === "string") return `“${input.query}”`;
82
+ if (typeof input.session_id === "string") return String(input.session_id).slice(0, 18);
83
+ const keys = Object.entries(input)
84
+ .filter(([, v]) => typeof v === "string" || typeof v === "number")
85
+ .map(([k, v]) => `${k}=${v}`)
86
+ .join(" ");
87
+ return keys.slice(0, 60);
88
+ }
89
+
90
+ function PromptCard({ text }: { text: string }) {
91
+ const [copied, setCopied] = useState(false);
92
+ return (
93
+ <div
94
+ style={{
95
+ border: "1px solid var(--af-accent)",
96
+ borderRadius: 10,
97
+ margin: "10px 0",
98
+ overflow: "hidden",
99
+ background: "var(--af-accent-subtle)",
100
+ }}
101
+ >
102
+ <div
103
+ style={{
104
+ display: "flex",
105
+ alignItems: "center",
106
+ gap: 8,
107
+ padding: "8px 12px",
108
+ borderBottom: "1px solid var(--af-border-subtle)",
109
+ }}
110
+ >
111
+ <Sparkles size={13} color="var(--af-accent)" />
112
+ <span style={{ fontSize: 12, fontWeight: 600, color: "var(--af-accent)", flex: 1 }}>
113
+ Handoff prompt — paste into your next agent session
114
+ </span>
115
+ <button
116
+ type="button"
117
+ onClick={() => {
118
+ navigator.clipboard
119
+ ?.writeText(text)
120
+ .then(() => {
121
+ setCopied(true);
122
+ setTimeout(() => setCopied(false), 2000);
123
+ })
124
+ .catch(() => {});
125
+ }}
126
+ style={{
127
+ display: "inline-flex",
128
+ alignItems: "center",
129
+ gap: 5,
130
+ padding: "4px 10px",
131
+ borderRadius: 6,
132
+ border: "1px solid var(--af-accent)",
133
+ background: copied ? "var(--af-accent)" : "transparent",
134
+ color: copied ? "#fff" : "var(--af-accent)",
135
+ fontSize: 11.5,
136
+ fontWeight: 600,
137
+ cursor: "pointer",
138
+ transition: "all 0.12s",
139
+ }}
140
+ >
141
+ {copied ? <Check size={12} /> : <ClipboardCopy size={12} />}
142
+ {copied ? "Copied" : "Copy"}
143
+ </button>
144
+ </div>
145
+ <pre
146
+ style={{
147
+ margin: 0,
148
+ padding: "12px 14px",
149
+ fontSize: 12,
150
+ lineHeight: 1.55,
151
+ fontFamily: "var(--font-mono)",
152
+ whiteSpace: "pre-wrap",
153
+ wordBreak: "break-word",
154
+ maxHeight: 380,
155
+ overflowY: "auto",
156
+ color: "var(--af-text)",
157
+ background: "var(--af-surface)",
158
+ }}
159
+ >
160
+ {text}
161
+ </pre>
162
+ </div>
163
+ );
164
+ }
165
+
166
+ function Markdown({ text }: { text: string }) {
167
+ return (
168
+ <ReactMarkdown
169
+ remarkPlugins={[remarkGfm]}
170
+ components={{
171
+ a: (props) => {
172
+ if (classifyHref(props.href) === "text") return <>{props.children}</>;
173
+ return (
174
+ <a
175
+ {...props}
176
+ target="_blank"
177
+ rel="noopener noreferrer"
178
+ style={{ color: "var(--af-accent)", textDecoration: "underline" }}
179
+ />
180
+ );
181
+ },
182
+ pre: (props) => {
183
+ const child = props.children as React.ReactElement<{ className?: string; children?: unknown }> | undefined;
184
+ const cls = child?.props?.className ?? "";
185
+ if (cls.includes("language-prompt")) {
186
+ return <PromptCard text={String(child?.props?.children ?? "").replace(/\n$/, "")} />;
187
+ }
188
+ return <pre {...props} />;
189
+ },
190
+ }}
191
+ >
192
+ {text}
193
+ </ReactMarkdown>
194
+ );
195
+ }
196
+
197
+ function ToolChip({ seg }: { seg: ToolSegment }) {
198
+ return (
199
+ <div
200
+ style={{
201
+ display: "inline-flex",
202
+ alignItems: "center",
203
+ gap: 7,
204
+ padding: "5px 10px",
205
+ margin: "6px 0",
206
+ borderRadius: 7,
207
+ border: "1px solid var(--af-border-subtle)",
208
+ background: "var(--af-surface-hover)",
209
+ fontSize: 11.5,
210
+ fontFamily: "var(--font-mono)",
211
+ color: seg.isError ? "var(--af-danger)" : "var(--af-text-secondary)",
212
+ maxWidth: "100%",
213
+ }}
214
+ >
215
+ <span style={{ color: "var(--af-accent)", flexShrink: 0, display: "inline-flex" }}>
216
+ {TOOL_ICONS[seg.name] ?? <Search size={12} />}
217
+ </span>
218
+ <span style={{ fontWeight: 600, flexShrink: 0 }}>{seg.name}</span>
219
+ <span style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{toolLabel(seg)}</span>
220
+ {seg.done ? (
221
+ <span style={{ flexShrink: 0, color: seg.isError ? "var(--af-danger)" : "var(--af-text-tertiary)" }}>
222
+ → {seg.isError ? "error" : (seg.brief ?? "done")}
223
+ </span>
224
+ ) : (
225
+ <Loader2 size={11} style={{ flexShrink: 0, animation: "spin 1s linear infinite" }} />
226
+ )}
227
+ </div>
228
+ );
229
+ }
230
+
231
+ function chatUrl(id: string | null): string {
232
+ return id ? `/agent?chat=${id}` : "/agent";
233
+ }
234
+
235
+ export function AgentChat() {
236
+ const [chats, setChats] = useState<ChatListItem[]>([]);
237
+ const [chat, setChat] = useState<Chat | null>(null);
238
+ const [input, setInput] = useState("");
239
+ const [sending, setSending] = useState(false);
240
+ const [index, setIndex] = useState<IndexState | null>(null);
241
+ const [suggestions, setSuggestions] = useState<Suggestion[]>(FALLBACK_SUGGESTIONS);
242
+ const subscriptionRef = useRef<AbortController | null>(null);
243
+ /** The chat the user currently intends to view. Fetches resolve out of
244
+ * order (click A then quickly B), so every async setChat checks it. */
245
+ const selectedIdRef = useRef<string | null>(null);
246
+ const scrollRef = useRef<HTMLDivElement>(null);
247
+ const pinnedToBottom = useRef(true);
248
+
249
+ const running = chat?.status === "running";
250
+
251
+ const refreshList = useCallback(async () => {
252
+ try {
253
+ const res = await fetch("/api/agent/chats");
254
+ const data = (await res.json()) as { chats: ChatListItem[] };
255
+ setChats(data.chats);
256
+ } catch {
257
+ /* list refresh is best-effort */
258
+ }
259
+ }, []);
260
+
261
+ const autoScroll = useCallback(() => {
262
+ if (!pinnedToBottom.current) return;
263
+ requestAnimationFrame(() => {
264
+ scrollRef.current?.scrollTo({ top: scrollRef.current.scrollHeight });
265
+ });
266
+ }, []);
267
+
268
+ /** Follow the active run over SSE, folding events in with the shared
269
+ * reducer. The persisted snapshot we already rendered carries lastSeq,
270
+ * so ?after= resumes exactly where it left off. If the stream dies
271
+ * without a terminal event (server restart, dropped connection), re-open
272
+ * the chat after a beat — otherwise the view is stuck on "running" with
273
+ * nothing left to update it. */
274
+ const subscribe = useCallback(
275
+ (target: Chat) => {
276
+ subscriptionRef.current?.abort();
277
+ if (target.status !== "running") return;
278
+ const ctrl = new AbortController();
279
+ subscriptionRef.current = ctrl;
280
+ const stale = () => ctrl.signal.aborted || selectedIdRef.current !== target.id;
281
+ void (async () => {
282
+ let terminal = false;
283
+ try {
284
+ const res = await fetch(`/api/agent/chats/${target.id}/events?after=${target.lastSeq}`, {
285
+ signal: ctrl.signal,
286
+ });
287
+ if (!res.body) return;
288
+ const reader = res.body.getReader();
289
+ const decoder = new TextDecoder();
290
+ let buffer = "";
291
+ while (true) {
292
+ const { done, value } = await reader.read();
293
+ if (done) break;
294
+ buffer += decoder.decode(value, { stream: true });
295
+ const lines = buffer.split("\n");
296
+ buffer = lines.pop() ?? "";
297
+ for (const line of lines) {
298
+ if (!line.startsWith("data: ")) continue;
299
+ let event: RunEvent | { type: "sync"; status: string };
300
+ try {
301
+ event = JSON.parse(line.slice(6));
302
+ } catch {
303
+ continue;
304
+ }
305
+ if (event.type === "sync") {
306
+ // No live run — re-fetch the authoritative snapshot.
307
+ const fresh = await fetch(`/api/agent/chats/${target.id}`);
308
+ if (fresh.ok && !stale()) setChat((await fresh.json()) as Chat);
309
+ terminal = true;
310
+ continue;
311
+ }
312
+ setChat((prev) => (prev && prev.id === target.id ? applyRunEvent(prev, event as RunEvent) : prev));
313
+ if (event.type === "delta") autoScroll();
314
+ if (event.type === "done" || event.type === "error") terminal = true;
315
+ }
316
+ }
317
+ if (terminal) void refreshList();
318
+ } catch {
319
+ /* aborted on chat switch/unmount, or the connection died */
320
+ }
321
+ if (!terminal && !stale()) {
322
+ setTimeout(() => {
323
+ if (!stale()) void openChatRef.current(target.id, false);
324
+ }, 2000);
325
+ }
326
+ })();
327
+ },
328
+ [autoScroll, refreshList],
329
+ );
330
+
331
+ const openChat = useCallback(
332
+ async (id: string | null, pushUrl = true) => {
333
+ subscriptionRef.current?.abort();
334
+ selectedIdRef.current = id;
335
+ if (pushUrl) window.history.replaceState(null, "", chatUrl(id));
336
+ if (!id) {
337
+ setChat(null);
338
+ return;
339
+ }
340
+ try {
341
+ const res = await fetch(`/api/agent/chats/${id}`);
342
+ if (selectedIdRef.current !== id) return; // user moved on mid-fetch
343
+ if (!res.ok) {
344
+ setChat(null);
345
+ return;
346
+ }
347
+ const fresh = (await res.json()) as Chat;
348
+ if (selectedIdRef.current !== id) return;
349
+ setChat(fresh);
350
+ pinnedToBottom.current = true;
351
+ autoScroll();
352
+ subscribe(fresh);
353
+ } catch {
354
+ if (selectedIdRef.current === id) setChat(null);
355
+ }
356
+ },
357
+ [autoScroll, subscribe],
358
+ );
359
+
360
+ /** subscribe's dead-stream recovery calls openChat; a ref breaks the
361
+ * otherwise-circular useCallback dependency. */
362
+ const openChatRef = useRef(openChat);
363
+ openChatRef.current = openChat;
364
+
365
+ useEffect(() => {
366
+ void refreshList();
367
+ const fromUrl = new URLSearchParams(window.location.search).get("chat");
368
+ if (fromUrl) void openChat(fromUrl, false);
369
+ return () => subscriptionRef.current?.abort();
370
+ // mount-only: URL restore + initial list
371
+ // eslint-disable-next-line react-hooks/exhaustive-deps
372
+ }, []);
373
+
374
+ // While any listed chat is running, poll the list so badges flip when
375
+ // runs land — including the open chat, whose SSE stream can die silently.
376
+ useEffect(() => {
377
+ if (!chats.some((c) => c.status === "running")) return;
378
+ const t = setInterval(() => void refreshList(), 5000);
379
+ return () => clearInterval(t);
380
+ }, [chats, refreshList]);
381
+
382
+ // If the open chat says "running" but the list disagrees, its SSE feed
383
+ // missed the terminal event — re-fetch the authoritative state.
384
+ useEffect(() => {
385
+ if (!chat || chat.status !== "running") return;
386
+ const listed = chats.find((c) => c.id === chat.id);
387
+ if (listed && listed.status !== "running") void openChat(chat.id, false);
388
+ }, [chats, chat, openChat]);
389
+
390
+ const refreshIndex = useCallback(async () => {
391
+ setIndex((prev) => ({ sessions: prev?.sessions ?? 0, building: true }));
392
+ try {
393
+ const res = await fetch("/api/agent/index", { method: "POST" });
394
+ if (!res.body) return;
395
+ const reader = res.body.getReader();
396
+ const decoder = new TextDecoder();
397
+ let buffer = "";
398
+ while (true) {
399
+ const { done, value } = await reader.read();
400
+ if (done) break;
401
+ buffer += decoder.decode(value, { stream: true });
402
+ const lines = buffer.split("\n");
403
+ buffer = lines.pop() ?? "";
404
+ for (const line of lines) {
405
+ if (!line.startsWith("data: ")) continue;
406
+ try {
407
+ const data = JSON.parse(line.slice(6)) as { type: string; built?: number; total?: number; sessions?: number };
408
+ if (data.type === "progress") {
409
+ setIndex({ sessions: data.total ?? 0, building: true, progress: { built: data.built ?? 0, total: data.total ?? 0 } });
410
+ } else if (data.type === "done") {
411
+ setIndex({ sessions: data.sessions ?? 0, building: false });
412
+ }
413
+ } catch {
414
+ /* skip */
415
+ }
416
+ }
417
+ }
418
+ } catch {
419
+ setIndex((prev) => (prev ? { ...prev, building: false } : null));
420
+ }
421
+ }, []);
422
+
423
+ useEffect(() => {
424
+ let cancelled = false;
425
+ fetch("/api/agent/index")
426
+ .then((r) => r.json())
427
+ .then((stats: { sessions: number; building: boolean }) => {
428
+ if (cancelled) return;
429
+ setIndex({ sessions: stats.sessions, building: stats.building });
430
+ if (stats.sessions === 0 && !stats.building) void refreshIndex();
431
+ })
432
+ .catch(() => {});
433
+ return () => {
434
+ cancelled = true;
435
+ };
436
+ }, [refreshIndex]);
437
+
438
+ useEffect(() => {
439
+ let cancelled = false;
440
+ let timer: ReturnType<typeof setTimeout> | undefined;
441
+ const load = (attempt: number) => {
442
+ fetch("/api/agent/suggestions")
443
+ .then((r) => r.json())
444
+ .then((data: { suggestions?: Suggestion[]; refreshing?: boolean }) => {
445
+ if (cancelled) return;
446
+ if (Array.isArray(data.suggestions) && data.suggestions.length > 0) setSuggestions(data.suggestions);
447
+ if (data.refreshing && attempt < 2) timer = setTimeout(() => load(attempt + 1), SUGGESTION_REFETCH_MS);
448
+ })
449
+ .catch(() => {});
450
+ };
451
+ load(0);
452
+ return () => {
453
+ cancelled = true;
454
+ if (timer) clearTimeout(timer);
455
+ };
456
+ }, []);
457
+
458
+ const send = useCallback(
459
+ async (question: string) => {
460
+ const text = question.trim();
461
+ if (!text || sending || running) return;
462
+ setSending(true);
463
+ setInput("");
464
+ pinnedToBottom.current = true;
465
+ try {
466
+ let id = chat?.id;
467
+ if (!id) {
468
+ const created = await fetch("/api/agent/chats", { method: "POST" });
469
+ id = ((await created.json()) as { id: string }).id;
470
+ selectedIdRef.current = id;
471
+ window.history.replaceState(null, "", chatUrl(id));
472
+ }
473
+ const res = await fetch(`/api/agent/chats/${id}/messages`, {
474
+ method: "POST",
475
+ headers: { "Content-Type": "application/json" },
476
+ body: JSON.stringify({ text }),
477
+ });
478
+ if (selectedIdRef.current !== id) return; // user switched chats mid-send
479
+ if (!res.ok) {
480
+ const err = ((await res.json().catch(() => ({}))) as { error?: string }).error ?? `HTTP ${res.status}`;
481
+ // A brand-new conversation has no chat state yet — fetch what the
482
+ // create call persisted so the error has somewhere to render.
483
+ const base: Chat =
484
+ chat ??
485
+ ((await fetch(`/api/agent/chats/${id}`).then((r) => (r.ok ? r.json() : null)).catch(() => null)) as Chat | null) ?? {
486
+ version: 1,
487
+ id,
488
+ title: chatTitle(text),
489
+ created_at: new Date().toISOString(),
490
+ updated_at: new Date().toISOString(),
491
+ status: "idle",
492
+ lastSeq: 0,
493
+ messages: [],
494
+ };
495
+ setChat({
496
+ ...base,
497
+ messages: [
498
+ ...base.messages,
499
+ { role: "user", text },
500
+ { role: "assistant", segments: [{ kind: "text", text: `**Error:** ${err}` }] },
501
+ ],
502
+ });
503
+ void refreshList();
504
+ return;
505
+ }
506
+ const fresh = (await res.json()) as Chat;
507
+ setChat(fresh);
508
+ autoScroll();
509
+ subscribe(fresh);
510
+ void refreshList();
511
+ } finally {
512
+ setSending(false);
513
+ }
514
+ },
515
+ [chat, sending, running, autoScroll, subscribe, refreshList],
516
+ );
517
+
518
+ const stop = useCallback(() => {
519
+ if (!chat) return;
520
+ void fetch(`/api/agent/chats/${chat.id}/stop`, { method: "POST" });
521
+ }, [chat]);
522
+
523
+ const removeChat = useCallback(
524
+ async (id: string) => {
525
+ await fetch(`/api/agent/chats/${id}`, { method: "DELETE" });
526
+ if (chat?.id === id) void openChat(null);
527
+ void refreshList();
528
+ },
529
+ [chat?.id, openChat, refreshList],
530
+ );
531
+
532
+ const indexChip = index && (
533
+ <span
534
+ style={{
535
+ display: "inline-flex",
536
+ alignItems: "center",
537
+ gap: 6,
538
+ padding: "4px 10px",
539
+ borderRadius: 100,
540
+ border: "1px solid var(--af-border-subtle)",
541
+ background: "var(--af-surface)",
542
+ fontSize: 11,
543
+ fontFamily: "var(--font-mono)",
544
+ color: "var(--af-text-tertiary)",
545
+ whiteSpace: "nowrap",
546
+ }}
547
+ >
548
+ {index.building ? (
549
+ <>
550
+ <Loader2 size={11} style={{ animation: "spin 1s linear infinite" }} />
551
+ {index.progress ? `indexing ${index.progress.built}/${index.progress.total}` : "indexing…"}
552
+ </>
553
+ ) : (
554
+ <>{index.sessions.toLocaleString()} sessions indexed</>
555
+ )}
556
+ </span>
557
+ );
558
+
559
+ const emptyState = !chat || chat.messages.length === 0;
560
+
561
+ return (
562
+ <div style={{ height: "100%", display: "flex", minHeight: 0 }}>
563
+ <style>{`@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`}</style>
564
+
565
+ {/* Conversation list */}
566
+ <div
567
+ style={{
568
+ width: 230,
569
+ flexShrink: 0,
570
+ borderRight: "1px solid var(--af-border-subtle)",
571
+ display: "flex",
572
+ flexDirection: "column",
573
+ minHeight: 0,
574
+ background: "var(--af-surface)",
575
+ }}
576
+ >
577
+ <div style={{ padding: "12px 12px 8px" }}>
578
+ <button
579
+ type="button"
580
+ onClick={() => void openChat(null)}
581
+ style={{
582
+ width: "100%",
583
+ display: "inline-flex",
584
+ alignItems: "center",
585
+ justifyContent: "center",
586
+ gap: 7,
587
+ padding: "8px 12px",
588
+ borderRadius: 8,
589
+ border: "1px solid var(--af-border-subtle)",
590
+ background: "var(--af-surface-hover)",
591
+ color: "var(--af-text)",
592
+ fontSize: 12.5,
593
+ fontWeight: 600,
594
+ cursor: "pointer",
595
+ fontFamily: "inherit",
596
+ }}
597
+ >
598
+ <MessageSquarePlus size={14} />
599
+ New chat
600
+ </button>
601
+ </div>
602
+ <div style={{ flex: 1, overflowY: "auto", padding: "0 8px 12px" }}>
603
+ {chats.length === 0 && (
604
+ <div style={{ padding: "14px 8px", fontSize: 11.5, color: "var(--af-text-tertiary)", lineHeight: 1.5 }}>
605
+ No conversations yet. Runs keep going even if you leave the page.
606
+ </div>
607
+ )}
608
+ {chats.map((c) => {
609
+ const active = c.id === chat?.id;
610
+ return (
611
+ <div
612
+ key={c.id}
613
+ onClick={() => void openChat(c.id)}
614
+ style={{
615
+ display: "flex",
616
+ alignItems: "center",
617
+ gap: 8,
618
+ padding: "8px 10px",
619
+ marginBottom: 2,
620
+ borderRadius: 8,
621
+ cursor: "pointer",
622
+ background: active ? "var(--af-accent-subtle)" : "transparent",
623
+ border: `1px solid ${active ? "var(--af-accent)" : "transparent"}`,
624
+ }}
625
+ >
626
+ <div style={{ flex: 1, minWidth: 0 }}>
627
+ <div
628
+ style={{
629
+ fontSize: 12,
630
+ fontWeight: active ? 600 : 500,
631
+ color: "var(--af-text)",
632
+ overflow: "hidden",
633
+ textOverflow: "ellipsis",
634
+ whiteSpace: "nowrap",
635
+ }}
636
+ >
637
+ {c.title}
638
+ </div>
639
+ <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 2 }}>
640
+ {c.status === "running" && (
641
+ <Loader2 size={10} color="var(--af-accent)" style={{ animation: "spin 1s linear infinite" }} />
642
+ )}
643
+ {c.status === "error" && (
644
+ <span style={{ width: 6, height: 6, borderRadius: 3, background: "var(--af-danger)" }} />
645
+ )}
646
+ <span style={{ fontSize: 10.5, color: "var(--af-text-tertiary)", fontFamily: "var(--font-mono)" }}>
647
+ {c.status === "running" ? "running" : formatRelative(c.updated_at)}
648
+ </span>
649
+ </div>
650
+ </div>
651
+ <button
652
+ type="button"
653
+ title="Delete conversation"
654
+ onClick={(e) => {
655
+ e.stopPropagation();
656
+ void removeChat(c.id);
657
+ }}
658
+ style={{
659
+ border: "none",
660
+ background: "transparent",
661
+ color: "var(--af-text-tertiary)",
662
+ cursor: "pointer",
663
+ padding: 2,
664
+ borderRadius: 4,
665
+ flexShrink: 0,
666
+ }}
667
+ >
668
+ <X size={12} />
669
+ </button>
670
+ </div>
671
+ );
672
+ })}
673
+ </div>
674
+ </div>
675
+
676
+ {/* Chat column */}
677
+ <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0, minWidth: 0 }}>
678
+ {/* Header */}
679
+ <div
680
+ style={{
681
+ display: "flex",
682
+ alignItems: "center",
683
+ gap: 10,
684
+ padding: "14px 28px",
685
+ borderBottom: "1px solid var(--af-border-subtle)",
686
+ background: "color-mix(in srgb, var(--background) 82%, transparent)",
687
+ backdropFilter: "blur(10px)",
688
+ flexShrink: 0,
689
+ }}
690
+ >
691
+ <Sparkles size={16} color="var(--af-accent)" />
692
+ <div style={{ flex: 1, minWidth: 0 }}>
693
+ <div style={{ fontSize: 14, fontWeight: 600, color: "var(--af-text)" }}>Agent</div>
694
+ <div style={{ fontSize: 11, color: "var(--af-text-tertiary)" }}>
695
+ Search, synthesize, and hand off your local session history
696
+ </div>
697
+ </div>
698
+ {indexChip}
699
+ <a
700
+ href="/runs"
701
+ title="LLM runs — inspect every local model call"
702
+ style={{
703
+ display: "inline-flex",
704
+ alignItems: "center",
705
+ gap: 5,
706
+ padding: "6px 10px",
707
+ borderRadius: 6,
708
+ border: "1px solid var(--af-border-subtle)",
709
+ background: "transparent",
710
+ color: "var(--af-text-tertiary)",
711
+ fontSize: 11.5,
712
+ textDecoration: "none",
713
+ }}
714
+ >
715
+ <Radio size={13} />
716
+ Runs
717
+ </a>
718
+ <button
719
+ type="button"
720
+ title="Refresh search index"
721
+ onClick={() => void refreshIndex()}
722
+ disabled={index?.building ?? false}
723
+ style={{
724
+ display: "inline-flex",
725
+ alignItems: "center",
726
+ padding: 6,
727
+ borderRadius: 6,
728
+ border: "1px solid var(--af-border-subtle)",
729
+ background: "transparent",
730
+ color: "var(--af-text-tertiary)",
731
+ cursor: index?.building ? "wait" : "pointer",
732
+ }}
733
+ >
734
+ <RefreshCw size={13} />
735
+ </button>
736
+ </div>
737
+
738
+ {/* Messages */}
739
+ <div
740
+ ref={scrollRef}
741
+ onScroll={() => {
742
+ const el = scrollRef.current;
743
+ if (!el) return;
744
+ pinnedToBottom.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
745
+ }}
746
+ style={{ flex: 1, minHeight: 0, overflowY: "auto", padding: "22px 28px" }}
747
+ >
748
+ <div style={{ maxWidth: 780, margin: "0 auto", display: "flex", flexDirection: "column", gap: 18 }}>
749
+ {emptyState && (
750
+ <div style={{ paddingTop: 48, textAlign: "center" }}>
751
+ <Sparkles size={26} color="var(--af-accent)" style={{ marginBottom: 12 }} />
752
+ <div style={{ fontSize: 15, fontWeight: 600, color: "var(--af-text)", marginBottom: 6 }}>
753
+ Ask anything about your past sessions
754
+ </div>
755
+ <div style={{ fontSize: 12.5, color: "var(--af-text-secondary)", marginBottom: 26, lineHeight: 1.5 }}>
756
+ Every conversation is searched locally — nothing leaves this machine.
757
+ <br />
758
+ Find past work, get it synthesized, or turn it into a prompt for your next agent run.
759
+ </div>
760
+ <div style={{ display: "flex", flexDirection: "column", gap: 8, maxWidth: 520, margin: "0 auto" }}>
761
+ {suggestions.map((s) => (
762
+ <button
763
+ key={s.category}
764
+ type="button"
765
+ onClick={() => void send(s.label)}
766
+ disabled={sending}
767
+ style={{
768
+ display: "flex",
769
+ alignItems: "center",
770
+ gap: 10,
771
+ padding: "10px 14px",
772
+ border: "1px solid var(--af-border-subtle)",
773
+ borderRadius: 8,
774
+ background: "var(--af-surface)",
775
+ color: "var(--af-text)",
776
+ fontSize: 12.5,
777
+ cursor: sending ? "wait" : "pointer",
778
+ textAlign: "left",
779
+ transition: "all 0.12s",
780
+ fontFamily: "inherit",
781
+ }}
782
+ onMouseEnter={(e) => {
783
+ e.currentTarget.style.borderColor = "var(--af-accent)";
784
+ e.currentTarget.style.background = "var(--af-accent-subtle)";
785
+ }}
786
+ onMouseLeave={(e) => {
787
+ e.currentTarget.style.borderColor = "var(--af-border-subtle)";
788
+ e.currentTarget.style.background = "var(--af-surface)";
789
+ }}
790
+ >
791
+ <span style={{ color: "var(--af-accent)", flexShrink: 0, display: "inline-flex" }}>
792
+ {SUGGESTION_ICONS[s.category]}
793
+ </span>
794
+ {s.label}
795
+ </button>
796
+ ))}
797
+ </div>
798
+ </div>
799
+ )}
800
+
801
+ {chat?.messages.map((msg, i) =>
802
+ msg.role === "user" ? (
803
+ <div key={i} style={{ display: "flex", justifyContent: "flex-end" }}>
804
+ <div
805
+ style={{
806
+ maxWidth: "82%",
807
+ padding: "9px 14px",
808
+ borderRadius: 12,
809
+ background: "var(--af-surface-hover)",
810
+ border: "1px solid var(--af-border-subtle)",
811
+ fontSize: 13,
812
+ lineHeight: 1.5,
813
+ color: "var(--af-text)",
814
+ whiteSpace: "pre-wrap",
815
+ wordBreak: "break-word",
816
+ }}
817
+ >
818
+ {msg.text}
819
+ </div>
820
+ </div>
821
+ ) : (
822
+ <div key={i} style={{ minWidth: 0 }}>
823
+ {msg.segments.length === 0 && running && i === chat.messages.length - 1 && (
824
+ <div
825
+ style={{ display: "flex", alignItems: "center", gap: 8, color: "var(--af-text-tertiary)", fontSize: 12 }}
826
+ >
827
+ <Loader2 size={13} style={{ animation: "spin 1s linear infinite" }} />
828
+ Thinking…
829
+ </div>
830
+ )}
831
+ {msg.segments.map((seg: Segment, j: number) =>
832
+ seg.kind === "tool" ? (
833
+ <div key={j}>
834
+ <ToolChip seg={seg} />
835
+ </div>
836
+ ) : (
837
+ <div key={j} className="sl-prose" style={{ fontSize: 13.5, lineHeight: 1.6 }}>
838
+ <Markdown text={seg.text} />
839
+ </div>
840
+ ),
841
+ )}
842
+ </div>
843
+ ),
844
+ )}
845
+ </div>
846
+ </div>
847
+
848
+ {/* Composer */}
849
+ <div
850
+ style={{
851
+ borderTop: "1px solid var(--af-border-subtle)",
852
+ padding: "14px 28px 18px",
853
+ background: "var(--background)",
854
+ flexShrink: 0,
855
+ }}
856
+ >
857
+ <form
858
+ onSubmit={(e) => {
859
+ e.preventDefault();
860
+ void send(input);
861
+ }}
862
+ style={{ maxWidth: 780, margin: "0 auto", display: "flex", gap: 8, alignItems: "flex-end" }}
863
+ >
864
+ <textarea
865
+ value={input}
866
+ onChange={(e) => setInput(e.target.value)}
867
+ onKeyDown={(e) => {
868
+ if (e.key === "Enter" && !e.shiftKey) {
869
+ e.preventDefault();
870
+ void send(input);
871
+ }
872
+ }}
873
+ placeholder={
874
+ running
875
+ ? "The agent is working — you can leave this page and come back…"
876
+ : "Search your session history, ask for a synthesis, or request a handoff prompt…"
877
+ }
878
+ rows={Math.min(4, Math.max(1, input.split("\n").length))}
879
+ style={{
880
+ flex: 1,
881
+ resize: "none",
882
+ padding: "10px 14px",
883
+ borderRadius: 10,
884
+ border: "1px solid var(--af-border-subtle)",
885
+ background: "var(--af-surface)",
886
+ color: "var(--af-text)",
887
+ fontSize: 13,
888
+ lineHeight: 1.5,
889
+ fontFamily: "inherit",
890
+ outline: "none",
891
+ }}
892
+ />
893
+ {running ? (
894
+ <button
895
+ type="button"
896
+ onClick={stop}
897
+ title="Stop this run"
898
+ style={{
899
+ display: "inline-flex",
900
+ alignItems: "center",
901
+ justifyContent: "center",
902
+ width: 40,
903
+ height: 40,
904
+ borderRadius: 10,
905
+ border: "1px solid var(--af-danger)",
906
+ background: "transparent",
907
+ color: "var(--af-danger)",
908
+ cursor: "pointer",
909
+ flexShrink: 0,
910
+ }}
911
+ >
912
+ <Square size={14} />
913
+ </button>
914
+ ) : (
915
+ <button
916
+ type="submit"
917
+ disabled={!input.trim() || sending}
918
+ style={{
919
+ display: "inline-flex",
920
+ alignItems: "center",
921
+ justifyContent: "center",
922
+ width: 40,
923
+ height: 40,
924
+ borderRadius: 10,
925
+ border: "none",
926
+ background: input.trim() && !sending ? "var(--af-accent)" : "var(--af-border-subtle)",
927
+ color: input.trim() && !sending ? "#fff" : "var(--af-text-tertiary)",
928
+ cursor: input.trim() && !sending ? "pointer" : "not-allowed",
929
+ flexShrink: 0,
930
+ }}
931
+ >
932
+ <Send size={15} />
933
+ </button>
934
+ )}
935
+ </form>
936
+ </div>
937
+ </div>
938
+ </div>
939
+ );
940
+ }