fleetlens 0.13.2 → 0.15.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 +4 -1
- package/app/apps/web/.next/build-manifest.json +3 -3
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/routes-manifest.json +21 -3
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
- package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/api/ask/route.js +3 -3
- package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js +2 -2
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js +4 -4
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js +4 -3
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team/autostart/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route.js +7 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/autostart/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js +8 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/onboarding/start/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/build-manifest.json +9 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js +8 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.map +5 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/api/team/sync-projects/route_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/changelog/page.js +1 -1
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js +1 -1
- package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js +1 -1
- package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js +1 -1
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page.js +2 -2
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js +2 -2
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js +1 -1
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js +1 -1
- package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js +3 -3
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page.js +2 -2
- package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/team/onboarding/page/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/build-manifest.json +17 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/next-font-manifest.json +6 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/react-loadable-manifest.json +1 -0
- package/app/apps/web/.next/server/app/team/onboarding/page/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js +19 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js.map +5 -0
- package/app/apps/web/.next/server/app/team/onboarding/page.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/team/onboarding/page_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/team/page.js +3 -2
- package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js +1 -1
- package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app-paths-manifest.json +4 -1
- package/app/apps/web/.next/server/chunks/02dz_web__next-internal_server_app_api_team_onboarding_start_route_actions_0zeptl_.js +3 -0
- package/app/apps/web/.next/server/chunks/0fuv_zod_v4_classic_external_0.z.vap.js +39 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__038-.1r._.js +6 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0~utv~v._.js → [root-of-the-server]__03ewxxu._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__03rmbz6._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__065lib8._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0e.f-t.._.js → [root-of-the-server]__0a0~7d6._.js} +2 -2
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0gril0u._.js → [root-of-the-server]__0acikq.._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0et2sqb._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[externals]__09v47rg._.js → [root-of-the-server]__0jbc7tz._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mq0or2._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0mvl0qn._.js +3 -0
- package/app/apps/web/.next/server/chunks/{[root-of-the-server]__0n79hpm._.js → [root-of-the-server]__0ow1kn1._.js} +4 -4
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~xt99s._.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team_autostart_route_actions_10-4s71.js +3 -0
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team_sync-projects_route_actions_0r_2f5n.js +3 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +13 -0
- package/app/apps/web/.next/server/chunks/packages_09wmnv.._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_0o6.gpk._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0z7mg0b._.js → [root-of-the-server]__08l86e-._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0a7n44-._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0c00i76._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0i.q-1h._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0r4tks8._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0stu~~r._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ufi3.w._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0o03ymy._.js → [root-of-the-server]__10~b4bf._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_0.97l5.._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{apps_web_app_settings_0u6z6rj._.js → apps_web_0q2x4a~._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/apps_web_114yo0v._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_team_onboarding_page_actions_0pss~.k.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0c-tpft._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_team-config_ts_10egg08._.js +39 -0
- package/app/apps/web/.next/server/chunks/ssr/{node_modules_next_dist_0t-hj0x._.js → node_modules_0yrkyef._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_0w2y7fz._.js +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0jxvcfi.js +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0s5yhs4.js +4 -0
- package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
- package/app/apps/web/.next/server/functions-config-manifest.json +3 -1
- package/app/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/app/apps/web/.next/server/middleware-manifest.json +5 -5
- package/app/apps/web/.next/server/pages/500.html +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/app/apps/web/.next/static/chunks/{0wwcx42ng8i2e.js → 0-3_u.90d79wi.js} +1 -1
- package/app/apps/web/.next/static/chunks/00a9oyw~h5i1p.js +1 -0
- package/app/apps/web/.next/static/chunks/040s42ypidk7s.css +1 -0
- package/app/apps/web/.next/static/chunks/06eiwghjkkbnh.js +1 -0
- package/app/apps/web/.next/static/chunks/06qeg4pt5jdqs.js +1 -0
- package/app/apps/web/.next/static/chunks/{0e_fzy0n8ij-o.js → 0u6gxpqhl0gc7.js} +1 -1
- package/app/apps/web/.next/static/chunks/18cj08-la37o-.js +1 -0
- package/app/apps/web/app/api/team/autostart/route.ts +41 -0
- package/app/apps/web/app/api/team/onboarding/start/route.ts +96 -0
- package/app/apps/web/app/api/team/sync-projects/route.ts +39 -0
- package/app/apps/web/app/globals.css +82 -0
- package/app/apps/web/app/settings/ai-features-form.tsx +1 -1
- package/app/apps/web/app/settings/page.tsx +21 -7
- package/app/apps/web/app/{settings → team}/force-sync-button.tsx +2 -4
- package/app/apps/web/app/team/onboarding/onboarding-wizard.tsx +503 -0
- package/app/apps/web/app/team/onboarding/page.tsx +28 -0
- package/app/apps/web/app/team/page.tsx +60 -81
- package/app/apps/web/app/{settings/team-connection-section.tsx → team/sync-activity-section.tsx} +6 -23
- package/app/apps/web/app/team/synced-projects-form.tsx +201 -0
- package/app/apps/web/app/team/synced-projects-section.tsx +37 -0
- package/app/apps/web/components/autostart-toggle.tsx +59 -0
- package/app/apps/web/components/previous-cycles-trend.tsx +4 -1
- package/app/apps/web/components/project-sync-picker.tsx +109 -0
- package/app/apps/web/components/team-chip.tsx +1 -1
- package/app/apps/web/components/team-welcome-banner.tsx +1 -1
- package/app/apps/web/lib/autostart-data.ts +31 -0
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/sync-projects-data.ts +36 -0
- package/app/apps/web/lib/team-config.ts +21 -4
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +2089 -1622
- package/dist/index.js +1462 -230
- package/package.json +1 -1
- package/app/apps/web/.next/server/app/api/team-config/route/app-paths-manifest.json +0 -3
- package/app/apps/web/.next/server/app/api/team-config/route.js +0 -8
- package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +0 -1
- package/app/apps/web/.next/server/app/api/team-config/route_client-reference-manifest.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__08agijy._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0atn.dt._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0b.b7~1._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0i06ito._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0y5p73y._.js +0 -3
- package/app/apps/web/.next/server/chunks/_0euqdko._.js +0 -49
- package/app/apps/web/.next/server/chunks/apps_web__next-internal_server_app_api_team-config_route_actions_0ad2gak.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_0c-sz9n._.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_0wywuu.._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__09yxy53._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0q9qy0z._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_team_team-sync-form_tsx_0choz0h._.js +0 -3
- package/app/apps/web/.next/static/chunks/083~hzkv577.5.css +0 -1
- package/app/apps/web/.next/static/chunks/0h.ua.-myj.6z.js +0 -1
- package/app/apps/web/.next/static/chunks/0uyctl-m8iydb.js +0 -1
- package/app/apps/web/.next/static/chunks/14joqbsr9ihhu.js +0 -1
- package/app/apps/web/app/api/team-config/route.ts +0 -39
- package/app/apps/web/app/team/team-sync-form.tsx +0 -144
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route/build-manifest.json +0 -0
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route/server-reference-manifest.json +0 -0
- /package/app/apps/web/.next/server/app/api/{team-config → team/autostart}/route.js.map +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{bZs5UyzsYBqC6MFY5n-M_ → C4y6rare_ZCcXXQLc8ysA}/_ssgManifest.js +0 -0
package/dist/daemon-worker.js
CHANGED
|
@@ -3977,13 +3977,138 @@ async function readJsonl2(filePath) {
|
|
|
3977
3977
|
}
|
|
3978
3978
|
return out;
|
|
3979
3979
|
}
|
|
3980
|
+
function dedupeReplayedLines(lines) {
|
|
3981
|
+
const keepAt = /* @__PURE__ */ new Map();
|
|
3982
|
+
lines.forEach((line, i) => {
|
|
3983
|
+
const rec = line;
|
|
3984
|
+
if (typeof rec.uuid !== "string")
|
|
3985
|
+
return;
|
|
3986
|
+
const prev = keepAt.get(rec.uuid);
|
|
3987
|
+
if (prev === void 0) {
|
|
3988
|
+
keepAt.set(rec.uuid, i);
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
const prevHasTs = lines[prev].timestamp !== void 0;
|
|
3992
|
+
if (!prevHasTs && rec.timestamp !== void 0)
|
|
3993
|
+
keepAt.set(rec.uuid, i);
|
|
3994
|
+
});
|
|
3995
|
+
return lines.filter((line, i) => {
|
|
3996
|
+
const rec = line;
|
|
3997
|
+
return typeof rec.uuid !== "string" || keepAt.get(rec.uuid) === i;
|
|
3998
|
+
});
|
|
3999
|
+
}
|
|
4000
|
+
function textFromContent(content) {
|
|
4001
|
+
if (typeof content === "string")
|
|
4002
|
+
return content;
|
|
4003
|
+
if (Array.isArray(content)) {
|
|
4004
|
+
const parts = [];
|
|
4005
|
+
for (const b of content) {
|
|
4006
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
4007
|
+
const t = b.text;
|
|
4008
|
+
if (typeof t === "string")
|
|
4009
|
+
parts.push(t);
|
|
4010
|
+
}
|
|
4011
|
+
}
|
|
4012
|
+
if (parts.length)
|
|
4013
|
+
return parts.join("\n");
|
|
4014
|
+
}
|
|
4015
|
+
return void 0;
|
|
4016
|
+
}
|
|
4017
|
+
function partitionCoworkLines(lines) {
|
|
4018
|
+
const mainLines = [];
|
|
4019
|
+
const subagentGroups = /* @__PURE__ */ new Map();
|
|
4020
|
+
for (const line of lines) {
|
|
4021
|
+
const ptid = line.parent_tool_use_id;
|
|
4022
|
+
if (typeof ptid === "string") {
|
|
4023
|
+
const g = subagentGroups.get(ptid);
|
|
4024
|
+
if (g)
|
|
4025
|
+
g.push(line);
|
|
4026
|
+
else
|
|
4027
|
+
subagentGroups.set(ptid, [line]);
|
|
4028
|
+
} else {
|
|
4029
|
+
mainLines.push(line);
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
return { mainLines, subagentGroups };
|
|
4033
|
+
}
|
|
4034
|
+
function buildCoworkSubagents(subagentGroups, allLines, sessionStartMs) {
|
|
4035
|
+
if (subagentGroups.size === 0)
|
|
4036
|
+
return [];
|
|
4037
|
+
const refs = /* @__PURE__ */ new Map();
|
|
4038
|
+
for (const line of allLines) {
|
|
4039
|
+
const rec = line;
|
|
4040
|
+
const content = rec.message?.content;
|
|
4041
|
+
if (!Array.isArray(content))
|
|
4042
|
+
continue;
|
|
4043
|
+
for (const b of content) {
|
|
4044
|
+
if (!b || typeof b !== "object")
|
|
4045
|
+
continue;
|
|
4046
|
+
const block = b;
|
|
4047
|
+
if (block.type === "tool_use" && block.name === "Agent" && typeof block.id === "string") {
|
|
4048
|
+
const input = block.input ?? {};
|
|
4049
|
+
const ref = refs.get(block.id) ?? {};
|
|
4050
|
+
if (typeof rec.uuid === "string")
|
|
4051
|
+
ref.parentUuid = rec.uuid;
|
|
4052
|
+
if (typeof input.description === "string")
|
|
4053
|
+
ref.description = input.description;
|
|
4054
|
+
if (typeof input.subagent_type === "string")
|
|
4055
|
+
ref.agentType = input.subagent_type;
|
|
4056
|
+
if (typeof input.prompt === "string")
|
|
4057
|
+
ref.prompt = input.prompt;
|
|
4058
|
+
refs.set(block.id, ref);
|
|
4059
|
+
} else if (block.type === "tool_result" && typeof block.tool_use_id === "string" && subagentGroups.has(block.tool_use_id)) {
|
|
4060
|
+
const ref = refs.get(block.tool_use_id) ?? {};
|
|
4061
|
+
ref.finalText = textFromContent(block.content) ?? ref.finalText;
|
|
4062
|
+
refs.set(block.tool_use_id, ref);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
const runs = [];
|
|
4067
|
+
for (const [toolUseId, groupLines] of subagentGroups) {
|
|
4068
|
+
const summary = summarizeSubagentLines(groupLines);
|
|
4069
|
+
const ref = refs.get(toolUseId) ?? {};
|
|
4070
|
+
const finalText = ref.finalText ?? summary.finalText;
|
|
4071
|
+
const finalPreview = finalText ? finalText.replace(/\s+/g, " ").trim().slice(0, 240) : summary.finalPreview;
|
|
4072
|
+
const startTOffsetMs = sessionStartMs !== void 0 && summary.startMs !== void 0 ? Math.max(0, summary.startMs - sessionStartMs) : void 0;
|
|
4073
|
+
const endTOffsetMs = sessionStartMs !== void 0 && summary.endMs !== void 0 ? Math.max(0, summary.endMs - sessionStartMs) : void 0;
|
|
4074
|
+
const durationMs = summary.startMs !== void 0 && summary.endMs !== void 0 ? summary.endMs - summary.startMs : void 0;
|
|
4075
|
+
runs.push({
|
|
4076
|
+
agentId: toolUseId,
|
|
4077
|
+
agentType: ref.agentType ?? "unknown",
|
|
4078
|
+
description: ref.description ?? (ref.prompt ? ref.prompt.replace(/\s+/g, " ").trim().slice(0, 80) : "(no description)"),
|
|
4079
|
+
startMs: summary.startMs,
|
|
4080
|
+
endMs: summary.endMs,
|
|
4081
|
+
durationMs,
|
|
4082
|
+
startTOffsetMs,
|
|
4083
|
+
endTOffsetMs,
|
|
4084
|
+
eventCount: summary.eventCount,
|
|
4085
|
+
totalUsage: summary.totalUsage,
|
|
4086
|
+
parentUuid: ref.parentUuid,
|
|
4087
|
+
parentToolUseId: toolUseId,
|
|
4088
|
+
runInBackground: false,
|
|
4089
|
+
finalPreview,
|
|
4090
|
+
finalText,
|
|
4091
|
+
prompt: ref.prompt,
|
|
4092
|
+
model: summary.model,
|
|
4093
|
+
toolCalls: summary.toolCalls,
|
|
4094
|
+
toolCallCount: summary.toolCallCount,
|
|
4095
|
+
assistantMessageCount: summary.assistantMessageCount
|
|
4096
|
+
});
|
|
4097
|
+
}
|
|
4098
|
+
runs.sort((a, b) => (a.startMs ?? 0) - (b.startMs ?? 0));
|
|
4099
|
+
return runs;
|
|
4100
|
+
}
|
|
3980
4101
|
async function parseCoworkSession(file) {
|
|
3981
4102
|
const [lines, coworkMeta, spaceIdToPath] = await Promise.all([
|
|
3982
4103
|
readJsonl2(file.auditPath),
|
|
3983
4104
|
readCoworkMeta(file.metaPath),
|
|
3984
4105
|
loadSpacesForWorkspace(file.workspaceDir)
|
|
3985
4106
|
]);
|
|
3986
|
-
const
|
|
4107
|
+
const deduped = dedupeReplayedLines(lines);
|
|
4108
|
+
const { mainLines, subagentGroups } = partitionCoworkLines(deduped);
|
|
4109
|
+
const { meta: baseMeta, events } = parseTranscript(mainLines);
|
|
4110
|
+
const sessionStartMs = baseMeta.firstTimestamp ? Date.parse(baseMeta.firstTimestamp) : void 0;
|
|
4111
|
+
const subagents = buildCoworkSubagents(subagentGroups, deduped, sessionStartMs);
|
|
3987
4112
|
const { projectName, projectDir, cwd } = resolveProject(coworkMeta, spaceIdToPath);
|
|
3988
4113
|
const meta = {
|
|
3989
4114
|
...baseMeta,
|
|
@@ -3996,7 +4121,7 @@ async function parseCoworkSession(file) {
|
|
|
3996
4121
|
cwd: cwd ?? baseMeta.cwd,
|
|
3997
4122
|
model: baseMeta.model ?? coworkMeta.model
|
|
3998
4123
|
};
|
|
3999
|
-
return { meta, events };
|
|
4124
|
+
return { meta, events, subagents };
|
|
4000
4125
|
}
|
|
4001
4126
|
function cacheKeyFor(file) {
|
|
4002
4127
|
return {
|
|
@@ -4047,8 +4172,12 @@ async function getCoworkSession(id, opts = {}) {
|
|
|
4047
4172
|
const cached = detailCache5.get(file.auditPath);
|
|
4048
4173
|
if (cached && cacheKeyMatches(cached.key, key))
|
|
4049
4174
|
return cached.detail;
|
|
4050
|
-
const { meta, events } = await parseCoworkSession(file);
|
|
4051
|
-
const detail = {
|
|
4175
|
+
const { meta, events, subagents } = await parseCoworkSession(file);
|
|
4176
|
+
const detail = {
|
|
4177
|
+
...meta,
|
|
4178
|
+
events,
|
|
4179
|
+
...subagents.length ? { subagents } : {}
|
|
4180
|
+
};
|
|
4052
4181
|
detailCache5.set(file.auditPath, { detail, key });
|
|
4053
4182
|
return detail;
|
|
4054
4183
|
}
|
|
@@ -4057,6 +4186,7 @@ var init_cowork = __esm({
|
|
|
4057
4186
|
"../parser/dist/cowork.js"() {
|
|
4058
4187
|
"use strict";
|
|
4059
4188
|
init_analytics();
|
|
4189
|
+
init_claude_code();
|
|
4060
4190
|
init_parser();
|
|
4061
4191
|
DEFAULT_COWORK_ROOT = defaultCoworkRoot();
|
|
4062
4192
|
SESSION_ID_RE = /^local_[0-9a-f-]+$/;
|
|
@@ -4067,8 +4197,17 @@ var init_cowork = __esm({
|
|
|
4067
4197
|
});
|
|
4068
4198
|
|
|
4069
4199
|
// ../parser/dist/team-config.js
|
|
4070
|
-
import { readFileSync as readFileSync3, writeFileSync, unlinkSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
4200
|
+
import { readFileSync as readFileSync3, writeFileSync, unlinkSync, mkdirSync as mkdirSync2, renameSync } from "node:fs";
|
|
4071
4201
|
import { join as join2 } from "node:path";
|
|
4202
|
+
function shouldSyncProject(repoName, sp) {
|
|
4203
|
+
if (!sp)
|
|
4204
|
+
return true;
|
|
4205
|
+
if (sp.excluded.includes(repoName))
|
|
4206
|
+
return false;
|
|
4207
|
+
if (sp.included.includes(repoName))
|
|
4208
|
+
return true;
|
|
4209
|
+
return sp.autoIncludeNew;
|
|
4210
|
+
}
|
|
4072
4211
|
function readTeamConfig(dir) {
|
|
4073
4212
|
const d = dir ?? cclensHome();
|
|
4074
4213
|
try {
|
|
@@ -4080,7 +4219,9 @@ function readTeamConfig(dir) {
|
|
|
4080
4219
|
function writeTeamConfig(config, dir) {
|
|
4081
4220
|
const d = dir ?? cclensHome();
|
|
4082
4221
|
mkdirSync2(d, { recursive: true });
|
|
4083
|
-
|
|
4222
|
+
const tmp = join2(d, `${CONFIG_FILE}.${process.pid}.tmp`);
|
|
4223
|
+
writeFileSync(tmp, JSON.stringify(config, null, 2), { mode: 384 });
|
|
4224
|
+
renameSync(tmp, join2(d, CONFIG_FILE));
|
|
4084
4225
|
}
|
|
4085
4226
|
function clearTeamConfig(dir) {
|
|
4086
4227
|
const d = dir ?? cclensHome();
|
|
@@ -4154,6 +4295,7 @@ __export(fs_exports, {
|
|
|
4154
4295
|
readJsonlFile: () => readJsonlFile,
|
|
4155
4296
|
readTeamConfig: () => readTeamConfig,
|
|
4156
4297
|
sessionIdFromFileName: () => sessionIdFromFileName,
|
|
4298
|
+
shouldSyncProject: () => shouldSyncProject,
|
|
4157
4299
|
walkJsonlFiles: () => walkJsonlFiles,
|
|
4158
4300
|
writeTeamConfig: () => writeTeamConfig
|
|
4159
4301
|
});
|
|
@@ -4375,9 +4517,14 @@ function buildCalibrationCurve(events, snapshots, tier = "pro-max-20x", granular
|
|
|
4375
4517
|
curve.push({
|
|
4376
4518
|
ts: new Date(cur).toISOString(),
|
|
4377
4519
|
real_5h: real5,
|
|
4378
|
-
|
|
4520
|
+
// Predicted utilization LEGITIMATELY exceeds 200 on overage (the
|
|
4521
|
+
// extrapolated cycle close). The old min(200) clamp silently capped it,
|
|
4522
|
+
// so cyclePeaks.peakPct could never carry a *predicted* overage to the
|
|
4523
|
+
// team server (which now accepts up to 10000). Keep only a generous
|
|
4524
|
+
// finite ceiling as a corruption guard, matching the wire cap.
|
|
4525
|
+
pred_5h: Math.max(0, Math.min(1e4, p5)),
|
|
4379
4526
|
real_7d: real7,
|
|
4380
|
-
pred_7d: Math.max(0, Math.min(
|
|
4527
|
+
pred_7d: Math.max(0, Math.min(1e4, p7)),
|
|
4381
4528
|
cycle_end_5h: r5k != null ? new Date(r5k).toISOString() : null,
|
|
4382
4529
|
cycle_end_7d: r7k != null ? new Date(r7k).toISOString() : null
|
|
4383
4530
|
});
|
|
@@ -4971,7 +5118,7 @@ __export(file_probe_exports, {
|
|
|
4971
5118
|
probeArtifactSignals: () => probeArtifactSignals
|
|
4972
5119
|
});
|
|
4973
5120
|
import { createHash } from "node:crypto";
|
|
4974
|
-
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as
|
|
5121
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
4975
5122
|
import { homedir as homedir2 } from "node:os";
|
|
4976
5123
|
import { join as join7, relative, resolve as resolve2, sep } from "node:path";
|
|
4977
5124
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
@@ -5002,7 +5149,7 @@ function listMarkdownFiles(dir) {
|
|
|
5002
5149
|
const full = join7(dir, name);
|
|
5003
5150
|
let stat;
|
|
5004
5151
|
try {
|
|
5005
|
-
stat =
|
|
5152
|
+
stat = statSync4(full);
|
|
5006
5153
|
} catch {
|
|
5007
5154
|
continue;
|
|
5008
5155
|
}
|
|
@@ -5019,19 +5166,19 @@ function firstSeenDateOf(file) {
|
|
|
5019
5166
|
const out = execFileSync2(
|
|
5020
5167
|
"git",
|
|
5021
5168
|
["log", "--diff-filter=A", "--follow", "--format=%cs", "--", file],
|
|
5022
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
5169
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname7(file) }
|
|
5023
5170
|
).trim().split("\n").filter(Boolean);
|
|
5024
5171
|
if (out.length > 0) return out[out.length - 1];
|
|
5025
5172
|
} catch {
|
|
5026
5173
|
}
|
|
5027
5174
|
try {
|
|
5028
|
-
const s =
|
|
5029
|
-
return
|
|
5175
|
+
const s = statSync4(file);
|
|
5176
|
+
return localDay(s.birthtime || s.mtime);
|
|
5030
5177
|
} catch {
|
|
5031
|
-
return (
|
|
5178
|
+
return todayLocal();
|
|
5032
5179
|
}
|
|
5033
5180
|
}
|
|
5034
|
-
function
|
|
5181
|
+
function dirname7(p) {
|
|
5035
5182
|
const i = p.lastIndexOf(sep);
|
|
5036
5183
|
return i < 0 ? "." : p.slice(0, i);
|
|
5037
5184
|
}
|
|
@@ -5046,13 +5193,15 @@ function detectGitEmail(cwd) {
|
|
|
5046
5193
|
return null;
|
|
5047
5194
|
}
|
|
5048
5195
|
}
|
|
5049
|
-
function
|
|
5050
|
-
const d = /* @__PURE__ */ new Date();
|
|
5196
|
+
function localDay(d) {
|
|
5051
5197
|
const y = d.getFullYear();
|
|
5052
5198
|
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
5053
5199
|
const day = String(d.getDate()).padStart(2, "0");
|
|
5054
5200
|
return `${y}-${m}-${day}`;
|
|
5055
5201
|
}
|
|
5202
|
+
function todayLocal() {
|
|
5203
|
+
return localDay(/* @__PURE__ */ new Date());
|
|
5204
|
+
}
|
|
5056
5205
|
function scanAuthored(roots, subdir, day) {
|
|
5057
5206
|
const out = [];
|
|
5058
5207
|
for (const r of roots) {
|
|
@@ -5077,11 +5226,11 @@ function scanEdited(roots, day) {
|
|
|
5077
5226
|
if (firstSeen === day) continue;
|
|
5078
5227
|
let stat;
|
|
5079
5228
|
try {
|
|
5080
|
-
stat =
|
|
5229
|
+
stat = statSync4(f);
|
|
5081
5230
|
} catch {
|
|
5082
5231
|
continue;
|
|
5083
5232
|
}
|
|
5084
|
-
const mtimeDay =
|
|
5233
|
+
const mtimeDay = localDay(stat.mtime);
|
|
5085
5234
|
if (mtimeDay !== day) continue;
|
|
5086
5235
|
const rel = relative(dir, f);
|
|
5087
5236
|
out.push({ pathHash: pathHash(`${r.label}:skills:${rel}`), lineDelta: 0 });
|
|
@@ -5103,7 +5252,7 @@ function claudemdLineDelta(extraRoots, authorEmail, day) {
|
|
|
5103
5252
|
const out = execFileSync2(
|
|
5104
5253
|
"git",
|
|
5105
5254
|
["log", "--numstat", "--format=COMMIT %cs %ae", "--", file],
|
|
5106
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd:
|
|
5255
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], cwd: dirname7(file) }
|
|
5107
5256
|
);
|
|
5108
5257
|
let currentDate = "";
|
|
5109
5258
|
let currentEmail = "";
|
|
@@ -5154,7 +5303,7 @@ var init_file_probe = __esm({
|
|
|
5154
5303
|
|
|
5155
5304
|
// src/daemon-worker.ts
|
|
5156
5305
|
import { spawn as spawn2 } from "node:child_process";
|
|
5157
|
-
import {
|
|
5306
|
+
import { closeSync, existsSync as existsSync13, mkdirSync as mkdirSync13, openSync, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "node:fs";
|
|
5158
5307
|
import { dirname as dirname10, join as join11 } from "node:path";
|
|
5159
5308
|
import { fileURLToPath } from "node:url";
|
|
5160
5309
|
|
|
@@ -5300,6 +5449,21 @@ function normalizeExtra(raw) {
|
|
|
5300
5449
|
init_storage();
|
|
5301
5450
|
init_fs();
|
|
5302
5451
|
|
|
5452
|
+
// src/daemon-log.ts
|
|
5453
|
+
init_fs();
|
|
5454
|
+
import { appendFileSync as appendFileSync2 } from "node:fs";
|
|
5455
|
+
function appendDaemonLogLine(level, msg) {
|
|
5456
|
+
try {
|
|
5457
|
+
appendFileSync2(
|
|
5458
|
+
cclensPath("daemon.log"),
|
|
5459
|
+
`${(/* @__PURE__ */ new Date()).toISOString()} ${level.toUpperCase()} ${msg}
|
|
5460
|
+
`,
|
|
5461
|
+
"utf8"
|
|
5462
|
+
);
|
|
5463
|
+
} catch {
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
|
|
5303
5467
|
// src/usage/backoff.ts
|
|
5304
5468
|
var BASE_INTERVAL_MS = 5 * 60 * 1e3;
|
|
5305
5469
|
var MAX_INTERVAL_MS = 60 * 60 * 1e3;
|
|
@@ -5322,7 +5486,7 @@ import { randomUUID } from "node:crypto";
|
|
|
5322
5486
|
|
|
5323
5487
|
// ../entries/dist/fs.js
|
|
5324
5488
|
init_fs();
|
|
5325
|
-
import { readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, renameSync as
|
|
5489
|
+
import { readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, renameSync as renameSync3, existsSync as existsSync3 } from "node:fs";
|
|
5326
5490
|
import { randomBytes } from "node:crypto";
|
|
5327
5491
|
import { join as join4 } from "node:path";
|
|
5328
5492
|
|
|
@@ -5375,8 +5539,8 @@ function skippedTrivialEnrichment(generatedAt) {
|
|
|
5375
5539
|
retry_count: 0
|
|
5376
5540
|
};
|
|
5377
5541
|
}
|
|
5378
|
-
function entryKey(sessionId,
|
|
5379
|
-
return `${sessionId}__${
|
|
5542
|
+
function entryKey(sessionId, localDay2) {
|
|
5543
|
+
return `${sessionId}__${localDay2}`;
|
|
5380
5544
|
}
|
|
5381
5545
|
function parseEntryKey(key) {
|
|
5382
5546
|
const m = /^([^_]+(?:_[^_]+)*)__(\d{4}-\d{2}-\d{2})$/.exec(key);
|
|
@@ -5397,7 +5561,7 @@ var PROMPT_FRAME_ORIGIN = {
|
|
|
5397
5561
|
|
|
5398
5562
|
// ../entries/dist/digest-fs.js
|
|
5399
5563
|
init_fs();
|
|
5400
|
-
import { readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync2, renameSync, chmodSync, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "node:fs";
|
|
5564
|
+
import { readFileSync as readFileSync4, readdirSync, writeFileSync as writeFileSync2, renameSync as renameSync2, chmodSync, mkdirSync as mkdirSync3, existsSync as existsSync2 } from "node:fs";
|
|
5401
5565
|
import { dirname as dirname2, join as join3 } from "node:path";
|
|
5402
5566
|
var digestsDirCached = null;
|
|
5403
5567
|
function parseDigestFile(path7, expectedVersion) {
|
|
@@ -5438,7 +5602,7 @@ function writeDayDigest(digest) {
|
|
|
5438
5602
|
} catch {
|
|
5439
5603
|
}
|
|
5440
5604
|
}
|
|
5441
|
-
|
|
5605
|
+
renameSync2(tmp, final);
|
|
5442
5606
|
}
|
|
5443
5607
|
function readDayDigest(date) {
|
|
5444
5608
|
const p = dayDigestPath(date);
|
|
@@ -5471,7 +5635,7 @@ function writeWeekDigest(digest) {
|
|
|
5471
5635
|
} catch {
|
|
5472
5636
|
}
|
|
5473
5637
|
}
|
|
5474
|
-
|
|
5638
|
+
renameSync2(tmp, final);
|
|
5475
5639
|
}
|
|
5476
5640
|
function readWeekDigest(monday) {
|
|
5477
5641
|
const p = weekDigestPath(monday);
|
|
@@ -5504,8 +5668,8 @@ function entriesDir() {
|
|
|
5504
5668
|
entriesDirCached = envOverride ?? cclensPath("entries");
|
|
5505
5669
|
return entriesDirCached;
|
|
5506
5670
|
}
|
|
5507
|
-
function pathFor(sessionId,
|
|
5508
|
-
return join4(entriesDir(), `${entryKey(sessionId,
|
|
5671
|
+
function pathFor(sessionId, localDay2) {
|
|
5672
|
+
return join4(entriesDir(), `${entryKey(sessionId, localDay2)}.json`);
|
|
5509
5673
|
}
|
|
5510
5674
|
function tmpPathFor(final) {
|
|
5511
5675
|
return `${final}.tmp.${process.pid}.${randomBytes(4).toString("hex")}`;
|
|
@@ -5517,7 +5681,7 @@ function writeEntry(entry) {
|
|
|
5517
5681
|
const tmp = tmpPathFor(final);
|
|
5518
5682
|
const json = JSON.stringify(entry, null, 2);
|
|
5519
5683
|
writeFileSync3(tmp, json, { encoding: "utf8" });
|
|
5520
|
-
|
|
5684
|
+
renameSync3(tmp, final);
|
|
5521
5685
|
}
|
|
5522
5686
|
var PRESERVABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
5523
5687
|
"done",
|
|
@@ -5549,26 +5713,29 @@ function writeEntryPreservingEnrichment(fresh) {
|
|
|
5549
5713
|
}
|
|
5550
5714
|
const tmp = tmpPathFor(final);
|
|
5551
5715
|
writeFileSync3(tmp, JSON.stringify(toWrite, null, 2), { encoding: "utf8" });
|
|
5552
|
-
|
|
5716
|
+
renameSync3(tmp, final);
|
|
5553
5717
|
}
|
|
5554
|
-
function readEntry(sessionId,
|
|
5555
|
-
const p = pathFor(sessionId,
|
|
5718
|
+
function readEntry(sessionId, localDay2) {
|
|
5719
|
+
const p = pathFor(sessionId, localDay2);
|
|
5556
5720
|
if (!existsSync3(p))
|
|
5557
5721
|
return null;
|
|
5558
5722
|
const raw = readFileSync5(p, "utf8");
|
|
5559
5723
|
return JSON.parse(raw);
|
|
5560
5724
|
}
|
|
5725
|
+
function entryExists(sessionId, localDay2) {
|
|
5726
|
+
return existsSync3(pathFor(sessionId, localDay2));
|
|
5727
|
+
}
|
|
5561
5728
|
function listEntryKeys() {
|
|
5562
5729
|
const dir = entriesDir();
|
|
5563
5730
|
if (!existsSync3(dir))
|
|
5564
5731
|
return [];
|
|
5565
5732
|
return readdirSync2(dir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
|
|
5566
5733
|
}
|
|
5567
|
-
function listEntriesForDay(
|
|
5734
|
+
function listEntriesForDay(localDay2) {
|
|
5568
5735
|
const out = [];
|
|
5569
5736
|
for (const key of listEntryKeys()) {
|
|
5570
5737
|
const parsed = parseEntryKey(key);
|
|
5571
|
-
if (!parsed || parsed.local_day !==
|
|
5738
|
+
if (!parsed || parsed.local_day !== localDay2)
|
|
5572
5739
|
continue;
|
|
5573
5740
|
const e = readEntry(parsed.session_id, parsed.local_day);
|
|
5574
5741
|
if (e)
|
|
@@ -5577,1657 +5744,1984 @@ function listEntriesForDay(localDay) {
|
|
|
5577
5744
|
return out;
|
|
5578
5745
|
}
|
|
5579
5746
|
|
|
5580
|
-
// src/
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
utilization: raw.extra_usage.utilization
|
|
5604
|
-
} : null
|
|
5605
|
-
};
|
|
5606
|
-
}
|
|
5607
|
-
function bucketToRollup(b) {
|
|
5608
|
-
return {
|
|
5609
|
-
day: b.date,
|
|
5610
|
-
agentTimeMs: b.airTimeMs,
|
|
5611
|
-
sessions: b.sessions,
|
|
5612
|
-
toolCalls: b.toolCalls,
|
|
5613
|
-
turns: b.turns,
|
|
5614
|
-
tokens: { ...b.tokens }
|
|
5615
|
-
};
|
|
5616
|
-
}
|
|
5617
|
-
function buildRollupsForRange(sessions, sinceDay) {
|
|
5618
|
-
const startCounts = /* @__PURE__ */ new Map();
|
|
5619
|
-
for (const s of sessions) {
|
|
5620
|
-
const d = sessionDay(s);
|
|
5621
|
-
if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
|
|
5622
|
-
}
|
|
5623
|
-
return dailyActivity(sessions).filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({ ...bucketToRollup(b), uniqueSessions: startCounts.get(b.date) ?? 0 }));
|
|
5747
|
+
// src/perception/build-entries.ts
|
|
5748
|
+
init_dist();
|
|
5749
|
+
import { statSync, readFileSync as readFileSync6 } from "node:fs";
|
|
5750
|
+
import { basename, dirname as dirname3 } from "node:path";
|
|
5751
|
+
|
|
5752
|
+
// ../entries/dist/signals.js
|
|
5753
|
+
init_dist();
|
|
5754
|
+
var TEAMMATE_RE = /^<teammate-message\b/;
|
|
5755
|
+
var SKILL_LOAD_RE = /^Base directory for this skill:/;
|
|
5756
|
+
var SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
|
|
5757
|
+
function classifyUserInputSource(text) {
|
|
5758
|
+
if (!text)
|
|
5759
|
+
return "human";
|
|
5760
|
+
const stripped = stripFrameworkBoilerplate(text);
|
|
5761
|
+
if (!stripped)
|
|
5762
|
+
return "system_instruction";
|
|
5763
|
+
if (TEAMMATE_RE.test(stripped))
|
|
5764
|
+
return "teammate";
|
|
5765
|
+
if (SKILL_LOAD_RE.test(stripped))
|
|
5766
|
+
return "skill_load";
|
|
5767
|
+
if (SLASH_COMMAND_RE2.test(stripped))
|
|
5768
|
+
return "slash_command";
|
|
5769
|
+
return "human";
|
|
5624
5770
|
}
|
|
5625
|
-
|
|
5626
|
-
|
|
5771
|
+
var HAPPY_RE = /\b(?:yay|yaay|yass|woohoo|nice|great|love(?:ly)?|perfect|amazing|awesome)\s*!|!!!/gi;
|
|
5772
|
+
var SATISFIED_RE = /\b(?:thanks|thank you|looks good|lgtm|works|that works|all good|sounds good)\b/gi;
|
|
5773
|
+
var DISSATISFIED_RE = /\b(?:that'?s (?:not|wrong)|try again|no, (?:that|this)|not quite|incorrect)\b/gi;
|
|
5774
|
+
var FRUSTRATED_RE = /\b(?:broken|stop|why (?:did|are|would) you|give up|ugh|argh|wtf)\b/gi;
|
|
5775
|
+
function countSatisfactionSignals(text) {
|
|
5776
|
+
if (!text)
|
|
5777
|
+
return { happy: 0, satisfied: 0, dissatisfied: 0, frustrated: 0 };
|
|
5627
5778
|
return {
|
|
5628
|
-
|
|
5629
|
-
|
|
5779
|
+
happy: (text.match(HAPPY_RE) ?? []).length,
|
|
5780
|
+
satisfied: (text.match(SATISFIED_RE) ?? []).length,
|
|
5781
|
+
dissatisfied: (text.match(DISSATISFIED_RE) ?? []).length,
|
|
5782
|
+
frustrated: (text.match(FRUSTRATED_RE) ?? []).length
|
|
5630
5783
|
};
|
|
5631
5784
|
}
|
|
5632
|
-
|
|
5633
|
-
|
|
5785
|
+
var INSTRUCTION_RE = /\b(?:can you|please|let's|let us|i need|could you|would you)\s+([^.?!\n]{5,200})/gi;
|
|
5786
|
+
function extractUserInstructions(text) {
|
|
5787
|
+
if (!text)
|
|
5788
|
+
return [];
|
|
5789
|
+
INSTRUCTION_RE.lastIndex = 0;
|
|
5634
5790
|
const out = [];
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5791
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5792
|
+
let m;
|
|
5793
|
+
while ((m = INSTRUCTION_RE.exec(text)) !== null) {
|
|
5794
|
+
const phrase = m[1].trim().replace(/\s+/g, " ");
|
|
5795
|
+
const lc = phrase.toLowerCase();
|
|
5796
|
+
if (seen.has(lc))
|
|
5797
|
+
continue;
|
|
5798
|
+
seen.add(lc);
|
|
5799
|
+
out.push(phrase);
|
|
5800
|
+
if (out.length >= 5)
|
|
5801
|
+
break;
|
|
5639
5802
|
}
|
|
5640
5803
|
return out;
|
|
5641
5804
|
}
|
|
5642
|
-
function
|
|
5643
|
-
const
|
|
5644
|
-
if (s
|
|
5645
|
-
return
|
|
5805
|
+
function classifySubagentRole(sa) {
|
|
5806
|
+
const text = `${sa.description} ${sa.prompt_preview}`.toLowerCase();
|
|
5807
|
+
if (/\b(re-?review|review[s]?|verify|audit|spec[- ]review|code[- ]quality|code[- ]reuse|efficiency review|sanity check|quick test)\b/.test(text)) {
|
|
5808
|
+
return "reviewer";
|
|
5646
5809
|
}
|
|
5647
|
-
|
|
5810
|
+
if (/\b(implement|build chunk|build task|initialize|create the|add the)\b/.test(text)) {
|
|
5811
|
+
return "implementer";
|
|
5812
|
+
}
|
|
5813
|
+
if (/\bexplore\b|\binventory\b|\bmap\b.*\b(codebase|repo|branch)\b|\baudit\b.*coverage/.test(text)) {
|
|
5814
|
+
return "explorer";
|
|
5815
|
+
}
|
|
5816
|
+
if (/\b(investigate|research|reverse[- ]engineer|study|analy[sz]e|look up|tell me|brief me on|find out)\b/.test(text)) {
|
|
5817
|
+
return "researcher";
|
|
5818
|
+
}
|
|
5819
|
+
if (/\b(env(?:ironment)? setup|configure|setup|bootstrap)\b/.test(text)) {
|
|
5820
|
+
return "env-setup";
|
|
5821
|
+
}
|
|
5822
|
+
if (/\b(polish|cleanup|fix|refactor)\b/.test(text)) {
|
|
5823
|
+
return "polish";
|
|
5824
|
+
}
|
|
5825
|
+
return "other";
|
|
5648
5826
|
}
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5827
|
+
var STOCK_SUBAGENT_PREFIXES = /^(general-purpose|Explore|Plan|claude-code-guide|playwright-qa-verifier|statusline-setup|frontend-design:|code-review:|code-simplifier:|codex:|superpowers:)/;
|
|
5828
|
+
function isStockSubagentType(type) {
|
|
5829
|
+
return STOCK_SUBAGENT_PREFIXES.test(type);
|
|
5830
|
+
}
|
|
5831
|
+
function inferWorkingShape(entry) {
|
|
5832
|
+
if (entry.numbers.turn_count < 2)
|
|
5833
|
+
return null;
|
|
5834
|
+
const subagents = entry.subagents ?? [];
|
|
5835
|
+
const roles = subagents.map(classifySubagentRole);
|
|
5836
|
+
const reviewerCount = roles.filter((r) => r === "reviewer").length;
|
|
5837
|
+
const implementerCount = roles.filter((r) => r === "implementer").length;
|
|
5838
|
+
const explorerOrResearcher = roles.filter((r) => r === "explorer" || r === "researcher").length;
|
|
5839
|
+
const hasBackground = subagents.some((sa) => sa.background);
|
|
5840
|
+
if (implementerCount >= 2) {
|
|
5841
|
+
const chunkRefs = subagents.filter((sa) => /\b(chunk|task)\s*\d+/i.test(`${sa.description} ${sa.prompt_preview}`));
|
|
5842
|
+
if (chunkRefs.length >= 2)
|
|
5843
|
+
return "chunk-implementation";
|
|
5656
5844
|
}
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
for (const d of e.edited_dirs ?? []) {
|
|
5665
|
-
const r = resolveRepo(d);
|
|
5666
|
-
if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
|
|
5667
|
-
}
|
|
5668
|
-
const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
5669
|
-
if (dominant) return dominant;
|
|
5845
|
+
if (reviewerCount >= 3 && implementerCount === 0) {
|
|
5846
|
+
const lensSigs = /* @__PURE__ */ new Set();
|
|
5847
|
+
for (let i = 0; i < subagents.length; i++) {
|
|
5848
|
+
if (roles[i] !== "reviewer")
|
|
5849
|
+
continue;
|
|
5850
|
+
const sig = subagents[i].description.toLowerCase().replace(/[^a-z0-9 ]/g, "").split(/\s+/).filter((w) => w.length > 2).slice(0, 3).join("|");
|
|
5851
|
+
lensSigs.add(sig);
|
|
5670
5852
|
}
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
const sessionRepo = /* @__PURE__ */ new Map();
|
|
5674
|
-
for (const e of entries) {
|
|
5675
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
5676
|
-
const r = repoForEntry(e);
|
|
5677
|
-
if (r) sessionRepo.set(e.session_id, r);
|
|
5853
|
+
if (lensSigs.size >= 3)
|
|
5854
|
+
return "reviewer-triad";
|
|
5678
5855
|
}
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
if (privateProjects.has(name) || privateProjects.has(canonical)) continue;
|
|
5684
|
-
const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
|
|
5685
|
-
const key = repo ?? name;
|
|
5686
|
-
const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
|
|
5687
|
-
const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
|
|
5688
|
-
cur.agentTimeMs += ms;
|
|
5689
|
-
cur.sessions += 1;
|
|
5690
|
-
projects.set(key, cur);
|
|
5856
|
+
if (reviewerCount >= 2)
|
|
5857
|
+
return "spec-review-loop";
|
|
5858
|
+
if (explorerOrResearcher >= 2 || explorerOrResearcher >= 1 && implementerCount >= 1) {
|
|
5859
|
+
return "research-then-build";
|
|
5691
5860
|
}
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
let longCount = 0;
|
|
5703
|
-
let longTotalMin = 0;
|
|
5704
|
-
let longMaxSingleMin = 0;
|
|
5705
|
-
for (const e of entries) {
|
|
5706
|
-
if (privateProjects.has(projectRepoName(e.project)) || privateProjects.has(e.project)) continue;
|
|
5707
|
-
const shape = e.signals?.working_shape ?? null;
|
|
5708
|
-
if (shape) {
|
|
5709
|
-
const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
|
|
5710
|
-
cur.sessions += 1;
|
|
5711
|
-
cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
|
|
5712
|
-
workingShapes.set(shape, cur);
|
|
5713
|
-
}
|
|
5714
|
-
for (const [name, count] of Object.entries(e.skills)) {
|
|
5715
|
-
if (count > 0) skills.set(name, (skills.get(name) ?? 0) + 1);
|
|
5716
|
-
}
|
|
5717
|
-
for (const sa of e.subagents) {
|
|
5718
|
-
subagents.set(sa.type, (subagents.get(sa.type) ?? 0) + 1);
|
|
5719
|
-
}
|
|
5720
|
-
toolErrors += e.numbers.tool_errors;
|
|
5721
|
-
if (e.signals?.brainstorm_warmup) brainstorm += 1;
|
|
5722
|
-
if (e.numbers.exit_plan_calls > 0) planMode += 1;
|
|
5723
|
-
prs += e.numbers.prs;
|
|
5724
|
-
commits += e.numbers.commits;
|
|
5725
|
-
pushes += e.numbers.pushes;
|
|
5726
|
-
if (e.github_repos?.length) {
|
|
5727
|
-
const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
|
|
5728
|
-
const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
|
|
5729
|
-
for (const r of e.github_repos) set.add(r);
|
|
5730
|
-
projectRepos.set(key, set);
|
|
5731
|
-
}
|
|
5732
|
-
if (e.flags.includes("long_autonomous")) {
|
|
5733
|
-
longCount += 1;
|
|
5734
|
-
longTotalMin += e.numbers.active_min;
|
|
5735
|
-
if (e.numbers.active_min > longMaxSingleMin) longMaxSingleMin = e.numbers.active_min;
|
|
5736
|
-
}
|
|
5861
|
+
if (hasBackground && subagents.length >= 1)
|
|
5862
|
+
return "background-coordinated";
|
|
5863
|
+
if (subagents.length === 0) {
|
|
5864
|
+
const first = (entry.first_user || "").trim().toLowerCase();
|
|
5865
|
+
if (/^continue\b/.test(first) || first === "continue.")
|
|
5866
|
+
return "solo-continuation";
|
|
5867
|
+
const skills = Object.keys(entry.skills ?? {});
|
|
5868
|
+
if (skills.some((s) => /brainstorm|writing-plans|brainstorming/i.test(s)))
|
|
5869
|
+
return "solo-design";
|
|
5870
|
+
return "solo-build";
|
|
5737
5871
|
}
|
|
5738
|
-
return
|
|
5739
|
-
projects: Array.from(projects.entries()).map(([key, v]) => {
|
|
5740
|
-
const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
|
|
5741
|
-
const repos = v.resolved ? [v.resolved, ...extras] : extras;
|
|
5742
|
-
return {
|
|
5743
|
-
project: v.project,
|
|
5744
|
-
agentTimeMs: v.agentTimeMs,
|
|
5745
|
-
sessions: v.sessions,
|
|
5746
|
-
...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
|
|
5747
|
-
};
|
|
5748
|
-
}).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
|
|
5749
|
-
workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
|
|
5750
|
-
concurrencyPeak: stats.peakConcurrent,
|
|
5751
|
-
parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
|
|
5752
|
-
// Wire schema requires integer minutes (active_min is a float).
|
|
5753
|
-
longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
|
|
5754
|
-
toolErrors,
|
|
5755
|
-
skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
|
|
5756
|
-
subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
|
|
5757
|
-
brainstormWarmupSessions: brainstorm,
|
|
5758
|
-
planModeUsed: planMode,
|
|
5759
|
-
prs,
|
|
5760
|
-
commits,
|
|
5761
|
-
pushes
|
|
5762
|
-
};
|
|
5872
|
+
return "solo-build";
|
|
5763
5873
|
}
|
|
5764
|
-
function
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
const
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5874
|
+
function detectPromptFrames(text) {
|
|
5875
|
+
if (!text)
|
|
5876
|
+
return [];
|
|
5877
|
+
const out = [];
|
|
5878
|
+
if (/<teammate-message\b/i.test(text))
|
|
5879
|
+
out.push("teammate");
|
|
5880
|
+
if (/<task-notification\b/i.test(text))
|
|
5881
|
+
out.push("task-notification");
|
|
5882
|
+
if (/<local-command-caveat\b/i.test(text))
|
|
5883
|
+
out.push("local-command-caveat");
|
|
5884
|
+
if (/<command-(message|name)\b/i.test(text))
|
|
5885
|
+
out.push("slash-command");
|
|
5886
|
+
if (/\[Image #\d+\]/.test(text))
|
|
5887
|
+
out.push("image-attached");
|
|
5888
|
+
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)) {
|
|
5889
|
+
out.push("handoff-prose");
|
|
5779
5890
|
}
|
|
5780
|
-
return
|
|
5781
|
-
}
|
|
5782
|
-
function buildRichBlocksForDay(day, daySessions, privateProjects, enrichmentOptIn, resolveRepo) {
|
|
5783
|
-
const entries = listEntriesForDay(day);
|
|
5784
|
-
if (entries.length === 0) return void 0;
|
|
5785
|
-
const rich = buildRichRollupBlocks(day, daySessions, entries, privateProjects, resolveRepo);
|
|
5786
|
-
const enriched = enrichmentOptIn ? buildEnrichedExtras(entries) : void 0;
|
|
5787
|
-
return enriched ? { rich, enriched } : { rich };
|
|
5788
|
-
}
|
|
5789
|
-
function buildIngestPayload(inputs) {
|
|
5790
|
-
const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
|
|
5791
|
-
return {
|
|
5792
|
-
ingestId: randomUUID(),
|
|
5793
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5794
|
-
cliVersion: "0.13.2",
|
|
5795
|
-
...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
|
|
5796
|
-
...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
|
|
5797
|
-
...richRollup ? { richRollup } : {},
|
|
5798
|
-
...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
|
|
5799
|
-
...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
|
|
5800
|
-
...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
|
|
5801
|
-
...inputs.planTier ? { planTier: inputs.planTier } : {},
|
|
5802
|
-
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {}
|
|
5803
|
-
};
|
|
5804
|
-
}
|
|
5805
|
-
async function pushToTeamServer(config, payload) {
|
|
5806
|
-
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
5807
|
-
method: "POST",
|
|
5808
|
-
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
5809
|
-
headers: {
|
|
5810
|
-
"Content-Type": "application/json",
|
|
5811
|
-
Authorization: `Bearer ${config.bearerToken}`
|
|
5812
|
-
},
|
|
5813
|
-
body: JSON.stringify(payload)
|
|
5814
|
-
});
|
|
5815
|
-
const body = await res.json().catch(() => null);
|
|
5816
|
-
return { ok: res.ok, status: res.status, body };
|
|
5891
|
+
return out;
|
|
5817
5892
|
}
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5893
|
+
var STOCK_SKILL_PREFIXES = /^(superpowers|mcp__|frontend-design|code-review|codex|claude-code-guide|claude-api):/i;
|
|
5894
|
+
function classifySkill(name) {
|
|
5895
|
+
if (name.startsWith("(ToolSearch:"))
|
|
5896
|
+
return "infra";
|
|
5897
|
+
if (STOCK_SKILL_PREFIXES.test(name))
|
|
5898
|
+
return "stock";
|
|
5899
|
+
if (name === "using-superpowers")
|
|
5900
|
+
return "stock";
|
|
5901
|
+
return "user";
|
|
5826
5902
|
}
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5903
|
+
var EXTERNAL_REF_PATTERNS = [
|
|
5904
|
+
{ kind: "linear-kip", re: /\bKIP-\d+\b/i },
|
|
5905
|
+
{ kind: "github-issue-pr", re: /\b(issue|pr|pull request)\s*#\d+\b|github\.com\/[^\s]+\/(issues|pull)\/\d+/i },
|
|
5906
|
+
{ kind: "branch-ref", re: /\b(branch|feat|fix|chore|refactor)[/:]\s*[\w./-]+|on `[\w./-]+`/i },
|
|
5907
|
+
{ kind: "url", re: /https?:\/\/\S+/ }
|
|
5908
|
+
];
|
|
5909
|
+
function detectExternalRef(text) {
|
|
5910
|
+
if (!text)
|
|
5911
|
+
return null;
|
|
5912
|
+
if (/<teammate-message|<task-notification|<local-command-caveat/i.test(text.slice(0, 400)))
|
|
5913
|
+
return null;
|
|
5914
|
+
for (const { kind, re } of EXTERNAL_REF_PATTERNS) {
|
|
5915
|
+
const m = text.match(re);
|
|
5916
|
+
if (m)
|
|
5917
|
+
return { kind, preview: text.slice(0, 160) };
|
|
5838
5918
|
}
|
|
5839
5919
|
return null;
|
|
5840
5920
|
}
|
|
5841
|
-
function
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
if (
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
const commondirFile = join5(gd, "commondir");
|
|
5850
|
-
if (existsSync4(commondirFile)) {
|
|
5851
|
-
const common = readFileSync6(commondirFile, "utf8").trim();
|
|
5852
|
-
return join5(isAbsolute(common) ? common : resolve(gd, common), "config");
|
|
5853
|
-
}
|
|
5854
|
-
return join5(gd, "config");
|
|
5921
|
+
function bucketVerbosity(n) {
|
|
5922
|
+
if (n < 100)
|
|
5923
|
+
return "short";
|
|
5924
|
+
if (n < 500)
|
|
5925
|
+
return "medium";
|
|
5926
|
+
if (n < 2e3)
|
|
5927
|
+
return "long";
|
|
5928
|
+
return "very_long";
|
|
5855
5929
|
}
|
|
5856
|
-
function
|
|
5857
|
-
const
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
repo = null;
|
|
5875
|
-
}
|
|
5876
|
-
cache.set(dir, repo);
|
|
5877
|
-
return repo;
|
|
5930
|
+
function computeEntrySignals(entry) {
|
|
5931
|
+
const fu = entry.first_user || "";
|
|
5932
|
+
const verbosity = bucketVerbosity(fu.length);
|
|
5933
|
+
const ext = detectExternalRef(fu);
|
|
5934
|
+
const external_refs = ext ? [ext] : [];
|
|
5935
|
+
const skillNames = Object.keys(entry.skills ?? {});
|
|
5936
|
+
const brainstorm_warmup = skillNames.some((s) => /brainstorm|writing-plans/i.test(s));
|
|
5937
|
+
const fuTrim = fu.trim().toLowerCase();
|
|
5938
|
+
const frames = detectPromptFrames(fu);
|
|
5939
|
+
const continuation_kind = /^continue\b/.test(fuTrim) || fuTrim === "continue." ? "literal-continue" : frames.includes("handoff-prose") ? "handoff-prose" : "none";
|
|
5940
|
+
return {
|
|
5941
|
+
working_shape: inferWorkingShape(entry),
|
|
5942
|
+
prompt_frames: frames,
|
|
5943
|
+
subagent_roles: (entry.subagents ?? []).map(classifySubagentRole),
|
|
5944
|
+
verbosity,
|
|
5945
|
+
external_refs,
|
|
5946
|
+
brainstorm_warmup,
|
|
5947
|
+
continuation_kind
|
|
5878
5948
|
};
|
|
5879
5949
|
}
|
|
5880
5950
|
|
|
5881
|
-
//
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
import { dirname as dirname4 } from "node:path";
|
|
5885
|
-
var QUEUE_PATH = cclensPath("ingest-queue.jsonl");
|
|
5886
|
-
var MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
5887
|
-
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
5888
|
-
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
5889
|
-
mkdirSync5(dirname4(queuePath), { recursive: true });
|
|
5890
|
-
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
5891
|
-
appendFileSync2(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
5892
|
-
try {
|
|
5893
|
-
const { size } = statSync2(queuePath);
|
|
5894
|
-
if (size > MAX_SIZE_BYTES) {
|
|
5895
|
-
pruneOldest(queuePath);
|
|
5896
|
-
}
|
|
5897
|
-
} catch {
|
|
5898
|
-
}
|
|
5899
|
-
}
|
|
5900
|
-
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
5901
|
-
let lines;
|
|
5902
|
-
try {
|
|
5903
|
-
lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
5904
|
-
} catch {
|
|
5905
|
-
return [];
|
|
5906
|
-
}
|
|
5907
|
-
const now = Date.now();
|
|
5908
|
-
const valid = [];
|
|
5909
|
-
for (const line of lines) {
|
|
5910
|
-
try {
|
|
5911
|
-
const entry = JSON.parse(line);
|
|
5912
|
-
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
5913
|
-
valid.push(entry);
|
|
5914
|
-
}
|
|
5915
|
-
} catch {
|
|
5916
|
-
}
|
|
5917
|
-
}
|
|
5918
|
-
writeFileSync4(queuePath, "", "utf8");
|
|
5919
|
-
return valid.map((e) => e.payload);
|
|
5920
|
-
}
|
|
5921
|
-
function pruneOldest(queuePath) {
|
|
5922
|
-
let lines;
|
|
5923
|
-
try {
|
|
5924
|
-
lines = readFileSync7(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
5925
|
-
} catch {
|
|
5926
|
-
return;
|
|
5927
|
-
}
|
|
5928
|
-
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
5929
|
-
writeFileSync4(queuePath, keep.join("\n") + "\n", "utf8");
|
|
5951
|
+
// ../entries/dist/trivial.js
|
|
5952
|
+
function isTrivial(n) {
|
|
5953
|
+
return n.active_min < 1 && n.turn_count < 3 && n.tools_total === 0;
|
|
5930
5954
|
}
|
|
5931
5955
|
|
|
5932
|
-
//
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
return "pro";
|
|
5946
|
-
case "default_claude_max":
|
|
5947
|
-
case "default_claude_max_5x":
|
|
5948
|
-
return "pro-max";
|
|
5949
|
-
case "default_claude_max_20x":
|
|
5950
|
-
return "pro-max-20x";
|
|
5951
|
-
default:
|
|
5952
|
-
return "custom";
|
|
5953
|
-
}
|
|
5954
|
-
}
|
|
5955
|
-
async function fetchProfile(nowMs = Date.now()) {
|
|
5956
|
-
const creds = readOAuthCredentials();
|
|
5957
|
-
if (!creds) return null;
|
|
5958
|
-
if (creds.expiresAt - 6e4 <= nowMs) return null;
|
|
5959
|
-
let res;
|
|
5960
|
-
try {
|
|
5961
|
-
res = await fetch(PROFILE_ENDPOINT, {
|
|
5962
|
-
headers: {
|
|
5963
|
-
Authorization: `Bearer ${creds.accessToken}`,
|
|
5964
|
-
"anthropic-beta": BETA_HEADER2
|
|
5965
|
-
}
|
|
5966
|
-
});
|
|
5967
|
-
} catch {
|
|
5968
|
-
return null;
|
|
5969
|
-
}
|
|
5970
|
-
if (!res.ok) return null;
|
|
5971
|
-
const body = await res.json().catch(() => null);
|
|
5972
|
-
if (!body) return null;
|
|
5973
|
-
const rateLimitTier = body.organization?.rate_limit_tier ?? null;
|
|
5956
|
+
// ../entries/dist/build.js
|
|
5957
|
+
init_analytics();
|
|
5958
|
+
function viewEvent(ev) {
|
|
5959
|
+
let rawType;
|
|
5960
|
+
if (ev.role === "user" || ev.role === "tool-result")
|
|
5961
|
+
rawType = "user";
|
|
5962
|
+
else if (ev.role === "agent" || ev.role === "agent-thinking" || ev.role === "tool-call")
|
|
5963
|
+
rawType = "assistant";
|
|
5964
|
+
else if (ev.rawType === "summary")
|
|
5965
|
+
rawType = "summary";
|
|
5966
|
+
else
|
|
5967
|
+
rawType = "system";
|
|
5968
|
+
const rawCwd = ev.raw?.cwd;
|
|
5974
5969
|
return {
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5970
|
+
rawType,
|
|
5971
|
+
content: ev.blocks ?? [],
|
|
5972
|
+
model: ev.model,
|
|
5973
|
+
msgId: ev.messageId,
|
|
5974
|
+
usage: ev.usage ? {
|
|
5975
|
+
input_tokens: ev.usage.input,
|
|
5976
|
+
output_tokens: ev.usage.output,
|
|
5977
|
+
cache_read_input_tokens: ev.usage.cacheRead,
|
|
5978
|
+
cache_creation_input_tokens: ev.usage.cacheWrite
|
|
5979
|
+
} : void 0,
|
|
5980
|
+
cwd: rawCwd
|
|
5978
5981
|
};
|
|
5979
5982
|
}
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5983
|
+
var IDLE_GAP_MS4 = 3 * 60 * 1e3;
|
|
5984
|
+
var INTERRUPT_RE2 = /\[request interrupted|interrupted by user/i;
|
|
5985
|
+
var PR_TITLE_RE = /--title\s+["']([^"']+)["']/;
|
|
5986
|
+
var BASH_CHAIN_SPLIT_RE = /\|\||&&|;|\|/;
|
|
5987
|
+
var SHELL_NOISE = /* @__PURE__ */ new Set(["sudo", "time", "nohup", "nice", "env", "exec"]);
|
|
5988
|
+
var IMAGE_BLOCK_RE = /\[Image:\s*source:\s*[^\]]+\]/g;
|
|
5989
|
+
function parseMs(iso) {
|
|
5990
|
+
if (!iso)
|
|
5991
|
+
return void 0;
|
|
5992
|
+
const ms = Date.parse(iso);
|
|
5993
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
5989
5994
|
}
|
|
5990
|
-
function
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
{ mode: 384 }
|
|
5996
|
-
);
|
|
5995
|
+
function trunc(s, n) {
|
|
5996
|
+
if (!s)
|
|
5997
|
+
return "";
|
|
5998
|
+
const one = s.replace(/\s+/g, " ").trim();
|
|
5999
|
+
return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
|
|
5997
6000
|
}
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6001
|
+
function minimizeImages(s) {
|
|
6002
|
+
if (!s)
|
|
6003
|
+
return s;
|
|
6004
|
+
let counter = 0;
|
|
6005
|
+
return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
|
|
6006
|
+
}
|
|
6007
|
+
function bashVerb(cmd) {
|
|
6008
|
+
if (!cmd)
|
|
6009
|
+
return "?";
|
|
6010
|
+
let firstSeg = cmd.split(BASH_CHAIN_SPLIT_RE, 1)[0].trim();
|
|
6011
|
+
if (firstSeg.startsWith("cd ") || firstSeg === "cd") {
|
|
6012
|
+
const parts = cmd.split(/&&|;/, 3);
|
|
6013
|
+
if (parts.length >= 2)
|
|
6014
|
+
firstSeg = parts[1].trim();
|
|
6002
6015
|
}
|
|
6003
|
-
const
|
|
6004
|
-
|
|
6005
|
-
|
|
6016
|
+
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
6017
|
+
while (tokens.length && SHELL_NOISE.has(tokens[0]))
|
|
6018
|
+
tokens.shift();
|
|
6019
|
+
if (!tokens.length)
|
|
6020
|
+
return "?";
|
|
6021
|
+
let first = tokens[0];
|
|
6022
|
+
if (first.includes("=") && !first.startsWith("/")) {
|
|
6023
|
+
tokens.shift();
|
|
6024
|
+
if (!tokens.length)
|
|
6025
|
+
return "?";
|
|
6026
|
+
first = tokens[0];
|
|
6006
6027
|
}
|
|
6007
|
-
|
|
6008
|
-
|
|
6028
|
+
if (first.includes("/") && !first.startsWith("./")) {
|
|
6029
|
+
first = first.replace(/\/+$/, "").split("/").pop();
|
|
6030
|
+
}
|
|
6031
|
+
return first.replace(/^["']|["']$/g, "") || "?";
|
|
6009
6032
|
}
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
}
|
|
6018
|
-
|
|
6019
|
-
const win = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6020
|
-
return {
|
|
6021
|
-
capturedAt: raw.captured_at,
|
|
6022
|
-
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6023
|
-
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6024
|
-
sevenDayOpus: win(raw.seven_day_opus),
|
|
6025
|
-
sevenDaySonnet: win(raw.seven_day_sonnet),
|
|
6026
|
-
sevenDayOauthApps: win(raw.seven_day_oauth_apps),
|
|
6027
|
-
sevenDayCowork: win(raw.seven_day_cowork),
|
|
6028
|
-
extraUsage: raw.extra_usage ? {
|
|
6029
|
-
isEnabled: raw.extra_usage.is_enabled,
|
|
6030
|
-
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6031
|
-
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6032
|
-
utilization: raw.extra_usage.utilization
|
|
6033
|
-
} : null
|
|
6034
|
-
};
|
|
6033
|
+
function firstTextBlock(blocks) {
|
|
6034
|
+
if (!Array.isArray(blocks))
|
|
6035
|
+
return "";
|
|
6036
|
+
for (const b of blocks) {
|
|
6037
|
+
if (b && typeof b === "object" && b.type === "text") {
|
|
6038
|
+
return String(b.text ?? "");
|
|
6039
|
+
}
|
|
6040
|
+
}
|
|
6041
|
+
return "";
|
|
6035
6042
|
}
|
|
6036
|
-
function
|
|
6037
|
-
if (
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
return out;
|
|
6043
|
+
function blockText(blocks) {
|
|
6044
|
+
if (!blocks)
|
|
6045
|
+
return "";
|
|
6046
|
+
return blocks.filter((b) => !!b && typeof b === "object" && b.type === "text" && typeof b.text === "string").map((b) => b.text).join(" ");
|
|
6041
6047
|
}
|
|
6042
|
-
|
|
6043
|
-
const
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
);
|
|
6048
|
+
function groupEventsByLocalDay(events) {
|
|
6049
|
+
const byDay = /* @__PURE__ */ new Map();
|
|
6050
|
+
for (const ev of events) {
|
|
6051
|
+
if (!ev.timestamp)
|
|
6052
|
+
continue;
|
|
6053
|
+
const ms = Date.parse(ev.timestamp);
|
|
6054
|
+
if (Number.isNaN(ms))
|
|
6055
|
+
continue;
|
|
6056
|
+
const day = toLocalDay(ms);
|
|
6057
|
+
let bucket = byDay.get(day);
|
|
6058
|
+
if (!bucket) {
|
|
6059
|
+
bucket = [];
|
|
6060
|
+
byDay.set(day, bucket);
|
|
6061
|
+
}
|
|
6062
|
+
bucket.push(ev);
|
|
6058
6063
|
}
|
|
6059
|
-
return
|
|
6060
|
-
inserted: body.snapshotHistory.inserted ?? 0,
|
|
6061
|
-
skipped: body.snapshotHistory.skipped ?? 0,
|
|
6062
|
-
status: result.status
|
|
6063
|
-
};
|
|
6064
|
+
return byDay;
|
|
6064
6065
|
}
|
|
6065
|
-
function
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6066
|
+
function newTurn(startMs, userInput = "") {
|
|
6067
|
+
return {
|
|
6068
|
+
startMs,
|
|
6069
|
+
lastMs: startMs,
|
|
6070
|
+
eventTs: startMs !== void 0 ? [startMs] : [],
|
|
6071
|
+
userInput,
|
|
6072
|
+
toolsTotal: 0,
|
|
6073
|
+
toolMix: /* @__PURE__ */ new Map(),
|
|
6074
|
+
bashVerbs: /* @__PURE__ */ new Map(),
|
|
6075
|
+
skills: /* @__PURE__ */ new Map(),
|
|
6076
|
+
subagents: [],
|
|
6077
|
+
taskOps: 0,
|
|
6078
|
+
toolErrors: 0,
|
|
6079
|
+
consecSameToolMax: 0,
|
|
6080
|
+
consec: 0,
|
|
6081
|
+
endedInInterrupt: false,
|
|
6082
|
+
models: /* @__PURE__ */ new Map(),
|
|
6083
|
+
tokensIn: 0,
|
|
6084
|
+
tokensOut: 0,
|
|
6085
|
+
tokensCacheR: 0,
|
|
6086
|
+
tokensCacheW: 0,
|
|
6087
|
+
seenMsgIds: /* @__PURE__ */ new Set()
|
|
6088
|
+
};
|
|
6071
6089
|
}
|
|
6072
|
-
function
|
|
6073
|
-
const
|
|
6074
|
-
|
|
6090
|
+
function closeTurn(t, closed, cwd) {
|
|
6091
|
+
const ts = [...t.eventTs].sort((a, b) => a - b);
|
|
6092
|
+
let active = 0;
|
|
6093
|
+
for (let i = 1; i < ts.length; i++) {
|
|
6094
|
+
const dt = ts[i] - ts[i - 1];
|
|
6095
|
+
if (dt < IDLE_GAP_MS4)
|
|
6096
|
+
active += dt;
|
|
6097
|
+
}
|
|
6098
|
+
closed.push({
|
|
6099
|
+
startMs: t.startMs ?? 0,
|
|
6100
|
+
activeMs: active,
|
|
6101
|
+
userInput: t.userInput,
|
|
6102
|
+
firstAgentText: t.firstAgentText,
|
|
6103
|
+
lastAgentText: t.lastAgentText,
|
|
6104
|
+
toolsTotal: t.toolsTotal,
|
|
6105
|
+
toolMix: t.toolMix,
|
|
6106
|
+
bashVerbs: t.bashVerbs,
|
|
6107
|
+
skills: t.skills,
|
|
6108
|
+
subagents: t.subagents,
|
|
6109
|
+
taskOps: t.taskOps,
|
|
6110
|
+
toolErrors: t.toolErrors,
|
|
6111
|
+
consecSameToolMax: t.consecSameToolMax,
|
|
6112
|
+
endedInInterrupt: t.endedInInterrupt,
|
|
6113
|
+
models: t.models,
|
|
6114
|
+
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
|
|
6115
|
+
cwd
|
|
6116
|
+
});
|
|
6075
6117
|
}
|
|
6076
|
-
function
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6118
|
+
function aggregateDay(dayEvents, _sessionFallbackProject) {
|
|
6119
|
+
const prTitles = [];
|
|
6120
|
+
let commits = 0;
|
|
6121
|
+
let pushes = 0;
|
|
6122
|
+
const gitToolUseIds = /* @__PURE__ */ new Set();
|
|
6123
|
+
const githubRepos = /* @__PURE__ */ new Set();
|
|
6124
|
+
const editedDirCounts = /* @__PURE__ */ new Map();
|
|
6125
|
+
const modelTurns = /* @__PURE__ */ new Map();
|
|
6126
|
+
let totalToolErrors = 0;
|
|
6127
|
+
let totalInterrupts = 0;
|
|
6128
|
+
let exitPlanCalls = 0;
|
|
6129
|
+
let taskOpsTotal = 0;
|
|
6130
|
+
let firstUser;
|
|
6131
|
+
let finalAgent;
|
|
6132
|
+
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6133
|
+
const closed = [];
|
|
6134
|
+
let cur;
|
|
6135
|
+
const allTs = [];
|
|
6136
|
+
let lastEventCwd;
|
|
6137
|
+
for (const ev of dayEvents) {
|
|
6138
|
+
const tsMs = parseMs(ev.timestamp);
|
|
6139
|
+
if (tsMs !== void 0)
|
|
6140
|
+
allTs.push(tsMs);
|
|
6141
|
+
const view = viewEvent(ev);
|
|
6142
|
+
const rawType = view.rawType;
|
|
6143
|
+
const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
|
|
6144
|
+
const content = view.content;
|
|
6145
|
+
const eventCwd = view.cwd;
|
|
6146
|
+
if (eventCwd)
|
|
6147
|
+
lastEventCwd = eventCwd;
|
|
6148
|
+
if (rawType === "user") {
|
|
6149
|
+
const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
6150
|
+
if (!isToolResultOnly) {
|
|
6151
|
+
const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
|
|
6152
|
+
if (cur) {
|
|
6153
|
+
if (text && INTERRUPT_RE2.test(text)) {
|
|
6154
|
+
cur.endedInInterrupt = true;
|
|
6155
|
+
totalInterrupts++;
|
|
6156
|
+
}
|
|
6157
|
+
closeTurn(cur, closed, eventCwd);
|
|
6158
|
+
}
|
|
6159
|
+
cur = newTurn(tsMs, text);
|
|
6160
|
+
if (!firstUser && text && classifyUserInputSource(text) === "human") {
|
|
6161
|
+
firstUser = text;
|
|
6162
|
+
}
|
|
6163
|
+
} else {
|
|
6164
|
+
if (Array.isArray(content)) {
|
|
6165
|
+
for (const c of content) {
|
|
6166
|
+
if (!c || typeof c !== "object" || c.type !== "tool_result")
|
|
6167
|
+
continue;
|
|
6168
|
+
if (c.is_error) {
|
|
6169
|
+
totalToolErrors++;
|
|
6170
|
+
if (cur)
|
|
6171
|
+
cur.toolErrors++;
|
|
6172
|
+
}
|
|
6173
|
+
const tuId = c.tool_use_id;
|
|
6174
|
+
if (tuId && gitToolUseIds.has(tuId)) {
|
|
6175
|
+
const rc = c.content;
|
|
6176
|
+
const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
|
|
6177
|
+
for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
|
|
6178
|
+
if (m[1].toLowerCase() === "user-attachments")
|
|
6179
|
+
continue;
|
|
6180
|
+
githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
|
|
6181
|
+
}
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
if (cur && tsMs !== void 0) {
|
|
6186
|
+
cur.eventTs.push(tsMs);
|
|
6187
|
+
cur.lastMs = tsMs;
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
} else if (rawType === "assistant") {
|
|
6191
|
+
if (cur && tsMs !== void 0) {
|
|
6192
|
+
cur.eventTs.push(tsMs);
|
|
6193
|
+
cur.lastMs = tsMs;
|
|
6194
|
+
}
|
|
6195
|
+
const model = msg.model;
|
|
6196
|
+
if (model) {
|
|
6197
|
+
modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
|
|
6198
|
+
if (cur)
|
|
6199
|
+
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
6200
|
+
}
|
|
6201
|
+
const msgId = msg.id;
|
|
6202
|
+
const usage = msg.usage;
|
|
6203
|
+
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
|
|
6204
|
+
cur.seenMsgIds.add(msgId);
|
|
6205
|
+
cur.tokensIn += usage.input_tokens ?? 0;
|
|
6206
|
+
cur.tokensOut += usage.output_tokens ?? 0;
|
|
6207
|
+
cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
|
|
6208
|
+
cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
|
|
6209
|
+
}
|
|
6210
|
+
if (Array.isArray(content)) {
|
|
6211
|
+
for (const c of content) {
|
|
6212
|
+
if (!c || typeof c !== "object")
|
|
6213
|
+
continue;
|
|
6214
|
+
const ct = c;
|
|
6215
|
+
if (ct.type === "text" && ct.text) {
|
|
6216
|
+
finalAgent = ct.text;
|
|
6217
|
+
if (cur) {
|
|
6218
|
+
if (cur.firstAgentText === void 0)
|
|
6219
|
+
cur.firstAgentText = ct.text;
|
|
6220
|
+
cur.lastAgentText = ct.text;
|
|
6221
|
+
}
|
|
6222
|
+
} else if (ct.type === "tool_use") {
|
|
6223
|
+
const name = ct.name ?? "unknown";
|
|
6224
|
+
if (cur) {
|
|
6225
|
+
cur.toolsTotal++;
|
|
6226
|
+
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
6227
|
+
if (name === cur.lastTool)
|
|
6228
|
+
cur.consec++;
|
|
6229
|
+
else {
|
|
6230
|
+
cur.lastTool = name;
|
|
6231
|
+
cur.consec = 1;
|
|
6232
|
+
}
|
|
6233
|
+
if (cur.consec > cur.consecSameToolMax)
|
|
6234
|
+
cur.consecSameToolMax = cur.consec;
|
|
6235
|
+
if (name === "Skill") {
|
|
6236
|
+
const sk = String(ct.input?.skill ?? "unknown");
|
|
6237
|
+
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
6238
|
+
}
|
|
6239
|
+
if (name === "Agent") {
|
|
6240
|
+
const inp = ct.input ?? {};
|
|
6241
|
+
cur.subagents.push({
|
|
6242
|
+
type: String(inp.subagent_type ?? "general-purpose"),
|
|
6243
|
+
description: trunc(String(inp.description ?? ""), 80),
|
|
6244
|
+
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
6245
|
+
background: Boolean(inp.run_in_background)
|
|
6246
|
+
});
|
|
6247
|
+
}
|
|
6248
|
+
if (name === "ExitPlanMode")
|
|
6249
|
+
exitPlanCalls++;
|
|
6250
|
+
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
6251
|
+
cur.taskOps++;
|
|
6252
|
+
taskOpsTotal++;
|
|
6253
|
+
}
|
|
6254
|
+
}
|
|
6255
|
+
if (name === "Bash") {
|
|
6256
|
+
const cmd = String(ct.input?.command ?? "");
|
|
6257
|
+
if (cur)
|
|
6258
|
+
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
6259
|
+
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
6260
|
+
const m = cmd.match(PR_TITLE_RE);
|
|
6261
|
+
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
6262
|
+
}
|
|
6263
|
+
if (/\bgit\s+commit\b/.test(cmd))
|
|
6264
|
+
commits++;
|
|
6265
|
+
if (/\bgit\s+push\b/.test(cmd))
|
|
6266
|
+
pushes++;
|
|
6267
|
+
if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
|
|
6268
|
+
gitToolUseIds.add(ct.id);
|
|
6269
|
+
}
|
|
6270
|
+
if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
|
|
6271
|
+
cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
|
|
6272
|
+
}
|
|
6273
|
+
if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
|
|
6274
|
+
const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
|
|
6275
|
+
const slash = fp.lastIndexOf("/");
|
|
6276
|
+
if (slash > 0) {
|
|
6277
|
+
const dir = fp.slice(0, slash);
|
|
6278
|
+
editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
}
|
|
6283
|
+
}
|
|
6284
|
+
}
|
|
6082
6285
|
}
|
|
6083
|
-
|
|
6286
|
+
if (cur)
|
|
6287
|
+
closeTurn(cur, closed, lastEventCwd);
|
|
6288
|
+
const tsSorted = [...allTs].sort((a, b) => a - b);
|
|
6289
|
+
let activeMs2 = 0;
|
|
6290
|
+
for (let i = 1; i < tsSorted.length; i++) {
|
|
6291
|
+
const dt = tsSorted[i] - tsSorted[i - 1];
|
|
6292
|
+
if (dt < IDLE_GAP_MS4)
|
|
6293
|
+
activeMs2 += dt;
|
|
6294
|
+
}
|
|
6295
|
+
const tsSortedStrings = dayEvents.map((e) => e.timestamp).filter((t) => !!t).sort();
|
|
6296
|
+
const start_iso = tsSortedStrings[0] ?? "";
|
|
6297
|
+
const end_iso = tsSortedStrings[tsSortedStrings.length - 1] ?? "";
|
|
6298
|
+
return {
|
|
6299
|
+
closed,
|
|
6300
|
+
firstUser,
|
|
6301
|
+
finalAgent,
|
|
6302
|
+
prTitles,
|
|
6303
|
+
commits,
|
|
6304
|
+
pushes,
|
|
6305
|
+
githubRepos: [...githubRepos].slice(0, 5),
|
|
6306
|
+
editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
|
|
6307
|
+
modelTurns,
|
|
6308
|
+
totalToolErrors,
|
|
6309
|
+
totalInterrupts,
|
|
6310
|
+
exitPlanCalls,
|
|
6311
|
+
taskOpsTotal,
|
|
6312
|
+
cwdCounts,
|
|
6313
|
+
start_iso,
|
|
6314
|
+
end_iso,
|
|
6315
|
+
activeMs: activeMs2
|
|
6316
|
+
};
|
|
6084
6317
|
}
|
|
6085
|
-
|
|
6086
|
-
const
|
|
6087
|
-
|
|
6088
|
-
|
|
6318
|
+
function buildTopTools(closed) {
|
|
6319
|
+
const toolMix = /* @__PURE__ */ new Map();
|
|
6320
|
+
const bashVerbsAll = /* @__PURE__ */ new Map();
|
|
6321
|
+
for (const t of closed) {
|
|
6322
|
+
for (const [k, v] of t.toolMix)
|
|
6323
|
+
toolMix.set(k, (toolMix.get(k) ?? 0) + v);
|
|
6324
|
+
for (const [k, v] of t.bashVerbs)
|
|
6325
|
+
bashVerbsAll.set(k, (bashVerbsAll.get(k) ?? 0) + v);
|
|
6089
6326
|
}
|
|
6090
|
-
const
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
return
|
|
6327
|
+
const top = [...toolMix.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3);
|
|
6328
|
+
return top.map(([name, count]) => {
|
|
6329
|
+
if (name === "Bash" && bashVerbsAll.size > 0) {
|
|
6330
|
+
const sub = [...bashVerbsAll.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([v, c]) => c > 1 ? `${v}\xD7${c}` : v).join(", ");
|
|
6331
|
+
return `Bash\xD7${count} (${sub})`;
|
|
6332
|
+
}
|
|
6333
|
+
return count > 1 ? `${name}\xD7${count}` : name;
|
|
6334
|
+
});
|
|
6335
|
+
}
|
|
6336
|
+
function buildEntries(sessionDetail) {
|
|
6337
|
+
const byDay = groupEventsByLocalDay(sessionDetail.events);
|
|
6338
|
+
const entries = [];
|
|
6339
|
+
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6340
|
+
const sessionFallbackProject = canonicalProjectName(sessionDetail.projectName ?? "");
|
|
6341
|
+
for (const [local_day, dayEvents] of byDay) {
|
|
6342
|
+
if (dayEvents.length === 0)
|
|
6343
|
+
continue;
|
|
6344
|
+
const agg = aggregateDay(dayEvents, sessionFallbackProject);
|
|
6345
|
+
const { closed } = agg;
|
|
6346
|
+
const turnCount = closed.length;
|
|
6347
|
+
const toolsTotal = closed.reduce((s, t) => s + t.toolsTotal, 0);
|
|
6348
|
+
const tokensTotal = closed.reduce((s, t) => s + t.totalTokens, 0);
|
|
6349
|
+
const subagentCalls = closed.reduce((s, t) => s + t.subagents.length, 0);
|
|
6350
|
+
const skillsMap = /* @__PURE__ */ new Map();
|
|
6351
|
+
for (const t of closed) {
|
|
6352
|
+
for (const [k, v] of t.skills)
|
|
6353
|
+
skillsMap.set(k, (skillsMap.get(k) ?? 0) + v);
|
|
6354
|
+
}
|
|
6355
|
+
const skillCalls = [...skillsMap.values()].reduce((a, b) => a + b, 0);
|
|
6356
|
+
const taskOps = agg.taskOpsTotal;
|
|
6357
|
+
const interrupts = agg.totalInterrupts;
|
|
6358
|
+
const toolErrors = agg.totalToolErrors;
|
|
6359
|
+
const consecMax = Math.max(0, ...closed.map((t) => t.consecSameToolMax));
|
|
6360
|
+
const exitPlanCalls = agg.exitPlanCalls;
|
|
6361
|
+
const prs = agg.prTitles.length;
|
|
6362
|
+
const commits = agg.commits;
|
|
6363
|
+
const pushes = agg.pushes;
|
|
6364
|
+
const activeMin = Math.round(agg.activeMs / 6e3) / 10;
|
|
6365
|
+
const numbers = {
|
|
6366
|
+
active_min: activeMin,
|
|
6367
|
+
turn_count: turnCount,
|
|
6368
|
+
tools_total: toolsTotal,
|
|
6369
|
+
subagent_calls: subagentCalls,
|
|
6370
|
+
skill_calls: skillCalls,
|
|
6371
|
+
task_ops: taskOps,
|
|
6372
|
+
interrupts,
|
|
6373
|
+
tool_errors: toolErrors,
|
|
6374
|
+
consec_same_tool_max: consecMax,
|
|
6375
|
+
exit_plan_calls: exitPlanCalls,
|
|
6376
|
+
prs,
|
|
6377
|
+
commits,
|
|
6378
|
+
pushes,
|
|
6379
|
+
tokens_total: tokensTotal
|
|
6380
|
+
};
|
|
6381
|
+
const longestTurnActiveMs = Math.max(0, ...closed.map((t) => t.activeMs));
|
|
6382
|
+
const flags = [];
|
|
6383
|
+
if (interrupts >= 3)
|
|
6384
|
+
flags.push("interrupt_heavy");
|
|
6385
|
+
if (toolErrors >= 20)
|
|
6386
|
+
flags.push("high_errors");
|
|
6387
|
+
if (consecMax >= 8)
|
|
6388
|
+
flags.push("loop_suspected");
|
|
6389
|
+
if (agg.activeMs < 5 * 6e4 && prs >= 1)
|
|
6390
|
+
flags.push("fast_ship");
|
|
6391
|
+
if (exitPlanCalls > 0)
|
|
6392
|
+
flags.push("plan_used");
|
|
6393
|
+
const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
|
|
6394
|
+
if (subagentTurns >= 3)
|
|
6395
|
+
flags.push("orchestrated");
|
|
6396
|
+
if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
|
|
6397
|
+
flags.push("long_autonomous");
|
|
6398
|
+
const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
|
|
6399
|
+
const modelMix = Object.fromEntries(agg.modelTurns);
|
|
6400
|
+
const firstUserRaw = agg.firstUser ?? "";
|
|
6401
|
+
const first_user = trunc(minimizeImages(firstUserRaw), 400);
|
|
6402
|
+
const finalAgentRaw = agg.finalAgent ?? "";
|
|
6403
|
+
const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
|
|
6404
|
+
let project = sessionFallbackProject;
|
|
6405
|
+
if (agg.cwdCounts.size > 0) {
|
|
6406
|
+
const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
6407
|
+
project = canonicalProjectName(best[0]);
|
|
6408
|
+
}
|
|
6409
|
+
const top_tools = buildTopTools(closed);
|
|
6410
|
+
const skills = Object.fromEntries(skillsMap);
|
|
6411
|
+
const subagents = closed.flatMap((t) => t.subagents);
|
|
6412
|
+
const humanText = dayEvents.filter((ev) => {
|
|
6413
|
+
const v = viewEvent(ev);
|
|
6414
|
+
if (v.rawType !== "user")
|
|
6415
|
+
return false;
|
|
6416
|
+
const content = v.content;
|
|
6417
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
6418
|
+
if (isToolResultOnly)
|
|
6419
|
+
return false;
|
|
6420
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
6421
|
+
return classifyUserInputSource(text) === "human";
|
|
6422
|
+
}).map((ev) => blockText(ev.blocks) || ev.preview || "").join("\n");
|
|
6423
|
+
const satisfaction_signals = countSatisfactionSignals(humanText);
|
|
6424
|
+
const user_input_sources = {
|
|
6425
|
+
human: 0,
|
|
6426
|
+
teammate: 0,
|
|
6427
|
+
skill_load: 0,
|
|
6428
|
+
slash_command: 0,
|
|
6429
|
+
system_instruction: 0
|
|
6430
|
+
};
|
|
6431
|
+
for (const ev of dayEvents) {
|
|
6432
|
+
const v = viewEvent(ev);
|
|
6433
|
+
if (v.rawType !== "user")
|
|
6434
|
+
continue;
|
|
6435
|
+
const content = v.content;
|
|
6436
|
+
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
6437
|
+
if (isToolResultOnly)
|
|
6438
|
+
continue;
|
|
6439
|
+
const text = blockText(ev.blocks) || ev.preview || "";
|
|
6440
|
+
const src = classifyUserInputSource(text);
|
|
6441
|
+
user_input_sources[src]++;
|
|
6442
|
+
}
|
|
6443
|
+
const pendingUserInstructions = extractUserInstructions(humanText).slice(0, 5);
|
|
6444
|
+
const trivial = isTrivial(numbers);
|
|
6445
|
+
const entry = {
|
|
6446
|
+
version: CURRENT_ENTRY_SCHEMA_VERSION,
|
|
6447
|
+
agent: sessionDetail.agent,
|
|
6448
|
+
session_id: sessionDetail.id,
|
|
6449
|
+
local_day,
|
|
6450
|
+
project,
|
|
6451
|
+
start_iso: agg.start_iso,
|
|
6452
|
+
end_iso: agg.end_iso,
|
|
6453
|
+
numbers,
|
|
6454
|
+
flags,
|
|
6455
|
+
primary_model: primaryModel,
|
|
6456
|
+
model_mix: modelMix,
|
|
6457
|
+
first_user,
|
|
6458
|
+
final_agent,
|
|
6459
|
+
pr_titles: agg.prTitles,
|
|
6460
|
+
...agg.githubRepos.length ? { github_repos: agg.githubRepos } : {},
|
|
6461
|
+
...agg.editedDirs.length ? { edited_dirs: agg.editedDirs } : {},
|
|
6462
|
+
top_tools,
|
|
6463
|
+
skills,
|
|
6464
|
+
subagents,
|
|
6465
|
+
satisfaction_signals,
|
|
6466
|
+
user_input_sources,
|
|
6467
|
+
enrichment: trivial ? skippedTrivialEnrichment(generatedAt) : { ...pendingEnrichment(), user_instructions: pendingUserInstructions },
|
|
6468
|
+
generated_at: generatedAt,
|
|
6469
|
+
source_jsonl: sessionDetail.filePath ?? "",
|
|
6470
|
+
source_checkpoint: { byte_offset: 0, last_event_ts: agg.end_iso || null }
|
|
6471
|
+
};
|
|
6472
|
+
entry.signals = computeEntrySignals(entry);
|
|
6473
|
+
entries.push(entry);
|
|
6097
6474
|
}
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
error: `HTTP ${result.status}`
|
|
6118
|
-
};
|
|
6119
|
-
}
|
|
6120
|
-
inserted += result.inserted;
|
|
6121
|
-
skipped += result.skipped;
|
|
6122
|
-
lastSnapshotAt = latestCapturedAt(batch) ?? lastSnapshotAt;
|
|
6123
|
-
} catch (err) {
|
|
6124
|
-
log3("warn", `team backfill: batch ${i + 1}/${batches.length} error: ${err.message}`);
|
|
6125
|
-
return {
|
|
6126
|
-
paired: true,
|
|
6127
|
-
sentSnapshots: wire.length,
|
|
6128
|
-
insertedSnapshots: inserted,
|
|
6129
|
-
skippedSnapshots: skipped,
|
|
6130
|
-
batches: i,
|
|
6131
|
-
lastSnapshotAt,
|
|
6132
|
-
error: err.message
|
|
6133
|
-
};
|
|
6475
|
+
entries.sort((a, b) => a.local_day.localeCompare(b.local_day));
|
|
6476
|
+
return entries;
|
|
6477
|
+
}
|
|
6478
|
+
|
|
6479
|
+
// src/perception/build-entries.ts
|
|
6480
|
+
function decodeProjectDirName(projectDir) {
|
|
6481
|
+
if (projectDir.startsWith("-")) {
|
|
6482
|
+
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
6483
|
+
}
|
|
6484
|
+
return projectDir.replace(/-/g, "/");
|
|
6485
|
+
}
|
|
6486
|
+
function buildEntriesForFile(filePath) {
|
|
6487
|
+
const stat = statSync(filePath);
|
|
6488
|
+
const raw = readFileSync6(filePath, "utf8");
|
|
6489
|
+
const rawLines = raw.split("\n").filter(Boolean).map((l) => {
|
|
6490
|
+
try {
|
|
6491
|
+
return JSON.parse(l);
|
|
6492
|
+
} catch {
|
|
6493
|
+
return null;
|
|
6134
6494
|
}
|
|
6495
|
+
}).filter((x) => x !== null);
|
|
6496
|
+
if (rawLines.length === 0) return null;
|
|
6497
|
+
const { meta, events } = parseTranscript(rawLines);
|
|
6498
|
+
const fileName = basename(filePath);
|
|
6499
|
+
const projectDir = basename(dirname3(filePath));
|
|
6500
|
+
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
6501
|
+
const sd = {
|
|
6502
|
+
...meta,
|
|
6503
|
+
id: sessionId,
|
|
6504
|
+
filePath,
|
|
6505
|
+
projectDir,
|
|
6506
|
+
projectName: meta.cwd ?? decodeProjectDirName(projectDir),
|
|
6507
|
+
events
|
|
6508
|
+
};
|
|
6509
|
+
const built = buildEntries(sd);
|
|
6510
|
+
for (const e of built) {
|
|
6511
|
+
e.source_checkpoint.byte_offset = stat.size;
|
|
6135
6512
|
}
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6513
|
+
return built;
|
|
6514
|
+
}
|
|
6515
|
+
|
|
6516
|
+
// src/team/push.ts
|
|
6517
|
+
init_storage();
|
|
6518
|
+
var SNAPSHOT_FRESHNESS_MS = 10 * 60 * 1e3;
|
|
6519
|
+
var POST_TIMEOUT_MS = 15e3;
|
|
6520
|
+
function readLatestUsageSnapshotForWire(filePath, nowMs = Date.now()) {
|
|
6521
|
+
const raw = latestClaudeCodeSnapshot(filePath);
|
|
6522
|
+
if (!raw) return null;
|
|
6523
|
+
const capturedMs = Date.parse(raw.captured_at);
|
|
6524
|
+
if (Number.isNaN(capturedMs)) return null;
|
|
6525
|
+
if (nowMs - capturedMs > SNAPSHOT_FRESHNESS_MS) return null;
|
|
6526
|
+
const toWire = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6140
6527
|
return {
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6528
|
+
capturedAt: raw.captured_at,
|
|
6529
|
+
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6530
|
+
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6531
|
+
sevenDayOpus: toWire(raw.seven_day_opus),
|
|
6532
|
+
sevenDaySonnet: toWire(raw.seven_day_sonnet),
|
|
6533
|
+
sevenDayOauthApps: toWire(raw.seven_day_oauth_apps),
|
|
6534
|
+
sevenDayCowork: toWire(raw.seven_day_cowork),
|
|
6535
|
+
extraUsage: raw.extra_usage ? {
|
|
6536
|
+
isEnabled: raw.extra_usage.is_enabled,
|
|
6537
|
+
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6538
|
+
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6539
|
+
utilization: raw.extra_usage.utilization
|
|
6540
|
+
} : null
|
|
6147
6541
|
};
|
|
6148
6542
|
}
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
writeFileSync6(join6(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
|
|
6543
|
+
function bucketToRollup(b) {
|
|
6544
|
+
return {
|
|
6545
|
+
day: b.date,
|
|
6546
|
+
agentTimeMs: b.airTimeMs,
|
|
6547
|
+
sessions: b.sessions,
|
|
6548
|
+
toolCalls: b.toolCalls,
|
|
6549
|
+
turns: b.turns,
|
|
6550
|
+
tokens: { ...b.tokens }
|
|
6551
|
+
};
|
|
6159
6552
|
}
|
|
6160
|
-
function
|
|
6161
|
-
|
|
6553
|
+
function buildRollupsForRange(sessions, sinceDay) {
|
|
6554
|
+
const startCounts = /* @__PURE__ */ new Map();
|
|
6555
|
+
for (const s of sessions) {
|
|
6556
|
+
const d = sessionDay(s);
|
|
6557
|
+
if (d) startCounts.set(d, (startCounts.get(d) ?? 0) + 1);
|
|
6558
|
+
}
|
|
6559
|
+
return dailyActivity(sessions).filter((b) => !sinceDay || b.date >= sinceDay).map((b) => ({ ...bucketToRollup(b), uniqueSessions: startCounts.get(b.date) ?? 0 }));
|
|
6162
6560
|
}
|
|
6163
|
-
function
|
|
6164
|
-
|
|
6561
|
+
function dayBoundsMs(day) {
|
|
6562
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
6563
|
+
return {
|
|
6564
|
+
startMs: new Date(y, m - 1, d, 0, 0, 0, 0).getTime(),
|
|
6565
|
+
endMs: new Date(y, m - 1, d + 1, 0, 0, 0, 0).getTime()
|
|
6566
|
+
};
|
|
6165
6567
|
}
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
return {
|
|
6174
|
-
id: command.id,
|
|
6175
|
-
ok: false,
|
|
6176
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6177
|
-
error: `Unknown command type: ${command.type}`
|
|
6178
|
-
};
|
|
6568
|
+
function clipSegmentsToDay(segments, bounds) {
|
|
6569
|
+
if (!segments || segments.length === 0) return [];
|
|
6570
|
+
const out = [];
|
|
6571
|
+
for (const seg of segments) {
|
|
6572
|
+
const s = Math.max(seg.startMs, bounds.startMs);
|
|
6573
|
+
const e = Math.min(seg.endMs, bounds.endMs);
|
|
6574
|
+
if (e > s) out.push({ startMs: s, endMs: e });
|
|
6179
6575
|
}
|
|
6576
|
+
return out;
|
|
6180
6577
|
}
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
const
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
log3("info", `command ${command.id}: backfill-activity from ${targetDay} (${days} days)`);
|
|
6190
|
-
let sessions;
|
|
6191
|
-
try {
|
|
6192
|
-
sessions = await listSessions2({ limit: 1e4 });
|
|
6193
|
-
} catch (err) {
|
|
6194
|
-
return {
|
|
6195
|
-
id: command.id,
|
|
6196
|
-
ok: false,
|
|
6197
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6198
|
-
error: `Failed to read sessions: ${err.message}`
|
|
6199
|
-
};
|
|
6200
|
-
}
|
|
6201
|
-
const rollups = buildRollupsForRange(sessions, targetDay);
|
|
6202
|
-
if (rollups.length === 0) {
|
|
6203
|
-
return {
|
|
6204
|
-
id: command.id,
|
|
6205
|
-
ok: true,
|
|
6206
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6207
|
-
summary: { pushed: 0, fromDay: targetDay }
|
|
6208
|
-
};
|
|
6209
|
-
}
|
|
6210
|
-
const privateProjects = new Set(config.privateProjects ?? []);
|
|
6211
|
-
const enrichmentOptIn = !!config.enrichmentOptIn;
|
|
6212
|
-
const resolveRepo = createRepoResolver();
|
|
6213
|
-
let pushed = 0;
|
|
6214
|
-
for (const rollup of rollups) {
|
|
6215
|
-
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
6216
|
-
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, privateProjects, enrichmentOptIn, resolveRepo);
|
|
6217
|
-
let artifactSignals = null;
|
|
6218
|
-
try {
|
|
6219
|
-
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: [process.cwd()] });
|
|
6220
|
-
} catch {
|
|
6221
|
-
}
|
|
6222
|
-
const payload = buildIngestPayload({
|
|
6223
|
-
rollup,
|
|
6224
|
-
richExtras: richBlocks?.rich,
|
|
6225
|
-
enrichedExtras: richBlocks?.enriched,
|
|
6226
|
-
artifactSignals: artifactSignals ?? void 0
|
|
6227
|
-
});
|
|
6228
|
-
const result = await pushToTeamServer(config, payload);
|
|
6229
|
-
if (!result.ok) {
|
|
6230
|
-
return {
|
|
6231
|
-
id: command.id,
|
|
6232
|
-
ok: false,
|
|
6233
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6234
|
-
error: `Push failed on ${rollup.day} (HTTP ${result.status}); pushed ${pushed}/${rollups.length} before failing`
|
|
6235
|
-
};
|
|
6236
|
-
}
|
|
6237
|
-
pushed++;
|
|
6578
|
+
function filterSyncedSessions(sessions, syncProjects) {
|
|
6579
|
+
if (!syncProjects) return sessions;
|
|
6580
|
+
return sessions.filter((s) => shouldSyncProject(projectRepoName(s.projectName), syncProjects));
|
|
6581
|
+
}
|
|
6582
|
+
function sessionTouchesDay(s, day) {
|
|
6583
|
+
const bounds = dayBoundsMs(day);
|
|
6584
|
+
if (s.activeSegments && s.activeSegments.length > 0) {
|
|
6585
|
+
return s.activeSegments.some((seg) => seg.endMs > bounds.startMs && seg.startMs < bounds.endMs);
|
|
6238
6586
|
}
|
|
6239
|
-
|
|
6240
|
-
return {
|
|
6241
|
-
id: command.id,
|
|
6242
|
-
ok: true,
|
|
6243
|
-
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6244
|
-
summary: { pushed, fromDay: targetDay }
|
|
6245
|
-
};
|
|
6587
|
+
return sessionDay(s) === day;
|
|
6246
6588
|
}
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
}
|
|
6255
|
-
|
|
6256
|
-
const
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
writeTeamConfig(nextConfig);
|
|
6266
|
-
};
|
|
6267
|
-
const collectedCommands = /* @__PURE__ */ new Map();
|
|
6268
|
-
const collectCommands = (commands) => {
|
|
6269
|
-
if (!commands) return;
|
|
6270
|
-
for (const cmd of commands) {
|
|
6271
|
-
if (!collectedCommands.has(cmd.id)) collectedCommands.set(cmd.id, cmd);
|
|
6272
|
-
}
|
|
6273
|
-
};
|
|
6274
|
-
const dispatchAndReport = async () => {
|
|
6275
|
-
try {
|
|
6276
|
-
if (collectedCommands.size === 0) return;
|
|
6277
|
-
const results = [];
|
|
6278
|
-
for (const cmd of collectedCommands.values()) {
|
|
6279
|
-
if (inFlightCommands.has(cmd.id)) continue;
|
|
6280
|
-
inFlightCommands.add(cmd.id);
|
|
6281
|
-
try {
|
|
6282
|
-
const result = await dispatchCommand(cmd, config, log3);
|
|
6283
|
-
results.push(result);
|
|
6284
|
-
} finally {
|
|
6285
|
-
inFlightCommands.delete(cmd.id);
|
|
6286
|
-
}
|
|
6287
|
-
}
|
|
6288
|
-
if (results.length === 0) return;
|
|
6289
|
-
const resultsPayload = {
|
|
6290
|
-
ingestId: randomUUID3(),
|
|
6291
|
-
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6292
|
-
commandResults: results
|
|
6293
|
-
};
|
|
6294
|
-
const r = await pushToTeamServer(config, resultsPayload);
|
|
6295
|
-
if (!r.ok) {
|
|
6296
|
-
log3("warn", `team commandResults push failed (${r.status}); will retry on next sync`);
|
|
6297
|
-
} else {
|
|
6298
|
-
log3("info", `team commandResults push ok: ${results.length} result${results.length === 1 ? "" : "s"}`);
|
|
6299
|
-
}
|
|
6300
|
-
} catch (err) {
|
|
6301
|
-
log3("warn", `team command dispatch error: ${err.message}`);
|
|
6302
|
-
}
|
|
6303
|
-
};
|
|
6304
|
-
const usageBackfill = await runTeamBackfill(log3, USAGE_LOG2, config, {
|
|
6305
|
-
sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
|
|
6306
|
-
});
|
|
6307
|
-
if (usageBackfill.lastSnapshotAt) {
|
|
6308
|
-
persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
|
|
6309
|
-
}
|
|
6310
|
-
const sessions = await listSessions2({ limit: 1e4 });
|
|
6311
|
-
const rollups = buildRollupsForRange(sessions, config.lastSyncedDay);
|
|
6312
|
-
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
|
|
6313
|
-
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
6314
|
-
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
6315
|
-
if (rollups.length === 0) {
|
|
6316
|
-
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
6317
|
-
if (!hasLiveData) {
|
|
6318
|
-
if (usageBackfill.sentSnapshots === 0) log3("info", "team push: nothing to sync");
|
|
6319
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
6320
|
-
}
|
|
6321
|
-
const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks });
|
|
6322
|
-
const result = await pushToTeamServer(config, payload);
|
|
6323
|
-
if (!result.ok) {
|
|
6324
|
-
const errLine = `team push failed (${result.status})`;
|
|
6325
|
-
log3("warn", `${errLine}; queueing`);
|
|
6326
|
-
writeLastPushFailure(payload, errLine);
|
|
6327
|
-
enqueuePayload(payload);
|
|
6328
|
-
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
6329
|
-
}
|
|
6330
|
-
writeLastPushSuccess(payload);
|
|
6331
|
-
collectCommands(result.body?.commands);
|
|
6332
|
-
let queuedDrained2 = 0;
|
|
6333
|
-
const backlog = dequeuePayloads();
|
|
6334
|
-
for (let i = 0; i < backlog.length; i++) {
|
|
6335
|
-
const qResult = await pushToTeamServer(config, backlog[i]);
|
|
6336
|
-
if (!qResult.ok) {
|
|
6337
|
-
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
6338
|
-
break;
|
|
6339
|
-
}
|
|
6340
|
-
collectCommands(qResult.body?.commands);
|
|
6341
|
-
queuedDrained2++;
|
|
6589
|
+
function buildRichRollupBlocks(day, daySessions, entries, resolveRepo) {
|
|
6590
|
+
const bounds = dayBoundsMs(day);
|
|
6591
|
+
const clippedSessions = [];
|
|
6592
|
+
for (const s of daySessions) {
|
|
6593
|
+
const clipped = clipSegmentsToDay(s.activeSegments, bounds);
|
|
6594
|
+
if (clipped.length === 0) continue;
|
|
6595
|
+
clippedSessions.push({ ...s, activeSegments: clipped });
|
|
6596
|
+
}
|
|
6597
|
+
const bursts = computeBurstsFromSessions(clippedSessions);
|
|
6598
|
+
const stats = summarizeBursts(bursts);
|
|
6599
|
+
const repoForEntry = (e) => {
|
|
6600
|
+
if (resolveRepo) {
|
|
6601
|
+
const own = resolveRepo(e.project);
|
|
6602
|
+
if (own) return own;
|
|
6603
|
+
const counts = /* @__PURE__ */ new Map();
|
|
6604
|
+
for (const d of e.edited_dirs ?? []) {
|
|
6605
|
+
const r = resolveRepo(d);
|
|
6606
|
+
if (r) counts.set(r, (counts.get(r) ?? 0) + 1);
|
|
6342
6607
|
}
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
6608
|
+
const dominant = [...counts.entries()].sort((a, b) => b[1] - a[1])[0]?.[0];
|
|
6609
|
+
if (dominant) return dominant;
|
|
6346
6610
|
}
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6351
|
-
const
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
if (!result.ok) {
|
|
6387
|
-
const errLine = `team push failed on ${rollup.day} (${result.status})`;
|
|
6388
|
-
log3("warn", `${errLine}; queueing`);
|
|
6389
|
-
writeLastPushFailure(payload, errLine);
|
|
6390
|
-
enqueuePayload(payload);
|
|
6391
|
-
queued++;
|
|
6392
|
-
failedDay = rollup.day;
|
|
6393
|
-
break;
|
|
6394
|
-
}
|
|
6395
|
-
writeLastPushSuccess(payload);
|
|
6396
|
-
collectCommands(result.body?.commands);
|
|
6397
|
-
pushed++;
|
|
6398
|
-
lastPushedDay = rollup.day;
|
|
6611
|
+
return e.github_repos?.length === 1 ? e.github_repos[0] : null;
|
|
6612
|
+
};
|
|
6613
|
+
const sessionRepo = /* @__PURE__ */ new Map();
|
|
6614
|
+
for (const e of entries) {
|
|
6615
|
+
const r = repoForEntry(e);
|
|
6616
|
+
if (r) sessionRepo.set(e.session_id, r);
|
|
6617
|
+
}
|
|
6618
|
+
const projects = /* @__PURE__ */ new Map();
|
|
6619
|
+
for (const s of clippedSessions) {
|
|
6620
|
+
const canonical = canonicalProjectName(s.projectName);
|
|
6621
|
+
const name = projectRepoName(s.projectName);
|
|
6622
|
+
const repo = resolveRepo?.(canonical) ?? sessionRepo.get(s.id);
|
|
6623
|
+
const key = repo ?? name;
|
|
6624
|
+
const ms = s.activeSegments.reduce((sum, seg) => sum + (seg.endMs - seg.startMs), 0);
|
|
6625
|
+
const cur = projects.get(key) ?? { project: name, agentTimeMs: 0, sessions: 0, ...repo ? { resolved: repo } : {} };
|
|
6626
|
+
cur.agentTimeMs += ms;
|
|
6627
|
+
cur.sessions += 1;
|
|
6628
|
+
projects.set(key, cur);
|
|
6629
|
+
}
|
|
6630
|
+
const projectRepos = /* @__PURE__ */ new Map();
|
|
6631
|
+
const workingShapes = /* @__PURE__ */ new Map();
|
|
6632
|
+
const skills = /* @__PURE__ */ new Map();
|
|
6633
|
+
const subagents = /* @__PURE__ */ new Map();
|
|
6634
|
+
let toolErrors = 0;
|
|
6635
|
+
let brainstorm = 0;
|
|
6636
|
+
let planMode = 0;
|
|
6637
|
+
let prs = 0;
|
|
6638
|
+
let commits = 0;
|
|
6639
|
+
let pushes = 0;
|
|
6640
|
+
let longCount = 0;
|
|
6641
|
+
let longTotalMin = 0;
|
|
6642
|
+
let longMaxSingleMin = 0;
|
|
6643
|
+
for (const e of entries) {
|
|
6644
|
+
const shape = e.signals?.working_shape ?? null;
|
|
6645
|
+
if (shape) {
|
|
6646
|
+
const cur = workingShapes.get(shape) ?? { sessions: 0, agentTimeMs: 0 };
|
|
6647
|
+
cur.sessions += 1;
|
|
6648
|
+
cur.agentTimeMs += Math.round(e.numbers.active_min * 6e4);
|
|
6649
|
+
workingShapes.set(shape, cur);
|
|
6399
6650
|
}
|
|
6400
|
-
|
|
6401
|
-
if (
|
|
6402
|
-
} else {
|
|
6403
|
-
persistConfig({ lastSyncedDay: today });
|
|
6651
|
+
for (const [name, count] of Object.entries(e.skills)) {
|
|
6652
|
+
if (count > 0) skills.set(name, (skills.get(name) ?? 0) + 1);
|
|
6404
6653
|
}
|
|
6405
|
-
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6654
|
+
for (const sa of e.subagents) {
|
|
6655
|
+
subagents.set(sa.type, (subagents.get(sa.type) ?? 0) + 1);
|
|
6656
|
+
}
|
|
6657
|
+
toolErrors += e.numbers.tool_errors;
|
|
6658
|
+
if (e.signals?.brainstorm_warmup) brainstorm += 1;
|
|
6659
|
+
if (e.numbers.exit_plan_calls > 0) planMode += 1;
|
|
6660
|
+
prs += e.numbers.prs;
|
|
6661
|
+
commits += e.numbers.commits;
|
|
6662
|
+
pushes += e.numbers.pushes;
|
|
6663
|
+
if (e.github_repos?.length) {
|
|
6664
|
+
const key = sessionRepo.get(e.session_id) ?? projectRepoName(e.project);
|
|
6665
|
+
const set = projectRepos.get(key) ?? /* @__PURE__ */ new Set();
|
|
6666
|
+
for (const r of e.github_repos) set.add(r);
|
|
6667
|
+
projectRepos.set(key, set);
|
|
6417
6668
|
}
|
|
6418
|
-
if (
|
|
6419
|
-
|
|
6669
|
+
if (e.flags.includes("long_autonomous")) {
|
|
6670
|
+
longCount += 1;
|
|
6671
|
+
longTotalMin += e.numbers.active_min;
|
|
6672
|
+
if (e.numbers.active_min > longMaxSingleMin) longMaxSingleMin = e.numbers.active_min;
|
|
6420
6673
|
}
|
|
6421
|
-
await dispatchAndReport();
|
|
6422
|
-
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
6423
|
-
} catch (err) {
|
|
6424
|
-
const message = err.message;
|
|
6425
|
-
log3("warn", `team push error: ${message}`);
|
|
6426
|
-
writeLastPushFailure(
|
|
6427
|
-
{ ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
6428
|
-
`team push error: ${message}`
|
|
6429
|
-
);
|
|
6430
|
-
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
6431
6674
|
}
|
|
6675
|
+
return {
|
|
6676
|
+
projects: Array.from(projects.entries()).map(([key, v]) => {
|
|
6677
|
+
const extras = [...projectRepos.get(key) ?? []].filter((r) => r !== v.resolved);
|
|
6678
|
+
const repos = v.resolved ? [v.resolved, ...extras] : extras;
|
|
6679
|
+
return {
|
|
6680
|
+
project: v.project,
|
|
6681
|
+
agentTimeMs: v.agentTimeMs,
|
|
6682
|
+
sessions: v.sessions,
|
|
6683
|
+
...repos.length ? { githubRepos: repos.slice(0, 5) } : {}
|
|
6684
|
+
};
|
|
6685
|
+
}).sort((a, b) => b.agentTimeMs - a.agentTimeMs),
|
|
6686
|
+
workingShapes: Array.from(workingShapes.entries()).map(([shape, v]) => ({ shape, ...v })).sort((a, b) => b.sessions - a.sessions),
|
|
6687
|
+
concurrencyPeak: stats.peakConcurrent,
|
|
6688
|
+
parallelMinutes: Math.round(stats.totalParallelMs / 6e4),
|
|
6689
|
+
// Wire schema requires integer minutes (active_min is a float).
|
|
6690
|
+
longAutonomous: { count: longCount, totalMin: Math.round(longTotalMin), maxSingleMin: Math.round(longMaxSingleMin) },
|
|
6691
|
+
toolErrors,
|
|
6692
|
+
skillsLoaded: Array.from(skills.entries()).map(([name, sessions]) => ({ name, sessions })).sort((a, b) => b.sessions - a.sessions),
|
|
6693
|
+
subagentsDispatched: Array.from(subagents.entries()).map(([type, count]) => ({ type, count })).sort((a, b) => b.count - a.count),
|
|
6694
|
+
brainstormWarmupSessions: brainstorm,
|
|
6695
|
+
planModeUsed: planMode,
|
|
6696
|
+
prs,
|
|
6697
|
+
commits,
|
|
6698
|
+
pushes
|
|
6699
|
+
};
|
|
6432
6700
|
}
|
|
6433
|
-
|
|
6434
|
-
const
|
|
6435
|
-
const
|
|
6436
|
-
const
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
const TOLERANCE_MS = cycleKey === "cycle_end_7d" ? 12 * HOUR : 0;
|
|
6442
|
-
const byCycle = /* @__PURE__ */ new Map();
|
|
6443
|
-
for (const p of dump.curve) {
|
|
6444
|
-
const k = p[cycleKey];
|
|
6445
|
-
if (!k) continue;
|
|
6446
|
-
const ms = Date.parse(k);
|
|
6447
|
-
if (Number.isNaN(ms)) continue;
|
|
6448
|
-
const bucket = Math.round(ms / HOUR) * HOUR;
|
|
6449
|
-
const arr = byCycle.get(bucket) ?? [];
|
|
6450
|
-
arr.push(p);
|
|
6451
|
-
byCycle.set(bucket, arr);
|
|
6701
|
+
function buildEnrichedExtras(entries) {
|
|
6702
|
+
const outcomeMix = {};
|
|
6703
|
+
const helpfulnessMix = {};
|
|
6704
|
+
const goalMix = {};
|
|
6705
|
+
for (const e of entries) {
|
|
6706
|
+
if (e.enrichment.status !== "done") continue;
|
|
6707
|
+
if (e.enrichment.outcome) {
|
|
6708
|
+
outcomeMix[e.enrichment.outcome] = (outcomeMix[e.enrichment.outcome] ?? 0) + 1;
|
|
6452
6709
|
}
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
const last = merged[merged.length - 1];
|
|
6456
|
-
if (last && endMs - last.endMs <= TOLERANCE_MS) {
|
|
6457
|
-
last.endMs = endMs;
|
|
6458
|
-
last.points.push(...points);
|
|
6459
|
-
} else {
|
|
6460
|
-
merged.push({ endMs, points: [...points] });
|
|
6461
|
-
}
|
|
6710
|
+
if (e.enrichment.claude_helpfulness) {
|
|
6711
|
+
helpfulnessMix[e.enrichment.claude_helpfulness] = (helpfulnessMix[e.enrichment.claude_helpfulness] ?? 0) + 1;
|
|
6462
6712
|
}
|
|
6463
|
-
const
|
|
6464
|
-
|
|
6465
|
-
let peak = 0;
|
|
6466
|
-
let source = "predicted";
|
|
6467
|
-
for (const p of points) {
|
|
6468
|
-
const r = p[realKey];
|
|
6469
|
-
if (typeof r === "number" && r > peak) {
|
|
6470
|
-
peak = r;
|
|
6471
|
-
source = "real";
|
|
6472
|
-
}
|
|
6473
|
-
const v = p[predKey] ?? 0;
|
|
6474
|
-
if (v > peak) {
|
|
6475
|
-
peak = v;
|
|
6476
|
-
source = "predicted";
|
|
6477
|
-
}
|
|
6478
|
-
}
|
|
6479
|
-
out.push({
|
|
6480
|
-
endsAt: new Date(endMs).toISOString(),
|
|
6481
|
-
peakPct: Math.round(peak * 10) / 10,
|
|
6482
|
-
source,
|
|
6483
|
-
current: endMs > nowMs
|
|
6484
|
-
});
|
|
6713
|
+
for (const [cat, mins] of Object.entries(e.enrichment.goal_categories)) {
|
|
6714
|
+
if (typeof mins === "number") goalMix[cat] = (goalMix[cat] ?? 0) + mins;
|
|
6485
6715
|
}
|
|
6486
|
-
|
|
6487
|
-
};
|
|
6488
|
-
return {
|
|
6489
|
-
fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
|
|
6490
|
-
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 12)
|
|
6491
|
-
};
|
|
6716
|
+
}
|
|
6717
|
+
return { outcomeMix, helpfulnessMix, goalMix };
|
|
6492
6718
|
}
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
init_fs();
|
|
6497
|
-
import { statSync as statSync4, readFileSync as readFileSync10 } from "node:fs";
|
|
6498
|
-
import { basename, dirname as dirname8 } from "node:path";
|
|
6499
|
-
|
|
6500
|
-
// ../entries/dist/signals.js
|
|
6501
|
-
init_dist();
|
|
6502
|
-
var TEAMMATE_RE = /^<teammate-message\b/;
|
|
6503
|
-
var SKILL_LOAD_RE = /^Base directory for this skill:/;
|
|
6504
|
-
var SLASH_COMMAND_RE2 = /^<command-name>|^<local-command-stdout>/;
|
|
6505
|
-
function classifyUserInputSource(text) {
|
|
6506
|
-
if (!text)
|
|
6507
|
-
return "human";
|
|
6508
|
-
const stripped = stripFrameworkBoilerplate(text);
|
|
6509
|
-
if (!stripped)
|
|
6510
|
-
return "system_instruction";
|
|
6511
|
-
if (TEAMMATE_RE.test(stripped))
|
|
6512
|
-
return "teammate";
|
|
6513
|
-
if (SKILL_LOAD_RE.test(stripped))
|
|
6514
|
-
return "skill_load";
|
|
6515
|
-
if (SLASH_COMMAND_RE2.test(stripped))
|
|
6516
|
-
return "slash_command";
|
|
6517
|
-
return "human";
|
|
6719
|
+
var ensuredSessions = /* @__PURE__ */ new Set();
|
|
6720
|
+
function resetEnsuredSessions() {
|
|
6721
|
+
ensuredSessions.clear();
|
|
6518
6722
|
}
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6723
|
+
function ensureEntriesForDay(day, daySessions) {
|
|
6724
|
+
for (const s of daySessions) {
|
|
6725
|
+
if (ensuredSessions.has(s.id)) continue;
|
|
6726
|
+
if ((s.agent ?? "claude-code") !== "claude-code") continue;
|
|
6727
|
+
if (entryExists(s.id, day)) continue;
|
|
6728
|
+
ensuredSessions.add(s.id);
|
|
6729
|
+
try {
|
|
6730
|
+
for (const e of buildEntriesForFile(s.filePath) ?? []) writeEntryPreservingEnrichment(e);
|
|
6731
|
+
} catch {
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6735
|
+
function buildRichBlocksForDay(day, daySessions, resolveRepo) {
|
|
6736
|
+
ensureEntriesForDay(day, daySessions);
|
|
6737
|
+
const sessionIds = new Set(daySessions.map((s) => s.id));
|
|
6738
|
+
const entries = listEntriesForDay(day).filter((e) => sessionIds.has(e.session_id));
|
|
6739
|
+
if (entries.length === 0) return void 0;
|
|
6740
|
+
const rich = buildRichRollupBlocks(day, daySessions, entries, resolveRepo);
|
|
6741
|
+
const enriched = buildEnrichedExtras(entries);
|
|
6742
|
+
return { rich, enriched };
|
|
6743
|
+
}
|
|
6744
|
+
function buildIngestPayload(inputs) {
|
|
6745
|
+
const richRollup = inputs.rollup && inputs.richExtras ? { ...inputs.rollup, ...inputs.richExtras } : void 0;
|
|
6746
|
+
return {
|
|
6747
|
+
ingestId: randomUUID(),
|
|
6748
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6749
|
+
cliVersion: "0.15.0",
|
|
6750
|
+
...richRollup || inputs.enrichedExtras || inputs.artifactSignals ? { schemaVersion: RICH_ROLLUP_SCHEMA_VERSION } : {},
|
|
6751
|
+
...inputs.rollup ? { dailyRollup: inputs.rollup } : {},
|
|
6752
|
+
...richRollup ? { richRollup } : {},
|
|
6753
|
+
...inputs.enrichedExtras ? { enrichedExtras: inputs.enrichedExtras } : {},
|
|
6754
|
+
...inputs.artifactSignals ? { artifactSignals: inputs.artifactSignals } : {},
|
|
6755
|
+
...inputs.usageSnapshot ? { usageSnapshot: inputs.usageSnapshot } : {},
|
|
6756
|
+
...inputs.planTier ? { planTier: inputs.planTier } : {},
|
|
6757
|
+
...inputs.cyclePeaks ? { cyclePeaks: inputs.cyclePeaks } : {},
|
|
6758
|
+
...inputs.syncLog && inputs.syncLog.length ? { syncLog: inputs.syncLog } : {}
|
|
6531
6759
|
};
|
|
6532
6760
|
}
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
|
|
6544
|
-
|
|
6761
|
+
async function pushToTeamServer(config, payload) {
|
|
6762
|
+
const res = await fetch(`${config.serverUrl}/api/ingest/metrics`, {
|
|
6763
|
+
method: "POST",
|
|
6764
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS),
|
|
6765
|
+
headers: {
|
|
6766
|
+
"Content-Type": "application/json",
|
|
6767
|
+
Authorization: `Bearer ${config.bearerToken}`
|
|
6768
|
+
},
|
|
6769
|
+
body: JSON.stringify(payload)
|
|
6770
|
+
});
|
|
6771
|
+
const body = await res.json().catch(() => null);
|
|
6772
|
+
return { ok: res.ok, status: res.status, body };
|
|
6773
|
+
}
|
|
6774
|
+
|
|
6775
|
+
// src/team/git-remote.ts
|
|
6776
|
+
import { existsSync as existsSync4, readFileSync as readFileSync7, statSync as statSync2 } from "node:fs";
|
|
6777
|
+
import { dirname as dirname4, isAbsolute, join as join5, resolve } from "node:path";
|
|
6778
|
+
var GITHUB_URL_RE = /github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git)?$/;
|
|
6779
|
+
function parseGithubRemote(url) {
|
|
6780
|
+
const m = url.trim().match(GITHUB_URL_RE);
|
|
6781
|
+
return m ? `${m[1]}/${m[2]}`.toLowerCase() : null;
|
|
6782
|
+
}
|
|
6783
|
+
function originUrl(configText) {
|
|
6784
|
+
let inOrigin = false;
|
|
6785
|
+
for (const line of configText.split("\n")) {
|
|
6786
|
+
const t = line.trim();
|
|
6787
|
+
if (t.startsWith("[")) {
|
|
6788
|
+
inOrigin = /^\[remote\s+"origin"\]/.test(t);
|
|
6545
6789
|
continue;
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6790
|
+
}
|
|
6791
|
+
if (!inOrigin) continue;
|
|
6792
|
+
const m = t.match(/^url\s*=\s*(.+)$/);
|
|
6793
|
+
if (m) return m[1].trim();
|
|
6550
6794
|
}
|
|
6551
|
-
return
|
|
6795
|
+
return null;
|
|
6552
6796
|
}
|
|
6553
|
-
function
|
|
6554
|
-
const
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
return "researcher";
|
|
6566
|
-
}
|
|
6567
|
-
if (/\b(env(?:ironment)? setup|configure|setup|bootstrap)\b/.test(text)) {
|
|
6568
|
-
return "env-setup";
|
|
6569
|
-
}
|
|
6570
|
-
if (/\b(polish|cleanup|fix|refactor)\b/.test(text)) {
|
|
6571
|
-
return "polish";
|
|
6797
|
+
function gitConfigPathFor(dir) {
|
|
6798
|
+
const dotGit = join5(dir, ".git");
|
|
6799
|
+
const st = statSync2(dotGit, { throwIfNoEntry: false });
|
|
6800
|
+
if (!st) return null;
|
|
6801
|
+
if (st.isDirectory()) return join5(dotGit, "config");
|
|
6802
|
+
const m = readFileSync7(dotGit, "utf8").match(/^gitdir:\s*(.+)\s*$/m);
|
|
6803
|
+
if (!m) return null;
|
|
6804
|
+
const gd = isAbsolute(m[1]) ? m[1] : resolve(dir, m[1]);
|
|
6805
|
+
const commondirFile = join5(gd, "commondir");
|
|
6806
|
+
if (existsSync4(commondirFile)) {
|
|
6807
|
+
const common = readFileSync7(commondirFile, "utf8").trim();
|
|
6808
|
+
return join5(isAbsolute(common) ? common : resolve(gd, common), "config");
|
|
6572
6809
|
}
|
|
6573
|
-
return "
|
|
6810
|
+
return join5(gd, "config");
|
|
6574
6811
|
}
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
return
|
|
6812
|
+
function createRepoResolver() {
|
|
6813
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6814
|
+
return (dir) => {
|
|
6815
|
+
if (!dir || !isAbsolute(dir)) return null;
|
|
6816
|
+
const hit = cache.get(dir);
|
|
6817
|
+
if (hit !== void 0) return hit;
|
|
6818
|
+
let repo = null;
|
|
6819
|
+
try {
|
|
6820
|
+
for (let d = dir; ; d = dirname4(d)) {
|
|
6821
|
+
const config = gitConfigPathFor(d);
|
|
6822
|
+
if (config && existsSync4(config)) {
|
|
6823
|
+
const url = originUrl(readFileSync7(config, "utf8"));
|
|
6824
|
+
repo = url ? parseGithubRemote(url) : null;
|
|
6825
|
+
break;
|
|
6826
|
+
}
|
|
6827
|
+
if (d === dirname4(d)) break;
|
|
6828
|
+
}
|
|
6829
|
+
} catch {
|
|
6830
|
+
repo = null;
|
|
6831
|
+
}
|
|
6832
|
+
cache.set(dir, repo);
|
|
6833
|
+
return repo;
|
|
6834
|
+
};
|
|
6578
6835
|
}
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
for (let i = 0; i < subagents.length; i++) {
|
|
6596
|
-
if (roles[i] !== "reviewer")
|
|
6597
|
-
continue;
|
|
6598
|
-
const sig = subagents[i].description.toLowerCase().replace(/[^a-z0-9 ]/g, "").split(/\s+/).filter((w) => w.length > 2).slice(0, 3).join("|");
|
|
6599
|
-
lensSigs.add(sig);
|
|
6836
|
+
|
|
6837
|
+
// src/team/queue.ts
|
|
6838
|
+
init_fs();
|
|
6839
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, appendFileSync as appendFileSync3, statSync as statSync3, mkdirSync as mkdirSync5 } from "node:fs";
|
|
6840
|
+
import { dirname as dirname5 } from "node:path";
|
|
6841
|
+
var QUEUE_PATH = cclensPath("ingest-queue.jsonl");
|
|
6842
|
+
var MAX_SIZE_BYTES = 10 * 1024 * 1024;
|
|
6843
|
+
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
6844
|
+
function enqueuePayload(payload, queuePath = QUEUE_PATH) {
|
|
6845
|
+
mkdirSync5(dirname5(queuePath), { recursive: true });
|
|
6846
|
+
const entry = { payload, enqueuedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
6847
|
+
appendFileSync3(queuePath, JSON.stringify(entry) + "\n", "utf8");
|
|
6848
|
+
try {
|
|
6849
|
+
const { size } = statSync3(queuePath);
|
|
6850
|
+
if (size > MAX_SIZE_BYTES) {
|
|
6851
|
+
pruneOldest(queuePath);
|
|
6600
6852
|
}
|
|
6601
|
-
|
|
6602
|
-
return "reviewer-triad";
|
|
6853
|
+
} catch {
|
|
6603
6854
|
}
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6855
|
+
}
|
|
6856
|
+
function dequeuePayloads(queuePath = QUEUE_PATH) {
|
|
6857
|
+
let lines;
|
|
6858
|
+
try {
|
|
6859
|
+
lines = readFileSync8(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
6860
|
+
} catch {
|
|
6861
|
+
return [];
|
|
6608
6862
|
}
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6863
|
+
const now = Date.now();
|
|
6864
|
+
const valid = [];
|
|
6865
|
+
for (const line of lines) {
|
|
6866
|
+
try {
|
|
6867
|
+
const entry = JSON.parse(line);
|
|
6868
|
+
if (now - new Date(entry.enqueuedAt).getTime() < MAX_AGE_MS) {
|
|
6869
|
+
valid.push(entry);
|
|
6870
|
+
}
|
|
6871
|
+
} catch {
|
|
6872
|
+
}
|
|
6619
6873
|
}
|
|
6620
|
-
|
|
6874
|
+
writeFileSync4(queuePath, "", "utf8");
|
|
6875
|
+
return valid.map((e) => e.payload);
|
|
6621
6876
|
}
|
|
6622
|
-
function
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
if (/<task-notification\b/i.test(text))
|
|
6629
|
-
out.push("task-notification");
|
|
6630
|
-
if (/<local-command-caveat\b/i.test(text))
|
|
6631
|
-
out.push("local-command-caveat");
|
|
6632
|
-
if (/<command-(message|name)\b/i.test(text))
|
|
6633
|
-
out.push("slash-command");
|
|
6634
|
-
if (/\[Image #\d+\]/.test(text))
|
|
6635
|
-
out.push("image-attached");
|
|
6636
|
-
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)) {
|
|
6637
|
-
out.push("handoff-prose");
|
|
6877
|
+
function pruneOldest(queuePath) {
|
|
6878
|
+
let lines;
|
|
6879
|
+
try {
|
|
6880
|
+
lines = readFileSync8(queuePath, "utf8").trim().split("\n").filter(Boolean);
|
|
6881
|
+
} catch {
|
|
6882
|
+
return;
|
|
6638
6883
|
}
|
|
6639
|
-
|
|
6884
|
+
const keep = lines.slice(Math.floor(lines.length / 2));
|
|
6885
|
+
writeFileSync4(queuePath, keep.join("\n") + "\n", "utf8");
|
|
6640
6886
|
}
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6887
|
+
|
|
6888
|
+
// src/team/backfill.ts
|
|
6889
|
+
init_storage();
|
|
6890
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
6891
|
+
|
|
6892
|
+
// src/usage/profile.ts
|
|
6893
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6894
|
+
import { dirname as dirname6 } from "node:path";
|
|
6895
|
+
var PROFILE_ENDPOINT = "https://api.anthropic.com/api/oauth/profile";
|
|
6896
|
+
var BETA_HEADER2 = "oauth-2025-04-20";
|
|
6897
|
+
var PROFILE_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6898
|
+
function mapRateLimitTier(rateLimitTier) {
|
|
6899
|
+
switch (rateLimitTier) {
|
|
6900
|
+
case "default_claude_pro":
|
|
6901
|
+
return "pro";
|
|
6902
|
+
case "default_claude_max":
|
|
6903
|
+
case "default_claude_max_5x":
|
|
6904
|
+
return "pro-max";
|
|
6905
|
+
case "default_claude_max_20x":
|
|
6906
|
+
return "pro-max-20x";
|
|
6907
|
+
default:
|
|
6908
|
+
return "custom";
|
|
6909
|
+
}
|
|
6650
6910
|
}
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6911
|
+
async function fetchProfile(nowMs = Date.now()) {
|
|
6912
|
+
const creds = readOAuthCredentials();
|
|
6913
|
+
if (!creds) return null;
|
|
6914
|
+
if (creds.expiresAt - 6e4 <= nowMs) return null;
|
|
6915
|
+
let res;
|
|
6916
|
+
try {
|
|
6917
|
+
res = await fetch(PROFILE_ENDPOINT, {
|
|
6918
|
+
headers: {
|
|
6919
|
+
Authorization: `Bearer ${creds.accessToken}`,
|
|
6920
|
+
"anthropic-beta": BETA_HEADER2
|
|
6921
|
+
}
|
|
6922
|
+
});
|
|
6923
|
+
} catch {
|
|
6659
6924
|
return null;
|
|
6660
|
-
|
|
6925
|
+
}
|
|
6926
|
+
if (!res.ok) return null;
|
|
6927
|
+
const body = await res.json().catch(() => null);
|
|
6928
|
+
if (!body) return null;
|
|
6929
|
+
const rateLimitTier = body.organization?.rate_limit_tier ?? null;
|
|
6930
|
+
return {
|
|
6931
|
+
planTier: mapRateLimitTier(rateLimitTier),
|
|
6932
|
+
rateLimitTier,
|
|
6933
|
+
organizationType: body.organization?.organization_type ?? null
|
|
6934
|
+
};
|
|
6935
|
+
}
|
|
6936
|
+
function readCachedProfile(cachePath) {
|
|
6937
|
+
if (!existsSync5(cachePath)) return null;
|
|
6938
|
+
try {
|
|
6939
|
+
const raw = JSON.parse(readFileSync9(cachePath, "utf8"));
|
|
6940
|
+
if (typeof raw.fetchedAtMs !== "number" || !raw.profile) return null;
|
|
6941
|
+
return raw;
|
|
6942
|
+
} catch {
|
|
6661
6943
|
return null;
|
|
6662
|
-
for (const { kind, re } of EXTERNAL_REF_PATTERNS) {
|
|
6663
|
-
const m = text.match(re);
|
|
6664
|
-
if (m)
|
|
6665
|
-
return { kind, preview: text.slice(0, 160) };
|
|
6666
6944
|
}
|
|
6667
|
-
return null;
|
|
6668
6945
|
}
|
|
6669
|
-
function
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
return "very_long";
|
|
6946
|
+
function writeCachedProfile(cachePath, profile, nowMs = Date.now()) {
|
|
6947
|
+
mkdirSync6(dirname6(cachePath), { recursive: true });
|
|
6948
|
+
writeFileSync5(
|
|
6949
|
+
cachePath,
|
|
6950
|
+
JSON.stringify({ fetchedAtMs: nowMs, profile }, null, 2),
|
|
6951
|
+
{ mode: 384 }
|
|
6952
|
+
);
|
|
6677
6953
|
}
|
|
6678
|
-
function
|
|
6679
|
-
const
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
const
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6954
|
+
async function getPlanTier(cachePath, nowMs = Date.now(), fetcher = fetchProfile) {
|
|
6955
|
+
const cached = readCachedProfile(cachePath);
|
|
6956
|
+
if (cached && nowMs - cached.fetchedAtMs < PROFILE_CACHE_TTL_MS) {
|
|
6957
|
+
return cached.profile.planTier;
|
|
6958
|
+
}
|
|
6959
|
+
const fresh = await fetcher(nowMs);
|
|
6960
|
+
if (!fresh) {
|
|
6961
|
+
return cached?.profile.planTier ?? null;
|
|
6962
|
+
}
|
|
6963
|
+
writeCachedProfile(cachePath, fresh, nowMs);
|
|
6964
|
+
return fresh.planTier;
|
|
6965
|
+
}
|
|
6966
|
+
|
|
6967
|
+
// src/team/backfill.ts
|
|
6968
|
+
init_fs();
|
|
6969
|
+
var USAGE_LOG = cclensPath("usage.jsonl");
|
|
6970
|
+
var PROFILE_CACHE = cclensPath("profile.json");
|
|
6971
|
+
var BATCH_SIZE = 500;
|
|
6972
|
+
var noopLog = () => {
|
|
6973
|
+
};
|
|
6974
|
+
function rawToWire(raw) {
|
|
6975
|
+
const win = (w) => w ? { utilization: w.utilization, resetsAt: w.resets_at } : null;
|
|
6688
6976
|
return {
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6977
|
+
capturedAt: raw.captured_at,
|
|
6978
|
+
fiveHour: { utilization: raw.five_hour.utilization, resetsAt: raw.five_hour.resets_at },
|
|
6979
|
+
sevenDay: { utilization: raw.seven_day.utilization, resetsAt: raw.seven_day.resets_at },
|
|
6980
|
+
sevenDayOpus: win(raw.seven_day_opus),
|
|
6981
|
+
sevenDaySonnet: win(raw.seven_day_sonnet),
|
|
6982
|
+
sevenDayOauthApps: win(raw.seven_day_oauth_apps),
|
|
6983
|
+
sevenDayCowork: win(raw.seven_day_cowork),
|
|
6984
|
+
extraUsage: raw.extra_usage ? {
|
|
6985
|
+
isEnabled: raw.extra_usage.is_enabled,
|
|
6986
|
+
monthlyLimitUsd: raw.extra_usage.monthly_limit,
|
|
6987
|
+
usedCreditsUsd: raw.extra_usage.used_credits,
|
|
6988
|
+
utilization: raw.extra_usage.utilization
|
|
6989
|
+
} : null
|
|
6696
6990
|
};
|
|
6697
6991
|
}
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6992
|
+
function chunk(items, size) {
|
|
6993
|
+
if (size <= 0) return [items];
|
|
6994
|
+
const out = [];
|
|
6995
|
+
for (let i = 0; i < items.length; i += size) out.push(items.slice(i, i + size));
|
|
6996
|
+
return out;
|
|
6702
6997
|
}
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6998
|
+
async function postBatch(config, snapshots, planTier) {
|
|
6999
|
+
const payload = {
|
|
7000
|
+
ingestId: `backfill-${randomUUID2()}`,
|
|
7001
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7002
|
+
snapshotHistory: snapshots,
|
|
7003
|
+
...planTier ? { planTier } : {}
|
|
7004
|
+
};
|
|
7005
|
+
const result = await pushToTeamServer(config, payload);
|
|
7006
|
+
if (!result.ok) {
|
|
7007
|
+
return { inserted: 0, skipped: snapshots.length, status: result.status };
|
|
7008
|
+
}
|
|
7009
|
+
const body = result.body;
|
|
7010
|
+
if (!body?.snapshotHistory) {
|
|
7011
|
+
throw new Error(
|
|
7012
|
+
"team server accepted snapshotHistory but did not return a result block \u2014 older image, upgrade required"
|
|
7013
|
+
);
|
|
7014
|
+
}
|
|
6717
7015
|
return {
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
msgId: ev.messageId,
|
|
6722
|
-
usage: ev.usage ? {
|
|
6723
|
-
input_tokens: ev.usage.input,
|
|
6724
|
-
output_tokens: ev.usage.output,
|
|
6725
|
-
cache_read_input_tokens: ev.usage.cacheRead,
|
|
6726
|
-
cache_creation_input_tokens: ev.usage.cacheWrite
|
|
6727
|
-
} : void 0,
|
|
6728
|
-
cwd: rawCwd
|
|
7016
|
+
inserted: body.snapshotHistory.inserted ?? 0,
|
|
7017
|
+
skipped: body.snapshotHistory.skipped ?? 0,
|
|
7018
|
+
status: result.status
|
|
6729
7019
|
};
|
|
6730
7020
|
}
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
function parseMs(iso) {
|
|
6738
|
-
if (!iso)
|
|
6739
|
-
return void 0;
|
|
6740
|
-
const ms = Date.parse(iso);
|
|
6741
|
-
return Number.isNaN(ms) ? void 0 : ms;
|
|
6742
|
-
}
|
|
6743
|
-
function trunc(s, n) {
|
|
6744
|
-
if (!s)
|
|
6745
|
-
return "";
|
|
6746
|
-
const one = s.replace(/\s+/g, " ").trim();
|
|
6747
|
-
return one.length <= n ? one : one.slice(0, n - 1) + "\u2026";
|
|
7021
|
+
function isAfterSince(snapshot, sinceCapturedAt) {
|
|
7022
|
+
if (!sinceCapturedAt) return true;
|
|
7023
|
+
const sinceMs = Date.parse(sinceCapturedAt);
|
|
7024
|
+
if (Number.isNaN(sinceMs)) return true;
|
|
7025
|
+
const capturedMs = Date.parse(snapshot.captured_at);
|
|
7026
|
+
return !Number.isNaN(capturedMs) && capturedMs > sinceMs;
|
|
6748
7027
|
}
|
|
6749
|
-
function
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
let counter = 0;
|
|
6753
|
-
return s.replace(IMAGE_BLOCK_RE, () => `[Image #${++counter}]`);
|
|
7028
|
+
function capturedAtMs(snapshot) {
|
|
7029
|
+
const ms = Date.parse(snapshot.captured_at);
|
|
7030
|
+
return Number.isNaN(ms) ? Number.MAX_SAFE_INTEGER : ms;
|
|
6754
7031
|
}
|
|
6755
|
-
function
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
if (parts.length >= 2)
|
|
6762
|
-
firstSeg = parts[1].trim();
|
|
6763
|
-
}
|
|
6764
|
-
const tokens = firstSeg.split(/\s+/).filter(Boolean);
|
|
6765
|
-
while (tokens.length && SHELL_NOISE.has(tokens[0]))
|
|
6766
|
-
tokens.shift();
|
|
6767
|
-
if (!tokens.length)
|
|
6768
|
-
return "?";
|
|
6769
|
-
let first = tokens[0];
|
|
6770
|
-
if (first.includes("=") && !first.startsWith("/")) {
|
|
6771
|
-
tokens.shift();
|
|
6772
|
-
if (!tokens.length)
|
|
6773
|
-
return "?";
|
|
6774
|
-
first = tokens[0];
|
|
6775
|
-
}
|
|
6776
|
-
if (first.includes("/") && !first.startsWith("./")) {
|
|
6777
|
-
first = first.replace(/\/+$/, "").split("/").pop();
|
|
7032
|
+
function latestCapturedAt(snapshots) {
|
|
7033
|
+
let latest = null;
|
|
7034
|
+
for (const s of snapshots) {
|
|
7035
|
+
const ms = Date.parse(s.capturedAt);
|
|
7036
|
+
if (Number.isNaN(ms)) continue;
|
|
7037
|
+
if (!latest || ms > latest.ms) latest = { at: s.capturedAt, ms };
|
|
6778
7038
|
}
|
|
6779
|
-
return
|
|
7039
|
+
return latest?.at;
|
|
6780
7040
|
}
|
|
6781
|
-
function
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
if (b && typeof b === "object" && b.type === "text") {
|
|
6786
|
-
return String(b.text ?? "");
|
|
6787
|
-
}
|
|
7041
|
+
async function runTeamBackfill(log3 = noopLog, filePath = USAGE_LOG, configOverride, options = {}) {
|
|
7042
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
7043
|
+
if (!config) {
|
|
7044
|
+
return { paired: false, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
6788
7045
|
}
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
}
|
|
6796
|
-
|
|
6797
|
-
const
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
const
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
7046
|
+
const raw = readSnapshots(filePath);
|
|
7047
|
+
const claudeOnly = raw.filter(
|
|
7048
|
+
(s) => (!s.agent || s.agent === "claude-code") && isAfterSince(s, options.sinceCapturedAt)
|
|
7049
|
+
).sort((a, b) => capturedAtMs(a) - capturedAtMs(b));
|
|
7050
|
+
if (claudeOnly.length === 0) {
|
|
7051
|
+
log3("info", "team backfill: no usage snapshots to send");
|
|
7052
|
+
return { paired: true, sentSnapshots: 0, insertedSnapshots: 0, skippedSnapshots: 0, batches: 0 };
|
|
7053
|
+
}
|
|
7054
|
+
const wire = claudeOnly.map(rawToWire);
|
|
7055
|
+
const batches = chunk(wire, BATCH_SIZE);
|
|
7056
|
+
const planTier = await getPlanTier(PROFILE_CACHE).catch(() => null) ?? void 0;
|
|
7057
|
+
let inserted = 0;
|
|
7058
|
+
let skipped = 0;
|
|
7059
|
+
let lastSnapshotAt;
|
|
7060
|
+
for (let i = 0; i < batches.length; i++) {
|
|
7061
|
+
const batch = batches[i];
|
|
7062
|
+
try {
|
|
7063
|
+
const result = await postBatch(config, batch, planTier);
|
|
7064
|
+
if (result.status >= 400) {
|
|
7065
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} failed (${result.status})`);
|
|
7066
|
+
return {
|
|
7067
|
+
paired: true,
|
|
7068
|
+
sentSnapshots: wire.length,
|
|
7069
|
+
insertedSnapshots: inserted,
|
|
7070
|
+
skippedSnapshots: skipped,
|
|
7071
|
+
batches: i,
|
|
7072
|
+
lastSnapshotAt,
|
|
7073
|
+
error: `HTTP ${result.status}`
|
|
7074
|
+
};
|
|
7075
|
+
}
|
|
7076
|
+
inserted += result.inserted;
|
|
7077
|
+
skipped += result.skipped;
|
|
7078
|
+
lastSnapshotAt = latestCapturedAt(batch) ?? lastSnapshotAt;
|
|
7079
|
+
} catch (err) {
|
|
7080
|
+
log3("warn", `team backfill: batch ${i + 1}/${batches.length} error: ${err.message}`);
|
|
7081
|
+
return {
|
|
7082
|
+
paired: true,
|
|
7083
|
+
sentSnapshots: wire.length,
|
|
7084
|
+
insertedSnapshots: inserted,
|
|
7085
|
+
skippedSnapshots: skipped,
|
|
7086
|
+
batches: i,
|
|
7087
|
+
lastSnapshotAt,
|
|
7088
|
+
error: err.message
|
|
7089
|
+
};
|
|
6809
7090
|
}
|
|
6810
|
-
bucket.push(ev);
|
|
6811
7091
|
}
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
7092
|
+
log3(
|
|
7093
|
+
"info",
|
|
7094
|
+
`team backfill: ${inserted} new, ${skipped} already-known across ${batches.length} batch${batches.length === 1 ? "" : "es"}`
|
|
7095
|
+
);
|
|
6815
7096
|
return {
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
bashVerbs: /* @__PURE__ */ new Map(),
|
|
6823
|
-
skills: /* @__PURE__ */ new Map(),
|
|
6824
|
-
subagents: [],
|
|
6825
|
-
taskOps: 0,
|
|
6826
|
-
toolErrors: 0,
|
|
6827
|
-
consecSameToolMax: 0,
|
|
6828
|
-
consec: 0,
|
|
6829
|
-
endedInInterrupt: false,
|
|
6830
|
-
models: /* @__PURE__ */ new Map(),
|
|
6831
|
-
tokensIn: 0,
|
|
6832
|
-
tokensOut: 0,
|
|
6833
|
-
tokensCacheR: 0,
|
|
6834
|
-
tokensCacheW: 0,
|
|
6835
|
-
seenMsgIds: /* @__PURE__ */ new Set()
|
|
7097
|
+
paired: true,
|
|
7098
|
+
sentSnapshots: wire.length,
|
|
7099
|
+
insertedSnapshots: inserted,
|
|
7100
|
+
skippedSnapshots: skipped,
|
|
7101
|
+
batches: batches.length,
|
|
7102
|
+
lastSnapshotAt
|
|
6836
7103
|
};
|
|
6837
7104
|
}
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
firstAgentText: t.firstAgentText,
|
|
6851
|
-
lastAgentText: t.lastAgentText,
|
|
6852
|
-
toolsTotal: t.toolsTotal,
|
|
6853
|
-
toolMix: t.toolMix,
|
|
6854
|
-
bashVerbs: t.bashVerbs,
|
|
6855
|
-
skills: t.skills,
|
|
6856
|
-
subagents: t.subagents,
|
|
6857
|
-
taskOps: t.taskOps,
|
|
6858
|
-
toolErrors: t.toolErrors,
|
|
6859
|
-
consecSameToolMax: t.consecSameToolMax,
|
|
6860
|
-
endedInInterrupt: t.endedInInterrupt,
|
|
6861
|
-
models: t.models,
|
|
6862
|
-
totalTokens: t.tokensIn + t.tokensOut + t.tokensCacheR + t.tokensCacheW,
|
|
6863
|
-
cwd
|
|
6864
|
-
});
|
|
6865
|
-
}
|
|
6866
|
-
function aggregateDay(dayEvents, _sessionFallbackProject) {
|
|
6867
|
-
const prTitles = [];
|
|
6868
|
-
let commits = 0;
|
|
6869
|
-
let pushes = 0;
|
|
6870
|
-
const gitToolUseIds = /* @__PURE__ */ new Set();
|
|
6871
|
-
const githubRepos = /* @__PURE__ */ new Set();
|
|
6872
|
-
const editedDirCounts = /* @__PURE__ */ new Map();
|
|
6873
|
-
const modelTurns = /* @__PURE__ */ new Map();
|
|
6874
|
-
let totalToolErrors = 0;
|
|
6875
|
-
let totalInterrupts = 0;
|
|
6876
|
-
let exitPlanCalls = 0;
|
|
6877
|
-
let taskOpsTotal = 0;
|
|
6878
|
-
let firstUser;
|
|
6879
|
-
let finalAgent;
|
|
6880
|
-
const cwdCounts = /* @__PURE__ */ new Map();
|
|
6881
|
-
const closed = [];
|
|
6882
|
-
let cur;
|
|
6883
|
-
const allTs = [];
|
|
6884
|
-
let lastEventCwd;
|
|
6885
|
-
for (const ev of dayEvents) {
|
|
6886
|
-
const tsMs = parseMs(ev.timestamp);
|
|
6887
|
-
if (tsMs !== void 0)
|
|
6888
|
-
allTs.push(tsMs);
|
|
6889
|
-
const view = viewEvent(ev);
|
|
6890
|
-
const rawType = view.rawType;
|
|
6891
|
-
const msg = { content: view.content, id: view.msgId, model: view.model, usage: view.usage };
|
|
6892
|
-
const content = view.content;
|
|
6893
|
-
const eventCwd = view.cwd;
|
|
6894
|
-
if (eventCwd)
|
|
6895
|
-
lastEventCwd = eventCwd;
|
|
6896
|
-
if (rawType === "user") {
|
|
6897
|
-
const isToolResultOnly = Array.isArray(content) && content.length > 0 && content.every((c) => c && typeof c === "object" && c.type === "tool_result");
|
|
6898
|
-
if (!isToolResultOnly) {
|
|
6899
|
-
const text = firstTextBlock(content) || (typeof content === "string" ? content : "");
|
|
6900
|
-
if (cur) {
|
|
6901
|
-
if (text && INTERRUPT_RE2.test(text)) {
|
|
6902
|
-
cur.endedInInterrupt = true;
|
|
6903
|
-
totalInterrupts++;
|
|
6904
|
-
}
|
|
6905
|
-
closeTurn(cur, closed, eventCwd);
|
|
6906
|
-
}
|
|
6907
|
-
cur = newTurn(tsMs, text);
|
|
6908
|
-
if (!firstUser && text && classifyUserInputSource(text) === "human") {
|
|
6909
|
-
firstUser = text;
|
|
6910
|
-
}
|
|
6911
|
-
} else {
|
|
6912
|
-
if (Array.isArray(content)) {
|
|
6913
|
-
for (const c of content) {
|
|
6914
|
-
if (!c || typeof c !== "object" || c.type !== "tool_result")
|
|
6915
|
-
continue;
|
|
6916
|
-
if (c.is_error) {
|
|
6917
|
-
totalToolErrors++;
|
|
6918
|
-
if (cur)
|
|
6919
|
-
cur.toolErrors++;
|
|
6920
|
-
}
|
|
6921
|
-
const tuId = c.tool_use_id;
|
|
6922
|
-
if (tuId && gitToolUseIds.has(tuId)) {
|
|
6923
|
-
const rc = c.content;
|
|
6924
|
-
const text = typeof rc === "string" ? rc : Array.isArray(rc) ? rc.map((b) => b && typeof b === "object" && typeof b.text === "string" ? b.text : "").join("\n") : "";
|
|
6925
|
-
for (const m of text.matchAll(/(?<![.\w])github\.com[:/]([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+?)(?:\.git\b|[/\s"'),]|$)/g)) {
|
|
6926
|
-
if (m[1].toLowerCase() === "user-attachments")
|
|
6927
|
-
continue;
|
|
6928
|
-
githubRepos.add(`${m[1]}/${m[2]}`.toLowerCase());
|
|
6929
|
-
}
|
|
6930
|
-
}
|
|
6931
|
-
}
|
|
6932
|
-
}
|
|
6933
|
-
if (cur && tsMs !== void 0) {
|
|
6934
|
-
cur.eventTs.push(tsMs);
|
|
6935
|
-
cur.lastMs = tsMs;
|
|
6936
|
-
}
|
|
6937
|
-
}
|
|
6938
|
-
} else if (rawType === "assistant") {
|
|
6939
|
-
if (cur && tsMs !== void 0) {
|
|
6940
|
-
cur.eventTs.push(tsMs);
|
|
6941
|
-
cur.lastMs = tsMs;
|
|
6942
|
-
}
|
|
6943
|
-
const model = msg.model;
|
|
6944
|
-
if (model) {
|
|
6945
|
-
modelTurns.set(model, (modelTurns.get(model) ?? 0) + 1);
|
|
6946
|
-
if (cur)
|
|
6947
|
-
cur.models.set(model, (cur.models.get(model) ?? 0) + 1);
|
|
6948
|
-
}
|
|
6949
|
-
const msgId = msg.id;
|
|
6950
|
-
const usage = msg.usage;
|
|
6951
|
-
if (cur && msgId && !cur.seenMsgIds.has(msgId) && usage) {
|
|
6952
|
-
cur.seenMsgIds.add(msgId);
|
|
6953
|
-
cur.tokensIn += usage.input_tokens ?? 0;
|
|
6954
|
-
cur.tokensOut += usage.output_tokens ?? 0;
|
|
6955
|
-
cur.tokensCacheR += usage.cache_read_input_tokens ?? 0;
|
|
6956
|
-
cur.tokensCacheW += usage.cache_creation_input_tokens ?? 0;
|
|
6957
|
-
}
|
|
6958
|
-
if (Array.isArray(content)) {
|
|
6959
|
-
for (const c of content) {
|
|
6960
|
-
if (!c || typeof c !== "object")
|
|
6961
|
-
continue;
|
|
6962
|
-
const ct = c;
|
|
6963
|
-
if (ct.type === "text" && ct.text) {
|
|
6964
|
-
finalAgent = ct.text;
|
|
6965
|
-
if (cur) {
|
|
6966
|
-
if (cur.firstAgentText === void 0)
|
|
6967
|
-
cur.firstAgentText = ct.text;
|
|
6968
|
-
cur.lastAgentText = ct.text;
|
|
6969
|
-
}
|
|
6970
|
-
} else if (ct.type === "tool_use") {
|
|
6971
|
-
const name = ct.name ?? "unknown";
|
|
6972
|
-
if (cur) {
|
|
6973
|
-
cur.toolsTotal++;
|
|
6974
|
-
cur.toolMix.set(name, (cur.toolMix.get(name) ?? 0) + 1);
|
|
6975
|
-
if (name === cur.lastTool)
|
|
6976
|
-
cur.consec++;
|
|
6977
|
-
else {
|
|
6978
|
-
cur.lastTool = name;
|
|
6979
|
-
cur.consec = 1;
|
|
6980
|
-
}
|
|
6981
|
-
if (cur.consec > cur.consecSameToolMax)
|
|
6982
|
-
cur.consecSameToolMax = cur.consec;
|
|
6983
|
-
if (name === "Skill") {
|
|
6984
|
-
const sk = String(ct.input?.skill ?? "unknown");
|
|
6985
|
-
cur.skills.set(sk, (cur.skills.get(sk) ?? 0) + 1);
|
|
6986
|
-
}
|
|
6987
|
-
if (name === "Agent") {
|
|
6988
|
-
const inp = ct.input ?? {};
|
|
6989
|
-
cur.subagents.push({
|
|
6990
|
-
type: String(inp.subagent_type ?? "general-purpose"),
|
|
6991
|
-
description: trunc(String(inp.description ?? ""), 80),
|
|
6992
|
-
prompt_preview: trunc(String(inp.prompt ?? ""), 240),
|
|
6993
|
-
background: Boolean(inp.run_in_background)
|
|
6994
|
-
});
|
|
6995
|
-
}
|
|
6996
|
-
if (name === "ExitPlanMode")
|
|
6997
|
-
exitPlanCalls++;
|
|
6998
|
-
if (name === "TodoWrite" || name === "TaskCreate" || name === "TaskUpdate") {
|
|
6999
|
-
cur.taskOps++;
|
|
7000
|
-
taskOpsTotal++;
|
|
7001
|
-
}
|
|
7002
|
-
}
|
|
7003
|
-
if (name === "Bash") {
|
|
7004
|
-
const cmd = String(ct.input?.command ?? "");
|
|
7005
|
-
if (cur)
|
|
7006
|
-
cur.bashVerbs.set(bashVerb(cmd), (cur.bashVerbs.get(bashVerb(cmd)) ?? 0) + 1);
|
|
7007
|
-
if (/\bgh\s+pr\s+create\b/.test(cmd)) {
|
|
7008
|
-
const m = cmd.match(PR_TITLE_RE);
|
|
7009
|
-
prTitles.push(m?.[1] ?? cmd.slice(0, 120));
|
|
7010
|
-
}
|
|
7011
|
-
if (/\bgit\s+commit\b/.test(cmd))
|
|
7012
|
-
commits++;
|
|
7013
|
-
if (/\bgit\s+push\b/.test(cmd))
|
|
7014
|
-
pushes++;
|
|
7015
|
-
if (ct.id && /\bgit\s+push\b|\bgh\s+pr\s+/.test(cmd))
|
|
7016
|
-
gitToolUseIds.add(ct.id);
|
|
7017
|
-
}
|
|
7018
|
-
if (eventCwd && (name === "Bash" || name === "Edit" || name === "Write" || name === "Read")) {
|
|
7019
|
-
cwdCounts.set(eventCwd, (cwdCounts.get(eventCwd) ?? 0) + 1);
|
|
7020
|
-
}
|
|
7021
|
-
if (name === "Edit" || name === "Write" || name === "MultiEdit" || name === "NotebookEdit") {
|
|
7022
|
-
const fp = String(ct.input?.file_path ?? ct.input?.notebook_path ?? "");
|
|
7023
|
-
const slash = fp.lastIndexOf("/");
|
|
7024
|
-
if (slash > 0) {
|
|
7025
|
-
const dir = fp.slice(0, slash);
|
|
7026
|
-
editedDirCounts.set(dir, (editedDirCounts.get(dir) ?? 0) + 1);
|
|
7027
|
-
}
|
|
7028
|
-
}
|
|
7029
|
-
}
|
|
7030
|
-
}
|
|
7031
|
-
}
|
|
7032
|
-
}
|
|
7105
|
+
|
|
7106
|
+
// src/team/sync-log.ts
|
|
7107
|
+
init_fs();
|
|
7108
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
7109
|
+
var MAX_LINES = 300;
|
|
7110
|
+
var MAX_MSG_CHARS = 1900;
|
|
7111
|
+
function readPendingSyncLog(watermark) {
|
|
7112
|
+
let raw;
|
|
7113
|
+
try {
|
|
7114
|
+
raw = readFileSync10(cclensPath("daemon.log"), "utf8");
|
|
7115
|
+
} catch {
|
|
7116
|
+
return { lines: [] };
|
|
7033
7117
|
}
|
|
7034
|
-
|
|
7035
|
-
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
if (
|
|
7041
|
-
|
|
7118
|
+
const lines = [];
|
|
7119
|
+
for (const line of raw.split("\n")) {
|
|
7120
|
+
const m = line.match(/^(\S+) (INFO|WARN|ERROR) (.*)$/);
|
|
7121
|
+
if (!m) continue;
|
|
7122
|
+
const [, ts, levelRaw, msg] = m;
|
|
7123
|
+
if (!msg.startsWith("[sync] ")) continue;
|
|
7124
|
+
if (watermark && ts <= watermark) continue;
|
|
7125
|
+
const clipped = msg.length > MAX_MSG_CHARS ? msg.slice(0, MAX_MSG_CHARS) + "\u2026" : msg;
|
|
7126
|
+
lines.push({ ts, level: levelRaw.toLowerCase(), msg: clipped });
|
|
7042
7127
|
}
|
|
7043
|
-
const
|
|
7044
|
-
|
|
7045
|
-
|
|
7128
|
+
const capped = lines.slice(0, MAX_LINES);
|
|
7129
|
+
return { lines: capped, watermark: capped.length ? capped[capped.length - 1].ts : void 0 };
|
|
7130
|
+
}
|
|
7131
|
+
|
|
7132
|
+
// src/team/last-push.ts
|
|
7133
|
+
init_fs();
|
|
7134
|
+
import { writeFileSync as writeFileSync6, unlinkSync as unlinkSync2, mkdirSync as mkdirSync7 } from "node:fs";
|
|
7135
|
+
import { join as join6 } from "node:path";
|
|
7136
|
+
var LAST_PUSH_FILE = "team-last-push.json";
|
|
7137
|
+
function write(record, dir) {
|
|
7138
|
+
const d = dir ?? cclensHome();
|
|
7139
|
+
mkdirSync7(d, { recursive: true });
|
|
7140
|
+
writeFileSync6(join6(d, LAST_PUSH_FILE), JSON.stringify(record, null, 2), { mode: 384 });
|
|
7141
|
+
}
|
|
7142
|
+
function writeLastPushSuccess(payload, dir) {
|
|
7143
|
+
write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: true, payload }, dir);
|
|
7144
|
+
}
|
|
7145
|
+
function writeLastPushFailure(payload, error, dir) {
|
|
7146
|
+
write({ pushedAt: (/* @__PURE__ */ new Date()).toISOString(), ok: false, payload, error }, dir);
|
|
7147
|
+
}
|
|
7148
|
+
|
|
7149
|
+
// src/team/commands.ts
|
|
7150
|
+
async function dispatchCommand(command, config, log3) {
|
|
7151
|
+
switch (command.type) {
|
|
7152
|
+
case "backfill-activity":
|
|
7153
|
+
return runActivityBackfill(command, config, log3);
|
|
7154
|
+
default:
|
|
7155
|
+
return {
|
|
7156
|
+
id: command.id,
|
|
7157
|
+
ok: false,
|
|
7158
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7159
|
+
error: `Unknown command type: ${command.type}`
|
|
7160
|
+
};
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
async function runActivityBackfill(command, config, log3) {
|
|
7164
|
+
const { listSessions: listSessions2, shouldSyncProject: shouldSyncProject2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
7165
|
+
const { toLocalDay: toLocalDay3, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7166
|
+
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
7167
|
+
const days = command.params.days;
|
|
7168
|
+
const todayMs = Date.now();
|
|
7169
|
+
const targetDayMs = todayMs - (days - 1) * 24 * 60 * 60 * 1e3;
|
|
7170
|
+
const targetDay = toLocalDay3(targetDayMs);
|
|
7171
|
+
log3("info", `command ${command.id}: backfill-activity from ${targetDay} (${days} days)`);
|
|
7172
|
+
let sessions;
|
|
7173
|
+
try {
|
|
7174
|
+
sessions = filterSyncedSessions(await listSessions2({ limit: 1e4 }), config.syncProjects);
|
|
7175
|
+
} catch (err) {
|
|
7176
|
+
return {
|
|
7177
|
+
id: command.id,
|
|
7178
|
+
ok: false,
|
|
7179
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7180
|
+
error: `Failed to read sessions: ${err.message}`
|
|
7181
|
+
};
|
|
7182
|
+
}
|
|
7183
|
+
const rollups = buildRollupsForRange(sessions, targetDay);
|
|
7184
|
+
if (rollups.length === 0) {
|
|
7185
|
+
return {
|
|
7186
|
+
id: command.id,
|
|
7187
|
+
ok: true,
|
|
7188
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7189
|
+
summary: { pushed: 0, fromDay: targetDay }
|
|
7190
|
+
};
|
|
7191
|
+
}
|
|
7192
|
+
const resolveRepo = createRepoResolver();
|
|
7193
|
+
let pushed = 0;
|
|
7194
|
+
for (const rollup of rollups) {
|
|
7195
|
+
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
7196
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
7197
|
+
let artifactSignals = null;
|
|
7198
|
+
try {
|
|
7199
|
+
const cwdAllowed = shouldSyncProject2(projectRepoName2(process.cwd()), config.syncProjects);
|
|
7200
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
|
|
7201
|
+
} catch {
|
|
7202
|
+
}
|
|
7203
|
+
const payload = buildIngestPayload({
|
|
7204
|
+
rollup,
|
|
7205
|
+
richExtras: richBlocks?.rich,
|
|
7206
|
+
enrichedExtras: richBlocks?.enriched,
|
|
7207
|
+
artifactSignals: artifactSignals ?? void 0
|
|
7208
|
+
});
|
|
7209
|
+
const result = await pushToTeamServer(config, payload);
|
|
7210
|
+
if (!result.ok) {
|
|
7211
|
+
return {
|
|
7212
|
+
id: command.id,
|
|
7213
|
+
ok: false,
|
|
7214
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7215
|
+
error: `Push failed on ${rollup.day} (HTTP ${result.status}); pushed ${pushed}/${rollups.length} before failing`
|
|
7216
|
+
};
|
|
7217
|
+
}
|
|
7218
|
+
pushed++;
|
|
7219
|
+
}
|
|
7220
|
+
log3("info", `command ${command.id}: backfill-activity ok \u2014 pushed ${pushed} day${pushed === 1 ? "" : "s"}`);
|
|
7046
7221
|
return {
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
commits,
|
|
7052
|
-
pushes,
|
|
7053
|
-
githubRepos: [...githubRepos].slice(0, 5),
|
|
7054
|
-
editedDirs: [...editedDirCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 8).map(([dir]) => dir),
|
|
7055
|
-
modelTurns,
|
|
7056
|
-
totalToolErrors,
|
|
7057
|
-
totalInterrupts,
|
|
7058
|
-
exitPlanCalls,
|
|
7059
|
-
taskOpsTotal,
|
|
7060
|
-
cwdCounts,
|
|
7061
|
-
start_iso,
|
|
7062
|
-
end_iso,
|
|
7063
|
-
activeMs: activeMs2
|
|
7222
|
+
id: command.id,
|
|
7223
|
+
ok: true,
|
|
7224
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7225
|
+
summary: { pushed, fromDay: targetDay }
|
|
7064
7226
|
};
|
|
7065
7227
|
}
|
|
7066
|
-
|
|
7067
|
-
|
|
7068
|
-
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7228
|
+
|
|
7229
|
+
// src/team/sync.ts
|
|
7230
|
+
init_fs();
|
|
7231
|
+
var inFlightCommands = /* @__PURE__ */ new Set();
|
|
7232
|
+
var USAGE_LOG2 = cclensPath("usage.jsonl");
|
|
7233
|
+
var PROFILE_CACHE2 = cclensPath("profile.json");
|
|
7234
|
+
var noopLog2 = () => {
|
|
7235
|
+
};
|
|
7236
|
+
function syncLogDelivered(body) {
|
|
7237
|
+
const blocks = body?.blocks;
|
|
7238
|
+
return !blocks || blocks.accepted.includes("syncLog");
|
|
7239
|
+
}
|
|
7240
|
+
function levelForStatus(s) {
|
|
7241
|
+
if (s === "ok" || s === "idle") return "info";
|
|
7242
|
+
if (s === "degraded" || s === "failed") return "warn";
|
|
7243
|
+
return "error";
|
|
7244
|
+
}
|
|
7245
|
+
function fmtDuration(ms) {
|
|
7246
|
+
return ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${ms}ms`;
|
|
7247
|
+
}
|
|
7248
|
+
function liveSnapshotStr(snap) {
|
|
7249
|
+
if (!snap) return void 0;
|
|
7250
|
+
const f = snap.fiveHour?.utilization;
|
|
7251
|
+
const s = snap.sevenDay?.utilization;
|
|
7252
|
+
const parts = [
|
|
7253
|
+
typeof f === "number" ? `5h ${Math.round(f)}%` : null,
|
|
7254
|
+
typeof s === "number" ? `7d ${Math.round(s)}%` : null
|
|
7255
|
+
].filter(Boolean);
|
|
7256
|
+
return parts.length ? parts.join(" / ") : void 0;
|
|
7257
|
+
}
|
|
7258
|
+
function buildSyncLine(status, trigger, s, elapsedMs, nextSyncMs) {
|
|
7259
|
+
const parts = [`[sync] ${status}`, trigger];
|
|
7260
|
+
if (s.pushedDays.length) {
|
|
7261
|
+
parts.push(
|
|
7262
|
+
s.pushedDays.length === 1 ? `pushed 1 day (${s.pushedDays[0]})` : `pushed ${s.pushedDays.length} days (${s.pushedDays[0]}\u2192${s.pushedDays[s.pushedDays.length - 1]})`
|
|
7263
|
+
);
|
|
7074
7264
|
}
|
|
7075
|
-
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7082
|
-
}
|
|
7265
|
+
if (s.queued) {
|
|
7266
|
+
parts.push(
|
|
7267
|
+
s.queuedDay ? `queued ${s.queued} for retry (${s.queuedDay}, HTTP ${s.queuedStatus})` : `queued ${s.queued} for retry`
|
|
7268
|
+
);
|
|
7269
|
+
}
|
|
7270
|
+
if (s.droppedDays.length) {
|
|
7271
|
+
parts.push(`dropped ${s.droppedDays.length} unrecoverable (${s.droppedDays.join(",")})`);
|
|
7272
|
+
}
|
|
7273
|
+
if (s.recoveredDays?.length) {
|
|
7274
|
+
parts.push(
|
|
7275
|
+
`recovered ${s.recoveredDays.length} dropped day${s.recoveredDays.length === 1 ? "" : "s"} (${s.recoveredDays.join(",")})`
|
|
7276
|
+
);
|
|
7277
|
+
}
|
|
7278
|
+
if (s.queuedDrained) parts.push(`drained ${s.queuedDrained} from backlog`);
|
|
7279
|
+
if (s.idleReason) parts.push(s.idleReason);
|
|
7280
|
+
if (s.live) parts.push(`live ${s.live}`);
|
|
7281
|
+
if (s.usageSnapshots > 0) {
|
|
7282
|
+
parts.push(`usage +${s.usageSnapshots} snapshot${s.usageSnapshots === 1 ? "" : "s"}`);
|
|
7283
|
+
}
|
|
7284
|
+
if (s.accepted?.length) parts.push(`server accepted ${s.accepted.join(",")}`);
|
|
7285
|
+
if (s.skipped && Object.keys(s.skipped).length) {
|
|
7286
|
+
parts.push(
|
|
7287
|
+
`server SKIPPED ${Object.entries(s.skipped).map(([k, v]) => `${k} (${v})`).join("; ")}`
|
|
7288
|
+
);
|
|
7289
|
+
}
|
|
7290
|
+
if (s.errorMsg) parts.push(s.errorMsg);
|
|
7291
|
+
parts.push(fmtDuration(elapsedMs));
|
|
7292
|
+
if (nextSyncMs) parts.push(`next ~${Math.round(nextSyncMs / 6e4)}m`);
|
|
7293
|
+
return parts.join(" \xB7 ");
|
|
7083
7294
|
}
|
|
7084
|
-
function
|
|
7085
|
-
const
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7295
|
+
async function runTeamSync(log3 = noopLog2, configOverride, options = {}) {
|
|
7296
|
+
const config = configOverride === void 0 ? readTeamConfig() : configOverride;
|
|
7297
|
+
if (!config) return { paired: false, pushed: 0, queued: 0, queuedDrained: 0 };
|
|
7298
|
+
if (config.setupPending) {
|
|
7299
|
+
log3(
|
|
7300
|
+
"info",
|
|
7301
|
+
buildSyncLine(
|
|
7302
|
+
"idle",
|
|
7303
|
+
options.trigger ?? "auto",
|
|
7304
|
+
{
|
|
7305
|
+
pushedDays: [],
|
|
7306
|
+
droppedDays: [],
|
|
7307
|
+
queued: 0,
|
|
7308
|
+
queuedDrained: 0,
|
|
7309
|
+
usageSnapshots: 0,
|
|
7310
|
+
idleReason: "setup pending \u2014 finish onboarding in the dashboard (/team/onboarding)"
|
|
7311
|
+
},
|
|
7312
|
+
0,
|
|
7313
|
+
options.nextSyncMs
|
|
7314
|
+
)
|
|
7315
|
+
);
|
|
7316
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, setupPending: true };
|
|
7317
|
+
}
|
|
7318
|
+
const startedAt = Date.now();
|
|
7319
|
+
const trigger = options.trigger ?? "auto";
|
|
7320
|
+
const summary = {
|
|
7321
|
+
pushedDays: [],
|
|
7322
|
+
droppedDays: [],
|
|
7323
|
+
queued: 0,
|
|
7324
|
+
queuedDrained: 0,
|
|
7325
|
+
usageSnapshots: 0
|
|
7326
|
+
};
|
|
7327
|
+
const finish = (status) => {
|
|
7328
|
+
log3(
|
|
7329
|
+
levelForStatus(status),
|
|
7330
|
+
buildSyncLine(status, trigger, summary, Date.now() - startedAt, options.nextSyncMs)
|
|
7331
|
+
);
|
|
7332
|
+
};
|
|
7333
|
+
const recordVerdict = (body) => {
|
|
7334
|
+
if (body?.blocks) {
|
|
7335
|
+
summary.accepted = [.../* @__PURE__ */ new Set([...summary.accepted ?? [], ...body.blocks.accepted])];
|
|
7336
|
+
summary.skipped = { ...summary.skipped, ...body.blocks.skipped };
|
|
7102
7337
|
}
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
7108
|
-
const
|
|
7109
|
-
const
|
|
7110
|
-
const
|
|
7111
|
-
|
|
7112
|
-
const
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7338
|
+
};
|
|
7339
|
+
const emit = options.onProgress ?? (() => {
|
|
7340
|
+
});
|
|
7341
|
+
try {
|
|
7342
|
+
resetEnsuredSessions();
|
|
7343
|
+
const { listSessions: listSessions2, loadCalibrationCurve: loadCalibrationCurve2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
7344
|
+
const { toLocalDay: toLocalDay3, projectRepoName: projectRepoName2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7345
|
+
const today = toLocalDay3(Date.now());
|
|
7346
|
+
let patches = {};
|
|
7347
|
+
const persistConfig = (patch) => {
|
|
7348
|
+
patches = { ...patches, ...patch };
|
|
7349
|
+
const onDisk = readTeamConfig();
|
|
7350
|
+
if (!onDisk) return;
|
|
7351
|
+
writeTeamConfig({ ...onDisk, ...patches });
|
|
7352
|
+
};
|
|
7353
|
+
const pendingLog = readPendingSyncLog(config.lastSyncedLogAt);
|
|
7354
|
+
const collectedCommands = /* @__PURE__ */ new Map();
|
|
7355
|
+
const collectCommands = (commands) => {
|
|
7356
|
+
if (!commands) return;
|
|
7357
|
+
for (const cmd of commands) {
|
|
7358
|
+
if (!collectedCommands.has(cmd.id)) collectedCommands.set(cmd.id, cmd);
|
|
7359
|
+
}
|
|
7360
|
+
};
|
|
7361
|
+
const dispatchAndReport = async () => {
|
|
7362
|
+
try {
|
|
7363
|
+
if (collectedCommands.size === 0) return;
|
|
7364
|
+
const results = [];
|
|
7365
|
+
for (const cmd of collectedCommands.values()) {
|
|
7366
|
+
if (inFlightCommands.has(cmd.id)) continue;
|
|
7367
|
+
inFlightCommands.add(cmd.id);
|
|
7368
|
+
try {
|
|
7369
|
+
const result = await dispatchCommand(cmd, config, log3);
|
|
7370
|
+
results.push(result);
|
|
7371
|
+
} finally {
|
|
7372
|
+
inFlightCommands.delete(cmd.id);
|
|
7373
|
+
}
|
|
7374
|
+
}
|
|
7375
|
+
if (results.length === 0) return;
|
|
7376
|
+
const resultsPayload = {
|
|
7377
|
+
ingestId: randomUUID3(),
|
|
7378
|
+
observedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7379
|
+
commandResults: results
|
|
7380
|
+
};
|
|
7381
|
+
const r = await pushToTeamServer(config, resultsPayload);
|
|
7382
|
+
if (!r.ok) {
|
|
7383
|
+
log3("warn", `team commandResults push failed (${r.status}); will retry on next sync`);
|
|
7384
|
+
} else {
|
|
7385
|
+
log3("info", `team commandResults push ok: ${results.length} result${results.length === 1 ? "" : "s"}`);
|
|
7386
|
+
}
|
|
7387
|
+
} catch (err) {
|
|
7388
|
+
log3("warn", `team command dispatch error: ${err.message}`);
|
|
7389
|
+
}
|
|
7390
|
+
};
|
|
7391
|
+
const isValidationPoison = (status2) => status2 === 400 || status2 === 422;
|
|
7392
|
+
const drainBacklog = async () => {
|
|
7393
|
+
let drained = 0;
|
|
7394
|
+
const backlog = dequeuePayloads();
|
|
7395
|
+
for (let i = 0; i < backlog.length; i++) {
|
|
7396
|
+
const queuedPayload = backlog[i];
|
|
7397
|
+
if (queuedPayload.richRollup?.projects?.some((p) => !shouldSyncProject(p.project, config.syncProjects))) {
|
|
7398
|
+
log3("info", "team push: dropped queued payload containing now-excluded project(s)");
|
|
7399
|
+
continue;
|
|
7400
|
+
}
|
|
7401
|
+
const qResult = await pushToTeamServer(config, queuedPayload);
|
|
7402
|
+
if (!qResult.ok) {
|
|
7403
|
+
if (isValidationPoison(qResult.status)) {
|
|
7404
|
+
log3("warn", `team push: queued payload rejected (HTTP ${qResult.status}); dropping unrecoverable item`);
|
|
7405
|
+
continue;
|
|
7406
|
+
}
|
|
7407
|
+
for (const remaining of backlog.slice(i)) enqueuePayload(remaining);
|
|
7408
|
+
break;
|
|
7409
|
+
}
|
|
7410
|
+
collectCommands(qResult.body?.commands);
|
|
7411
|
+
drained++;
|
|
7412
|
+
}
|
|
7413
|
+
return drained;
|
|
7128
7414
|
};
|
|
7129
|
-
|
|
7130
|
-
const
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
flags.push("plan_used");
|
|
7141
|
-
const subagentTurns = closed.filter((t) => t.subagents.length >= 1).length;
|
|
7142
|
-
if (subagentTurns >= 3)
|
|
7143
|
-
flags.push("orchestrated");
|
|
7144
|
-
if (longestTurnActiveMs >= 20 * 6e4 && interrupts === 0)
|
|
7145
|
-
flags.push("long_autonomous");
|
|
7146
|
-
const primaryModel = [...agg.modelTurns.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))[0]?.[0] ?? null;
|
|
7147
|
-
const modelMix = Object.fromEntries(agg.modelTurns);
|
|
7148
|
-
const firstUserRaw = agg.firstUser ?? "";
|
|
7149
|
-
const first_user = trunc(minimizeImages(firstUserRaw), 400);
|
|
7150
|
-
const finalAgentRaw = agg.finalAgent ?? "";
|
|
7151
|
-
const final_agent = trunc(minimizeImages(finalAgentRaw), 400);
|
|
7152
|
-
let project = sessionFallbackProject;
|
|
7153
|
-
if (agg.cwdCounts.size > 0) {
|
|
7154
|
-
const best = [...agg.cwdCounts.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
7155
|
-
project = canonicalProjectName(best[0]);
|
|
7415
|
+
emit({ type: "phase", phase: "usage-backfill" });
|
|
7416
|
+
const usageBackfill = await runTeamBackfill(log3, USAGE_LOG2, config, {
|
|
7417
|
+
sinceCapturedAt: options.forceUsageBackfill ? void 0 : config.lastSyncedUsageSnapshotAt
|
|
7418
|
+
});
|
|
7419
|
+
emit({
|
|
7420
|
+
type: "usage",
|
|
7421
|
+
inserted: usageBackfill.insertedSnapshots ?? 0,
|
|
7422
|
+
alreadyKnown: usageBackfill.skippedSnapshots ?? 0
|
|
7423
|
+
});
|
|
7424
|
+
if (usageBackfill.lastSnapshotAt) {
|
|
7425
|
+
persistConfig({ lastSyncedUsageSnapshotAt: usageBackfill.lastSnapshotAt });
|
|
7156
7426
|
}
|
|
7157
|
-
const
|
|
7158
|
-
const
|
|
7159
|
-
const
|
|
7160
|
-
const
|
|
7161
|
-
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
const
|
|
7165
|
-
|
|
7166
|
-
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
};
|
|
7179
|
-
for (const ev of dayEvents) {
|
|
7180
|
-
const v = viewEvent(ev);
|
|
7181
|
-
if (v.rawType !== "user")
|
|
7182
|
-
continue;
|
|
7183
|
-
const content = v.content;
|
|
7184
|
-
const isToolResultOnly = content.length > 0 && content.every((c) => c.type === "tool_result");
|
|
7185
|
-
if (isToolResultOnly)
|
|
7186
|
-
continue;
|
|
7187
|
-
const text = blockText(ev.blocks) || ev.preview || "";
|
|
7188
|
-
const src = classifyUserInputSource(text);
|
|
7189
|
-
user_input_sources[src]++;
|
|
7427
|
+
const unfilteredSessions = await listSessions2({ limit: 1e4 });
|
|
7428
|
+
const sessions = filterSyncedSessions(unfilteredSessions, config.syncProjects);
|
|
7429
|
+
const allRollups = buildRollupsForRange(sessions);
|
|
7430
|
+
const inWindow = (day) => !config.lastSyncedDay || day >= config.lastSyncedDay;
|
|
7431
|
+
const rollups = allRollups.filter((r) => inWindow(r.day));
|
|
7432
|
+
if (config.syncProjects) {
|
|
7433
|
+
const filteredDays = new Set(allRollups.map((r) => r.day));
|
|
7434
|
+
for (const r of buildRollupsForRange(unfilteredSessions)) {
|
|
7435
|
+
if (!filteredDays.has(r.day) && inWindow(r.day)) {
|
|
7436
|
+
rollups.push({
|
|
7437
|
+
day: r.day,
|
|
7438
|
+
agentTimeMs: 0,
|
|
7439
|
+
sessions: 0,
|
|
7440
|
+
uniqueSessions: 0,
|
|
7441
|
+
toolCalls: 0,
|
|
7442
|
+
turns: 0,
|
|
7443
|
+
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
7444
|
+
});
|
|
7445
|
+
}
|
|
7446
|
+
}
|
|
7447
|
+
rollups.sort((a, b) => a.day < b.day ? -1 : 1);
|
|
7190
7448
|
}
|
|
7191
|
-
|
|
7192
|
-
const
|
|
7193
|
-
const
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7449
|
+
emit({ type: "phase", phase: "activity", totalDays: rollups.length });
|
|
7450
|
+
const usageSnapshot = readLatestUsageSnapshotForWire(USAGE_LOG2) ?? void 0;
|
|
7451
|
+
const planTier = await getPlanTier(PROFILE_CACHE2).catch(() => null) ?? void 0;
|
|
7452
|
+
const cyclePeaks = await buildCyclePeaksForPush(planTier, loadCalibrationCurve2);
|
|
7453
|
+
if (rollups.length === 0) {
|
|
7454
|
+
const hasLiveData = Boolean(usageSnapshot || planTier || cyclePeaks);
|
|
7455
|
+
if (!hasLiveData) {
|
|
7456
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7457
|
+
if (usageBackfill.sentSnapshots > 0) {
|
|
7458
|
+
finish("ok");
|
|
7459
|
+
} else {
|
|
7460
|
+
summary.idleReason = "nothing to sync";
|
|
7461
|
+
finish("idle");
|
|
7462
|
+
}
|
|
7463
|
+
emit({ type: "done", pushed: 0, queued: 0, pushedDays: summary.pushedDays });
|
|
7464
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
7465
|
+
}
|
|
7466
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7467
|
+
summary.live = liveSnapshotStr(usageSnapshot);
|
|
7468
|
+
const payload = buildIngestPayload({ usageSnapshot, planTier, cyclePeaks, syncLog: pendingLog.lines });
|
|
7469
|
+
const result = await pushToTeamServer(config, payload);
|
|
7470
|
+
if (!result.ok) {
|
|
7471
|
+
if (isValidationPoison(result.status)) {
|
|
7472
|
+
const errLine2 = `team push: live-only payload rejected (HTTP ${result.status}); dropping unrecoverable payload`;
|
|
7473
|
+
writeLastPushFailure(payload, errLine2);
|
|
7474
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} (validation) \u2014 dropped unrecoverable`;
|
|
7475
|
+
finish("error");
|
|
7476
|
+
emit({ type: "done", pushed: 0, queued: 0, pushedDays: summary.pushedDays });
|
|
7477
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, usageBackfill };
|
|
7478
|
+
}
|
|
7479
|
+
const errLine = `team push failed (${result.status})`;
|
|
7480
|
+
writeLastPushFailure(payload, errLine);
|
|
7481
|
+
enqueuePayload(payload);
|
|
7482
|
+
summary.queued = 1;
|
|
7483
|
+
summary.errorMsg = `live-snapshot push HTTP ${result.status} \u2014 queued for retry`;
|
|
7484
|
+
finish("failed");
|
|
7485
|
+
emit({ type: "done", pushed: 0, queued: 1, pushedDays: summary.pushedDays });
|
|
7486
|
+
return { paired: true, pushed: 0, queued: 1, queuedDrained: 0, usageBackfill };
|
|
7487
|
+
}
|
|
7488
|
+
writeLastPushSuccess(payload);
|
|
7489
|
+
if (pendingLog.watermark && syncLogDelivered(result.body)) {
|
|
7490
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
7491
|
+
}
|
|
7492
|
+
collectCommands(result.body?.commands);
|
|
7493
|
+
recordVerdict(result.body);
|
|
7494
|
+
const queuedDrained2 = await drainBacklog();
|
|
7495
|
+
summary.queuedDrained = queuedDrained2;
|
|
7496
|
+
summary.idleReason = "no new daily activity";
|
|
7497
|
+
finish(summary.skipped && Object.keys(summary.skipped).length ? "degraded" : "idle");
|
|
7498
|
+
await dispatchAndReport();
|
|
7499
|
+
emit({ type: "done", pushed: 1, queued: 0, pushedDays: summary.pushedDays });
|
|
7500
|
+
return { paired: true, pushed: 1, queued: 0, queuedDrained: queuedDrained2, usageBackfill };
|
|
7501
|
+
}
|
|
7502
|
+
let pushed = 0;
|
|
7503
|
+
let queued = 0;
|
|
7504
|
+
let failedDay;
|
|
7505
|
+
const droppedDaysBefore = config.droppedDays ?? [];
|
|
7506
|
+
const workingDropped = [...droppedDaysBefore];
|
|
7507
|
+
let syncLogWasDelivered = false;
|
|
7508
|
+
const resolveRepo = createRepoResolver();
|
|
7509
|
+
const { probeArtifactSignals: probeArtifactSignals2 } = await Promise.resolve().then(() => (init_file_probe(), file_probe_exports));
|
|
7510
|
+
const buildDayPayload = (rollup, opts) => {
|
|
7511
|
+
const daySessions = sessions.filter((s) => sessionTouchesDay(s, rollup.day));
|
|
7512
|
+
const richBlocks = buildRichBlocksForDay(rollup.day, daySessions, resolveRepo);
|
|
7513
|
+
const tombstone = daySessions.length === 0 && !!config.syncProjects;
|
|
7514
|
+
const emptyRich = tombstone ? {
|
|
7515
|
+
projects: [],
|
|
7516
|
+
workingShapes: [],
|
|
7517
|
+
concurrencyPeak: 0,
|
|
7518
|
+
parallelMinutes: 0,
|
|
7519
|
+
longAutonomous: { count: 0, totalMin: 0, maxSingleMin: 0 },
|
|
7520
|
+
toolErrors: 0,
|
|
7521
|
+
skillsLoaded: [],
|
|
7522
|
+
subagentsDispatched: [],
|
|
7523
|
+
brainstormWarmupSessions: 0,
|
|
7524
|
+
planModeUsed: 0,
|
|
7525
|
+
prs: 0,
|
|
7526
|
+
commits: 0,
|
|
7527
|
+
pushes: 0
|
|
7528
|
+
} : void 0;
|
|
7529
|
+
let artifactSignals = null;
|
|
7530
|
+
try {
|
|
7531
|
+
const cwdAllowed = shouldSyncProject(projectRepoName2(process.cwd()), config.syncProjects);
|
|
7532
|
+
artifactSignals = probeArtifactSignals2({ day: rollup.day, extraRoots: cwdAllowed ? [process.cwd()] : [] });
|
|
7533
|
+
} catch {
|
|
7534
|
+
}
|
|
7535
|
+
return buildIngestPayload({
|
|
7536
|
+
rollup,
|
|
7537
|
+
richExtras: richBlocks?.rich ?? emptyRich,
|
|
7538
|
+
enrichedExtras: richBlocks?.enriched ?? (tombstone ? { outcomeMix: {}, helpfulnessMix: {}, goalMix: {} } : void 0),
|
|
7539
|
+
artifactSignals: artifactSignals ?? void 0,
|
|
7540
|
+
// Current-cycle data only on the latest day so older days aren't tagged
|
|
7541
|
+
// with today's snapshot / peaks.
|
|
7542
|
+
usageSnapshot: opts.latest ? usageSnapshot : void 0,
|
|
7543
|
+
planTier,
|
|
7544
|
+
cyclePeaks: opts.latest ? cyclePeaks : void 0,
|
|
7545
|
+
syncLog: opts.syncLog
|
|
7546
|
+
});
|
|
7219
7547
|
};
|
|
7220
|
-
|
|
7221
|
-
|
|
7548
|
+
const rollupsDesc = [...rollups].reverse();
|
|
7549
|
+
for (let i = 0; i < rollupsDesc.length; i++) {
|
|
7550
|
+
const rollup = rollupsDesc[i];
|
|
7551
|
+
const isLatest = i === 0;
|
|
7552
|
+
const payload = buildDayPayload(rollup, {
|
|
7553
|
+
latest: isLatest,
|
|
7554
|
+
syncLog: i === 0 ? pendingLog.lines : void 0
|
|
7555
|
+
});
|
|
7556
|
+
const result = await pushToTeamServer(config, payload);
|
|
7557
|
+
if (!result.ok) {
|
|
7558
|
+
if (isValidationPoison(result.status)) {
|
|
7559
|
+
const errLine2 = `team push: ${rollup.day} rejected (HTTP ${result.status}); skipping past unrecoverable day`;
|
|
7560
|
+
writeLastPushFailure(payload, errLine2);
|
|
7561
|
+
summary.droppedDays.push(rollup.day);
|
|
7562
|
+
if (!workingDropped.includes(rollup.day)) workingDropped.push(rollup.day);
|
|
7563
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "dropped" });
|
|
7564
|
+
continue;
|
|
7565
|
+
}
|
|
7566
|
+
const errLine = `team push failed on ${rollup.day} (${result.status})`;
|
|
7567
|
+
writeLastPushFailure(payload, errLine);
|
|
7568
|
+
enqueuePayload(payload);
|
|
7569
|
+
queued++;
|
|
7570
|
+
summary.queuedDay = rollup.day;
|
|
7571
|
+
summary.queuedStatus = result.status;
|
|
7572
|
+
failedDay = rollup.day;
|
|
7573
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "queued" });
|
|
7574
|
+
break;
|
|
7575
|
+
}
|
|
7576
|
+
writeLastPushSuccess(payload);
|
|
7577
|
+
collectCommands(result.body?.commands);
|
|
7578
|
+
recordVerdict(result.body);
|
|
7579
|
+
if (i === 0) syncLogWasDelivered = syncLogDelivered(result.body);
|
|
7580
|
+
pushed++;
|
|
7581
|
+
summary.pushedDays.push(rollup.day);
|
|
7582
|
+
emit({ type: "day", day: rollup.day, index: i + 1, total: rollupsDesc.length, outcome: "pushed" });
|
|
7583
|
+
}
|
|
7584
|
+
if (!failedDay) {
|
|
7585
|
+
const retryDay = [...droppedDaysBefore].sort().find((d) => !summary.droppedDays.includes(d));
|
|
7586
|
+
if (retryDay && summary.pushedDays.includes(retryDay)) {
|
|
7587
|
+
const idx = workingDropped.indexOf(retryDay);
|
|
7588
|
+
if (idx >= 0) workingDropped.splice(idx, 1);
|
|
7589
|
+
summary.recoveredDays = [retryDay];
|
|
7590
|
+
}
|
|
7591
|
+
const retryRollup = retryDay && !summary.pushedDays.includes(retryDay) ? allRollups.find((r) => r.day === retryDay) ?? (config.syncProjects ? {
|
|
7592
|
+
day: retryDay,
|
|
7593
|
+
agentTimeMs: 0,
|
|
7594
|
+
sessions: 0,
|
|
7595
|
+
uniqueSessions: 0,
|
|
7596
|
+
toolCalls: 0,
|
|
7597
|
+
turns: 0,
|
|
7598
|
+
tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
7599
|
+
} : void 0) : void 0;
|
|
7600
|
+
if (retryDay && retryRollup) {
|
|
7601
|
+
const payload = buildDayPayload(retryRollup, { latest: false });
|
|
7602
|
+
const result = await pushToTeamServer(config, payload);
|
|
7603
|
+
if (result.ok) {
|
|
7604
|
+
writeLastPushSuccess(payload);
|
|
7605
|
+
collectCommands(result.body?.commands);
|
|
7606
|
+
recordVerdict(result.body);
|
|
7607
|
+
const idx = workingDropped.indexOf(retryDay);
|
|
7608
|
+
if (idx >= 0) workingDropped.splice(idx, 1);
|
|
7609
|
+
summary.recoveredDays = [retryDay];
|
|
7610
|
+
} else {
|
|
7611
|
+
writeLastPushFailure(payload, `team push: dropped-day retry ${retryDay} failed (HTTP ${result.status})`);
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7614
|
+
}
|
|
7615
|
+
const nextDropped = [...new Set(workingDropped)].sort().slice(-90);
|
|
7616
|
+
const prevDropped = [...new Set(droppedDaysBefore)].sort();
|
|
7617
|
+
const droppedChanged = nextDropped.length !== prevDropped.length || nextDropped.some((d, i) => d !== prevDropped[i]);
|
|
7618
|
+
const droppedPatch = droppedChanged ? { droppedDays: nextDropped } : {};
|
|
7619
|
+
if (failedDay) {
|
|
7620
|
+
if (droppedChanged) persistConfig(droppedPatch);
|
|
7621
|
+
} else {
|
|
7622
|
+
persistConfig({ lastSyncedDay: today, ...droppedPatch });
|
|
7623
|
+
}
|
|
7624
|
+
if (syncLogWasDelivered && pendingLog.watermark) {
|
|
7625
|
+
persistConfig({ lastSyncedLogAt: pendingLog.watermark });
|
|
7626
|
+
}
|
|
7627
|
+
const queuedDrained = !failedDay ? await drainBacklog() : 0;
|
|
7628
|
+
summary.queued = queued;
|
|
7629
|
+
summary.queuedDrained = queuedDrained;
|
|
7630
|
+
summary.usageSnapshots = usageBackfill.sentSnapshots;
|
|
7631
|
+
let status;
|
|
7632
|
+
if (failedDay) status = "failed";
|
|
7633
|
+
else if (summary.droppedDays.length || summary.skipped && Object.keys(summary.skipped).length) status = "degraded";
|
|
7634
|
+
else status = "ok";
|
|
7635
|
+
finish(status);
|
|
7636
|
+
await dispatchAndReport();
|
|
7637
|
+
emit({ type: "done", pushed, queued, pushedDays: summary.pushedDays });
|
|
7638
|
+
return { paired: true, pushed, queued, queuedDrained, usageBackfill, failedDay };
|
|
7639
|
+
} catch (err) {
|
|
7640
|
+
const message = err.message;
|
|
7641
|
+
summary.errorMsg = `sync aborted: ${message}`;
|
|
7642
|
+
emit({ type: "error", message });
|
|
7643
|
+
finish("error");
|
|
7644
|
+
writeLastPushFailure(
|
|
7645
|
+
{ ingestId: "n/a", observedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
7646
|
+
`team push error: ${message}`
|
|
7647
|
+
);
|
|
7648
|
+
return { paired: true, pushed: 0, queued: 0, queuedDrained: 0, error: message };
|
|
7222
7649
|
}
|
|
7223
|
-
entries.sort((a, b) => a.local_day.localeCompare(b.local_day));
|
|
7224
|
-
return entries;
|
|
7225
7650
|
}
|
|
7651
|
+
async function buildCyclePeaksForPush(planTier, loadCurve) {
|
|
7652
|
+
const validTiers = ["pro", "pro-max", "pro-max-20x", "custom"];
|
|
7653
|
+
const tier = validTiers.find((t) => t === planTier) ?? "pro-max-20x";
|
|
7654
|
+
const dump = await loadCurve(tier).catch(() => null);
|
|
7655
|
+
if (!dump || dump.curve.length === 0) return void 0;
|
|
7656
|
+
const HOUR = 36e5;
|
|
7657
|
+
const nowMs = Date.now();
|
|
7658
|
+
const peaksFor = (cycleKey, realKey, predKey, maxCycles) => {
|
|
7659
|
+
const TOLERANCE_MS = cycleKey === "cycle_end_7d" ? 12 * HOUR : 0;
|
|
7660
|
+
const byCycle = /* @__PURE__ */ new Map();
|
|
7661
|
+
for (const p of dump.curve) {
|
|
7662
|
+
const k = p[cycleKey];
|
|
7663
|
+
if (!k) continue;
|
|
7664
|
+
const ms = Date.parse(k);
|
|
7665
|
+
if (Number.isNaN(ms)) continue;
|
|
7666
|
+
const bucket = Math.round(ms / HOUR) * HOUR;
|
|
7667
|
+
const arr = byCycle.get(bucket) ?? [];
|
|
7668
|
+
arr.push(p);
|
|
7669
|
+
byCycle.set(bucket, arr);
|
|
7670
|
+
}
|
|
7671
|
+
const merged = [];
|
|
7672
|
+
for (const [endMs, points] of Array.from(byCycle.entries()).sort((a, b) => a[0] - b[0])) {
|
|
7673
|
+
const last = merged[merged.length - 1];
|
|
7674
|
+
if (last && endMs - last.endMs <= TOLERANCE_MS) {
|
|
7675
|
+
last.endMs = endMs;
|
|
7676
|
+
last.points.push(...points);
|
|
7677
|
+
} else {
|
|
7678
|
+
merged.push({ endMs, points: [...points] });
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
const out = [];
|
|
7682
|
+
for (const { endMs, points } of merged) {
|
|
7683
|
+
let peak = 0;
|
|
7684
|
+
let source = "predicted";
|
|
7685
|
+
for (const p of points) {
|
|
7686
|
+
const r = p[realKey];
|
|
7687
|
+
if (typeof r === "number" && r > peak) {
|
|
7688
|
+
peak = r;
|
|
7689
|
+
source = "real";
|
|
7690
|
+
}
|
|
7691
|
+
const v = p[predKey] ?? 0;
|
|
7692
|
+
if (v > peak) {
|
|
7693
|
+
peak = v;
|
|
7694
|
+
source = "predicted";
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7697
|
+
out.push({
|
|
7698
|
+
endsAt: new Date(endMs).toISOString(),
|
|
7699
|
+
peakPct: Math.round(peak * 10) / 10,
|
|
7700
|
+
source,
|
|
7701
|
+
current: endMs > nowMs
|
|
7702
|
+
});
|
|
7703
|
+
}
|
|
7704
|
+
return out.slice(-maxCycles);
|
|
7705
|
+
};
|
|
7706
|
+
return {
|
|
7707
|
+
// 7d cycle history cap (26 ≈ six months) MUST stay in lock-step with two
|
|
7708
|
+
// downstream sites or the strip silently truncates: the server read
|
|
7709
|
+
// (loadMembership7dCyclePeaks maxCyclesPerMember in plan-queries.ts) and
|
|
7710
|
+
// the renderer (CyclePeaksStrip maxBars in member-plan-block.tsx). The 5h
|
|
7711
|
+
// cap (24) is independent — leave it.
|
|
7712
|
+
fiveHour: peaksFor("cycle_end_5h", "real_5h", "pred_5h", 24),
|
|
7713
|
+
sevenDay: peaksFor("cycle_end_7d", "real_7d", "pred_7d", 26)
|
|
7714
|
+
};
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
// src/perception/worker.ts
|
|
7718
|
+
import { statSync as statSync5 } from "node:fs";
|
|
7719
|
+
init_fs();
|
|
7226
7720
|
|
|
7227
7721
|
// src/perception/state.ts
|
|
7228
7722
|
init_fs();
|
|
7229
|
-
import { existsSync as existsSync7, readFileSync as
|
|
7230
|
-
import { dirname as
|
|
7723
|
+
import { existsSync as existsSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, renameSync as renameSync4 } from "node:fs";
|
|
7724
|
+
import { dirname as dirname8 } from "node:path";
|
|
7231
7725
|
var STALE_MS = 15 * 60 * 1e3;
|
|
7232
7726
|
var pathCached = null;
|
|
7233
7727
|
function statePath() {
|
|
@@ -7245,15 +7739,15 @@ function readState() {
|
|
|
7245
7739
|
file_checkpoints: {}
|
|
7246
7740
|
};
|
|
7247
7741
|
}
|
|
7248
|
-
const raw =
|
|
7742
|
+
const raw = readFileSync11(p, "utf8");
|
|
7249
7743
|
return JSON.parse(raw);
|
|
7250
7744
|
}
|
|
7251
7745
|
function writeState(s) {
|
|
7252
7746
|
const p = statePath();
|
|
7253
|
-
mkdirSync8(
|
|
7747
|
+
mkdirSync8(dirname8(p), { recursive: true });
|
|
7254
7748
|
const tmp = `${p}.tmp`;
|
|
7255
7749
|
writeFileSync7(tmp, JSON.stringify(s, null, 2), "utf8");
|
|
7256
|
-
|
|
7750
|
+
renameSync4(tmp, p);
|
|
7257
7751
|
}
|
|
7258
7752
|
function markSweepStart() {
|
|
7259
7753
|
const s = readState();
|
|
@@ -7306,12 +7800,6 @@ async function listAllSessionJsonls(root) {
|
|
|
7306
7800
|
function log(msg) {
|
|
7307
7801
|
console.error(`[perception] ${msg}`);
|
|
7308
7802
|
}
|
|
7309
|
-
function decodeProjectDirName(projectDir) {
|
|
7310
|
-
if (projectDir.startsWith("-")) {
|
|
7311
|
-
return "/" + projectDir.slice(1).replace(/-/g, "/");
|
|
7312
|
-
}
|
|
7313
|
-
return projectDir.replace(/-/g, "/");
|
|
7314
|
-
}
|
|
7315
7803
|
async function runPerceptionSweep(opts = {}) {
|
|
7316
7804
|
const state = readState();
|
|
7317
7805
|
if (state.sweep_in_progress && !isSweepStale()) {
|
|
@@ -7325,33 +7813,12 @@ async function runPerceptionSweep(opts = {}) {
|
|
|
7325
7813
|
const files = await listAllSessionJsonls(opts.projectsRoot);
|
|
7326
7814
|
for (const f of files) {
|
|
7327
7815
|
try {
|
|
7328
|
-
const stat =
|
|
7816
|
+
const stat = statSync5(f);
|
|
7329
7817
|
const prev = state.file_checkpoints[f];
|
|
7330
7818
|
if (prev && prev.byte_offset >= stat.size) continue;
|
|
7331
|
-
const
|
|
7332
|
-
|
|
7333
|
-
try {
|
|
7334
|
-
return JSON.parse(l);
|
|
7335
|
-
} catch {
|
|
7336
|
-
return null;
|
|
7337
|
-
}
|
|
7338
|
-
}).filter((x) => x !== null);
|
|
7339
|
-
if (rawLines.length === 0) continue;
|
|
7340
|
-
const { meta, events } = parseTranscript(rawLines);
|
|
7341
|
-
const fileName = basename(f);
|
|
7342
|
-
const projectDir = basename(dirname8(f));
|
|
7343
|
-
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
7344
|
-
const sd = {
|
|
7345
|
-
...meta,
|
|
7346
|
-
id: sessionId,
|
|
7347
|
-
filePath: f,
|
|
7348
|
-
projectDir,
|
|
7349
|
-
projectName: meta.cwd ?? decodeProjectDirName(projectDir),
|
|
7350
|
-
events
|
|
7351
|
-
};
|
|
7352
|
-
const built = buildEntries(sd);
|
|
7819
|
+
const built = buildEntriesForFile(f);
|
|
7820
|
+
if (built === null) continue;
|
|
7353
7821
|
for (const e of built) {
|
|
7354
|
-
e.source_checkpoint.byte_offset = stat.size;
|
|
7355
7822
|
writeEntryPreservingEnrichment(e);
|
|
7356
7823
|
entries++;
|
|
7357
7824
|
}
|
|
@@ -7378,7 +7845,7 @@ async function runPerceptionSweep(opts = {}) {
|
|
|
7378
7845
|
const built = buildEntries(detail);
|
|
7379
7846
|
for (const e of built) {
|
|
7380
7847
|
try {
|
|
7381
|
-
const stat =
|
|
7848
|
+
const stat = statSync5(meta.filePath);
|
|
7382
7849
|
e.source_checkpoint.byte_offset = stat.size;
|
|
7383
7850
|
} catch {
|
|
7384
7851
|
}
|
|
@@ -11535,12 +12002,12 @@ var ENRICHMENT_SYSTEM_PROMPT = SYSTEM_PROMPT;
|
|
|
11535
12002
|
// ../entries/dist/llm-runner.js
|
|
11536
12003
|
init_fs();
|
|
11537
12004
|
import { spawn } from "node:child_process";
|
|
11538
|
-
import { mkdirSync as mkdirSync10, appendFileSync as
|
|
12005
|
+
import { mkdirSync as mkdirSync10, appendFileSync as appendFileSync4, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "node:fs";
|
|
11539
12006
|
import { join as join10 } from "node:path";
|
|
11540
12007
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
11541
12008
|
|
|
11542
12009
|
// ../entries/dist/tmux-runner.js
|
|
11543
|
-
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync8, readdirSync as readdirSync5, realpathSync, readFileSync as
|
|
12010
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, existsSync as existsSync8, readdirSync as readdirSync5, realpathSync, readFileSync as readFileSync12, unlinkSync as unlinkSync3, renameSync as renameSync5 } from "node:fs";
|
|
11544
12011
|
import { join as join9 } from "node:path";
|
|
11545
12012
|
import { homedir as homedir4 } from "node:os";
|
|
11546
12013
|
import { spawnSync } from "node:child_process";
|
|
@@ -11579,7 +12046,7 @@ function ensureRuntimeCwdTrusted(cwd) {
|
|
|
11579
12046
|
const cfgPath = join9(homedir4(), ".claude.json");
|
|
11580
12047
|
let cfg;
|
|
11581
12048
|
try {
|
|
11582
|
-
cfg = JSON.parse(
|
|
12049
|
+
cfg = JSON.parse(readFileSync12(cfgPath, "utf8"));
|
|
11583
12050
|
} catch {
|
|
11584
12051
|
return;
|
|
11585
12052
|
}
|
|
@@ -11595,7 +12062,7 @@ function ensureRuntimeCwdTrusted(cwd) {
|
|
|
11595
12062
|
const tmp = `${cfgPath}.cclens-tmp-${process.pid}`;
|
|
11596
12063
|
try {
|
|
11597
12064
|
writeFileSync8(tmp, JSON.stringify(cfg, null, 2));
|
|
11598
|
-
|
|
12065
|
+
renameSync5(tmp, cfgPath);
|
|
11599
12066
|
} catch {
|
|
11600
12067
|
try {
|
|
11601
12068
|
unlinkSync3(tmp);
|
|
@@ -11731,7 +12198,7 @@ function snapshotAllTranscripts() {
|
|
|
11731
12198
|
function readJsonlCwd(path7, maxLines = 8) {
|
|
11732
12199
|
let raw;
|
|
11733
12200
|
try {
|
|
11734
|
-
raw =
|
|
12201
|
+
raw = readFileSync12(path7, "utf8");
|
|
11735
12202
|
} catch {
|
|
11736
12203
|
return void 0;
|
|
11737
12204
|
}
|
|
@@ -11788,7 +12255,7 @@ async function discoverTranscript(targetCwd, existingBefore, startMs, timeoutMs,
|
|
|
11788
12255
|
function parseTranscript2(path7, defaultModel) {
|
|
11789
12256
|
let raw;
|
|
11790
12257
|
try {
|
|
11791
|
-
raw =
|
|
12258
|
+
raw = readFileSync12(path7, "utf8");
|
|
11792
12259
|
} catch {
|
|
11793
12260
|
return { assembled: "", inputTokens: 0, outputTokens: 0, modelUsed: defaultModel, done: false };
|
|
11794
12261
|
}
|
|
@@ -11896,7 +12363,7 @@ function tracePath(runId) {
|
|
|
11896
12363
|
}
|
|
11897
12364
|
function safeAppend(path7, line) {
|
|
11898
12365
|
try {
|
|
11899
|
-
|
|
12366
|
+
appendFileSync4(path7, line);
|
|
11900
12367
|
} catch {
|
|
11901
12368
|
}
|
|
11902
12369
|
}
|
|
@@ -12291,7 +12758,7 @@ function applyEnrichmentError(entry, errorMessage, model, costUsd, generatedAt,
|
|
|
12291
12758
|
|
|
12292
12759
|
// ../entries/dist/budget.js
|
|
12293
12760
|
init_fs();
|
|
12294
|
-
import { appendFileSync as
|
|
12761
|
+
import { appendFileSync as appendFileSync5, readFileSync as readFileSync13, existsSync as existsSync10, mkdirSync as mkdirSync11 } from "node:fs";
|
|
12295
12762
|
import { dirname as dirname9 } from "node:path";
|
|
12296
12763
|
var spendPathCached = null;
|
|
12297
12764
|
function spendPath() {
|
|
@@ -12303,7 +12770,7 @@ function spendPath() {
|
|
|
12303
12770
|
function appendSpend(record) {
|
|
12304
12771
|
const p = spendPath();
|
|
12305
12772
|
mkdirSync11(dirname9(p), { recursive: true });
|
|
12306
|
-
|
|
12773
|
+
appendFileSync5(p, JSON.stringify(record) + "\n", { encoding: "utf8" });
|
|
12307
12774
|
}
|
|
12308
12775
|
function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
12309
12776
|
const p = spendPath();
|
|
@@ -12312,7 +12779,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
|
12312
12779
|
const year = now.getUTCFullYear();
|
|
12313
12780
|
const month = now.getUTCMonth();
|
|
12314
12781
|
let total = 0;
|
|
12315
|
-
const raw =
|
|
12782
|
+
const raw = readFileSync13(p, "utf8");
|
|
12316
12783
|
for (const line of raw.split("\n")) {
|
|
12317
12784
|
if (!line)
|
|
12318
12785
|
continue;
|
|
@@ -12334,7 +12801,7 @@ function monthToDateSpend(now = /* @__PURE__ */ new Date()) {
|
|
|
12334
12801
|
|
|
12335
12802
|
// ../entries/dist/pipeline-lock.js
|
|
12336
12803
|
init_fs();
|
|
12337
|
-
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as
|
|
12804
|
+
import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync4, existsSync as existsSync11, readFileSync as readFileSync14, statSync as statSync6 } from "node:fs";
|
|
12338
12805
|
var STALE_MS2 = 60 * 1e3;
|
|
12339
12806
|
var HEARTBEAT_MS = 30 * 1e3;
|
|
12340
12807
|
var lockPathCached = null;
|
|
@@ -12383,10 +12850,10 @@ function interactiveLockFresh(nowMs) {
|
|
|
12383
12850
|
if (!existsSync11(p))
|
|
12384
12851
|
return false;
|
|
12385
12852
|
try {
|
|
12386
|
-
const mtime =
|
|
12853
|
+
const mtime = statSync6(p).mtimeMs;
|
|
12387
12854
|
if (nowMs - mtime > STALE_MS2)
|
|
12388
12855
|
return false;
|
|
12389
|
-
const pid = Number(
|
|
12856
|
+
const pid = Number(readFileSync14(p, "utf8").trim());
|
|
12390
12857
|
if (!Number.isFinite(pid) || pid <= 0)
|
|
12391
12858
|
return false;
|
|
12392
12859
|
if (!pidAlive(pid))
|
|
@@ -12401,7 +12868,7 @@ function interactiveLockFresh(nowMs) {
|
|
|
12401
12868
|
var THIRTY_MIN_MS = 30 * 60 * 1e3;
|
|
12402
12869
|
|
|
12403
12870
|
// ../entries/dist/settings.js
|
|
12404
|
-
import { readFileSync as
|
|
12871
|
+
import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, renameSync as renameSync6, chmodSync as chmodSync2, mkdirSync as mkdirSync12, existsSync as existsSync12 } from "node:fs";
|
|
12405
12872
|
init_fs();
|
|
12406
12873
|
var DEFAULT_SETTINGS = {
|
|
12407
12874
|
ai_features: {
|
|
@@ -12436,7 +12903,7 @@ function readSettings() {
|
|
|
12436
12903
|
if (!existsSync12(p))
|
|
12437
12904
|
return DEFAULT_SETTINGS;
|
|
12438
12905
|
try {
|
|
12439
|
-
const raw =
|
|
12906
|
+
const raw = readFileSync15(p, "utf8");
|
|
12440
12907
|
return fromDisk(JSON.parse(raw));
|
|
12441
12908
|
} catch {
|
|
12442
12909
|
return DEFAULT_SETTINGS;
|
|
@@ -15053,8 +15520,8 @@ function toLocalDay2(ms) {
|
|
|
15053
15520
|
const day = String(d.getDate()).padStart(2, "0");
|
|
15054
15521
|
return `${y}-${m}-${day}`;
|
|
15055
15522
|
}
|
|
15056
|
-
function mondayOf(
|
|
15057
|
-
const d = /* @__PURE__ */ new Date(`${
|
|
15523
|
+
function mondayOf(localDay2) {
|
|
15524
|
+
const d = /* @__PURE__ */ new Date(`${localDay2}T00:00:00`);
|
|
15058
15525
|
const dow = d.getDay();
|
|
15059
15526
|
const offset = dow === 0 ? -6 : 1 - dow;
|
|
15060
15527
|
d.setDate(d.getDate() + offset);
|
|
@@ -15193,14 +15660,14 @@ async function getUpdateAvailability() {
|
|
|
15193
15660
|
if (isDevMode()) return null;
|
|
15194
15661
|
const latest = await fetchLatestVersion();
|
|
15195
15662
|
if (latest === null) return {
|
|
15196
|
-
current: "0.
|
|
15663
|
+
current: "0.15.0",
|
|
15197
15664
|
latest: null,
|
|
15198
15665
|
updateAvailable: false
|
|
15199
15666
|
};
|
|
15200
15667
|
return {
|
|
15201
|
-
current: "0.
|
|
15668
|
+
current: "0.15.0",
|
|
15202
15669
|
latest,
|
|
15203
|
-
updateAvailable: shouldUpdate("0.
|
|
15670
|
+
updateAvailable: shouldUpdate("0.15.0", latest)
|
|
15204
15671
|
};
|
|
15205
15672
|
}
|
|
15206
15673
|
|
|
@@ -15212,15 +15679,11 @@ var WATCHDOG_INTERVAL_MS = 5 * 1e3;
|
|
|
15212
15679
|
var AUTO_UPDATE_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
15213
15680
|
mkdirSync13(dirname10(USAGE_LOG3), { recursive: true });
|
|
15214
15681
|
function log2(level, message) {
|
|
15215
|
-
|
|
15216
|
-
`;
|
|
15217
|
-
try {
|
|
15218
|
-
appendFileSync5(DAEMON_LOG, line, "utf8");
|
|
15219
|
-
} catch {
|
|
15220
|
-
}
|
|
15682
|
+
appendDaemonLogLine(level, message);
|
|
15221
15683
|
}
|
|
15222
15684
|
var nextPollAtMs = 0;
|
|
15223
15685
|
var nextTeamSyncAtMs = 0;
|
|
15686
|
+
var firstTeamSync = true;
|
|
15224
15687
|
var currentIntervalMs = BASE_INTERVAL_MS;
|
|
15225
15688
|
var waitingForRefresh = false;
|
|
15226
15689
|
var updateCheckInFlight = false;
|
|
@@ -15252,7 +15715,7 @@ var perceptionHandle = setInterval(async () => {
|
|
|
15252
15715
|
}, PERCEPTION_INTERVAL_MS);
|
|
15253
15716
|
function readUpdateState() {
|
|
15254
15717
|
try {
|
|
15255
|
-
return JSON.parse(
|
|
15718
|
+
return JSON.parse(readFileSync16(UPDATE_STATE, "utf8"));
|
|
15256
15719
|
} catch {
|
|
15257
15720
|
return null;
|
|
15258
15721
|
}
|
|
@@ -15429,7 +15892,11 @@ async function runLoop() {
|
|
|
15429
15892
|
}
|
|
15430
15893
|
const teamNow = Date.now();
|
|
15431
15894
|
if (teamNow >= nextTeamSyncAtMs) {
|
|
15432
|
-
await runTeamSync(log2
|
|
15895
|
+
await runTeamSync(log2, void 0, {
|
|
15896
|
+
trigger: firstTeamSync ? "boot" : "auto",
|
|
15897
|
+
nextSyncMs: BASE_INTERVAL_MS
|
|
15898
|
+
});
|
|
15899
|
+
firstTeamSync = false;
|
|
15433
15900
|
nextTeamSyncAtMs = Date.now() + BASE_INTERVAL_MS;
|
|
15434
15901
|
}
|
|
15435
15902
|
if (Date.now() >= nextUpdateCheckAtMs) {
|