fleetlens 0.15.7 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/app-path-routes-manifest.json +9 -0
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/routes-manifest.json +62 -0
- package/app/apps/web/.next/server/app/_global-error/page.js +1 -1
- package/app/apps/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/agent/page/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/agent/page/build-manifest.json +17 -0
- package/app/apps/web/.next/server/app/agent/page/next-font-manifest.json +6 -0
- package/app/apps/web/.next/server/app/agent/page/react-loadable-manifest.json +1 -0
- package/app/apps/web/.next/server/app/agent/page/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/agent/page.js +18 -0
- package/app/apps/web/.next/server/app/agent/page.js.map +5 -0
- package/app/apps/web/.next/server/app/agent/page.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/agent/page_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js +7 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/events/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/messages/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js +7 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js +7 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/chats/[id]/stop/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route.js +7 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/chats/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/index/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/index/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/index/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/index/route.js +9 -0
- package/app/apps/web/.next/server/app/api/agent/index/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/index/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/index/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route.js +10 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/mcp/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route.js +12 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/agent/suggestions/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/ask/route.js +3 -4
- package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +3 -3
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/projects/open-folder/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/credentials/route.js +5 -5
- package/app/apps/web/.next/server/app/api/settings/credentials/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js +3 -3
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team/autostart/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js +2 -2
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js +2 -2
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js +2 -2
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js +2 -2
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js +2 -2
- package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js +2 -2
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page.js +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/page.js +1 -1
- package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app-paths-manifest.json +9 -0
- 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
- package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_agent_chats_[id]_stop_route_actions_036fon_.js +3 -0
- package/app/apps/web/.next/server/chunks/09og__next-internal_server_app_api_agent_chats_[id]_messages_route_actions_0ubwh4q.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__01~cgp8._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__02faosk._.js +30 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__02puo0u._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0367vbv._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__04g-b-m._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__06xto5z._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__096_llw._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__09cgmam._.js +28 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0_5tf-2._.js +21 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gvw6dk._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mx_9mb._.js +5 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0pirsh0._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0sbhe0s._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0tk8p3d._.js +28 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0u6pamj._.js +28 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__123tfcb._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__12jomao._.js +28 -0
- package/app/apps/web/.next/server/chunks/_132c.4p._.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_chats_[id]_route_actions_0b95y8h.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_chats_route_actions_07b4w1x.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_index_route_actions_03~ylvr.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_mcp_route_actions_0-b30h..js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_agent_suggestions_route_actions_0r_5s~k.js +3 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0sr4wr_.js +13 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0z_xi9k.js +16 -0
- package/app/apps/web/.next/server/chunks/{packages_0o6.gpk._.js → packages_entries_dist_0-0s5ui._.js} +2 -2
- package/app/apps/web/.next/server/chunks/packages_entries_dist_0~0d7ku._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +8 -8
- package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0e.gxk3._.js → [root-of-the-server]__0.wm54e._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__04il6m2._.js → [root-of-the-server]__04qodwp._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0k1x-c9._.js → [root-of-the-server]__07~kk2~._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__03uoeiw._.js → [root-of-the-server]__0_78i1t._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0amklf0._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0nr2~y1._.js → [root-of-the-server]__0d5y90m._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__03svi~4._.js → [root-of-the-server]__0iv286q._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0j683am._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0rvzdnx._.js → [root-of-the-server]__0kml6jt._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__08l86e-._.js → [root-of-the-server]__0lmftab._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ns6ydi._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0vo.r~s._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~8.fgs._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/_0k8yioi._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{_13jdm1g._.js → _0~r5ica._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_agent_page_actions_0tuled1.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_[id]_session-view_tsx_0-9t5~.._.js +12 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0x014hn._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_agent-chat_tsx_0xbtx2n._.js +5 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{node_modules_0yrkyef._.js → node_modules_next_dist_0t-hj0x._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0~g07z5.js +4 -0
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
- package/app/apps/web/.next/server/functions-config-manifest.json +8 -0
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/middleware-manifest.json +5 -5
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/04feoajklmrl3.js +3 -0
- package/app/apps/web/.next/static/chunks/07bzlfv97o1ig.js +10 -0
- package/app/apps/web/.next/static/chunks/0d~7p_ye7v7_5.css +1 -0
- package/app/apps/web/.next/static/chunks/0iiqpl6d_3i5~.js +1 -0
- package/app/apps/web/.next/static/chunks/0mry~475v0fa3.js +14 -0
- package/app/apps/web/.next/static/chunks/0qwgm81o9o~um.js +1 -0
- package/app/apps/web/.next/static/chunks/{0y3448zrint0k.js → 0~rycwo36m~c3.js} +1 -1
- package/app/apps/web/.next/static/chunks/12zmbd3mlcbn_.js +1 -0
- package/app/apps/web/.next/static/chunks/16bapkvp5.x37.js +1 -0
- package/app/apps/web/app/agent/page.tsx +12 -0
- package/app/apps/web/app/api/agent/chats/[id]/events/route.ts +86 -0
- package/app/apps/web/app/api/agent/chats/[id]/messages/route.ts +47 -0
- package/app/apps/web/app/api/agent/chats/[id]/route.ts +19 -0
- package/app/apps/web/app/api/agent/chats/[id]/stop/route.ts +10 -0
- package/app/apps/web/app/api/agent/chats/route.ts +26 -0
- package/app/apps/web/app/api/agent/index/route.ts +56 -0
- package/app/apps/web/app/api/agent/mcp/route.ts +31 -0
- package/app/apps/web/app/api/agent/suggestions/route.ts +150 -0
- package/app/apps/web/components/agent-chat.tsx +940 -0
- package/app/apps/web/components/sidebar.tsx +8 -21
- package/app/apps/web/lib/agent/chat-model.ts +125 -0
- package/app/apps/web/lib/agent/chat-store.test.ts +125 -0
- package/app/apps/web/lib/agent/chat-store.ts +59 -0
- package/app/apps/web/lib/agent/extract.test.ts +141 -0
- package/app/apps/web/lib/agent/extract.ts +75 -0
- package/app/apps/web/lib/agent/index-store.ts +206 -0
- package/app/apps/web/lib/agent/links.test.ts +27 -0
- package/app/apps/web/lib/agent/links.ts +10 -0
- package/app/apps/web/lib/agent/mcp.test.ts +83 -0
- package/app/apps/web/lib/agent/mcp.ts +85 -0
- package/app/apps/web/lib/agent/prompt.test.ts +38 -0
- package/app/apps/web/lib/agent/prompt.ts +44 -0
- package/app/apps/web/lib/agent/run.ts +426 -0
- package/app/apps/web/lib/agent/search.test.ts +111 -0
- package/app/apps/web/lib/agent/search.ts +144 -0
- package/app/apps/web/lib/agent/suggestions.test.ts +156 -0
- package/app/apps/web/lib/agent/suggestions.ts +167 -0
- package/app/apps/web/lib/agent/tools.test.ts +98 -0
- package/app/apps/web/lib/agent/tools.ts +176 -0
- package/app/apps/web/lib/agent/types.ts +50 -0
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/app/apps/web/vitest.config.ts +4 -0
- package/dist/daemon-worker.js +9 -4
- package/dist/index.js +19 -12
- package/menubar/FleetlensMenubar.app/Contents/Info.plist +2 -2
- package/menubar/FleetlensMenubar.app/Contents/MacOS/FleetlensMenubar +0 -0
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__03ewxxu._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__03rmbz6._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__05z0pcw._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0acikq.._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b5r~-1._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jbc7tz._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0ow1kn1._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0t7i-hy._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~xt99s._.js +0 -3
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +0 -21
- package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0g-tomj._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +0 -12
- package/app/apps/web/.next/static/chunks/02xxpexzk3rf-.js +0 -1
- package/app/apps/web/.next/static/chunks/044mtrzh7d7va.js +0 -10
- package/app/apps/web/.next/static/chunks/05-k9oryi9wz1.js +0 -14
- package/app/apps/web/.next/static/chunks/05b0dn.r00hq1.js +0 -1
- package/app/apps/web/.next/static/chunks/0t2on1dhydyb2.css +0 -1
- package/app/apps/web/.next/static/chunks/0tliv91mhmrzn.js +0 -1
- /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → bTww6lPxII4wde65VQDrF}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → bTww6lPxII4wde65VQDrF}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{5IC6kxw0JoPeCIPdnTpEp → bTww6lPxII4wde65VQDrF}/_ssgManifest.js +0 -0
|
@@ -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
|
-
|
|
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="/
|
|
223
|
-
active={pathname === "/runs" || pathname.startsWith("/runs/")}
|
|
224
|
-
icon={<
|
|
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
|
-
|
|
220
|
+
Agent
|
|
234
221
|
</NavLink>
|
|
235
222
|
</nav>
|
|
236
223
|
|
|
@@ -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
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { canonicalProjectName, toLocalDay, type SessionDetail } from "@claude-lens/parser";
|
|
2
|
+
import { INDEX_VERSION, type IndexChunk, type IndexDoc } from "./types";
|
|
3
|
+
|
|
4
|
+
const CHUNK_MAX = 4000;
|
|
5
|
+
const DOC_TEXT_MAX = 300_000;
|
|
6
|
+
const TITLE_MAX = 120;
|
|
7
|
+
|
|
8
|
+
/** Harness-injected user events that aren't real human input. */
|
|
9
|
+
const SKIP_PREFIXES = [
|
|
10
|
+
"<command-name>",
|
|
11
|
+
"<local-command",
|
|
12
|
+
"Base directory for this skill:",
|
|
13
|
+
"<task-notification>",
|
|
14
|
+
"[SYSTEM NOTIFICATION",
|
|
15
|
+
"Caveat: the messages below",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function cleanText(text: string): string {
|
|
19
|
+
return text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "").trimEnd();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Tool calls, tool results, and thinking are deliberately excluded —
|
|
23
|
+
* search should find what was said, not every file path a tool printed. */
|
|
24
|
+
export function extractIndexDoc(
|
|
25
|
+
session: SessionDetail,
|
|
26
|
+
file: { mtimeMs: number; sizeBytes: number },
|
|
27
|
+
): IndexDoc {
|
|
28
|
+
const chunks: IndexChunk[] = [];
|
|
29
|
+
let totalChars = 0;
|
|
30
|
+
let truncated = false;
|
|
31
|
+
|
|
32
|
+
for (const e of session.events) {
|
|
33
|
+
if (e.role !== "user" && e.role !== "agent") continue;
|
|
34
|
+
if (e.role === "user" && e.teammateMessage) continue;
|
|
35
|
+
for (const b of e.blocks) {
|
|
36
|
+
if (b.type !== "text" || typeof b.text !== "string") continue;
|
|
37
|
+
if (SKIP_PREFIXES.some((p) => b.text.startsWith(p))) continue;
|
|
38
|
+
let text = cleanText(b.text);
|
|
39
|
+
if (!text.trim()) continue;
|
|
40
|
+
if (text.length > CHUNK_MAX) {
|
|
41
|
+
text = text.slice(0, CHUNK_MAX);
|
|
42
|
+
truncated = true;
|
|
43
|
+
}
|
|
44
|
+
if (totalChars + text.length > DOC_TEXT_MAX) {
|
|
45
|
+
truncated = true;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
totalChars += text.length;
|
|
49
|
+
chunks.push({ role: e.role, text, idx: e.index });
|
|
50
|
+
}
|
|
51
|
+
if (totalChars >= DOC_TEXT_MAX) break;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const firstUser = chunks.find((c) => c.role === "user");
|
|
55
|
+
const rawTitle = session.firstUserPreview?.trim() || firstUser?.text.trim() || "(untitled session)";
|
|
56
|
+
const title = rawTitle.replace(/\s+/g, " ").slice(0, TITLE_MAX);
|
|
57
|
+
|
|
58
|
+
const startMs = session.firstTimestamp ? Date.parse(session.firstTimestamp) : NaN;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
version: INDEX_VERSION,
|
|
62
|
+
sessionId: session.id,
|
|
63
|
+
agent: session.agent ?? "claude-code",
|
|
64
|
+
project: canonicalProjectName(session.projectName),
|
|
65
|
+
...(session.repoName ? { repoName: session.repoName } : {}),
|
|
66
|
+
day: Number.isNaN(startMs) ? "" : toLocalDay(startMs),
|
|
67
|
+
startIso: session.firstTimestamp,
|
|
68
|
+
endIso: session.lastTimestamp,
|
|
69
|
+
title,
|
|
70
|
+
chunks,
|
|
71
|
+
...(truncated ? { truncated } : {}),
|
|
72
|
+
mtimeMs: file.mtimeMs,
|
|
73
|
+
sizeBytes: file.sizeBytes,
|
|
74
|
+
};
|
|
75
|
+
}
|