fleetlens 0.6.4 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/apps/web/.next/BUILD_ID +1 -1
- package/app/apps/web/.next/app-path-routes-manifest.json +1 -0
- package/app/apps/web/.next/build-manifest.json +5 -5
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/routes-manifest.json +6 -0
- package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/_not-found/page.js +3 -3
- 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 +2 -2
- 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 +3 -3
- 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 +3 -3
- 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 +1 -1
- 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 +3 -3
- 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 +1 -1
- package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/events/route.js +2 -2
- package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/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 +3 -3
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page/app-paths-manifest.json +3 -0
- package/app/apps/web/.next/server/app/changelog/page/build-manifest.json +17 -0
- package/app/apps/web/.next/server/app/changelog/page/next-font-manifest.json +6 -0
- package/app/apps/web/.next/server/app/changelog/page/react-loadable-manifest.json +1 -0
- package/app/apps/web/.next/server/app/changelog/page/server-reference-manifest.json +4 -0
- package/app/apps/web/.next/server/app/changelog/page.js +17 -0
- package/app/apps/web/.next/server/app/changelog/page.js.map +5 -0
- package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -0
- package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +3 -0
- package/app/apps/web/.next/server/app/day/[date]/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/day/[date]/page.js +4 -4
- 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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/day/page.js +3 -3
- 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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/digest/[date]/page.js +3 -3
- 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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/digest/page.js +3 -3
- 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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/insights/[key]/page.js +3 -3
- package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/insights/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/insights/page.js +3 -3
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/page.js +4 -4
- package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/parallelism/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/parallelism/page.js +3 -3
- package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/projects/[slug]/page.js +4 -4
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/projects/page.js +4 -4
- package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/runs/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/runs/page.js +3 -3
- 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/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/sessions/[id]/page.js +4 -4
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/sessions/page.js +4 -4
- package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/settings/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/settings/page.js +3 -3
- 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/usage/page/build-manifest.json +2 -2
- package/app/apps/web/.next/server/app/usage/page.js +3 -3
- 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 +1 -0
- package/app/apps/web/.next/server/chunks/{[externals]__0o43ejh._.js → [externals]__0fpbr8-._.js} +2 -2
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0-vgrh1._.js +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0.z48ue._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0dctov1._.js +5 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0e8n_x4._.js +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0jprdks._.js +2 -2
- package/app/apps/web/.next/server/chunks/node_modules_next_036my~r._.js +1 -1
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0ck.78s.js +1 -1
- package/app/apps/web/.next/server/chunks/packages_09hmrqx._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_118y1lp._.js +3 -0
- package/app/apps/web/.next/server/chunks/packages_entries_dist_node_0bckr7-.js +26 -17
- package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_122oe0-.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0vvlth5._.js → [root-of-the-server]__02~v50d._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__04eq6z2._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__09rjz7_._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0e.gxk3._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0p1x6v2._.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]__0rvzdnx._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0sp6.jt._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0tejjy_._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0w52fbc._.js +3 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0wwa6gh._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ytdqxt._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0yv-a8z._.js +12 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__10~b4bf._.js +12 -0
- package/app/apps/web/.next/server/chunks/ssr/_02s1n-x._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0woap-3._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web__next-internal_server_app_changelog_page_actions_12_04gy.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_settings_ai-features-form_tsx_01p699p._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0ly1xes._.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_components_changelog-mark-read_tsx_0hm41sv._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_0m67-xi._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/{node_modules_0i2xw~e._.js → node_modules_0fx6-.x._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0uwo3v3.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_10_oegc._.js +3 -0
- package/app/apps/web/.next/server/middleware-build-manifest.js +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/0.3mqbj24kwtc.js +6 -0
- package/app/apps/web/.next/static/chunks/05b689hq3x49w.js +1 -0
- package/app/apps/web/.next/static/chunks/07-fq4im3pmjp.js +4 -0
- package/app/apps/web/.next/static/chunks/{0mykrrv810ra-.js → 0kjot8367asxe.js} +1 -1
- package/app/apps/web/.next/static/chunks/0n3-prt61k7pr.js +1 -0
- package/app/apps/web/.next/static/chunks/{0_7303jr0sf0w.js → 0q2vd~aj.1hpz.js} +3 -3
- package/app/apps/web/.next/static/chunks/0qmh5da6gfm56.js +1 -0
- package/app/apps/web/.next/static/chunks/{0k9pksi_i.odf.js → 111-bgx08j2nq.js} +1 -1
- package/app/apps/web/.next/static/chunks/179_t_igg8ghh.js +14 -0
- package/app/apps/web/.next/static/chunks/{turbopack-0_p36yduv.waj.js → turbopack-00w.w1u3c~d-6.js} +1 -1
- package/app/apps/web/app/api/ask/route.ts +1 -1
- package/app/apps/web/app/api/digest/week/[startDate]/route.ts +2 -1
- package/app/apps/web/app/api/settings/route.ts +1 -0
- package/app/apps/web/app/changelog/page.tsx +238 -0
- package/app/apps/web/app/insights/[key]/page.tsx +8 -2
- package/app/apps/web/app/layout.tsx +12 -7
- package/app/apps/web/app/page.tsx +19 -15
- package/app/apps/web/app/parallelism/gantt-chart.tsx +7 -18
- package/app/apps/web/app/projects/projects-view.tsx +3 -0
- package/app/apps/web/app/sessions/[id]/session-view.tsx +168 -41
- package/app/apps/web/app/sessions/page.tsx +22 -12
- package/app/apps/web/app/sessions/sessions-grid.tsx +82 -9
- package/app/apps/web/app/settings/ai-features-form.tsx +20 -1
- package/app/apps/web/app/settings/page.tsx +1 -0
- package/app/apps/web/app/usage/page.tsx +161 -52
- package/app/apps/web/components/agent-badge.tsx +43 -0
- package/app/apps/web/components/agent-icon.tsx +38 -0
- package/app/apps/web/components/agent-mix-chip.tsx +42 -0
- package/app/apps/web/components/{ask-claude.tsx → ask.tsx} +13 -12
- package/app/apps/web/components/changelog-icon.tsx +62 -0
- package/app/apps/web/components/changelog-mark-read.tsx +12 -0
- package/app/apps/web/components/day-digest.tsx +1 -1
- package/app/apps/web/components/live-sessions-widget.tsx +5 -1
- package/app/apps/web/components/outcome-pill.tsx +7 -2
- package/app/apps/web/components/sidebar.tsx +8 -1
- package/app/apps/web/components/week-digest.tsx +2 -0
- package/app/apps/web/lib/ai/session-summary.ts +1 -1
- package/app/apps/web/lib/calibration-data.ts +4 -64
- package/app/apps/web/lib/changelog-data.generated.ts +3 -0
- package/app/apps/web/lib/changelog.ts +67 -0
- package/app/apps/web/lib/cycle-peaks.test.ts +185 -0
- package/app/apps/web/lib/cycle-peaks.ts +104 -0
- package/app/apps/web/lib/data.ts +7 -8
- package/app/apps/web/lib/date-range.ts +9 -2
- package/app/apps/web/lib/usage-data.ts +24 -1
- package/app/apps/web/lib/validate-settings.ts +1 -0
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +3162 -2354
- package/dist/index.js +4419 -3742
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/[externals]__0_xztrf._.js +0 -3
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0455vu3._.js +0 -5
- package/app/apps/web/.next/server/chunks/packages_0e7q9u_._.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_0n6p2ot._.js +0 -3
- package/app/apps/web/.next/server/chunks/packages_parser_dist_fs_0bc7ptm.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0-94ji~._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0_6.inc._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0gio_ck._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0jdyefm._.js +0 -12
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0ln9jsw._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0u5d7de._.js +0 -12
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~_8vff._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__11v.eqw._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_usage-data_ts_0xtv99n._.js +0 -3
- package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0-jvt~a._.js +0 -3
- package/app/apps/web/.next/static/chunks/04m3slg_5ph02.js +0 -1
- package/app/apps/web/.next/static/chunks/0coa1800xqgfj.js +0 -1
- package/app/apps/web/.next/static/chunks/0k1psab.bybh9.js +0 -14
- package/app/apps/web/.next/static/chunks/0xfu09fn_358k.js +0 -4
- package/app/apps/web/.next/static/chunks/17u2ww8ya5dv1.js +0 -6
- /package/app/apps/web/.next/static/{2jcMG-92q2XKei8dN7BMd → tiK9aMHXkgnKLNmxj9J1P}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{2jcMG-92q2XKei8dN7BMd → tiK9aMHXkgnKLNmxj9J1P}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{2jcMG-92q2XKei8dN7BMd → tiK9aMHXkgnKLNmxj9J1P}/_ssgManifest.js +0 -0
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Usage page — historical utilization analytics, per agent.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* One tab per agent that has snapshots on disk. Each tab renders the
|
|
5
|
+
* same dashboard layout (burndown charts + previous-cycles trend) so
|
|
6
|
+
* Claude Code and Codex are peers, not primary/secondary.
|
|
7
7
|
*
|
|
8
|
-
* Reads from ~/.cclens/usage.jsonl —
|
|
8
|
+
* Reads from ~/.cclens/usage.jsonl — the daemon writes both agents'
|
|
9
|
+
* snapshots there, tagged with `agent`. Filtering happens at read time.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import { Activity } from "lucide-react";
|
|
13
|
+
import Link from "next/link";
|
|
12
14
|
import {
|
|
13
15
|
readUsageSnapshots,
|
|
14
|
-
|
|
16
|
+
readUsageSnapshotsByAgent,
|
|
17
|
+
latestUsageSnapshotByAgent,
|
|
15
18
|
readCachedPlanTier,
|
|
16
19
|
PLAN_TIER_LABELS,
|
|
17
20
|
} from "@/lib/usage-data";
|
|
@@ -22,16 +25,53 @@ import {
|
|
|
22
25
|
} from "@/lib/calibration-data";
|
|
23
26
|
import { UsageChartsDashboard } from "@/components/usage-charts-dashboard";
|
|
24
27
|
import { PreviousCyclesTrend } from "@/components/previous-cycles-trend";
|
|
28
|
+
import {
|
|
29
|
+
type AgentKind,
|
|
30
|
+
agentMetadata,
|
|
31
|
+
getAgentMetadata,
|
|
32
|
+
isAgentKind,
|
|
33
|
+
} from "@claude-lens/parser";
|
|
25
34
|
|
|
26
35
|
export const dynamic = "force-dynamic";
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
function agentLabel(kind: AgentKind): string {
|
|
38
|
+
return getAgentMetadata(kind)?.displayName ?? kind;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function agentAccent(kind: AgentKind): string {
|
|
42
|
+
return getAgentMetadata(kind)?.accentColor ?? "var(--af-text-tertiary)";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default async function UsagePage({
|
|
46
|
+
searchParams,
|
|
47
|
+
}: {
|
|
48
|
+
searchParams: Promise<{ agent?: string }>;
|
|
49
|
+
}) {
|
|
50
|
+
const params = await searchParams;
|
|
51
|
+
const allSnapshots = readUsageSnapshots();
|
|
52
|
+
const agentsWithData = new Set<AgentKind>();
|
|
53
|
+
for (const s of allSnapshots) {
|
|
54
|
+
const k = (s.agent ?? "claude-code") as AgentKind;
|
|
55
|
+
agentsWithData.add(k);
|
|
56
|
+
}
|
|
57
|
+
// Always show Claude in the tab strip even if empty — it's the canonical
|
|
58
|
+
// source. Codex appears only once it has at least one snapshot.
|
|
59
|
+
agentsWithData.add("claude-code");
|
|
60
|
+
|
|
61
|
+
const requested: AgentKind | undefined = isAgentKind(params.agent) ? params.agent : undefined;
|
|
62
|
+
const selected: AgentKind =
|
|
63
|
+
requested && agentsWithData.has(requested)
|
|
64
|
+
? requested
|
|
65
|
+
: agentsWithData.has("claude-code")
|
|
66
|
+
? "claude-code"
|
|
67
|
+
: (Array.from(agentsWithData)[0] as AgentKind);
|
|
68
|
+
|
|
69
|
+
const snapshots = readUsageSnapshotsByAgent(selected);
|
|
70
|
+
const latest = latestUsageSnapshotByAgent(selected);
|
|
71
|
+
const tier = selected === "claude-code" ? readCachedPlanTier() : null;
|
|
72
|
+
const calibration = selected === "claude-code" ? await readCalibrationDump() : null;
|
|
73
|
+
const predicted = calibration ? predictedSeriesFor(calibration) : null;
|
|
74
|
+
const cycles7d = calibration ? previousCyclesTrend(calibration, "7d") : [];
|
|
35
75
|
|
|
36
76
|
return (
|
|
37
77
|
<div
|
|
@@ -43,7 +83,6 @@ export default async function UsagePage() {
|
|
|
43
83
|
padding: "20px 32px",
|
|
44
84
|
}}
|
|
45
85
|
>
|
|
46
|
-
{/* Header */}
|
|
47
86
|
<header
|
|
48
87
|
style={{ display: "flex", alignItems: "baseline", gap: 14, flexWrap: "wrap" }}
|
|
49
88
|
>
|
|
@@ -64,7 +103,7 @@ export default async function UsagePage() {
|
|
|
64
103
|
<span style={{ fontSize: 11, color: "var(--af-text-tertiary)" }}>
|
|
65
104
|
historical plan utilization · current usage in sidebar
|
|
66
105
|
</span>
|
|
67
|
-
{tier && (
|
|
106
|
+
{tier && selected === "claude-code" && (
|
|
68
107
|
<span
|
|
69
108
|
style={{
|
|
70
109
|
marginLeft: "auto",
|
|
@@ -90,14 +129,39 @@ export default async function UsagePage() {
|
|
|
90
129
|
)}
|
|
91
130
|
</span>
|
|
92
131
|
)}
|
|
132
|
+
{selected === "codex" && latest?.plan_type && (
|
|
133
|
+
<span
|
|
134
|
+
style={{
|
|
135
|
+
marginLeft: "auto",
|
|
136
|
+
display: "inline-flex",
|
|
137
|
+
alignItems: "center",
|
|
138
|
+
gap: 8,
|
|
139
|
+
fontSize: 12,
|
|
140
|
+
padding: "4px 10px",
|
|
141
|
+
border: "1px solid var(--af-border-subtle)",
|
|
142
|
+
borderRadius: 999,
|
|
143
|
+
fontFamily: "var(--font-mono)",
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<span style={{ color: "var(--af-text-tertiary)" }}>plan</span>
|
|
147
|
+
<span style={{ color: "var(--af-text)", fontWeight: 600 }}>
|
|
148
|
+
Codex {latest.plan_type}
|
|
149
|
+
</span>
|
|
150
|
+
</span>
|
|
151
|
+
)}
|
|
93
152
|
</header>
|
|
94
153
|
|
|
154
|
+
{/* Agent tab strip — one Link per agent that has snapshots. */}
|
|
155
|
+
<AgentTabs agents={Array.from(agentsWithData)} selected={selected} />
|
|
156
|
+
|
|
95
157
|
{!latest ? (
|
|
96
|
-
<EmptyState />
|
|
158
|
+
<EmptyState agent={selected} />
|
|
97
159
|
) : (
|
|
98
160
|
<>
|
|
99
|
-
|
|
100
|
-
|
|
161
|
+
{cycles7d.length > 0 && (
|
|
162
|
+
<PreviousCyclesTrend windowLabel="7d" cycles={cycles7d} />
|
|
163
|
+
)}
|
|
164
|
+
<UsageChartsDashboard snapshots={snapshots} predicted={predicted ?? undefined} />
|
|
101
165
|
<div
|
|
102
166
|
style={{
|
|
103
167
|
fontSize: 11,
|
|
@@ -106,7 +170,7 @@ export default async function UsagePage() {
|
|
|
106
170
|
}}
|
|
107
171
|
suppressHydrationWarning
|
|
108
172
|
>
|
|
109
|
-
Last
|
|
173
|
+
Last {agentLabel(selected)} poll: {new Date(latest.captured_at).toLocaleString()} ·{" "}
|
|
110
174
|
{snapshots.length} snapshot{snapshots.length === 1 ? "" : "s"} on disk
|
|
111
175
|
</div>
|
|
112
176
|
</>
|
|
@@ -115,7 +179,68 @@ export default async function UsagePage() {
|
|
|
115
179
|
);
|
|
116
180
|
}
|
|
117
181
|
|
|
118
|
-
function
|
|
182
|
+
function AgentTabs({ agents, selected }: { agents: AgentKind[]; selected: AgentKind }) {
|
|
183
|
+
// Claude first, then everything else alphabetically.
|
|
184
|
+
const sorted = [...agents].sort((a, b) => {
|
|
185
|
+
if (a === "claude-code") return -1;
|
|
186
|
+
if (b === "claude-code") return 1;
|
|
187
|
+
return a.localeCompare(b);
|
|
188
|
+
});
|
|
189
|
+
return (
|
|
190
|
+
<div
|
|
191
|
+
role="tablist"
|
|
192
|
+
style={{
|
|
193
|
+
display: "inline-flex",
|
|
194
|
+
gap: 4,
|
|
195
|
+
padding: 4,
|
|
196
|
+
background: "var(--af-border-subtle)",
|
|
197
|
+
borderRadius: 8,
|
|
198
|
+
alignSelf: "flex-start",
|
|
199
|
+
}}
|
|
200
|
+
>
|
|
201
|
+
{sorted.map((kind) => {
|
|
202
|
+
const isActive = kind === selected;
|
|
203
|
+
return (
|
|
204
|
+
<Link
|
|
205
|
+
key={kind}
|
|
206
|
+
role="tab"
|
|
207
|
+
aria-selected={isActive}
|
|
208
|
+
href={`/usage?agent=${kind}`}
|
|
209
|
+
style={{
|
|
210
|
+
display: "inline-flex",
|
|
211
|
+
alignItems: "center",
|
|
212
|
+
gap: 6,
|
|
213
|
+
padding: "6px 14px",
|
|
214
|
+
borderRadius: 6,
|
|
215
|
+
fontSize: 12,
|
|
216
|
+
fontWeight: 600,
|
|
217
|
+
letterSpacing: "0.01em",
|
|
218
|
+
color: isActive ? "var(--af-text)" : "var(--af-text-tertiary)",
|
|
219
|
+
background: isActive ? "var(--af-surface)" : "transparent",
|
|
220
|
+
boxShadow: isActive ? "0 1px 2px rgba(0,0,0,0.06)" : "none",
|
|
221
|
+
textDecoration: "none",
|
|
222
|
+
transition: "all 0.12s ease",
|
|
223
|
+
}}
|
|
224
|
+
>
|
|
225
|
+
<span
|
|
226
|
+
style={{
|
|
227
|
+
display: "inline-block",
|
|
228
|
+
width: 8,
|
|
229
|
+
height: 8,
|
|
230
|
+
borderRadius: 999,
|
|
231
|
+
background: agentAccent(kind),
|
|
232
|
+
opacity: isActive ? 1 : 0.6,
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
235
|
+
{agentLabel(kind)}
|
|
236
|
+
</Link>
|
|
237
|
+
);
|
|
238
|
+
})}
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function EmptyState({ agent }: { agent: AgentKind }) {
|
|
119
244
|
return (
|
|
120
245
|
<div
|
|
121
246
|
className="af-card"
|
|
@@ -131,7 +256,7 @@ function EmptyState() {
|
|
|
131
256
|
color: "var(--af-text)",
|
|
132
257
|
}}
|
|
133
258
|
>
|
|
134
|
-
No usage data yet
|
|
259
|
+
No {agentLabel(agent)} usage data yet
|
|
135
260
|
</div>
|
|
136
261
|
<p
|
|
137
262
|
style={{
|
|
@@ -140,43 +265,27 @@ function EmptyState() {
|
|
|
140
265
|
marginTop: 8,
|
|
141
266
|
}}
|
|
142
267
|
>
|
|
143
|
-
|
|
268
|
+
{agent === "claude-code"
|
|
269
|
+
? "Start the polling daemon to begin collecting metrics every 5 minutes:"
|
|
270
|
+
: "The daemon picks up Codex usage from disk on every poll cycle. Run a Codex session and the data will appear here within minutes."}
|
|
144
271
|
</p>
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
display: "inline-block",
|
|
148
|
-
marginTop: 14,
|
|
149
|
-
background: "var(--background)",
|
|
150
|
-
border: "1px solid var(--af-border-subtle)",
|
|
151
|
-
padding: "8px 16px",
|
|
152
|
-
borderRadius: 6,
|
|
153
|
-
fontSize: 13,
|
|
154
|
-
fontFamily: "var(--font-mono)",
|
|
155
|
-
color: "var(--af-text)",
|
|
156
|
-
}}
|
|
157
|
-
>
|
|
158
|
-
cclens daemon start
|
|
159
|
-
</pre>
|
|
160
|
-
<p
|
|
161
|
-
style={{
|
|
162
|
-
fontSize: 11,
|
|
163
|
-
color: "var(--af-text-tertiary)",
|
|
164
|
-
marginTop: 16,
|
|
165
|
-
}}
|
|
166
|
-
>
|
|
167
|
-
For a one-shot snapshot without the daemon, run{" "}
|
|
168
|
-
<code
|
|
272
|
+
{agent === "claude-code" && (
|
|
273
|
+
<pre
|
|
169
274
|
style={{
|
|
275
|
+
display: "inline-block",
|
|
276
|
+
marginTop: 14,
|
|
170
277
|
background: "var(--background)",
|
|
171
|
-
|
|
172
|
-
|
|
278
|
+
border: "1px solid var(--af-border-subtle)",
|
|
279
|
+
padding: "8px 16px",
|
|
280
|
+
borderRadius: 6,
|
|
281
|
+
fontSize: 13,
|
|
173
282
|
fontFamily: "var(--font-mono)",
|
|
283
|
+
color: "var(--af-text)",
|
|
174
284
|
}}
|
|
175
285
|
>
|
|
176
|
-
|
|
177
|
-
</
|
|
178
|
-
|
|
179
|
-
</p>
|
|
286
|
+
fleetlens daemon start
|
|
287
|
+
</pre>
|
|
288
|
+
)}
|
|
180
289
|
</div>
|
|
181
290
|
);
|
|
182
291
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentKind } from "@claude-lens/parser";
|
|
2
|
+
import { getAgentMetadata } from "@claude-lens/parser";
|
|
3
|
+
|
|
4
|
+
type AgentBadgeProps = {
|
|
5
|
+
/** Source agent. Undefined is treated as legacy claude-code (no badge). */
|
|
6
|
+
agent?: AgentKind;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Inline agent pill. Registry-driven: pulls accentColor + shortLabel
|
|
11
|
+
* directly off the AgentSource. Adding a new agent makes a new badge
|
|
12
|
+
* appear automatically with the source's declared color — no edits
|
|
13
|
+
* needed here.
|
|
14
|
+
*
|
|
15
|
+
* Returns null for "claude-code" (legacy default-hide so existing
|
|
16
|
+
* Claude rows render unchanged) and for unknown kinds.
|
|
17
|
+
*/
|
|
18
|
+
export function AgentBadge({ agent }: AgentBadgeProps) {
|
|
19
|
+
if (!agent || agent === "claude-code") return null;
|
|
20
|
+
const meta = getAgentMetadata(agent);
|
|
21
|
+
if (!meta) return null;
|
|
22
|
+
return (
|
|
23
|
+
<span
|
|
24
|
+
style={{
|
|
25
|
+
display: "inline-flex",
|
|
26
|
+
alignItems: "center",
|
|
27
|
+
borderRadius: 4,
|
|
28
|
+
padding: "2px 6px",
|
|
29
|
+
fontSize: 10,
|
|
30
|
+
letterSpacing: "0.04em",
|
|
31
|
+
textTransform: "uppercase",
|
|
32
|
+
whiteSpace: "nowrap",
|
|
33
|
+
fontWeight: 600,
|
|
34
|
+
color: meta.accentColor,
|
|
35
|
+
background: `color-mix(in srgb, ${meta.accentColor} 12%, transparent)`,
|
|
36
|
+
border: `1px solid color-mix(in srgb, ${meta.accentColor} 30%, transparent)`,
|
|
37
|
+
}}
|
|
38
|
+
title={`Source: ${meta.displayName}`}
|
|
39
|
+
>
|
|
40
|
+
{meta.shortLabel}
|
|
41
|
+
</span>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AgentKind } from "@claude-lens/parser";
|
|
2
|
+
import { getAgentMetadata } from "@claude-lens/parser";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
agent?: AgentKind;
|
|
6
|
+
/** Glyph size in px. Defaults to inherit-from-context (12 typical). */
|
|
7
|
+
size?: number;
|
|
8
|
+
/** Spoken label override; defaults to the agent's displayName. */
|
|
9
|
+
title?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Single-glyph agent indicator: ✦ for Claude, ◆ for Codex (or whatever
|
|
13
|
+
* the registered metadata declares), painted in the agent's accent color.
|
|
14
|
+
* Returns null for unknown / undefined agents. */
|
|
15
|
+
export function AgentIcon({ agent, size = 11, title }: Props) {
|
|
16
|
+
if (!agent) return null;
|
|
17
|
+
const meta = getAgentMetadata(agent);
|
|
18
|
+
if (!meta) return null;
|
|
19
|
+
return (
|
|
20
|
+
<span
|
|
21
|
+
aria-hidden
|
|
22
|
+
title={title ?? meta.displayName}
|
|
23
|
+
style={{
|
|
24
|
+
display: "inline-flex",
|
|
25
|
+
alignItems: "center",
|
|
26
|
+
justifyContent: "center",
|
|
27
|
+
flexShrink: 0,
|
|
28
|
+
width: size + 2,
|
|
29
|
+
height: size + 2,
|
|
30
|
+
fontSize: size,
|
|
31
|
+
lineHeight: 1,
|
|
32
|
+
color: meta.accentColor,
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{meta.iconChar}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AgentBreakdown } from "@claude-lens/parser";
|
|
2
|
+
import { getAgentMetadata } from "@claude-lens/parser";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Inline chip that surfaces "12 Claude · 3 Codex" on a project row when
|
|
6
|
+
* the project has activity from more than one source. Registry-driven:
|
|
7
|
+
* shortLabel + accentColor come from each AgentSource, so adding a new
|
|
8
|
+
* agent auto-appears here without edits.
|
|
9
|
+
*
|
|
10
|
+
* Returns null when there's only one agent — keeps single-agent rows
|
|
11
|
+
* visually unchanged.
|
|
12
|
+
*/
|
|
13
|
+
export function AgentMixChip({ perAgent }: { perAgent: AgentBreakdown[] }) {
|
|
14
|
+
if (perAgent.length < 2) return null;
|
|
15
|
+
return (
|
|
16
|
+
<span
|
|
17
|
+
style={{
|
|
18
|
+
display: "inline-flex",
|
|
19
|
+
alignItems: "center",
|
|
20
|
+
gap: 4,
|
|
21
|
+
fontSize: 10,
|
|
22
|
+
fontFamily: "var(--font-mono)",
|
|
23
|
+
color: "var(--af-text-tertiary)",
|
|
24
|
+
whiteSpace: "nowrap",
|
|
25
|
+
}}
|
|
26
|
+
title="Sessions split by source agent"
|
|
27
|
+
>
|
|
28
|
+
{perAgent.map((row, i) => {
|
|
29
|
+
const meta = getAgentMetadata(row.agent);
|
|
30
|
+
const label = meta?.shortLabel ?? row.agent;
|
|
31
|
+
const color = meta?.accentColor ?? "inherit";
|
|
32
|
+
return (
|
|
33
|
+
<span key={row.agent}>
|
|
34
|
+
{i > 0 && <span style={{ margin: "0 4px", opacity: 0.5 }}>·</span>}
|
|
35
|
+
<span style={{ color, fontWeight: 600 }}>{row.sessions}</span>{" "}
|
|
36
|
+
{label}
|
|
37
|
+
</span>
|
|
38
|
+
);
|
|
39
|
+
})}
|
|
40
|
+
</span>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Ask drawer for session analysis.
|
|
5
5
|
*
|
|
6
6
|
* Renders as a right-side panel with:
|
|
7
7
|
* - Quick-action buttons (Identify errors, Analyze performance, etc.)
|
|
8
8
|
* - Free-form textarea for custom questions
|
|
9
9
|
* - Streaming markdown response
|
|
10
10
|
*
|
|
11
|
-
* Calls POST /api/ask which spawns `claude -p` under the hood
|
|
12
|
-
* the
|
|
11
|
+
* Calls POST /api/ask which spawns `claude -p` under the hood today;
|
|
12
|
+
* the analyzer becomes pluggable when the LLM runner abstraction lands.
|
|
13
|
+
* The subject session can come from any registered agent.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import React, { useCallback, useRef, useState } from "react";
|
|
@@ -19,9 +20,9 @@ import {
|
|
|
19
20
|
AlertTriangle,
|
|
20
21
|
BarChart3,
|
|
21
22
|
Lightbulb,
|
|
22
|
-
MessageCircle,
|
|
23
23
|
Route,
|
|
24
24
|
Send,
|
|
25
|
+
Sparkles,
|
|
25
26
|
X,
|
|
26
27
|
Loader2,
|
|
27
28
|
} from "lucide-react";
|
|
@@ -58,7 +59,7 @@ const QUICK_ACTIONS = [
|
|
|
58
59
|
},
|
|
59
60
|
];
|
|
60
61
|
|
|
61
|
-
export function
|
|
62
|
+
export function AskButton({ onClick }: { onClick: () => void }) {
|
|
62
63
|
return (
|
|
63
64
|
<button
|
|
64
65
|
type="button"
|
|
@@ -86,13 +87,13 @@ export function AskClaudeButton({ onClick }: { onClick: () => void }) {
|
|
|
86
87
|
e.currentTarget.style.color = "var(--af-accent)";
|
|
87
88
|
}}
|
|
88
89
|
>
|
|
89
|
-
<
|
|
90
|
-
Ask
|
|
90
|
+
<Sparkles size={13} />
|
|
91
|
+
Ask
|
|
91
92
|
</button>
|
|
92
93
|
);
|
|
93
94
|
}
|
|
94
95
|
|
|
95
|
-
export function
|
|
96
|
+
export function AskDrawer({
|
|
96
97
|
sessionId,
|
|
97
98
|
onClose,
|
|
98
99
|
}: {
|
|
@@ -232,7 +233,7 @@ export function AskClaudeDrawer({
|
|
|
232
233
|
flexShrink: 0,
|
|
233
234
|
}}
|
|
234
235
|
>
|
|
235
|
-
<
|
|
236
|
+
<Sparkles size={16} color="var(--af-accent)" />
|
|
236
237
|
<span
|
|
237
238
|
style={{
|
|
238
239
|
fontSize: 14,
|
|
@@ -241,7 +242,7 @@ export function AskClaudeDrawer({
|
|
|
241
242
|
flex: 1,
|
|
242
243
|
}}
|
|
243
244
|
>
|
|
244
|
-
Ask
|
|
245
|
+
Ask about this session
|
|
245
246
|
</span>
|
|
246
247
|
<button
|
|
247
248
|
onClick={onClose}
|
|
@@ -280,8 +281,8 @@ export function AskClaudeDrawer({
|
|
|
280
281
|
lineHeight: 1.5,
|
|
281
282
|
}}
|
|
282
283
|
>
|
|
283
|
-
|
|
284
|
-
your own question.
|
|
284
|
+
Run an AI analysis on this session. Pick a quick action or
|
|
285
|
+
type your own question.
|
|
285
286
|
</div>
|
|
286
287
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
287
288
|
{QUICK_ACTIONS.map((action) => (
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Link from "next/link";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
import { Megaphone } from "lucide-react";
|
|
6
|
+
import { STORAGE_KEY } from "@/lib/changelog";
|
|
7
|
+
|
|
8
|
+
export function ChangelogIcon({ latestVersion, active }: { latestVersion: string | null; active: boolean }) {
|
|
9
|
+
// Server can't read localStorage, so render no dot until after mount to
|
|
10
|
+
// avoid a hydration mismatch flash.
|
|
11
|
+
const [unread, setUnread] = useState(false);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (!latestVersion) {
|
|
15
|
+
setUnread(false);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const seen = window.localStorage.getItem(STORAGE_KEY);
|
|
20
|
+
setUnread(seen !== latestVersion);
|
|
21
|
+
} catch {
|
|
22
|
+
setUnread(false);
|
|
23
|
+
}
|
|
24
|
+
}, [latestVersion]);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Link
|
|
28
|
+
href="/changelog"
|
|
29
|
+
aria-label={unread ? "Changelog (new entries)" : "Changelog"}
|
|
30
|
+
title={unread ? `What's new in v${latestVersion}` : "Changelog"}
|
|
31
|
+
style={{
|
|
32
|
+
position: "relative",
|
|
33
|
+
display: "inline-flex",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
justifyContent: "center",
|
|
36
|
+
width: 24,
|
|
37
|
+
height: 24,
|
|
38
|
+
borderRadius: 6,
|
|
39
|
+
color: active ? "var(--af-text)" : "var(--af-text-tertiary)",
|
|
40
|
+
background: active ? "var(--af-surface-hover)" : "transparent",
|
|
41
|
+
textDecoration: "none",
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<Megaphone size={14} />
|
|
45
|
+
{unread && (
|
|
46
|
+
<span
|
|
47
|
+
aria-hidden
|
|
48
|
+
style={{
|
|
49
|
+
position: "absolute",
|
|
50
|
+
top: 3,
|
|
51
|
+
right: 3,
|
|
52
|
+
width: 7,
|
|
53
|
+
height: 7,
|
|
54
|
+
borderRadius: "50%",
|
|
55
|
+
background: "#ef4444",
|
|
56
|
+
border: "1.5px solid var(--af-surface)",
|
|
57
|
+
}}
|
|
58
|
+
/>
|
|
59
|
+
)}
|
|
60
|
+
</Link>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { STORAGE_KEY } from "@/lib/changelog";
|
|
5
|
+
|
|
6
|
+
export function ChangelogMarkRead({ version }: { version: string | null }) {
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (!version) return;
|
|
9
|
+
window.localStorage.setItem(STORAGE_KEY, version);
|
|
10
|
+
}, [version]);
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
@@ -473,7 +473,7 @@ function SessionRow({ entry }: { entry: Entry }) {
|
|
|
473
473
|
<span style={{ fontSize: 11, fontFamily: "var(--font-mono)", color: "var(--af-text-tertiary)", width: 40, textAlign: "right" }}>
|
|
474
474
|
{durMin}m
|
|
475
475
|
</span>
|
|
476
|
-
{outcome && <OutcomePill outcome={outcome} size="sm" label="text" />}
|
|
476
|
+
{outcome && <OutcomePill outcome={outcome} size="sm" label="text" agent={entry.agent} />}
|
|
477
477
|
</div>
|
|
478
478
|
{summary && (
|
|
479
479
|
<div style={{
|
|
@@ -17,6 +17,7 @@ import { usePathname } from "next/navigation";
|
|
|
17
17
|
import Link from "next/link";
|
|
18
18
|
import { prettyProjectName } from "@/lib/format";
|
|
19
19
|
import { TeamBadge } from "@/components/team-badge";
|
|
20
|
+
import { AgentBadge } from "@/components/agent-badge";
|
|
20
21
|
import { OutcomePill } from "@/components/outcome-pill";
|
|
21
22
|
import type { DayOutcome, EntryEnrichmentStatus } from "@claude-lens/entries";
|
|
22
23
|
|
|
@@ -34,6 +35,7 @@ export type LiveSessionPick = {
|
|
|
34
35
|
lastTimestamp?: string;
|
|
35
36
|
teamName?: string;
|
|
36
37
|
agentName?: string;
|
|
38
|
+
agent?: import("@claude-lens/parser").AgentKind;
|
|
37
39
|
};
|
|
38
40
|
|
|
39
41
|
export type LiveEntrySummary = {
|
|
@@ -212,7 +214,7 @@ export function LiveSessionsWidget({
|
|
|
212
214
|
style={{ marginBottom: 4 }}
|
|
213
215
|
>
|
|
214
216
|
{sum.outcome ? (
|
|
215
|
-
<OutcomePill outcome={sum.outcome} size="sm" label="text" />
|
|
217
|
+
<OutcomePill outcome={sum.outcome} size="sm" label="text" agent={s.agent} />
|
|
216
218
|
) : sum.enrichmentStatus !== "skipped_trivial" ? (
|
|
217
219
|
<OutcomePill
|
|
218
220
|
outcome={null}
|
|
@@ -220,6 +222,7 @@ export function LiveSessionsWidget({
|
|
|
220
222
|
sessionId={s.id}
|
|
221
223
|
localDay={sum.localDay}
|
|
222
224
|
size="sm"
|
|
225
|
+
agent={s.agent}
|
|
223
226
|
/>
|
|
224
227
|
) : null}
|
|
225
228
|
</div>
|
|
@@ -248,6 +251,7 @@ export function LiveSessionsWidget({
|
|
|
248
251
|
)}
|
|
249
252
|
</span>
|
|
250
253
|
<TeamBadge session={s} linkable={false} />
|
|
254
|
+
<AgentBadge agent={s.agent} />
|
|
251
255
|
</div>
|
|
252
256
|
{/* Subtitle: what the agent is saying in response. */}
|
|
253
257
|
<div
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
2
|
import type { DayOutcome } from "@claude-lens/entries";
|
|
3
|
+
import type { AgentKind } from "@claude-lens/parser";
|
|
4
|
+
import { AgentIcon } from "@/components/agent-icon";
|
|
3
5
|
|
|
4
6
|
export type EntryOutcome = Exclude<DayOutcome, "idle">;
|
|
5
7
|
|
|
@@ -31,12 +33,13 @@ type Size = keyof typeof SIZES;
|
|
|
31
33
|
type Label = "icon" | "text" | "both";
|
|
32
34
|
|
|
33
35
|
type Props =
|
|
34
|
-
| { outcome: DayOutcome | EntryOutcome; size?: Size; label?: Label; pending?: never }
|
|
35
|
-
| { outcome: null; pending: true; sessionId?: string; localDay: string; size?: Size };
|
|
36
|
+
| { outcome: DayOutcome | EntryOutcome; size?: Size; label?: Label; agent?: AgentKind; pending?: never }
|
|
37
|
+
| { outcome: null; pending: true; sessionId?: string; localDay: string; size?: Size; agent?: AgentKind };
|
|
36
38
|
|
|
37
39
|
export function OutcomePill(props: Props) {
|
|
38
40
|
const size = props.size ?? "md";
|
|
39
41
|
const dim = SIZES[size];
|
|
42
|
+
const agentIconSize = dim.fontSize + 1;
|
|
40
43
|
|
|
41
44
|
if ("pending" in props && props.pending) {
|
|
42
45
|
const style: React.CSSProperties = {
|
|
@@ -59,6 +62,7 @@ export function OutcomePill(props: Props) {
|
|
|
59
62
|
style={style}
|
|
60
63
|
title={`Generate ${props.localDay} digest →`}
|
|
61
64
|
>
|
|
65
|
+
{props.agent && <AgentIcon agent={props.agent} size={agentIconSize} />}
|
|
62
66
|
<span aria-hidden style={{ lineHeight: 1 }}>{PENDING_STYLE.icon}</span>
|
|
63
67
|
<span>{PENDING_STYLE.label}</span>
|
|
64
68
|
</Link>
|
|
@@ -85,6 +89,7 @@ export function OutcomePill(props: Props) {
|
|
|
85
89
|
|
|
86
90
|
return (
|
|
87
91
|
<span style={style} title={variant.label}>
|
|
92
|
+
{props.agent && <AgentIcon agent={props.agent} size={agentIconSize} />}
|
|
88
93
|
{showIcon && <span aria-hidden style={{ lineHeight: 1 }}>{variant.icon}</span>}
|
|
89
94
|
{showText && <span>{variant.label}</span>}
|
|
90
95
|
</span>
|