fleetlens 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +119 -0
- 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 +1937 -979
- package/dist/index.js +11210 -1877
- 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/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/0u-n8fz-ms.ee.js +0 -6
- 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/.next/static/chunks/15w5_bct86ut6.js +0 -10
- 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/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{95-RytHk7ZQtfpZj5BLST → 8tTWPvUhtatZEoocIhamh}/_ssgManifest.js +0 -0
package/dist/daemon-worker.js
CHANGED
|
@@ -793,16 +793,18 @@ function findWorktreeMarker(projectName) {
|
|
|
793
793
|
return null;
|
|
794
794
|
}
|
|
795
795
|
function canonicalProjectName(projectName) {
|
|
796
|
-
const
|
|
796
|
+
const normalized = projectName.replace(/\/{2,}/g, "/");
|
|
797
|
+
const hit = findWorktreeMarker(normalized);
|
|
797
798
|
if (hit)
|
|
798
|
-
return
|
|
799
|
-
return
|
|
799
|
+
return normalized.slice(0, hit.idx);
|
|
800
|
+
return normalized;
|
|
800
801
|
}
|
|
801
802
|
function worktreeName(projectName) {
|
|
802
|
-
const
|
|
803
|
+
const normalized = projectName.replace(/\/{2,}/g, "/");
|
|
804
|
+
const hit = findWorktreeMarker(normalized);
|
|
803
805
|
if (!hit)
|
|
804
806
|
return null;
|
|
805
|
-
const tail =
|
|
807
|
+
const tail = normalized.slice(hit.idx + hit.marker.length);
|
|
806
808
|
const slash = tail.indexOf("/");
|
|
807
809
|
return slash >= 0 ? tail.slice(0, slash) : tail;
|
|
808
810
|
}
|
|
@@ -1265,786 +1267,386 @@ var init_analytics = __esm({
|
|
|
1265
1267
|
"../parser/dist/analytics.js"() {
|
|
1266
1268
|
"use strict";
|
|
1267
1269
|
BLANK_USAGE2 = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
|
|
1268
|
-
WORKTREE_MARKERS = [
|
|
1270
|
+
WORKTREE_MARKERS = [
|
|
1271
|
+
"/.claude/worktrees/",
|
|
1272
|
+
"/.worktrees/",
|
|
1273
|
+
"/claude/worktrees/"
|
|
1274
|
+
];
|
|
1269
1275
|
PR_CREATE_RE = /gh\s+pr\s+create\b/i;
|
|
1270
1276
|
PR_NUMBER_RE = /#(\d+)/;
|
|
1271
1277
|
TITLE_FLAG_RE = /--title\s+["']([^"']+)["']/;
|
|
1272
1278
|
}
|
|
1273
1279
|
});
|
|
1274
1280
|
|
|
1275
|
-
// ../parser/dist/
|
|
1276
|
-
function
|
|
1277
|
-
|
|
1278
|
-
return "";
|
|
1279
|
-
const one = s.replace(/\s+/g, " ").trim();
|
|
1280
|
-
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());
|
|
1281
1284
|
}
|
|
1282
|
-
function
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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;
|
|
1287
1294
|
}
|
|
1288
|
-
function
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
const
|
|
1292
|
-
const
|
|
1293
|
-
|
|
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 };
|
|
1294
1304
|
}
|
|
1295
|
-
function
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const
|
|
1300
|
-
|
|
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 };
|
|
1301
1312
|
}
|
|
1302
|
-
function
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
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 };
|
|
1307
1318
|
}
|
|
1308
|
-
function
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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;
|
|
1316
1343
|
}
|
|
1317
|
-
const
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
let
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
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++;
|
|
1328
1362
|
}
|
|
1329
|
-
|
|
1330
|
-
|
|
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();
|
|
1331
1369
|
}
|
|
1332
|
-
|
|
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;
|
|
1333
1384
|
}
|
|
1334
|
-
function
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
lastMs: startMs,
|
|
1338
|
-
eventTs: startMs !== void 0 ? [startMs] : [],
|
|
1339
|
-
userInput,
|
|
1340
|
-
firstToolsIfNoText: [],
|
|
1341
|
-
toolsTotal: 0,
|
|
1342
|
-
toolMix: /* @__PURE__ */ new Map(),
|
|
1343
|
-
bashVerbs: /* @__PURE__ */ new Map(),
|
|
1344
|
-
skills: /* @__PURE__ */ new Map(),
|
|
1345
|
-
subagents: [],
|
|
1346
|
-
taskOps: 0,
|
|
1347
|
-
toolErrors: 0,
|
|
1348
|
-
consecSameToolMax: 0,
|
|
1349
|
-
consec: 0,
|
|
1350
|
-
endedInInterrupt: false,
|
|
1351
|
-
models: /* @__PURE__ */ new Map(),
|
|
1352
|
-
tokensIn: 0,
|
|
1353
|
-
tokensOut: 0,
|
|
1354
|
-
tokensCacheR: 0,
|
|
1355
|
-
tokensCacheW: 0,
|
|
1356
|
-
seenMsgIds: /* @__PURE__ */ new Set()
|
|
1357
|
-
};
|
|
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;
|
|
1358
1388
|
}
|
|
1359
|
-
function
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
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);
|
|
1366
1398
|
}
|
|
1367
|
-
|
|
1368
|
-
closed.push({
|
|
1369
|
-
startMs: t.startMs ?? 0,
|
|
1370
|
-
activeMs: active,
|
|
1371
|
-
wallMs: wall,
|
|
1372
|
-
userInput: t.userInput,
|
|
1373
|
-
firstAgentText: t.firstAgentText,
|
|
1374
|
-
lastAgentText: t.lastAgentText,
|
|
1375
|
-
firstToolsIfNoText: t.firstToolsIfNoText,
|
|
1376
|
-
toolsTotal: t.toolsTotal,
|
|
1377
|
-
toolMix: t.toolMix,
|
|
1378
|
-
bashVerbs: t.bashVerbs,
|
|
1379
|
-
skills: t.skills,
|
|
1380
|
-
subagents: t.subagents,
|
|
1381
|
-
taskOps: t.taskOps,
|
|
1382
|
-
toolErrors: t.toolErrors,
|
|
1383
|
-
consecSameToolMax: t.consecSameToolMax,
|
|
1384
|
-
endedInInterrupt: t.endedInInterrupt,
|
|
1385
|
-
models: t.models,
|
|
1386
|
-
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW
|
|
1387
|
-
});
|
|
1399
|
+
return total;
|
|
1388
1400
|
}
|
|
1389
|
-
function
|
|
1390
|
-
if (
|
|
1391
|
-
return
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
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 });
|
|
1395
1478
|
}
|
|
1396
1479
|
}
|
|
1397
|
-
return
|
|
1480
|
+
return out;
|
|
1398
1481
|
}
|
|
1399
|
-
function
|
|
1400
|
-
if (
|
|
1401
|
-
return
|
|
1402
|
-
const
|
|
1403
|
-
|
|
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);
|
|
1404
1530
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
if (
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
}
|
|
1460
|
-
if (cur && tsMs !== void 0) {
|
|
1461
|
-
cur.eventTs.push(tsMs);
|
|
1462
|
-
cur.lastMs = tsMs;
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
} else if (rawType === "assistant") {
|
|
1466
|
-
if (cur && tsMs !== void 0) {
|
|
1467
|
-
cur.eventTs.push(tsMs);
|
|
1468
|
-
cur.lastMs = tsMs;
|
|
1469
|
-
}
|
|
1470
|
-
const model = msg.model;
|
|
1471
|
-
if (model) {
|
|
1472
|
-
modelTurnsGlobal.set(model, (modelTurnsGlobal.get(model) ?? 0) + 1);
|
|
1473
|
-
if (cur)
|
|
1474
|
-
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
1475
|
-
}
|
|
1476
|
-
const msgId = msg.id;
|
|
1477
|
-
const usage = msg.usage;
|
|
1478
|
-
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
|
|
1479
|
-
cur.seenMsgIds.add(msgId);
|
|
1480
|
-
cur.tokensIn += usage.input_tokens ?? 0;
|
|
1481
|
-
cur.tokensOut += usage.output_tokens ?? 0;
|
|
1482
|
-
cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
|
|
1483
|
-
cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
|
|
1484
|
-
}
|
|
1485
|
-
if (Array.isArray(content)) {
|
|
1486
|
-
for (const c of content) {
|
|
1487
|
-
if (!c || typeof c !== "object")
|
|
1488
|
-
continue;
|
|
1489
|
-
const ct = c;
|
|
1490
|
-
if (ct.type === "text" && ct.text) {
|
|
1491
|
-
finalAgentSession = ct.text;
|
|
1492
|
-
if (cur) {
|
|
1493
|
-
if (cur.firstAgentText === void 0)
|
|
1494
|
-
cur.firstAgentText = ct.text;
|
|
1495
|
-
cur.lastAgentText = ct.text;
|
|
1496
|
-
}
|
|
1497
|
-
} else if (ct.type === "tool_use") {
|
|
1498
|
-
const name = ct.name ?? "unknown";
|
|
1499
|
-
if (cur) {
|
|
1500
|
-
cur.toolsTotal++;
|
|
1501
|
-
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
1502
|
-
if (cur.firstAgentText === void 0 && cur.firstToolsIfNoText.length < 3) {
|
|
1503
|
-
cur.firstToolsIfNoText.push(name);
|
|
1504
|
-
}
|
|
1505
|
-
if (name === cur.lastTool)
|
|
1506
|
-
cur.consec++;
|
|
1507
|
-
else {
|
|
1508
|
-
cur.lastTool = name;
|
|
1509
|
-
cur.consec = 1;
|
|
1510
|
-
}
|
|
1511
|
-
if (cur.consec > cur.consecSameToolMax)
|
|
1512
|
-
cur.consecSameToolMax = cur.consec;
|
|
1513
|
-
if (name === "Skill") {
|
|
1514
|
-
const sk = String(ct.input?.skill ?? "unknown");
|
|
1515
|
-
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
1516
|
-
} else if (name === "ToolSearch") {
|
|
1517
|
-
const q = String(ct.input?.query ?? "");
|
|
1518
|
-
const key = `(ToolSearch: ${trunc(q, 60)})`;
|
|
1519
|
-
cur.skills.set(key, (cur.skills.get(key) ?? 0) + 1);
|
|
1520
|
-
}
|
|
1521
|
-
if (name === "Agent") {
|
|
1522
|
-
const inp = ct.input ?? {};
|
|
1523
|
-
cur.subagents.push({
|
|
1524
|
-
type: String(inp.subagent_type ?? "general-purpose"),
|
|
1525
|
-
model: typeof inp.model === "string" ? inp.model : void 0,
|
|
1526
|
-
description: trunc(String(inp.description ?? ""), 80),
|
|
1527
|
-
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
1528
|
-
background: Boolean(inp.run_in_background)
|
|
1529
|
-
});
|
|
1530
|
-
}
|
|
1531
|
-
if (name === "ExitPlanMode")
|
|
1532
|
-
exitPlanCalls++;
|
|
1533
|
-
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
1534
|
-
cur.taskOps++;
|
|
1535
|
-
taskOpsGlobal++;
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
if (name === "Bash") {
|
|
1539
|
-
const cmd = String(ct.input?.command ?? "");
|
|
1540
|
-
if (cur)
|
|
1541
|
-
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
1542
|
-
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
1543
|
-
const m = cmd.match(PR_TITLE_RE);
|
|
1544
|
-
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
1545
|
-
}
|
|
1546
|
-
if (/\bgit\s+commit\b/.test(cmd))
|
|
1547
|
-
commits++;
|
|
1548
|
-
if (/\bgit\s+push\b/.test(cmd))
|
|
1549
|
-
pushes++;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
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"() {
|
|
1534
|
+
"use strict";
|
|
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 }
|
|
1539
|
+
};
|
|
1540
|
+
RATE_PER_PCT_7D = {
|
|
1541
|
+
"pro": 1.2,
|
|
1542
|
+
"pro-max": 2.4,
|
|
1543
|
+
"pro-max-20x": 12,
|
|
1544
|
+
"custom": 12
|
|
1545
|
+
};
|
|
1546
|
+
RATE_PER_PCT_5H = {
|
|
1547
|
+
"pro": 0.2,
|
|
1548
|
+
"pro-max": 0.4,
|
|
1549
|
+
"pro-max-20x": 2,
|
|
1550
|
+
"custom": 2
|
|
1551
|
+
};
|
|
1552
|
+
RATE_PER_PCT = RATE_PER_PCT_7D;
|
|
1553
|
+
HOUR_MS = 36e5;
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
// ../parser/dist/team.js
|
|
1558
|
+
function groupByTeam(sessions, details) {
|
|
1559
|
+
const byTeam = /* @__PURE__ */ new Map();
|
|
1560
|
+
for (const s of sessions) {
|
|
1561
|
+
if (!s.teamName)
|
|
1562
|
+
continue;
|
|
1563
|
+
const arr = byTeam.get(s.teamName) ?? [];
|
|
1564
|
+
arr.push(s);
|
|
1565
|
+
byTeam.set(s.teamName, arr);
|
|
1566
|
+
}
|
|
1567
|
+
const views = [];
|
|
1568
|
+
for (const [teamName, group] of byTeam) {
|
|
1569
|
+
const view = buildTeamView(teamName, group, details);
|
|
1570
|
+
if (view)
|
|
1571
|
+
views.push(view);
|
|
1572
|
+
}
|
|
1573
|
+
return views;
|
|
1574
|
+
}
|
|
1575
|
+
function buildTeamView(teamName, group, details) {
|
|
1576
|
+
const candidates = group.filter((s) => s.isTeamLead);
|
|
1577
|
+
if (candidates.length === 0)
|
|
1578
|
+
return null;
|
|
1579
|
+
let lead;
|
|
1580
|
+
for (const c of candidates) {
|
|
1581
|
+
const d = details.get(c.sessionId);
|
|
1582
|
+
if (d && containsTeamCreate(d.events)) {
|
|
1583
|
+
lead = c;
|
|
1584
|
+
break;
|
|
1554
1585
|
}
|
|
1555
1586
|
}
|
|
1556
|
-
if (
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
let activeMsTotal = 0;
|
|
1560
|
-
for (let i = 1; i < tsSorted.length; i++) {
|
|
1561
|
-
const dt = tsSorted[i] - tsSorted[i - 1];
|
|
1562
|
-
if (dt < IDLE_GAP_MS)
|
|
1563
|
-
activeMsTotal += dt;
|
|
1564
|
-
}
|
|
1565
|
-
const turnActives = closed.map((t) => t.activeMs);
|
|
1566
|
-
const turnTools = closed.map((t) => t.toolsTotal);
|
|
1567
|
-
const turnTokens = closed.map((t) => t.totalTokens);
|
|
1568
|
-
const subagentTurns = closed.filter((t) => t.subagents.length > 0).length;
|
|
1569
|
-
const primaryModel = [...modelTurnsGlobal.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
1570
|
-
const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
|
|
1571
|
-
const longestTurnActive = Math.max(0, ...turnActives);
|
|
1572
|
-
const sessionSkills = /* @__PURE__ */ new Map();
|
|
1573
|
-
const sessionSubagents = [];
|
|
1574
|
-
for (const t of closed) {
|
|
1575
|
-
for (const [k, v] of t.skills)
|
|
1576
|
-
sessionSkills.set(k, (sessionSkills.get(k) ?? 0) + v);
|
|
1577
|
-
sessionSubagents.push(...t.subagents);
|
|
1578
|
-
}
|
|
1579
|
-
const skillCalls = [...sessionSkills.values()].reduce((a, b) => a + b, 0);
|
|
1580
|
-
const prs = prTitles.length;
|
|
1581
|
-
let outcome;
|
|
1582
|
-
if (prs > 0)
|
|
1583
|
-
outcome = "shipped";
|
|
1584
|
-
else if (commits > 0 && pushes > 0)
|
|
1585
|
-
outcome = "shipped-no-pr";
|
|
1586
|
-
else if (commits > 0)
|
|
1587
|
-
outcome = "partial";
|
|
1588
|
-
else if (activeMsTotal < 2 * 6e4 && closed.length < 3)
|
|
1589
|
-
outcome = "trivial";
|
|
1590
|
-
else
|
|
1591
|
-
outcome = "exploratory";
|
|
1592
|
-
const flags = [];
|
|
1593
|
-
if (totalInterrupts >= 3)
|
|
1594
|
-
flags.push("interrupt_heavy");
|
|
1595
|
-
if (totalToolErrors >= 20)
|
|
1596
|
-
flags.push("high_errors");
|
|
1597
|
-
if (consecMax >= 8)
|
|
1598
|
-
flags.push("loop_suspected");
|
|
1599
|
-
if (activeMsTotal < 5 * 6e4 && prs >= 1)
|
|
1600
|
-
flags.push("fast_ship");
|
|
1601
|
-
if (exitPlanCalls > 0)
|
|
1602
|
-
flags.push("plan_used");
|
|
1603
|
-
if (subagentTurns >= 3)
|
|
1604
|
-
flags.push("orchestrated");
|
|
1605
|
-
if (longestTurnActive >= 20 * 6e4 && totalInterrupts === 0)
|
|
1606
|
-
flags.push("long_autonomous");
|
|
1607
|
-
const maxTokens = Math.max(1, ...turnTokens);
|
|
1608
|
-
const maxTools = Math.max(1, ...turnTools);
|
|
1609
|
-
function significance(t) {
|
|
1610
|
-
return 0.5 * (t.totalTokens / maxTokens) + 0.5 * (t.toolsTotal / maxTools);
|
|
1611
|
-
}
|
|
1612
|
-
let substantive = closed.filter((t) => t.activeMs >= TURN_FLOOR_MS || t.toolsTotal >= 1);
|
|
1613
|
-
const droppedTrivial = closed.length - substantive.length;
|
|
1614
|
-
if (substantive.length > MAX_TURNS_IN_CAPSULE) {
|
|
1615
|
-
substantive = [...substantive].sort((a, b) => significance(b) - significance(a)).slice(0, MAX_TURNS_IN_CAPSULE);
|
|
1616
|
-
}
|
|
1617
|
-
substantive.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
|
|
1618
|
-
function renderTurn(t) {
|
|
1619
|
-
const tOffsetS = t.startMs && sessionFirstTs ? (t.startMs - sessionFirstTs) / 1e3 : 0;
|
|
1620
|
-
const topToolsArr = [];
|
|
1621
|
-
const topTools = [...t.toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
1622
|
-
for (const [name, count] of topTools) {
|
|
1623
|
-
if (name === "Bash" && t.bashVerbs.size > 0) {
|
|
1624
|
-
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(", ");
|
|
1625
|
-
topToolsArr.push(`Bash\xD7${count} (${sub})`);
|
|
1626
|
-
} else {
|
|
1627
|
-
topToolsArr.push(count > 1 ? `${name}\xD7${count}` : name);
|
|
1628
|
-
}
|
|
1629
|
-
}
|
|
1630
|
-
const firstAgentRaw = t.firstAgentText ?? (t.firstToolsIfNoText.length > 0 ? `(tools only: ${t.firstToolsIfNoText.join(", ")})` : "");
|
|
1631
|
-
const firstTrunc = trunc(minimizeImages(firstAgentRaw), 260);
|
|
1632
|
-
const finalTrunc = firstNSentences(minimizeImages(t.lastAgentText ?? ""), 3, 500);
|
|
1633
|
-
const ended = t.endedInInterrupt ? "interrupt" : t.lastAgentText ? "text" : "tool-in-flight";
|
|
1634
|
-
const model = [...t.models.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
1635
|
-
const turn = {
|
|
1636
|
-
t_offset_min: Math.round(tOffsetS / 6) / 10,
|
|
1637
|
-
active_min: Math.round(t.activeMs / 6e3) / 10,
|
|
1638
|
-
wall_min: Math.round(t.wallMs / 6e3) / 10,
|
|
1639
|
-
user_input: trunc(minimizeImages(t.userInput), 300),
|
|
1640
|
-
tools_total: t.toolsTotal,
|
|
1641
|
-
top_tools: topToolsArr,
|
|
1642
|
-
tokens: t.totalTokens,
|
|
1643
|
-
errors: t.toolErrors,
|
|
1644
|
-
consec_same_tool_max: t.consecSameToolMax,
|
|
1645
|
-
ended,
|
|
1646
|
-
model
|
|
1647
|
-
};
|
|
1648
|
-
if (firstTrunc && finalTrunc && firstTrunc === finalTrunc) {
|
|
1649
|
-
turn.agent = firstTrunc;
|
|
1650
|
-
} else if (firstAgentRaw && t.lastAgentText && t.firstAgentText === t.lastAgentText) {
|
|
1651
|
-
turn.agent = finalTrunc;
|
|
1652
|
-
} else {
|
|
1653
|
-
turn.first_agent = firstTrunc;
|
|
1654
|
-
turn.final_agent = finalTrunc;
|
|
1655
|
-
}
|
|
1656
|
-
if (t.skills.size > 0)
|
|
1657
|
-
turn.skills = Object.fromEntries(t.skills);
|
|
1658
|
-
if (t.subagents.length > 0)
|
|
1659
|
-
turn.subagents = t.subagents;
|
|
1660
|
-
return turn;
|
|
1661
|
-
}
|
|
1662
|
-
const capsule = {
|
|
1663
|
-
session_id: session.id,
|
|
1664
|
-
project: session.projectName,
|
|
1665
|
-
start_iso: session.firstTimestamp,
|
|
1666
|
-
end_iso: session.lastTimestamp,
|
|
1667
|
-
outcome,
|
|
1668
|
-
flags,
|
|
1669
|
-
primary_model: primaryModel,
|
|
1670
|
-
model_mix: Object.fromEntries(modelTurnsGlobal),
|
|
1671
|
-
numbers: {
|
|
1672
|
-
active_min: Math.round(activeMsTotal / 6e3) / 10,
|
|
1673
|
-
turn_count: closed.length,
|
|
1674
|
-
longest_turn_min: Math.round(longestTurnActive / 6e3) / 10,
|
|
1675
|
-
median_turn_min: Math.round(median(turnActives) / 6e3) / 10,
|
|
1676
|
-
p90_turn_min: Math.round(p90(turnActives) / 6e3) / 10,
|
|
1677
|
-
tools_total: turnTools.reduce((a, b) => a + b, 0),
|
|
1678
|
-
tokens_total: turnTokens.reduce((a, b) => a + b, 0),
|
|
1679
|
-
subagent_turns: subagentTurns,
|
|
1680
|
-
subagent_calls: sessionSubagents.length,
|
|
1681
|
-
skill_calls: skillCalls,
|
|
1682
|
-
task_ops: taskOpsGlobal,
|
|
1683
|
-
interrupts: totalInterrupts,
|
|
1684
|
-
tool_errors: totalToolErrors,
|
|
1685
|
-
consec_same_tool_max: consecMax,
|
|
1686
|
-
exit_plan_calls: exitPlanCalls,
|
|
1687
|
-
prs,
|
|
1688
|
-
commits,
|
|
1689
|
-
pushes
|
|
1690
|
-
},
|
|
1691
|
-
pr_titles: prTitles,
|
|
1692
|
-
first_user: trunc(minimizeImages(firstUserSession ?? ""), 400),
|
|
1693
|
-
final_agent: trunc(minimizeImages(finalAgentSession ?? ""), 400)
|
|
1694
|
-
};
|
|
1695
|
-
if (sessionSkills.size > 0)
|
|
1696
|
-
capsule.skills = Object.fromEntries(sessionSkills);
|
|
1697
|
-
if (sessionSubagents.length > 0)
|
|
1698
|
-
capsule.subagents = sessionSubagents;
|
|
1699
|
-
if (!compact) {
|
|
1700
|
-
capsule.numbers.turns_in_capsule = substantive.length;
|
|
1701
|
-
capsule.numbers.trivial_turns_dropped = droppedTrivial;
|
|
1702
|
-
capsule.turns = substantive.map(renderTurn);
|
|
1703
|
-
}
|
|
1704
|
-
return capsule;
|
|
1705
|
-
}
|
|
1706
|
-
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;
|
|
1707
|
-
var init_capsule = __esm({
|
|
1708
|
-
"../parser/dist/capsule.js"() {
|
|
1709
|
-
"use strict";
|
|
1710
|
-
IDLE_GAP_MS = 3 * 60 * 1e3;
|
|
1711
|
-
MAX_TURNS_IN_CAPSULE = 25;
|
|
1712
|
-
TURN_FLOOR_MS = 15 * 1e3;
|
|
1713
|
-
INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
|
|
1714
|
-
SENTENCE_SPLIT_RE = /(?<=[.!?])\s+(?=[A-Z[`])/;
|
|
1715
|
-
IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
|
|
1716
|
-
PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
|
|
1717
|
-
BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
|
|
1718
|
-
SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
|
|
1587
|
+
if (!lead) {
|
|
1588
|
+
const sorted = [...candidates].sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
|
|
1589
|
+
lead = sorted[0];
|
|
1719
1590
|
}
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
1729
|
-
const stop = new Date(end.getFullYear(), end.getMonth(), end.getDate());
|
|
1730
|
-
while (cur.getTime() <= stop.getTime()) {
|
|
1731
|
-
out.push(new Date(cur));
|
|
1732
|
-
cur.setDate(cur.getDate() + 1);
|
|
1591
|
+
const agentNameBySessionId = /* @__PURE__ */ new Map();
|
|
1592
|
+
for (const s of group)
|
|
1593
|
+
agentNameBySessionId.set(s.sessionId, s.agentName);
|
|
1594
|
+
const members = group.filter((s) => s.sessionId !== lead.sessionId).sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
|
|
1595
|
+
const sessionIdByAgentName = /* @__PURE__ */ new Map();
|
|
1596
|
+
for (const m of members) {
|
|
1597
|
+
if (m.agentName)
|
|
1598
|
+
sessionIdByAgentName.set(m.agentName, m.sessionId);
|
|
1733
1599
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
return cleaned || p;
|
|
1739
|
-
}
|
|
1740
|
-
function buildPeriodBundle(caps, opts) {
|
|
1741
|
-
const { period, trivial_dropped, sessions_total } = opts;
|
|
1742
|
-
const days = rangeDays(period.start, period.end);
|
|
1743
|
-
const dayKeys = new Map(days.map((d) => [isoDay(d), d]));
|
|
1744
|
-
const dayAgent = /* @__PURE__ */ new Map();
|
|
1745
|
-
const daySessions = /* @__PURE__ */ new Map();
|
|
1746
|
-
for (const k of dayKeys.keys()) {
|
|
1747
|
-
dayAgent.set(k, 0);
|
|
1748
|
-
daySessions.set(k, 0);
|
|
1749
|
-
}
|
|
1750
|
-
for (const c of caps) {
|
|
1751
|
-
if (!c.start_iso)
|
|
1752
|
-
continue;
|
|
1753
|
-
const d = isoDay(new Date(c.start_iso));
|
|
1754
|
-
if (!dayAgent.has(d))
|
|
1600
|
+
const messages = [];
|
|
1601
|
+
for (const s of group) {
|
|
1602
|
+
const d = details.get(s.sessionId);
|
|
1603
|
+
if (!d)
|
|
1755
1604
|
continue;
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
b.agent_min += c.numbers.active_min;
|
|
1773
|
-
b.prs += c.numbers.prs;
|
|
1774
|
-
b.commits += c.numbers.commits;
|
|
1775
|
-
projectAccum.set(canon, b);
|
|
1776
|
-
}
|
|
1777
|
-
const totalProjectMin = [...projectAccum.values()].reduce((a, b) => a + b.agent_min, 0);
|
|
1778
|
-
const project_shares = [...projectAccum.entries()].map(([name, b]) => ({
|
|
1779
|
-
name,
|
|
1780
|
-
display_name: prettyProjectName(name),
|
|
1781
|
-
agent_min: Math.round(b.agent_min * 10) / 10,
|
|
1782
|
-
share_pct: totalProjectMin === 0 ? 0 : Math.round(b.agent_min / totalProjectMin * 100),
|
|
1783
|
-
prs: b.prs,
|
|
1784
|
-
commits: b.commits
|
|
1785
|
-
})).sort((a, b) => b.agent_min - a.agent_min);
|
|
1786
|
-
const shipped_prs = [];
|
|
1787
|
-
for (const c of caps) {
|
|
1788
|
-
for (const title of c.pr_titles) {
|
|
1789
|
-
shipped_prs.push({
|
|
1790
|
-
title,
|
|
1791
|
-
project: prettyProjectName(canonicalProjectName(c.project ?? "")),
|
|
1792
|
-
session_id: c.session_id,
|
|
1793
|
-
start_iso: c.start_iso ?? "",
|
|
1794
|
-
active_min: c.numbers.active_min,
|
|
1795
|
-
commits: c.numbers.commits,
|
|
1796
|
-
subagents: c.numbers.subagent_calls,
|
|
1797
|
-
flags: c.flags
|
|
1605
|
+
const senderId = s.sessionId;
|
|
1606
|
+
const senderTeammateId = s.agentName ?? "team-lead";
|
|
1607
|
+
for (const ev of d.events) {
|
|
1608
|
+
const msg = extractSendMessage(ev);
|
|
1609
|
+
if (!msg)
|
|
1610
|
+
continue;
|
|
1611
|
+
const toTeammateId = msg.to;
|
|
1612
|
+
const toSessionId = toTeammateId === "team-lead" ? lead.sessionId : sessionIdByAgentName.get(toTeammateId) ?? "";
|
|
1613
|
+
messages.push({
|
|
1614
|
+
tsMs: ev.timestamp ? Date.parse(ev.timestamp) : 0,
|
|
1615
|
+
fromSessionId: senderId,
|
|
1616
|
+
fromTeammateId: senderTeammateId,
|
|
1617
|
+
toSessionId,
|
|
1618
|
+
toTeammateId,
|
|
1619
|
+
body: msg.body,
|
|
1620
|
+
kind: "message"
|
|
1798
1621
|
});
|
|
1799
1622
|
}
|
|
1800
1623
|
}
|
|
1801
|
-
|
|
1802
|
-
const
|
|
1803
|
-
const
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
for (const [k, v] of Object.entries(c.skills)) {
|
|
1809
|
-
skills_total[k] = (skills_total[k] ?? 0) + v;
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
totals.agent_min += c.numbers.active_min;
|
|
1813
|
-
totals.commits += c.numbers.commits;
|
|
1814
|
-
totals.pushes += c.numbers.pushes;
|
|
1815
|
-
totals.prs += c.numbers.prs;
|
|
1816
|
-
totals.subagent_calls += c.numbers.subagent_calls;
|
|
1817
|
-
totals.tool_errors += c.numbers.tool_errors;
|
|
1818
|
-
}
|
|
1819
|
-
totals.agent_min = Math.round(totals.agent_min * 10) / 10;
|
|
1820
|
-
const longest = [...caps].sort((a, b) => b.numbers.active_min - a.numbers.active_min)[0];
|
|
1821
|
-
const shipped = caps.filter((c) => c.numbers.prs >= 1);
|
|
1822
|
-
const fastest = shipped.sort((a, b) => a.numbers.active_min - b.numbers.active_min)[0];
|
|
1823
|
-
const mostErr = [...caps].sort((a, b) => b.numbers.tool_errors - a.numbers.tool_errors)[0];
|
|
1824
|
-
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];
|
|
1825
|
-
const outliers = {};
|
|
1826
|
-
if (longest && longest.numbers.active_min > 0) {
|
|
1827
|
-
outliers.longest_run = {
|
|
1828
|
-
session_id: longest.session_id,
|
|
1829
|
-
project: prettyProjectName(canonicalProjectName(longest.project ?? "")),
|
|
1830
|
-
active_min: longest.numbers.active_min,
|
|
1831
|
-
flags: longest.flags
|
|
1832
|
-
};
|
|
1833
|
-
}
|
|
1834
|
-
if (fastest) {
|
|
1835
|
-
outliers.fastest_ship = {
|
|
1836
|
-
session_id: fastest.session_id,
|
|
1837
|
-
project: prettyProjectName(canonicalProjectName(fastest.project ?? "")),
|
|
1838
|
-
active_min: fastest.numbers.active_min,
|
|
1839
|
-
pr_title: fastest.pr_titles[0] ?? ""
|
|
1840
|
-
};
|
|
1841
|
-
}
|
|
1842
|
-
if (mostErr && mostErr.numbers.tool_errors > 0) {
|
|
1843
|
-
outliers.most_errors = {
|
|
1844
|
-
session_id: mostErr.session_id,
|
|
1845
|
-
project: prettyProjectName(canonicalProjectName(mostErr.project ?? "")),
|
|
1846
|
-
active_min: mostErr.numbers.active_min,
|
|
1847
|
-
tool_errors: mostErr.numbers.tool_errors
|
|
1848
|
-
};
|
|
1849
|
-
}
|
|
1850
|
-
if (wandered) {
|
|
1851
|
-
outliers.wandered = {
|
|
1852
|
-
session_id: wandered.session_id,
|
|
1853
|
-
project: prettyProjectName(canonicalProjectName(wandered.project ?? "")),
|
|
1854
|
-
active_min: wandered.numbers.active_min
|
|
1855
|
-
};
|
|
1624
|
+
messages.sort((a, b) => a.tsMs - b.tsMs);
|
|
1625
|
+
const allTs = [];
|
|
1626
|
+
for (const s of group) {
|
|
1627
|
+
if (s.firstTimestamp)
|
|
1628
|
+
allTs.push(Date.parse(s.firstTimestamp));
|
|
1629
|
+
if (s.lastTimestamp)
|
|
1630
|
+
allTs.push(Date.parse(s.lastTimestamp));
|
|
1856
1631
|
}
|
|
1857
|
-
const
|
|
1632
|
+
const firstEventMs = allTs.length ? Math.min(...allTs) : 0;
|
|
1633
|
+
const lastEventMs = allTs.length ? Math.max(...allTs) : 0;
|
|
1858
1634
|
return {
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
counts: { sessions_total, substantive: caps.length, trivial_dropped },
|
|
1867
|
-
totals,
|
|
1868
|
-
by_day,
|
|
1869
|
-
project_shares,
|
|
1870
|
-
shipped_prs,
|
|
1871
|
-
flags_count,
|
|
1872
|
-
skills_total,
|
|
1873
|
-
outliers
|
|
1635
|
+
teamName,
|
|
1636
|
+
leadSessionId: lead.sessionId,
|
|
1637
|
+
memberSessionIds: members.map((m) => m.sessionId),
|
|
1638
|
+
agentNameBySessionId,
|
|
1639
|
+
messages,
|
|
1640
|
+
firstEventMs,
|
|
1641
|
+
lastEventMs
|
|
1874
1642
|
};
|
|
1875
1643
|
}
|
|
1876
|
-
function
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
const daysSinceMon = (dow + 6) % 7;
|
|
1880
|
-
const monday = new Date(d);
|
|
1881
|
-
monday.setDate(d.getDate() - daysSinceMon);
|
|
1882
|
-
const sunday = new Date(monday);
|
|
1883
|
-
sunday.setDate(monday.getDate() + 6);
|
|
1884
|
-
return { start: monday, end: sunday };
|
|
1885
|
-
}
|
|
1886
|
-
function priorCalendarWeek(ref = /* @__PURE__ */ new Date()) {
|
|
1887
|
-
const cur = calendarWeek(ref);
|
|
1888
|
-
const prevEnd = new Date(cur.start);
|
|
1889
|
-
prevEnd.setDate(cur.start.getDate() - 1);
|
|
1890
|
-
const prevStart = new Date(prevEnd);
|
|
1891
|
-
prevStart.setDate(prevEnd.getDate() - 6);
|
|
1892
|
-
return { start: prevStart, end: prevEnd };
|
|
1893
|
-
}
|
|
1894
|
-
function last4CompletedWeeks(ref = /* @__PURE__ */ new Date()) {
|
|
1895
|
-
const { end } = priorCalendarWeek(ref);
|
|
1896
|
-
const start = new Date(end);
|
|
1897
|
-
start.setDate(end.getDate() - 27);
|
|
1898
|
-
return { start, end };
|
|
1899
|
-
}
|
|
1900
|
-
function calendarMonth(ref = /* @__PURE__ */ new Date()) {
|
|
1901
|
-
const start = new Date(ref.getFullYear(), ref.getMonth(), 1);
|
|
1902
|
-
const end = new Date(ref.getFullYear(), ref.getMonth() + 1, 0);
|
|
1903
|
-
return { start, end };
|
|
1904
|
-
}
|
|
1905
|
-
function priorCalendarMonth(ref = /* @__PURE__ */ new Date()) {
|
|
1906
|
-
const start = new Date(ref.getFullYear(), ref.getMonth() - 1, 1);
|
|
1907
|
-
const end = new Date(ref.getFullYear(), ref.getMonth(), 0);
|
|
1908
|
-
return { start, end };
|
|
1909
|
-
}
|
|
1910
|
-
function aggregateConcurrency(bursts, period) {
|
|
1911
|
-
const days = rangeDays(period.start, period.end);
|
|
1912
|
-
const byDayKey = /* @__PURE__ */ new Map();
|
|
1913
|
-
for (const d of days)
|
|
1914
|
-
byDayKey.set(isoDay(d), { peak: 0, has_cross_project: false });
|
|
1915
|
-
for (const b of bursts) {
|
|
1916
|
-
const dayKey = isoDay(new Date(b.startMs));
|
|
1917
|
-
const cur = byDayKey.get(dayKey);
|
|
1918
|
-
if (!cur)
|
|
1644
|
+
function containsTeamCreate(events) {
|
|
1645
|
+
for (const ev of events) {
|
|
1646
|
+
if (ev.role !== "tool-call")
|
|
1919
1647
|
continue;
|
|
1920
|
-
if (
|
|
1921
|
-
|
|
1922
|
-
if (b.crossProject)
|
|
1923
|
-
cur.has_cross_project = true;
|
|
1924
|
-
}
|
|
1925
|
-
const by_day = days.map((d) => {
|
|
1926
|
-
const k = isoDay(d);
|
|
1927
|
-
const v = byDayKey.get(k);
|
|
1928
|
-
return { date: k, peak: v.peak, has_cross_project: v.has_cross_project };
|
|
1929
|
-
});
|
|
1930
|
-
let peak = 0;
|
|
1931
|
-
let peak_day;
|
|
1932
|
-
let multi_agent_days = 0;
|
|
1933
|
-
let cross_project_days = 0;
|
|
1934
|
-
for (const d of by_day) {
|
|
1935
|
-
if (d.peak > peak) {
|
|
1936
|
-
peak = d.peak;
|
|
1937
|
-
peak_day = d.date;
|
|
1938
|
-
}
|
|
1939
|
-
if (d.peak >= 3)
|
|
1940
|
-
multi_agent_days++;
|
|
1941
|
-
if (d.has_cross_project)
|
|
1942
|
-
cross_project_days++;
|
|
1943
|
-
}
|
|
1944
|
-
return { by_day, peak, peak_day, multi_agent_days, cross_project_days };
|
|
1945
|
-
}
|
|
1946
|
-
var DAY_NAMES;
|
|
1947
|
-
var init_aggregate = __esm({
|
|
1948
|
-
"../parser/dist/aggregate.js"() {
|
|
1949
|
-
"use strict";
|
|
1950
|
-
init_analytics();
|
|
1951
|
-
DAY_NAMES = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
1952
|
-
}
|
|
1953
|
-
});
|
|
1954
|
-
|
|
1955
|
-
// ../parser/dist/team.js
|
|
1956
|
-
function groupByTeam(sessions, details) {
|
|
1957
|
-
const byTeam = /* @__PURE__ */ new Map();
|
|
1958
|
-
for (const s of sessions) {
|
|
1959
|
-
if (!s.teamName)
|
|
1960
|
-
continue;
|
|
1961
|
-
const arr = byTeam.get(s.teamName) ?? [];
|
|
1962
|
-
arr.push(s);
|
|
1963
|
-
byTeam.set(s.teamName, arr);
|
|
1964
|
-
}
|
|
1965
|
-
const views = [];
|
|
1966
|
-
for (const [teamName, group] of byTeam) {
|
|
1967
|
-
const view = buildTeamView(teamName, group, details);
|
|
1968
|
-
if (view)
|
|
1969
|
-
views.push(view);
|
|
1970
|
-
}
|
|
1971
|
-
return views;
|
|
1972
|
-
}
|
|
1973
|
-
function buildTeamView(teamName, group, details) {
|
|
1974
|
-
const candidates = group.filter((s) => s.isTeamLead);
|
|
1975
|
-
if (candidates.length === 0)
|
|
1976
|
-
return null;
|
|
1977
|
-
let lead;
|
|
1978
|
-
for (const c of candidates) {
|
|
1979
|
-
const d = details.get(c.sessionId);
|
|
1980
|
-
if (d && containsTeamCreate(d.events)) {
|
|
1981
|
-
lead = c;
|
|
1982
|
-
break;
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
if (!lead) {
|
|
1986
|
-
const sorted = [...candidates].sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
|
|
1987
|
-
lead = sorted[0];
|
|
1988
|
-
}
|
|
1989
|
-
const agentNameBySessionId = /* @__PURE__ */ new Map();
|
|
1990
|
-
for (const s of group)
|
|
1991
|
-
agentNameBySessionId.set(s.sessionId, s.agentName);
|
|
1992
|
-
const members = group.filter((s) => s.sessionId !== lead.sessionId).sort((a, b) => Date.parse(a.firstTimestamp ?? "") - Date.parse(b.firstTimestamp ?? ""));
|
|
1993
|
-
const sessionIdByAgentName = /* @__PURE__ */ new Map();
|
|
1994
|
-
for (const m of members) {
|
|
1995
|
-
if (m.agentName)
|
|
1996
|
-
sessionIdByAgentName.set(m.agentName, m.sessionId);
|
|
1997
|
-
}
|
|
1998
|
-
const messages = [];
|
|
1999
|
-
for (const s of group) {
|
|
2000
|
-
const d = details.get(s.sessionId);
|
|
2001
|
-
if (!d)
|
|
2002
|
-
continue;
|
|
2003
|
-
const senderId = s.sessionId;
|
|
2004
|
-
const senderTeammateId = s.agentName ?? "team-lead";
|
|
2005
|
-
for (const ev of d.events) {
|
|
2006
|
-
const msg = extractSendMessage(ev);
|
|
2007
|
-
if (!msg)
|
|
2008
|
-
continue;
|
|
2009
|
-
const toTeammateId = msg.to;
|
|
2010
|
-
const toSessionId = toTeammateId === "team-lead" ? lead.sessionId : sessionIdByAgentName.get(toTeammateId) ?? "";
|
|
2011
|
-
messages.push({
|
|
2012
|
-
tsMs: ev.timestamp ? Date.parse(ev.timestamp) : 0,
|
|
2013
|
-
fromSessionId: senderId,
|
|
2014
|
-
fromTeammateId: senderTeammateId,
|
|
2015
|
-
toSessionId,
|
|
2016
|
-
toTeammateId,
|
|
2017
|
-
body: msg.body,
|
|
2018
|
-
kind: "message"
|
|
2019
|
-
});
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2022
|
-
messages.sort((a, b) => a.tsMs - b.tsMs);
|
|
2023
|
-
const allTs = [];
|
|
2024
|
-
for (const s of group) {
|
|
2025
|
-
if (s.firstTimestamp)
|
|
2026
|
-
allTs.push(Date.parse(s.firstTimestamp));
|
|
2027
|
-
if (s.lastTimestamp)
|
|
2028
|
-
allTs.push(Date.parse(s.lastTimestamp));
|
|
2029
|
-
}
|
|
2030
|
-
const firstEventMs = allTs.length ? Math.min(...allTs) : 0;
|
|
2031
|
-
const lastEventMs = allTs.length ? Math.max(...allTs) : 0;
|
|
2032
|
-
return {
|
|
2033
|
-
teamName,
|
|
2034
|
-
leadSessionId: lead.sessionId,
|
|
2035
|
-
memberSessionIds: members.map((m) => m.sessionId),
|
|
2036
|
-
agentNameBySessionId,
|
|
2037
|
-
messages,
|
|
2038
|
-
firstEventMs,
|
|
2039
|
-
lastEventMs
|
|
2040
|
-
};
|
|
2041
|
-
}
|
|
2042
|
-
function containsTeamCreate(events) {
|
|
2043
|
-
for (const ev of events) {
|
|
2044
|
-
if (ev.role !== "tool-call")
|
|
2045
|
-
continue;
|
|
2046
|
-
if (ev.toolName === "TeamCreate")
|
|
2047
|
-
return true;
|
|
1648
|
+
if (ev.toolName === "TeamCreate")
|
|
1649
|
+
return true;
|
|
2048
1650
|
}
|
|
2049
1651
|
return false;
|
|
2050
1652
|
}
|
|
@@ -2074,15 +1676,19 @@ var init_team = __esm({
|
|
|
2074
1676
|
// ../parser/dist/index.js
|
|
2075
1677
|
var dist_exports = {};
|
|
2076
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,
|
|
2077
1683
|
aggregateConcurrency: () => aggregateConcurrency,
|
|
2078
|
-
buildCapsule: () => buildCapsule,
|
|
2079
1684
|
buildGanttDay: () => buildGanttDay,
|
|
2080
1685
|
buildMegaRows: () => buildMegaRows,
|
|
2081
|
-
buildPeriodBundle: () => buildPeriodBundle,
|
|
2082
1686
|
buildPresentation: () => buildPresentation,
|
|
1687
|
+
buildSpendIndex: () => buildSpendIndex,
|
|
2083
1688
|
calendarMonth: () => calendarMonth,
|
|
2084
1689
|
calendarWeek: () => calendarWeek,
|
|
2085
1690
|
canonicalProjectName: () => canonicalProjectName,
|
|
1691
|
+
collectSnapPairRates: () => collectSnapPairRates,
|
|
2086
1692
|
computeActiveSegments: () => computeActiveSegments,
|
|
2087
1693
|
computeBurstsFromSessions: () => computeBurstsFromSessions,
|
|
2088
1694
|
computeParallelism: () => computeParallelism,
|
|
@@ -2091,11 +1697,18 @@ __export(dist_exports, {
|
|
|
2091
1697
|
daysBetween: () => daysBetween,
|
|
2092
1698
|
detectParallelRuns: () => detectParallelRuns,
|
|
2093
1699
|
detectPrMarkers: () => detectPrMarkers,
|
|
1700
|
+
dollarsInRange: () => dollarsInRange,
|
|
1701
|
+
dollarsInWindow: () => dollarsInWindow,
|
|
1702
|
+
eventDollars: () => eventDollars,
|
|
2094
1703
|
groupByProject: () => groupByProject,
|
|
2095
1704
|
groupByTeam: () => groupByTeam,
|
|
1705
|
+
groupSnapsByCycle: () => groupSnapsByCycle,
|
|
2096
1706
|
highLevelMetrics: () => highLevelMetrics,
|
|
2097
1707
|
last4CompletedWeeks: () => last4CompletedWeeks,
|
|
1708
|
+
modelFamily: () => modelFamily,
|
|
2098
1709
|
parseTranscript: () => parseTranscript,
|
|
1710
|
+
predictAnchored: () => predictAnchored,
|
|
1711
|
+
predictUtilization: () => predictUtilization,
|
|
2099
1712
|
priorCalendarMonth: () => priorCalendarMonth,
|
|
2100
1713
|
priorCalendarWeek: () => priorCalendarWeek,
|
|
2101
1714
|
rowPrimaryIndex: () => rowPrimaryIndex,
|
|
@@ -2103,6 +1716,7 @@ __export(dist_exports, {
|
|
|
2103
1716
|
sessionDay: () => sessionDay,
|
|
2104
1717
|
summarizeBursts: () => summarizeBursts,
|
|
2105
1718
|
toLocalDay: () => toLocalDay,
|
|
1719
|
+
userSoloRate: () => userSoloRate,
|
|
2106
1720
|
worktreeName: () => worktreeName
|
|
2107
1721
|
});
|
|
2108
1722
|
var init_dist = __esm({
|
|
@@ -2112,8 +1726,8 @@ var init_dist = __esm({
|
|
|
2112
1726
|
init_parser();
|
|
2113
1727
|
init_presentation();
|
|
2114
1728
|
init_analytics();
|
|
2115
|
-
init_capsule();
|
|
2116
1729
|
init_aggregate();
|
|
1730
|
+
init_calibration();
|
|
2117
1731
|
init_team();
|
|
2118
1732
|
}
|
|
2119
1733
|
});
|
|
@@ -2122,6 +1736,7 @@ var init_dist = __esm({
|
|
|
2122
1736
|
var fs_exports = {};
|
|
2123
1737
|
__export(fs_exports, {
|
|
2124
1738
|
DEFAULT_ROOT: () => DEFAULT_ROOT,
|
|
1739
|
+
buildCalibrationCurve: () => buildCalibrationCurve,
|
|
2125
1740
|
cacheStats: () => cacheStats,
|
|
2126
1741
|
clearCaches: () => clearCaches,
|
|
2127
1742
|
decodeProjectName: () => decodeProjectName,
|
|
@@ -2130,6 +1745,9 @@ __export(fs_exports, {
|
|
|
2130
1745
|
invalidateFile: () => invalidateFile,
|
|
2131
1746
|
listProjects: () => listProjects,
|
|
2132
1747
|
listSessions: () => listSessions,
|
|
1748
|
+
loadCalibrationCurve: () => loadCalibrationCurve,
|
|
1749
|
+
loadCalibrationEvents: () => loadCalibrationEvents,
|
|
1750
|
+
loadCalibrationSnapshots: () => loadCalibrationSnapshots,
|
|
2133
1751
|
loadTeamForSession: () => loadTeamForSession,
|
|
2134
1752
|
loadUsageByDay: () => loadUsageByDay,
|
|
2135
1753
|
readJsonlFile: () => readJsonlFile,
|
|
@@ -2651,6 +2269,210 @@ async function loadUsageByDay(start, end) {
|
|
|
2651
2269
|
}
|
|
2652
2270
|
return { by_day: out };
|
|
2653
2271
|
}
|
|
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
|
+
}
|
|
2313
|
+
}
|
|
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
|
+
}
|
|
2654
2476
|
var DEFAULT_ROOT, metaCache, detailCache, fileListCache, FILE_LIST_TTL_MS, BLANK_USAGE3, USAGE_LOG;
|
|
2655
2477
|
var init_fs = __esm({
|
|
2656
2478
|
"../parser/dist/fs.js"() {
|
|
@@ -2658,6 +2480,7 @@ var init_fs = __esm({
|
|
|
2658
2480
|
init_parser();
|
|
2659
2481
|
init_analytics();
|
|
2660
2482
|
init_team();
|
|
2483
|
+
init_calibration();
|
|
2661
2484
|
DEFAULT_ROOT = path.join(os.homedir(), ".claude", "projects");
|
|
2662
2485
|
metaCache = /* @__PURE__ */ new Map();
|
|
2663
2486
|
detailCache = /* @__PURE__ */ new Map();
|
|
@@ -2669,9 +2492,9 @@ var init_fs = __esm({
|
|
|
2669
2492
|
});
|
|
2670
2493
|
|
|
2671
2494
|
// src/daemon-worker.ts
|
|
2672
|
-
import { appendFileSync as appendFileSync3, mkdirSync as
|
|
2673
|
-
import { dirname as
|
|
2674
|
-
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";
|
|
2675
2498
|
|
|
2676
2499
|
// src/usage/token.ts
|
|
2677
2500
|
import { execFileSync } from "node:child_process";
|
|
@@ -2759,250 +2582,1368 @@ async function fetchUsage() {
|
|
|
2759
2582
|
Authorization: `Bearer ${creds.accessToken}`,
|
|
2760
2583
|
"anthropic-beta": BETA_HEADER
|
|
2761
2584
|
}
|
|
2762
|
-
});
|
|
2763
|
-
} catch (err) {
|
|
2764
|
-
throw new UsageApiError(
|
|
2765
|
-
`Network error reaching Anthropic: ${err.message}`,
|
|
2766
|
-
"network"
|
|
2767
|
-
);
|
|
2768
|
-
}
|
|
2769
|
-
if (!res.ok) {
|
|
2770
|
-
throw new UsageApiError(
|
|
2771
|
-
`Usage endpoint returned ${res.status} ${res.statusText}`,
|
|
2772
|
-
"http"
|
|
2773
|
-
);
|
|
2585
|
+
});
|
|
2586
|
+
} catch (err) {
|
|
2587
|
+
throw new UsageApiError(
|
|
2588
|
+
`Network error reaching Anthropic: ${err.message}`,
|
|
2589
|
+
"network"
|
|
2590
|
+
);
|
|
2591
|
+
}
|
|
2592
|
+
if (!res.ok) {
|
|
2593
|
+
throw new UsageApiError(
|
|
2594
|
+
`Usage endpoint returned ${res.status} ${res.statusText}`,
|
|
2595
|
+
"http"
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2598
|
+
let body;
|
|
2599
|
+
try {
|
|
2600
|
+
body = await res.json();
|
|
2601
|
+
} catch (err) {
|
|
2602
|
+
throw new UsageApiError(`Failed to parse usage response: ${err.message}`, "parse");
|
|
2603
|
+
}
|
|
2604
|
+
const b = body;
|
|
2605
|
+
return {
|
|
2606
|
+
captured_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2607
|
+
five_hour: normalizeWindow(b.five_hour) ?? { utilization: null, resets_at: null },
|
|
2608
|
+
seven_day: normalizeWindow(b.seven_day) ?? { utilization: null, resets_at: null },
|
|
2609
|
+
seven_day_opus: normalizeWindow(b.seven_day_opus),
|
|
2610
|
+
seven_day_sonnet: normalizeWindow(b.seven_day_sonnet),
|
|
2611
|
+
seven_day_oauth_apps: normalizeWindow(b.seven_day_oauth_apps),
|
|
2612
|
+
seven_day_cowork: normalizeWindow(b.seven_day_cowork),
|
|
2613
|
+
extra_usage: normalizeExtra(b.extra_usage)
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
function normalizeWindow(raw) {
|
|
2617
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2618
|
+
const r = raw;
|
|
2619
|
+
return {
|
|
2620
|
+
utilization: typeof r.utilization === "number" ? r.utilization : null,
|
|
2621
|
+
resets_at: typeof r.resets_at === "string" ? r.resets_at : null
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
function normalizeExtra(raw) {
|
|
2625
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2626
|
+
const r = raw;
|
|
2627
|
+
return {
|
|
2628
|
+
is_enabled: r.is_enabled === true,
|
|
2629
|
+
monthly_limit: typeof r.monthly_limit === "number" ? r.monthly_limit : null,
|
|
2630
|
+
used_credits: typeof r.used_credits === "number" ? r.used_credits : null,
|
|
2631
|
+
utilization: typeof r.utilization === "number" ? r.utilization : null
|
|
2632
|
+
};
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
// src/usage/storage.ts
|
|
2636
|
+
import { appendFileSync, mkdirSync, readFileSync as readFileSync2, existsSync } from "node:fs";
|
|
2637
|
+
import { dirname } from "node:path";
|
|
2638
|
+
function appendSnapshot(filePath, snapshot) {
|
|
2639
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
2640
|
+
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
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
|
+
}
|
|
2659
|
+
|
|
2660
|
+
// src/usage/backoff.ts
|
|
2661
|
+
var BASE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2662
|
+
var MAX_INTERVAL_MS = 60 * 60 * 1e3;
|
|
2663
|
+
function nextIntervalMs(current, outcome) {
|
|
2664
|
+
if (outcome === "success") return BASE_INTERVAL_MS;
|
|
2665
|
+
if (outcome === "network") return current;
|
|
2666
|
+
return Math.min(Math.max(current, BASE_INTERVAL_MS) * 2, MAX_INTERVAL_MS);
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
// src/team/sync.ts
|
|
2670
|
+
import { homedir as homedir4 } from "node:os";
|
|
2671
|
+
import { join as join4 } from "node:path";
|
|
2672
|
+
|
|
2673
|
+
// src/team/config.ts
|
|
2674
|
+
import { readFileSync as readFileSync3, writeFileSync, unlinkSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
2675
|
+
import { join as join2 } from "node:path";
|
|
2676
|
+
import { homedir as homedir2 } from "node:os";
|
|
2677
|
+
var DEFAULT_DIR = join2(homedir2(), ".cclens");
|
|
2678
|
+
var CONFIG_FILE = "team.json";
|
|
2679
|
+
function readTeamConfig(dir = DEFAULT_DIR) {
|
|
2680
|
+
try {
|
|
2681
|
+
return JSON.parse(readFileSync3(join2(dir, CONFIG_FILE), "utf8"));
|
|
2682
|
+
} catch {
|
|
2683
|
+
return null;
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
function writeTeamConfig(config, dir = DEFAULT_DIR) {
|
|
2687
|
+
mkdirSync2(dir, { recursive: true });
|
|
2688
|
+
writeFileSync(join2(dir, CONFIG_FILE), JSON.stringify(config, null, 2), { mode: 384 });
|
|
2689
|
+
}
|
|
2690
|
+
|
|
2691
|
+
// src/team/push.ts
|
|
2692
|
+
init_dist();
|
|
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
|
+
}
|
|
2718
|
+
function bucketToRollup(b) {
|
|
2719
|
+
return {
|
|
2720
|
+
day: b.date,
|
|
2721
|
+
agentTimeMs: b.airTimeMs,
|
|
2722
|
+
sessions: b.sessions,
|
|
2723
|
+
toolCalls: b.toolCalls,
|
|
2724
|
+
turns: b.turns,
|
|
2725
|
+
tokens: { ...b.tokens }
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
function buildRollupsForRange(sessions, sinceDay) {
|
|
2729
|
+
const buckets = dailyActivity(sessions);
|
|
2730
|
+
const startCounts = /* @__PURE__ */ new Map();
|
|
2731
|
+
for (const s of sessions) {
|
|
2732
|
+
const d = sessionDay(s);
|
|
2733
|
+
if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
|
|
2734
|
+
}
|
|
2735
|
+
return buckets.filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({
|
|
2736
|
+
...bucketToRollup(b),
|
|
2737
|
+
sessions: startCounts.get(b.date) ?? 0
|
|
2738
|
+
}));
|
|
2739
|
+
}
|
|
2740
|
+
function buildIngestPayload(rollup, usageSnapshot, planTier, cyclePeaks) {
|
|
2741
|
+
return {
|
|
2742
|
+
ingestId: randomUUID(),
|
|
2743
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2744
|
+
...rollup ? { dailyRollup: rollup } : {},
|
|
2745
|
+
...usageSnapshot ? { usageSnapshot } : {},
|
|
2746
|
+
...planTier ? { planTier } : {},
|
|
2747
|
+
...cyclePeaks ? { cyclePeaks } : {}
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2750
|
+
async function pushToTeamServer(config, payload) {
|
|
2751
|
+
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
2752
|
+
method: "POST",
|
|
2753
|
+
headers: {
|
|
2754
|
+
"Content-Type": "application/json",
|
|
2755
|
+
Authorization: `Bearer ${config.bearerToken}`
|
|
2756
|
+
},
|
|
2757
|
+
body: JSON.stringify(payload)
|
|
2758
|
+
});
|
|
2759
|
+
const body = await res.json().catch(() => null);
|
|
2760
|
+
return { ok: res.ok, status: res.status, body };
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
// src/team/queue.ts
|
|
2764
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, appendFileSync as appendFileSync2, statSync, mkdirSync as mkdirSync3 } from "node:fs";
|
|
2765
|
+
import { join as join3, dirname as dirname2 } from "node:path";
|
|
2766
|
+
import { homedir as homedir3 } from "node:os";
|
|
2767
|
+
var QUEUE_PATH = join3(homedir3(), ".cclens", "ingest-queue.jsonl");
|
|
2768
|
+
var MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
2769
|
+
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2770
|
+
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
2771
|
+
mkdirSync3(dirname2(queuePath), { recursive: true });
|
|
2772
|
+
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2773
|
+
appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
2774
|
+
try {
|
|
2775
|
+
const { size } = statSync(queuePath);
|
|
2776
|
+
if (size > MAX_SIZE_BYTES) {
|
|
2777
|
+
pruneOldest(queuePath);
|
|
2778
|
+
}
|
|
2779
|
+
} catch {
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
2783
|
+
let lines;
|
|
2784
|
+
try {
|
|
2785
|
+
lines = readFileSync4(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
2786
|
+
} catch {
|
|
2787
|
+
return [];
|
|
2788
|
+
}
|
|
2789
|
+
const now = Date.now();
|
|
2790
|
+
const valid = [];
|
|
2791
|
+
for (const line of lines) {
|
|
2792
|
+
try {
|
|
2793
|
+
const entry = JSON.parse(line);
|
|
2794
|
+
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
2795
|
+
valid.push(entry);
|
|
2796
|
+
}
|
|
2797
|
+
} catch {
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
writeFileSync2(queuePath, "", "utf8");
|
|
2801
|
+
return valid.map((e) => e.payload);
|
|
2802
|
+
}
|
|
2803
|
+
function pruneOldest(queuePath) {
|
|
2804
|
+
let lines;
|
|
2805
|
+
try {
|
|
2806
|
+
lines = readFileSync4(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
2807
|
+
} catch {
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
2811
|
+
writeFileSync2(queuePath, keep.join("\n") + "\n", "utf8");
|
|
2812
|
+
}
|
|
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
|
+
|
|
2889
|
+
// src/team/sync.ts
|
|
2890
|
+
var USAGE_LOG2 = join4(homedir4(), ".cclens", "usage.jsonl");
|
|
2891
|
+
var PROFILE_CACHE = join4(homedir4(), ".cclens", "profile.json");
|
|
2892
|
+
var noopLog = () => {
|
|
2893
|
+
};
|
|
2894
|
+
async function runTeamSync(log3 = noopLog, configOverride) {
|
|
2895
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
2896
|
+
if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
2897
|
+
try {
|
|
2898
|
+
const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
2899
|
+
const { toLocalDay: toLocalDay2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
2900
|
+
const today = toLocalDay2(Date.now());
|
|
2901
|
+
const sessions = await listSessions2({ limit: 1e4 });
|
|
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);
|
|
2906
|
+
if (rollups.length === 0) {
|
|
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 };
|
|
2931
|
+
}
|
|
2932
|
+
let pushed = 0;
|
|
2933
|
+
let queued = 0;
|
|
2934
|
+
let failedDay;
|
|
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
|
+
);
|
|
2946
|
+
const result = await pushToTeamServer(config, payload);
|
|
2947
|
+
if (!result.ok) {
|
|
2948
|
+
log3("warn", `team push failed on ${rollup.day} (${result.status}); queueing`);
|
|
2949
|
+
enqueuePayload(payload);
|
|
2950
|
+
queued++;
|
|
2951
|
+
failedDay = rollup.day;
|
|
2952
|
+
break;
|
|
2953
|
+
}
|
|
2954
|
+
pushed++;
|
|
2955
|
+
}
|
|
2956
|
+
const nextConfig = { ...config, lastSyncedDay: today };
|
|
2957
|
+
if (pushed > 0 || !failedDay) writeTeamConfig(nextConfig);
|
|
2958
|
+
let queuedDrained = 0;
|
|
2959
|
+
if (!failedDay) {
|
|
2960
|
+
const backlog = dequeuePayloads();
|
|
2961
|
+
for (let i = 0; i < backlog.length; i++) {
|
|
2962
|
+
const qResult = await pushToTeamServer(config, backlog[i]);
|
|
2963
|
+
if (!qResult.ok) {
|
|
2964
|
+
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
2965
|
+
break;
|
|
2966
|
+
}
|
|
2967
|
+
queuedDrained++;
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
if (pushed > 0) {
|
|
2971
|
+
log3("info", `team push ok: ${pushed} day${pushed === 1 ? "" : "s"} pushed` + (queuedDrained ? `, ${queuedDrained} queued retried` : "") + (queued ? `, ${queued} queued for retry` : ""));
|
|
2972
|
+
}
|
|
2973
|
+
return { paired: true, pushed, queued, queuedDrained, failedDay };
|
|
2974
|
+
} catch (err) {
|
|
2975
|
+
const message = err.message;
|
|
2976
|
+
log3("warn", `team push error: ${message}`);
|
|
2977
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
2978
|
+
}
|
|
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
|
+
}
|
|
2774
3543
|
}
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
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;
|
|
2780
3552
|
}
|
|
2781
|
-
const
|
|
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] ?? "";
|
|
2782
3556
|
return {
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
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
|
|
2791
3572
|
};
|
|
2792
3573
|
}
|
|
2793
|
-
function
|
|
2794
|
-
|
|
2795
|
-
const
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
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
|
+
});
|
|
2800
3591
|
}
|
|
2801
|
-
function
|
|
2802
|
-
|
|
2803
|
-
const
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
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;
|
|
2810
3724
|
}
|
|
2811
3725
|
|
|
2812
|
-
//
|
|
2813
|
-
import {
|
|
2814
|
-
import {
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
appendFileSync(filePath, JSON.stringify(snapshot) + "\n", "utf8");
|
|
2818
|
-
}
|
|
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";
|
|
2819
3731
|
|
|
2820
|
-
//
|
|
2821
|
-
var
|
|
2822
|
-
var
|
|
2823
|
-
|
|
2824
|
-
if (outcome === "success") return BASE_INTERVAL_MS;
|
|
2825
|
-
if (outcome === "network") return current;
|
|
2826
|
-
return Math.min(Math.max(current, BASE_INTERVAL_MS) * 2, MAX_INTERVAL_MS);
|
|
2827
|
-
}
|
|
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;
|
|
2828
3736
|
|
|
2829
|
-
//
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
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))
|
|
2839
3787
|
return null;
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
function writeTeamConfig(config, dir = DEFAULT_DIR) {
|
|
2843
|
-
mkdirSync2(dir, { recursive: true });
|
|
2844
|
-
writeFileSync(join2(dir, CONFIG_FILE), JSON.stringify(config, null, 2), { mode: 384 });
|
|
3788
|
+
const raw = readFileSync6(p, "utf8");
|
|
3789
|
+
return JSON.parse(raw);
|
|
2845
3790
|
}
|
|
2846
3791
|
|
|
2847
|
-
// src/
|
|
2848
|
-
|
|
2849
|
-
import {
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
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
|
+
};
|
|
2866
3812
|
}
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
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);
|
|
2890
3846
|
}
|
|
2891
3847
|
|
|
2892
|
-
// src/
|
|
2893
|
-
import {
|
|
2894
|
-
import { join as
|
|
2895
|
-
import { homedir as
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
2900
|
-
mkdirSync3(dirname2(queuePath), { recursive: true });
|
|
2901
|
-
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2902
|
-
appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
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 = [];
|
|
2903
3855
|
try {
|
|
2904
|
-
const
|
|
2905
|
-
|
|
2906
|
-
|
|
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
|
+
}
|
|
2907
3864
|
}
|
|
2908
3865
|
} catch {
|
|
2909
3866
|
}
|
|
3867
|
+
return out;
|
|
2910
3868
|
}
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
} catch {
|
|
2916
|
-
return [];
|
|
2917
|
-
}
|
|
2918
|
-
const now = Date.now();
|
|
2919
|
-
const valid = [];
|
|
2920
|
-
for (const line of lines) {
|
|
2921
|
-
try {
|
|
2922
|
-
const entry = JSON.parse(line);
|
|
2923
|
-
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
2924
|
-
valid.push(entry);
|
|
2925
|
-
}
|
|
2926
|
-
} catch {
|
|
2927
|
-
}
|
|
2928
|
-
}
|
|
2929
|
-
writeFileSync2(queuePath, "", "utf8");
|
|
2930
|
-
return valid.map((e) => e.payload);
|
|
3869
|
+
|
|
3870
|
+
// src/perception/worker.ts
|
|
3871
|
+
function log(msg) {
|
|
3872
|
+
console.error(`[perception] ${msg}`);
|
|
2931
3873
|
}
|
|
2932
|
-
function
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
lines = readFileSync4(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
2936
|
-
} catch {
|
|
2937
|
-
return;
|
|
3874
|
+
function decodeProjectDirName(projectDir) {
|
|
3875
|
+
if (projectDir.startsWith("-")) {
|
|
3876
|
+
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
2938
3877
|
}
|
|
2939
|
-
|
|
2940
|
-
writeFileSync2(queuePath, keep.join("\n") + "\n", "utf8");
|
|
3878
|
+
return projectDir.replace(/-/g, "/");
|
|
2941
3879
|
}
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
};
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
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;
|
|
2949
3889
|
try {
|
|
2950
|
-
const
|
|
2951
|
-
const
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
2983
|
-
break;
|
|
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++;
|
|
2984
3922
|
}
|
|
2985
|
-
|
|
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}`);
|
|
2986
3932
|
}
|
|
2987
3933
|
}
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
}
|
|
2991
|
-
return { paired: true, pushed, queued, queuedDrained, failedDay };
|
|
2992
|
-
} catch (err) {
|
|
2993
|
-
const message = err.message;
|
|
2994
|
-
log2("warn", `team push error: ${message}`);
|
|
2995
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
3934
|
+
} finally {
|
|
3935
|
+
markSweepEnd();
|
|
2996
3936
|
}
|
|
3937
|
+
return { sessionsProcessed: sessions, entriesWritten: entries, errors };
|
|
2997
3938
|
}
|
|
2998
3939
|
|
|
2999
3940
|
// src/daemon-worker.ts
|
|
3000
|
-
var STATE_DIR =
|
|
3001
|
-
var
|
|
3002
|
-
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");
|
|
3003
3944
|
var WATCHDOG_INTERVAL_MS = 5 * 1e3;
|
|
3004
|
-
|
|
3005
|
-
function
|
|
3945
|
+
mkdirSync7(dirname6(USAGE_LOG3), { recursive: true });
|
|
3946
|
+
function log2(level, message) {
|
|
3006
3947
|
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${message}
|
|
3007
3948
|
`;
|
|
3008
3949
|
try {
|
|
@@ -3013,21 +3954,37 @@ function log(level, message) {
|
|
|
3013
3954
|
var nextPollAtMs = 0;
|
|
3014
3955
|
var currentIntervalMs = BASE_INTERVAL_MS;
|
|
3015
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);
|
|
3016
3973
|
async function tick() {
|
|
3017
3974
|
try {
|
|
3018
3975
|
const snapshot = await fetchUsage();
|
|
3019
|
-
appendSnapshot(
|
|
3020
|
-
|
|
3976
|
+
appendSnapshot(USAGE_LOG3, snapshot);
|
|
3977
|
+
log2(
|
|
3021
3978
|
"info",
|
|
3022
3979
|
`snapshot 5h=${snapshot.five_hour.utilization}% 7d=${snapshot.seven_day.utilization}%`
|
|
3023
3980
|
);
|
|
3024
3981
|
return "success";
|
|
3025
3982
|
} catch (err) {
|
|
3026
3983
|
if (err instanceof UsageApiError) {
|
|
3027
|
-
|
|
3984
|
+
log2("warn", `poll failed (${err.code}): ${err.message}`);
|
|
3028
3985
|
return err.code === "network" ? "network" : "auth";
|
|
3029
3986
|
}
|
|
3030
|
-
|
|
3987
|
+
log2("error", `unexpected error: ${err.stack ?? err}`);
|
|
3031
3988
|
return "auth";
|
|
3032
3989
|
}
|
|
3033
3990
|
}
|
|
@@ -3035,7 +3992,7 @@ function scheduleAfter(now, outcome) {
|
|
|
3035
3992
|
const prev = currentIntervalMs;
|
|
3036
3993
|
currentIntervalMs = nextIntervalMs(currentIntervalMs, outcome);
|
|
3037
3994
|
if (currentIntervalMs !== prev) {
|
|
3038
|
-
|
|
3995
|
+
log2(
|
|
3039
3996
|
"info",
|
|
3040
3997
|
`poll interval ${outcome === "success" ? "reset" : "backoff"} to ${currentIntervalMs / 1e3}s`
|
|
3041
3998
|
);
|
|
@@ -3050,40 +4007,41 @@ async function runLoop() {
|
|
|
3050
4007
|
const now = Date.now();
|
|
3051
4008
|
if (now >= nextPollAtMs) {
|
|
3052
4009
|
if (nextPollAtMs > 0 && now - nextPollAtMs > currentIntervalMs) {
|
|
3053
|
-
|
|
4010
|
+
log2(
|
|
3054
4011
|
"info",
|
|
3055
4012
|
`wake-from-sleep catch-up: ${Math.round((now - nextPollAtMs + currentIntervalMs) / 1e3)}s since last poll`
|
|
3056
4013
|
);
|
|
3057
4014
|
}
|
|
3058
4015
|
const creds = readOAuthCredentials();
|
|
3059
4016
|
if (!creds) {
|
|
3060
|
-
|
|
4017
|
+
log2("warn", "no Claude Code OAuth token found; waiting");
|
|
3061
4018
|
waitingForRefresh = true;
|
|
3062
4019
|
nextPollAtMs = now + BASE_INTERVAL_MS;
|
|
3063
4020
|
} else if (!isUsable(creds, now)) {
|
|
3064
4021
|
if (!waitingForRefresh) {
|
|
3065
|
-
|
|
4022
|
+
log2("info", "token expired; waiting for Claude Code to refresh it");
|
|
3066
4023
|
waitingForRefresh = true;
|
|
3067
4024
|
}
|
|
3068
4025
|
} else {
|
|
3069
4026
|
if (waitingForRefresh) {
|
|
3070
|
-
|
|
4027
|
+
log2("info", "token refreshed; resuming polls");
|
|
3071
4028
|
waitingForRefresh = false;
|
|
3072
4029
|
}
|
|
3073
4030
|
const outcome = await tick();
|
|
3074
4031
|
scheduleAfter(Date.now(), outcome);
|
|
3075
4032
|
}
|
|
3076
|
-
await runTeamSync(
|
|
4033
|
+
await runTeamSync(log2);
|
|
3077
4034
|
}
|
|
3078
4035
|
await sleep(WATCHDOG_INTERVAL_MS);
|
|
3079
4036
|
}
|
|
3080
4037
|
}
|
|
3081
|
-
|
|
4038
|
+
log2(
|
|
3082
4039
|
"info",
|
|
3083
4040
|
`daemon started (pid=${process.pid}, interval=${BASE_INTERVAL_MS / 1e3}s, watchdog=${WATCHDOG_INTERVAL_MS / 1e3}s)`
|
|
3084
4041
|
);
|
|
3085
4042
|
void runLoop();
|
|
3086
4043
|
process.on("SIGTERM", () => {
|
|
3087
|
-
|
|
4044
|
+
clearInterval(perceptionHandle);
|
|
4045
|
+
log2("info", `daemon stopping (pid=${process.pid})`);
|
|
3088
4046
|
process.exit(0);
|
|
3089
4047
|
});
|