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