fleetlens 0.12.3 → 0.13.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/build-manifest.json +8 -8
- package/app/apps/web/.next/prerender-manifest.json +3 -3
- package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/_global-error.html +1 -1
- package/app/apps/web/.next/server/app/_global-error.rsc +7 -7
- package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
- package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
- package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
- package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/app/apps/web/.next/server/app/_not-found/page/build-manifest.json +5 -5
- 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.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
- 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 +1 -1
- 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.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +1 -1
- 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.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/events/route.js +2 -1
- package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/changelog/page/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/insights/page.js +3 -2
- package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/page.js +1 -1
- 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 +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/projects/page/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +5 -5
- 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/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/settings/page.js +1 -1
- 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/page/build-manifest.json +5 -5
- package/app/apps/web/.next/server/app/team/page.js +1 -1
- package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
- package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
- package/app/apps/web/.next/server/app/usage/page/build-manifest.json +5 -5
- 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/chunks/[externals]__0rv9ct5._.js +3 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gril0u._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0n79hpm._.js +9 -0
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~utv~v._.js +9 -0
- package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_02bw8.n.js +5 -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]__0exckd4._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{[root-of-the-server]__0-1_njm._.js → [root-of-the-server]__0k1x-c9._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lhwp-k._.js +8 -0
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o03ymy._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +3 -3
- package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/_0s3n5mz._.js +8 -0
- package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/{_0jw2sm7._.js → _12mbgs5._.js} +2 -2
- package/app/apps/web/.next/server/chunks/ssr/apps_web_0.mu7c7._.js +1 -1
- 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_insights_page_tsx_071kwyw._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_sessions-grid_tsx_0bri5x.._.js +3 -0
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0b-4-4j._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0c-tpft._.js +1 -1
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0l6hsch._.js +6 -0
- 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/node_modules_lucide-react_dist_esm_icons_0cepfi8._.js +3 -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/middleware-build-manifest.js +8 -8
- 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/02bv_ejzhhzvp.js +10 -0
- package/app/apps/web/.next/static/chunks/{07lbm-sdh9c2j.js → 0389wb_f0j78j.js} +1 -1
- package/app/apps/web/.next/static/chunks/03gy1evbn94qu.js +6 -0
- package/app/apps/web/.next/static/chunks/03rd.3cc_t~yf.js +4 -0
- package/app/apps/web/.next/static/chunks/{12jvvc_w.cb45.css → 083~hzkv577.5.css} +1 -1
- package/app/apps/web/.next/static/chunks/{0osdgl2.9cl1q.js → 0d2x-wfu.m0-z.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0lziv8y_6yp5w.js → 0e_fzy0n8ij-o.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0l4e3as18eexy.js → 0g223u9amib0q.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0h3.-_s-k0mj3.js → 0i.hc~2bj0_3d.js} +1 -1
- package/app/apps/web/.next/static/chunks/0m7dd-2zd9vl2.js +4 -0
- package/app/apps/web/.next/static/chunks/0nqklmbs5s3ax.js +6 -0
- package/app/apps/web/.next/static/chunks/{073081qv4uyn8.js → 0p--hzrway8~3.js} +1 -1
- package/app/apps/web/.next/static/chunks/0q8dg4txlzno0.js +1 -0
- package/app/apps/web/.next/static/chunks/{0gblp_v.kiu-..js → 0qv-gw888_5nu.js} +1 -1
- package/app/apps/web/.next/static/chunks/{14bjditmmrn7l.js → 0tirv_vo6q8uc.js} +1 -1
- package/app/apps/web/.next/static/chunks/{0qmh5da6gfm56.js → 0vz4~a~~hq399.js} +1 -1
- package/app/apps/web/.next/static/chunks/{13po66tt.68f3.js → 0wwcx42ng8i2e.js} +3 -3
- package/app/apps/web/.next/static/chunks/{0n0n.0279ot6c.js → 14vx702c32kn6.js} +1 -1
- package/app/apps/web/.next/static/chunks/{turbopack-0lb9w090w5ezx.js → turbopack-0mxu3~9vk.brh.js} +1 -1
- package/app/apps/web/app/api/digest/_shared/digest-route-helpers.ts +178 -0
- package/app/apps/web/app/api/digest/day/[date]/route.ts +34 -128
- package/app/apps/web/app/api/digest/month/[yearMonth]/route.ts +36 -121
- package/app/apps/web/app/api/digest/week/[startDate]/route.ts +36 -123
- package/app/apps/web/app/api/events/route.ts +47 -4
- package/app/apps/web/app/globals.css +23 -0
- package/app/apps/web/app/insights/page.tsx +20 -5
- package/app/apps/web/app/layout.tsx +1 -0
- package/app/apps/web/app/page.tsx +1 -1
- package/app/apps/web/app/parallelism/burst-detail-modal.tsx +637 -0
- package/app/apps/web/app/parallelism/concurrency-info-modal.tsx +228 -0
- package/app/apps/web/app/parallelism/gantt-chart-utils.ts +49 -0
- package/app/apps/web/app/parallelism/gantt-chart.tsx +16 -902
- package/app/apps/web/app/projects/[slug]/page.tsx +1 -1
- package/app/apps/web/app/sessions/[id]/session-view/colors.ts +31 -0
- package/app/apps/web/app/sessions/[id]/session-view/debug-list.tsx +78 -0
- package/app/apps/web/app/sessions/[id]/session-view/drawer.tsx +414 -0
- package/app/apps/web/app/sessions/[id]/session-view/entry-day-strip.tsx +124 -0
- package/app/apps/web/app/sessions/[id]/session-view/header-stats.tsx +217 -0
- package/app/apps/web/app/sessions/[id]/session-view/helpers.ts +135 -0
- package/app/apps/web/app/sessions/[id]/session-view/subagent-drawer.tsx +339 -0
- package/app/apps/web/app/sessions/[id]/session-view/token-stats.tsx +111 -0
- package/app/apps/web/app/sessions/[id]/session-view/tooltip.tsx +63 -0
- package/app/apps/web/app/sessions/[id]/session-view/types.ts +46 -0
- package/app/apps/web/app/sessions/[id]/session-view/workflows-shared.tsx +104 -0
- package/app/apps/web/app/sessions/[id]/session-view.tsx +701 -1577
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-hover-card.tsx +201 -0
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-idle-band.tsx +53 -0
- package/app/apps/web/app/sessions/[id]/team-tab/minimap-shared.ts +20 -0
- package/app/apps/web/app/sessions/[id]/team-tab/team-minimap.tsx +10 -265
- package/app/apps/web/app/sessions/sessions-grid.tsx +43 -19
- package/app/apps/web/components/digest-actions.tsx +103 -0
- package/app/apps/web/components/job-queue-widget.tsx +1 -1
- package/app/apps/web/components/live-badge.tsx +11 -3
- package/app/apps/web/components/live-sessions-widget.tsx +9 -3
- package/app/apps/web/components/month-digest-view.tsx +19 -64
- package/app/apps/web/components/outcome-pill.tsx +24 -4
- package/app/apps/web/components/searchable-select.tsx +215 -0
- package/app/apps/web/components/tail-mode.tsx +39 -8
- package/app/apps/web/components/week-digest-view.tsx +16 -71
- package/app/apps/web/lib/changelog-data.generated.ts +1 -1
- package/app/apps/web/lib/entries-index.ts +7 -8
- package/app/apps/web/lib/entries.ts +0 -9
- package/app/apps/web/lib/format.test.ts +221 -0
- package/app/apps/web/lib/utilization-tone.test.ts +56 -0
- package/app/apps/web/package.json +1 -1
- package/app/apps/web/tsconfig.tsbuildinfo +1 -1
- package/dist/daemon-worker.js +83 -45
- package/dist/index.js +452 -198
- package/package.json +1 -1
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0455vu3._.js +0 -5
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__04j3tng._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__0cnh3k4._.js +0 -9
- package/app/apps/web/.next/server/chunks/[root-of-the-server]__132eosx._.js +0 -9
- package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +0 -8
- package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +0 -8
- package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +0 -6
- package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0-lmm4p._.js +0 -3
- package/app/apps/web/.next/static/chunks/00xkqnvk4-d1-.js +0 -10
- package/app/apps/web/.next/static/chunks/0htgc_hr1bw67.js +0 -1
- package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +0 -4
- package/app/apps/web/.next/static/chunks/0okg13phslr1..js +0 -4
- package/app/apps/web/.next/static/chunks/0vf-3rauafbek.js +0 -6
- package/app/apps/web/.next/static/chunks/0vl27-vjob2_9.js +0 -6
- /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_buildManifest.js +0 -0
- /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_clientMiddlewareManifest.js +0 -0
- /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_ssgManifest.js +0 -0
|
@@ -2,146 +2,61 @@ import {
|
|
|
2
2
|
runMonthDigestPipeline, readSettings,
|
|
3
3
|
readMonthDigest, getCurrentMonthDigestFromCache,
|
|
4
4
|
} from "@claude-lens/entries/node";
|
|
5
|
-
import type { PipelineEvent } from "@claude-lens/entries/node";
|
|
6
|
-
import { InflightCoalescer } from "@/lib/inflight-coalesce";
|
|
7
5
|
import {
|
|
8
6
|
isValidYearMonth, currentYearMonth, currentWeekMonday, todayLocal,
|
|
9
7
|
} from "@/lib/entries";
|
|
10
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
handleDigestGet, handleDigestPost, jsonResponse, jsonResponseBare,
|
|
10
|
+
} from "../../_shared/digest-route-helpers";
|
|
11
11
|
|
|
12
12
|
export const runtime = "nodejs";
|
|
13
13
|
export const dynamic = "force-dynamic";
|
|
14
14
|
|
|
15
15
|
type Params = { params: Promise<{ yearMonth: string }> };
|
|
16
16
|
|
|
17
|
-
const coalescer = new InflightCoalescer<string, void>();
|
|
18
|
-
|
|
19
|
-
function updateJobFromEvent(jobId: string, ev: PipelineEvent): void {
|
|
20
|
-
if (ev.type === "status") {
|
|
21
|
-
updateJob(jobId, { progress: { phase: ev.phase, text: ev.text } });
|
|
22
|
-
} else if (ev.type === "entry") {
|
|
23
|
-
updateJob(jobId, { progress: { phase: "enrich", index: ev.index, total: ev.total } });
|
|
24
|
-
} else if (ev.type === "progress") {
|
|
25
|
-
updateJob(jobId, { progress: { phase: ev.phase, bytes: ev.bytes } });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
17
|
export async function GET(_req: Request, ctx: Params) {
|
|
30
18
|
const { yearMonth } = await ctx.params;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (cached) {
|
|
44
|
-
return new Response(JSON.stringify(cached), {
|
|
45
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
return new Response(JSON.stringify({ pending: true, current: true }), {
|
|
49
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const cached = readMonthDigest(yearMonth);
|
|
54
|
-
if (cached) {
|
|
55
|
-
return new Response(JSON.stringify(cached), {
|
|
56
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return new Response(JSON.stringify({ pending: true }), {
|
|
60
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
19
|
+
const valid = isValidYearMonth(yearMonth);
|
|
20
|
+
const isFuture = valid && yearMonth > currentYearMonth();
|
|
21
|
+
return handleDigestGet({
|
|
22
|
+
key: valid ? yearMonth : null,
|
|
23
|
+
invalidResponse: jsonResponse({ error: "invalid year-month" }, 400),
|
|
24
|
+
isFuture,
|
|
25
|
+
futureResponse: jsonResponseBare({ error: "future month" }, 400),
|
|
26
|
+
isCurrent: valid && yearMonth === currentYearMonth(),
|
|
27
|
+
readCurrentCache: () => getCurrentMonthDigestFromCache(yearMonth, Date.now()),
|
|
28
|
+
readPersisted: () => readMonthDigest(yearMonth),
|
|
29
|
+
pendingCurrentPayload: { pending: true, current: true },
|
|
30
|
+
currentFallsBackToPersisted: false,
|
|
61
31
|
});
|
|
62
32
|
}
|
|
63
33
|
|
|
64
34
|
export async function POST(req: Request, ctx: Params) {
|
|
65
35
|
const { yearMonth } = await ctx.params;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
if (yearMonth > currentYearMonth()) {
|
|
70
|
-
return new Response(JSON.stringify({ error: "future month" }), { status: 400 });
|
|
71
|
-
}
|
|
72
|
-
|
|
36
|
+
const valid = isValidYearMonth(yearMonth);
|
|
37
|
+
const isFuture = valid && yearMonth > currentYearMonth();
|
|
73
38
|
const url = new URL(req.url);
|
|
74
39
|
const force = url.searchParams.get("force") === "1";
|
|
75
|
-
const key = `month|${yearMonth}|${force ? 1 : 0}`;
|
|
76
|
-
|
|
77
|
-
const encoder = new TextEncoder();
|
|
78
40
|
const settings = readSettings();
|
|
79
41
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
let jobId: string | null = null;
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
await coalescer.run(key, async () => {
|
|
103
|
-
if (alreadyInflight) return;
|
|
104
|
-
jobId = registerJob({
|
|
105
|
-
kind: "monthly.synth",
|
|
106
|
-
label: `Month digest · ${yearMonth}`,
|
|
107
|
-
target: yearMonth,
|
|
108
|
-
caller: "user",
|
|
109
|
-
});
|
|
110
|
-
updateJob(jobId, { status: "running" });
|
|
111
|
-
for await (const ev of runMonthDigestPipeline(yearMonth, {
|
|
112
|
-
settings: settings.ai_features,
|
|
113
|
-
force,
|
|
114
|
-
currentYearMonth: currentYearMonth(),
|
|
115
|
-
currentWeekMonday: currentWeekMonday(),
|
|
116
|
-
todayLocalDay: todayLocal(),
|
|
117
|
-
})) {
|
|
118
|
-
send(ev);
|
|
119
|
-
if (jobId) updateJobFromEvent(jobId, ev);
|
|
120
|
-
}
|
|
121
|
-
if (jobId) completeJob(jobId, `/insights/month-${yearMonth}`);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
if (alreadyInflight) {
|
|
125
|
-
const d = readMonthDigest(yearMonth) ?? getCurrentMonthDigestFromCache(yearMonth, Date.now());
|
|
126
|
-
if (d) send({ type: "digest", digest: d });
|
|
127
|
-
send({ type: "status", phase: "persist", text: "coalesced with in-flight request" });
|
|
128
|
-
}
|
|
129
|
-
} catch (err) {
|
|
130
|
-
send({ type: "error", message: (err as Error).message });
|
|
131
|
-
if (jobId) failJob(jobId, (err as Error).message);
|
|
132
|
-
} finally {
|
|
133
|
-
try { controller.enqueue(encoder.encode(`event: done\ndata: {}\n\n`)); } catch { /* ignore */ }
|
|
134
|
-
finish();
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
return new Response(stream, {
|
|
140
|
-
headers: {
|
|
141
|
-
"Content-Type": "text/event-stream",
|
|
142
|
-
"Cache-Control": "no-cache, no-transform",
|
|
143
|
-
Connection: "keep-alive",
|
|
144
|
-
"X-Accel-Buffering": "no",
|
|
145
|
-
},
|
|
42
|
+
return handleDigestPost({
|
|
43
|
+
key: valid ? yearMonth : null,
|
|
44
|
+
invalidResponse: jsonResponseBare({ error: "invalid year-month" }, 400),
|
|
45
|
+
isFuture,
|
|
46
|
+
futureResponse: jsonResponseBare({ error: "future month" }, 400),
|
|
47
|
+
coalesceScope: "month",
|
|
48
|
+
force,
|
|
49
|
+
jobKind: "monthly.synth",
|
|
50
|
+
jobLabel: `Month digest · ${yearMonth}`,
|
|
51
|
+
resultUrl: `/insights/month-${yearMonth}`,
|
|
52
|
+
runPipeline: () => runMonthDigestPipeline(yearMonth, {
|
|
53
|
+
settings: settings.ai_features,
|
|
54
|
+
force,
|
|
55
|
+
currentYearMonth: currentYearMonth(),
|
|
56
|
+
currentWeekMonday: currentWeekMonday(),
|
|
57
|
+
todayLocalDay: todayLocal(),
|
|
58
|
+
}),
|
|
59
|
+
readDigestForCoalescedRequest: () =>
|
|
60
|
+
readMonthDigest(yearMonth) ?? getCurrentMonthDigestFromCache(yearMonth, Date.now()),
|
|
146
61
|
});
|
|
147
62
|
}
|
|
@@ -2,149 +2,62 @@ import {
|
|
|
2
2
|
runWeekDigestPipeline, readSettings,
|
|
3
3
|
readWeekDigest, getCurrentWeekDigestFromCache,
|
|
4
4
|
} from "@claude-lens/entries/node";
|
|
5
|
-
import type { PipelineEvent } from "@claude-lens/entries/node";
|
|
6
|
-
import { InflightCoalescer } from "@/lib/inflight-coalesce";
|
|
7
5
|
import { asMonday, currentWeekMonday, todayLocal } from "@/lib/entries";
|
|
8
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
handleDigestGet, handleDigestPost, jsonResponse, jsonResponseBare,
|
|
8
|
+
} from "../../_shared/digest-route-helpers";
|
|
9
9
|
|
|
10
10
|
export const runtime = "nodejs";
|
|
11
11
|
export const dynamic = "force-dynamic";
|
|
12
12
|
|
|
13
13
|
type Params = { params: Promise<{ startDate: string }> };
|
|
14
14
|
|
|
15
|
-
const coalescer = new InflightCoalescer<string, void>();
|
|
16
|
-
|
|
17
|
-
function updateJobFromEvent(jobId: string, ev: PipelineEvent): void {
|
|
18
|
-
if (ev.type === "status") {
|
|
19
|
-
updateJob(jobId, { progress: { phase: ev.phase, text: ev.text } });
|
|
20
|
-
} else if (ev.type === "entry") {
|
|
21
|
-
updateJob(jobId, { progress: { phase: "enrich", index: ev.index, total: ev.total } });
|
|
22
|
-
} else if (ev.type === "progress") {
|
|
23
|
-
updateJob(jobId, { progress: { phase: ev.phase, bytes: ev.bytes } });
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
15
|
export async function GET(_req: Request, ctx: Params) {
|
|
28
16
|
const { startDate } = await ctx.params;
|
|
29
17
|
const monday = asMonday(startDate);
|
|
30
|
-
if (!monday) {
|
|
31
|
-
return new Response(JSON.stringify({ error: "invalid Monday date" }), {
|
|
32
|
-
status: 400, headers: { "content-type": "application/json" },
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
18
|
const today = todayLocal();
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
return new Response(JSON.stringify({ pending: true, current: true }), {
|
|
50
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const cached = readWeekDigest(monday);
|
|
55
|
-
if (cached) {
|
|
56
|
-
return new Response(JSON.stringify(cached), {
|
|
57
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
return new Response(JSON.stringify({ pending: true }), {
|
|
61
|
-
status: 200, headers: { "content-type": "application/json" },
|
|
19
|
+
const isFuture = monday !== null && monday > today;
|
|
20
|
+
return handleDigestGet({
|
|
21
|
+
key: monday,
|
|
22
|
+
invalidResponse: jsonResponse({ error: "invalid Monday date" }, 400),
|
|
23
|
+
isFuture,
|
|
24
|
+
futureResponse: jsonResponseBare({ error: "future date" }, 400),
|
|
25
|
+
isCurrent: monday !== null && monday === currentWeekMonday(),
|
|
26
|
+
readCurrentCache: () =>
|
|
27
|
+
monday !== null ? getCurrentWeekDigestFromCache(monday, Date.now()) : null,
|
|
28
|
+
readPersisted: () => (monday !== null ? readWeekDigest(monday) : null),
|
|
29
|
+
pendingCurrentPayload: { pending: true, current: true },
|
|
30
|
+
currentFallsBackToPersisted: true,
|
|
62
31
|
});
|
|
63
32
|
}
|
|
64
33
|
|
|
65
34
|
export async function POST(req: Request, ctx: Params) {
|
|
66
35
|
const { startDate } = await ctx.params;
|
|
67
36
|
const monday = asMonday(startDate);
|
|
68
|
-
|
|
69
|
-
return new Response(JSON.stringify({ error: "invalid Monday date" }), { status: 400 });
|
|
70
|
-
}
|
|
71
|
-
if (monday > todayLocal()) {
|
|
72
|
-
return new Response(JSON.stringify({ error: "future date" }), { status: 400 });
|
|
73
|
-
}
|
|
74
|
-
|
|
37
|
+
const isFuture = monday !== null && monday > todayLocal();
|
|
75
38
|
const url = new URL(req.url);
|
|
76
39
|
const force = url.searchParams.get("force") === "1";
|
|
77
|
-
const key = `week|${monday}|${force ? 1 : 0}`;
|
|
78
|
-
|
|
79
|
-
const encoder = new TextEncoder();
|
|
80
40
|
const settings = readSettings();
|
|
81
41
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const alreadyInflight = coalescer.inflight(key);
|
|
103
|
-
let jobId: string | null = null;
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
await coalescer.run(key, async () => {
|
|
107
|
-
if (alreadyInflight) return;
|
|
108
|
-
jobId = registerJob({
|
|
109
|
-
kind: "weekly.synth",
|
|
110
|
-
label: `Week digest · ${monday}`,
|
|
111
|
-
target: monday,
|
|
112
|
-
caller: "user",
|
|
113
|
-
});
|
|
114
|
-
updateJob(jobId, { status: "running" });
|
|
115
|
-
for await (const ev of runWeekDigestPipeline(monday, {
|
|
116
|
-
settings: settings.ai_features,
|
|
117
|
-
force,
|
|
118
|
-
currentWeekMonday: currentWeekMonday(),
|
|
119
|
-
todayLocalDay: todayLocal(),
|
|
120
|
-
})) {
|
|
121
|
-
send(ev);
|
|
122
|
-
if (jobId) updateJobFromEvent(jobId, ev);
|
|
123
|
-
}
|
|
124
|
-
if (jobId) completeJob(jobId, `/insights/week-${monday}`);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (alreadyInflight) {
|
|
128
|
-
const d = readWeekDigest(monday) ?? getCurrentWeekDigestFromCache(monday, Date.now());
|
|
129
|
-
if (d) send({ type: "digest", digest: d });
|
|
130
|
-
send({ type: "status", phase: "persist", text: "coalesced with in-flight request" });
|
|
131
|
-
}
|
|
132
|
-
} catch (err) {
|
|
133
|
-
send({ type: "error", message: (err as Error).message });
|
|
134
|
-
if (jobId) failJob(jobId, (err as Error).message);
|
|
135
|
-
} finally {
|
|
136
|
-
try { controller.enqueue(encoder.encode(`event: done\ndata: {}\n\n`)); } catch { /* ignore */ }
|
|
137
|
-
finish();
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
return new Response(stream, {
|
|
143
|
-
headers: {
|
|
144
|
-
"Content-Type": "text/event-stream",
|
|
145
|
-
"Cache-Control": "no-cache, no-transform",
|
|
146
|
-
Connection: "keep-alive",
|
|
147
|
-
"X-Accel-Buffering": "no",
|
|
148
|
-
},
|
|
42
|
+
return handleDigestPost({
|
|
43
|
+
key: monday,
|
|
44
|
+
invalidResponse: jsonResponseBare({ error: "invalid Monday date" }, 400),
|
|
45
|
+
isFuture,
|
|
46
|
+
futureResponse: jsonResponseBare({ error: "future date" }, 400),
|
|
47
|
+
coalesceScope: "week",
|
|
48
|
+
force,
|
|
49
|
+
jobKind: "weekly.synth",
|
|
50
|
+
jobLabel: `Week digest · ${monday}`,
|
|
51
|
+
resultUrl: `/insights/week-${monday}`,
|
|
52
|
+
runPipeline: () => runWeekDigestPipeline(monday as string, {
|
|
53
|
+
settings: settings.ai_features,
|
|
54
|
+
force,
|
|
55
|
+
currentWeekMonday: currentWeekMonday(),
|
|
56
|
+
todayLocalDay: todayLocal(),
|
|
57
|
+
}),
|
|
58
|
+
readDigestForCoalescedRequest: () =>
|
|
59
|
+
monday !== null
|
|
60
|
+
? (readWeekDigest(monday) ?? getCurrentWeekDigestFromCache(monday, Date.now()))
|
|
61
|
+
: null,
|
|
149
62
|
});
|
|
150
63
|
}
|
|
@@ -50,6 +50,11 @@ const HEARTBEAT_MS = 15_000;
|
|
|
50
50
|
/** Per-file debounce so a burst of writes doesn't flood subscribers. */
|
|
51
51
|
const DEBOUNCE_MS = 150;
|
|
52
52
|
|
|
53
|
+
/** Subagent writes coalesce per PARENT session over a much longer window — a
|
|
54
|
+
* 16-agent workflow fan-out writes constantly, so this caps that to at most
|
|
55
|
+
* one parent refresh every 2s instead of a storm. */
|
|
56
|
+
const SUBAGENT_DEBOUNCE_MS = 2_000;
|
|
57
|
+
|
|
53
58
|
export async function GET(request: Request) {
|
|
54
59
|
const encoder = new TextEncoder();
|
|
55
60
|
|
|
@@ -87,9 +92,10 @@ export async function GET(request: Request) {
|
|
|
87
92
|
const projectDir = parts[0]!;
|
|
88
93
|
const fileName = parts[parts.length - 1]!;
|
|
89
94
|
if (!fileName.endsWith(".jsonl")) return;
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
95
|
+
// Subagent child files under <session>/subagents/ are routed to
|
|
96
|
+
// emitSubagent (throttled, mapped to the parent) by the dispatch
|
|
97
|
+
// below — this early-return is a safety net so they never go
|
|
98
|
+
// through the per-file fast path here.
|
|
93
99
|
if (parts.includes("subagents")) return;
|
|
94
100
|
const sessionId = fileName.replace(/\.jsonl$/, "");
|
|
95
101
|
|
|
@@ -148,6 +154,42 @@ export async function GET(request: Request) {
|
|
|
148
154
|
);
|
|
149
155
|
}
|
|
150
156
|
|
|
157
|
+
// Background-agent transcripts (`<session>/subagents/agent-*.jsonl`, and
|
|
158
|
+
// workflow agents at `<session>/subagents/workflows/<runId>/agent-*.jsonl`)
|
|
159
|
+
// are written while the parent session sits idle. Map them back to the
|
|
160
|
+
// parent and emit a session-updated so the LIVE indicator (now nested-
|
|
161
|
+
// aware via lastActivityMs) refreshes. Coalesced per-parent over a long
|
|
162
|
+
// window so a big fan-out can't flood subscribers.
|
|
163
|
+
function emitSubagent(fullPath: string) {
|
|
164
|
+
const rel = path.relative(DEFAULT_ROOT, fullPath);
|
|
165
|
+
const parts = rel.split(path.sep);
|
|
166
|
+
const subIdx = parts.lastIndexOf("subagents");
|
|
167
|
+
if (subIdx < 2) return;
|
|
168
|
+
const projectDir = parts[0]!;
|
|
169
|
+
const sessionId = parts[subIdx - 1]!;
|
|
170
|
+
const parentJsonl = path.join(DEFAULT_ROOT, projectDir, `${sessionId}.jsonl`);
|
|
171
|
+
const key = `sub:${parentJsonl}`;
|
|
172
|
+
const prev = pending.get(key);
|
|
173
|
+
if (prev) clearTimeout(prev);
|
|
174
|
+
pending.set(
|
|
175
|
+
key,
|
|
176
|
+
setTimeout(async () => {
|
|
177
|
+
pending.delete(key);
|
|
178
|
+
if (closed) return;
|
|
179
|
+
invalidateFile(parentJsonl);
|
|
180
|
+
// Report the child's fresh mtime — the parent .jsonl may be stale,
|
|
181
|
+
// and LiveRefresher dedups per source by a monotonic mtime.
|
|
182
|
+
let mtimeMs = Date.now();
|
|
183
|
+
try {
|
|
184
|
+
mtimeMs = (await fs.stat(fullPath)).mtimeMs;
|
|
185
|
+
} catch {
|
|
186
|
+
// child gone already — fall back to now.
|
|
187
|
+
}
|
|
188
|
+
send({ type: "session-updated", sessionId, projectDir, mtimeMs });
|
|
189
|
+
}, SUBAGENT_DEBOUNCE_MS),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
151
193
|
let watcher: ReturnType<typeof watch> | null = null;
|
|
152
194
|
try {
|
|
153
195
|
watcher = watch(
|
|
@@ -157,7 +199,8 @@ export async function GET(request: Request) {
|
|
|
157
199
|
if (!filename) return;
|
|
158
200
|
const fullPath = path.join(DEFAULT_ROOT, filename.toString());
|
|
159
201
|
if (fullPath.endsWith(".jsonl")) {
|
|
160
|
-
|
|
202
|
+
if (fullPath.includes(`${path.sep}subagents${path.sep}`)) emitSubagent(fullPath);
|
|
203
|
+
else emit(fullPath);
|
|
161
204
|
return;
|
|
162
205
|
}
|
|
163
206
|
const base = path.basename(fullPath);
|
|
@@ -141,6 +141,29 @@ input::placeholder {
|
|
|
141
141
|
border-radius: 6px !important;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
+
/* Day-navigator step buttons on the session timeline minimap. */
|
|
145
|
+
.sl-day-nav-btn {
|
|
146
|
+
display: inline-flex;
|
|
147
|
+
align-items: center;
|
|
148
|
+
justify-content: center;
|
|
149
|
+
width: 26px;
|
|
150
|
+
height: 26px;
|
|
151
|
+
border: 1px solid var(--af-border-subtle);
|
|
152
|
+
border-radius: 6px;
|
|
153
|
+
background: var(--af-surface-hover);
|
|
154
|
+
color: var(--af-text-secondary);
|
|
155
|
+
cursor: pointer;
|
|
156
|
+
transition: background 0.15s ease, opacity 0.15s ease;
|
|
157
|
+
}
|
|
158
|
+
.sl-day-nav-btn:hover:not(:disabled) {
|
|
159
|
+
background: var(--af-surface-elevated);
|
|
160
|
+
color: var(--af-text-primary);
|
|
161
|
+
}
|
|
162
|
+
.sl-day-nav-btn:disabled {
|
|
163
|
+
opacity: 0.35;
|
|
164
|
+
cursor: default;
|
|
165
|
+
}
|
|
166
|
+
|
|
144
167
|
/* Scrollbar */
|
|
145
168
|
::-webkit-scrollbar {
|
|
146
169
|
width: 8px;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import Link from "next/link";
|
|
2
2
|
import { readSettings, readWeekDigest, interactiveLockFresh } from "@claude-lens/entries/node";
|
|
3
|
-
import {
|
|
3
|
+
import { listEntryKeys } from "@claude-lens/entries/fs";
|
|
4
|
+
import { parseEntryKey } from "@claude-lens/entries";
|
|
5
|
+
import { lastCompletedWeekMonday, currentWeekMonday } from "@/lib/entries";
|
|
4
6
|
import { WeekDigestView } from "@/components/week-digest-view";
|
|
5
7
|
import { InsightsTopBar } from "@/components/insights-top-bar";
|
|
6
8
|
|
|
@@ -24,9 +26,21 @@ function weekDates(monday: string): string[] {
|
|
|
24
26
|
return out;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
// Build once from the entries dir, then test the 7 weekdays against it.
|
|
30
|
+
// Earlier this called listEntriesForDay per day per render, which opened
|
|
31
|
+
// and parsed every matching entry file 14× before deciding the week is empty.
|
|
32
|
+
function presentDays(): Set<string> {
|
|
33
|
+
const out = new Set<string>();
|
|
34
|
+
for (const key of listEntryKeys()) {
|
|
35
|
+
const parsed = parseEntryKey(key);
|
|
36
|
+
if (parsed) out.add(parsed.local_day);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function hasAnyEntries(monday: string, present: Set<string>): boolean {
|
|
28
42
|
for (const d of weekDates(monday)) {
|
|
29
|
-
if (
|
|
43
|
+
if (present.has(d)) return true;
|
|
30
44
|
}
|
|
31
45
|
return false;
|
|
32
46
|
}
|
|
@@ -45,7 +59,8 @@ export default async function InsightsPage() {
|
|
|
45
59
|
const lastWeek = lastCompletedWeekMonday();
|
|
46
60
|
const cached = readWeekDigest(lastWeek);
|
|
47
61
|
const prior = readWeekDigest(priorMonday(lastWeek));
|
|
48
|
-
const
|
|
62
|
+
const present = presentDays();
|
|
63
|
+
const hasData = !!cached || hasAnyEntries(lastWeek, present);
|
|
49
64
|
|
|
50
65
|
// Auto-fire when AI is on and the digest isn't already cached. The interactive
|
|
51
66
|
// pipeline lock (heartbeat-refreshed) suppresses concurrent fires from the
|
|
@@ -58,7 +73,7 @@ export default async function InsightsPage() {
|
|
|
58
73
|
const prevMonday = priorMonday(lastWeek);
|
|
59
74
|
const currentMonday = currentWeekMonday();
|
|
60
75
|
const nextMonday = currentMonday > lastWeek ? currentMonday : null;
|
|
61
|
-
const nextHasData = nextMonday ? hasAnyEntries(nextMonday) : false;
|
|
76
|
+
const nextHasData = nextMonday ? hasAnyEntries(nextMonday, present) : false;
|
|
62
77
|
|
|
63
78
|
return (
|
|
64
79
|
<div style={{ paddingBottom: 64 }}>
|
|
@@ -78,6 +78,7 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|
|
78
78
|
lastAgentPreview: s.lastAgentPreview,
|
|
79
79
|
firstTimestamp: s.firstTimestamp,
|
|
80
80
|
lastTimestamp: s.lastTimestamp,
|
|
81
|
+
lastActivityMs: s.lastActivityMs,
|
|
81
82
|
teamName: s.teamName,
|
|
82
83
|
agentName: s.agentName,
|
|
83
84
|
agent: s.agent,
|