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.
Files changed (228) hide show
  1. package/app/apps/web/.next/BUILD_ID +1 -1
  2. package/app/apps/web/.next/build-manifest.json +8 -8
  3. package/app/apps/web/.next/prerender-manifest.json +3 -3
  4. package/app/apps/web/.next/server/app/_global-error/page/build-manifest.json +5 -5
  5. package/app/apps/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/app/apps/web/.next/server/app/_global-error.html +1 -1
  7. package/app/apps/web/.next/server/app/_global-error.rsc +7 -7
  8. package/app/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +2 -2
  9. package/app/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
  10. package/app/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +3 -3
  11. package/app/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +3 -3
  12. package/app/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/app/apps/web/.next/server/app/_not-found/page/build-manifest.json +5 -5
  14. package/app/apps/web/.next/server/app/_not-found/page.js +1 -1
  15. package/app/apps/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  16. package/app/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  17. package/app/apps/web/.next/server/app/api/ask/route.js.nft.json +1 -1
  18. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js +1 -1
  19. package/app/apps/web/.next/server/app/api/digest/day/[date]/route.js.nft.json +1 -1
  20. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js +1 -1
  21. package/app/apps/web/.next/server/app/api/digest/month/[yearMonth]/route.js.nft.json +1 -1
  22. package/app/apps/web/.next/server/app/api/digest/month-index/route.js.nft.json +1 -1
  23. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js +1 -1
  24. package/app/apps/web/.next/server/app/api/digest/week/[startDate]/route.js.nft.json +1 -1
  25. package/app/apps/web/.next/server/app/api/digest/week-index/route.js.nft.json +1 -1
  26. package/app/apps/web/.next/server/app/api/events/route.js +2 -1
  27. package/app/apps/web/.next/server/app/api/events/route.js.nft.json +1 -1
  28. package/app/apps/web/.next/server/app/api/jobs/route.js.nft.json +1 -1
  29. package/app/apps/web/.next/server/app/api/pdf/[key]/route.js.nft.json +1 -1
  30. package/app/apps/web/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -1
  31. package/app/apps/web/.next/server/app/api/runs/[runId]/stream/route.js.nft.json +1 -1
  32. package/app/apps/web/.next/server/app/api/runs/route.js.nft.json +1 -1
  33. package/app/apps/web/.next/server/app/api/settings/route.js.nft.json +1 -1
  34. package/app/apps/web/.next/server/app/api/team-config/route.js.nft.json +1 -1
  35. package/app/apps/web/.next/server/app/api/workflow-agent/route.js.nft.json +1 -1
  36. package/app/apps/web/.next/server/app/changelog/page/build-manifest.json +5 -5
  37. package/app/apps/web/.next/server/app/changelog/page.js +1 -1
  38. package/app/apps/web/.next/server/app/changelog/page.js.nft.json +1 -1
  39. package/app/apps/web/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  40. package/app/apps/web/.next/server/app/day/[date]/page/build-manifest.json +5 -5
  41. package/app/apps/web/.next/server/app/day/[date]/page.js +1 -1
  42. package/app/apps/web/.next/server/app/day/[date]/page.js.nft.json +1 -1
  43. package/app/apps/web/.next/server/app/day/[date]/page_client-reference-manifest.js +1 -1
  44. package/app/apps/web/.next/server/app/day/page/build-manifest.json +5 -5
  45. package/app/apps/web/.next/server/app/day/page.js +1 -1
  46. package/app/apps/web/.next/server/app/day/page.js.nft.json +1 -1
  47. package/app/apps/web/.next/server/app/day/page_client-reference-manifest.js +1 -1
  48. package/app/apps/web/.next/server/app/digest/[date]/page/build-manifest.json +5 -5
  49. package/app/apps/web/.next/server/app/digest/[date]/page.js +1 -1
  50. package/app/apps/web/.next/server/app/digest/[date]/page.js.nft.json +1 -1
  51. package/app/apps/web/.next/server/app/digest/[date]/page_client-reference-manifest.js +1 -1
  52. package/app/apps/web/.next/server/app/digest/page/build-manifest.json +5 -5
  53. package/app/apps/web/.next/server/app/digest/page.js +1 -1
  54. package/app/apps/web/.next/server/app/digest/page.js.nft.json +1 -1
  55. package/app/apps/web/.next/server/app/digest/page_client-reference-manifest.js +1 -1
  56. package/app/apps/web/.next/server/app/insights/[key]/page/build-manifest.json +5 -5
  57. package/app/apps/web/.next/server/app/insights/[key]/page.js +1 -1
  58. package/app/apps/web/.next/server/app/insights/[key]/page.js.nft.json +1 -1
  59. package/app/apps/web/.next/server/app/insights/[key]/page_client-reference-manifest.js +1 -1
  60. package/app/apps/web/.next/server/app/insights/page/build-manifest.json +5 -5
  61. package/app/apps/web/.next/server/app/insights/page.js +3 -2
  62. package/app/apps/web/.next/server/app/insights/page.js.nft.json +1 -1
  63. package/app/apps/web/.next/server/app/insights/page_client-reference-manifest.js +1 -1
  64. package/app/apps/web/.next/server/app/page/build-manifest.json +5 -5
  65. package/app/apps/web/.next/server/app/page.js +1 -1
  66. package/app/apps/web/.next/server/app/page.js.nft.json +1 -1
  67. package/app/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  68. package/app/apps/web/.next/server/app/parallelism/page/build-manifest.json +5 -5
  69. package/app/apps/web/.next/server/app/parallelism/page.js +1 -1
  70. package/app/apps/web/.next/server/app/parallelism/page.js.nft.json +1 -1
  71. package/app/apps/web/.next/server/app/parallelism/page_client-reference-manifest.js +1 -1
  72. package/app/apps/web/.next/server/app/print/[key]/page/build-manifest.json +5 -5
  73. package/app/apps/web/.next/server/app/print/[key]/page.js +1 -1
  74. package/app/apps/web/.next/server/app/print/[key]/page.js.nft.json +1 -1
  75. package/app/apps/web/.next/server/app/print/[key]/page_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next/server/app/projects/[slug]/page/build-manifest.json +5 -5
  77. package/app/apps/web/.next/server/app/projects/[slug]/page.js +1 -1
  78. package/app/apps/web/.next/server/app/projects/[slug]/page.js.nft.json +1 -1
  79. package/app/apps/web/.next/server/app/projects/[slug]/page_client-reference-manifest.js +1 -1
  80. package/app/apps/web/.next/server/app/projects/page/build-manifest.json +5 -5
  81. package/app/apps/web/.next/server/app/projects/page.js +1 -1
  82. package/app/apps/web/.next/server/app/projects/page.js.nft.json +1 -1
  83. package/app/apps/web/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  84. package/app/apps/web/.next/server/app/runs/page/build-manifest.json +5 -5
  85. package/app/apps/web/.next/server/app/runs/page.js +1 -1
  86. package/app/apps/web/.next/server/app/runs/page.js.nft.json +1 -1
  87. package/app/apps/web/.next/server/app/runs/page_client-reference-manifest.js +1 -1
  88. package/app/apps/web/.next/server/app/sessions/[id]/page/build-manifest.json +5 -5
  89. package/app/apps/web/.next/server/app/sessions/[id]/page.js +1 -1
  90. package/app/apps/web/.next/server/app/sessions/[id]/page.js.nft.json +1 -1
  91. package/app/apps/web/.next/server/app/sessions/[id]/page_client-reference-manifest.js +1 -1
  92. package/app/apps/web/.next/server/app/sessions/page/build-manifest.json +5 -5
  93. package/app/apps/web/.next/server/app/sessions/page.js +1 -1
  94. package/app/apps/web/.next/server/app/sessions/page.js.nft.json +1 -1
  95. package/app/apps/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  96. package/app/apps/web/.next/server/app/settings/page/build-manifest.json +5 -5
  97. package/app/apps/web/.next/server/app/settings/page.js +1 -1
  98. package/app/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
  99. package/app/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  100. package/app/apps/web/.next/server/app/team/page/build-manifest.json +5 -5
  101. package/app/apps/web/.next/server/app/team/page.js +1 -1
  102. package/app/apps/web/.next/server/app/team/page.js.nft.json +1 -1
  103. package/app/apps/web/.next/server/app/team/page_client-reference-manifest.js +1 -1
  104. package/app/apps/web/.next/server/app/usage/page/build-manifest.json +5 -5
  105. package/app/apps/web/.next/server/app/usage/page.js +1 -1
  106. package/app/apps/web/.next/server/app/usage/page.js.nft.json +1 -1
  107. package/app/apps/web/.next/server/app/usage/page_client-reference-manifest.js +1 -1
  108. package/app/apps/web/.next/server/chunks/[externals]__0rv9ct5._.js +3 -0
  109. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0gril0u._.js +9 -0
  110. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0n79hpm._.js +9 -0
  111. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0~utv~v._.js +9 -0
  112. package/app/apps/web/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_02bw8.n.js +5 -0
  113. package/app/apps/web/.next/server/chunks/packages_parser_dist_06d9se1._.js +1 -1
  114. package/app/apps/web/.next/server/chunks/packages_parser_dist_10ihc3~._.js +1 -1
  115. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0exckd4._.js +1 -1
  116. 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
  117. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0lhwp-k._.js +8 -0
  118. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0n-ax-5._.js +1 -1
  119. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0o03ymy._.js +1 -1
  120. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__0~.f_3z._.js +3 -3
  121. package/app/apps/web/.next/server/chunks/ssr/_0kajk31._.js +1 -1
  122. package/app/apps/web/.next/server/chunks/ssr/_0s3n5mz._.js +8 -0
  123. package/app/apps/web/.next/server/chunks/ssr/_0xu2pvc._.js +1 -1
  124. package/app/apps/web/.next/server/chunks/ssr/{_0jw2sm7._.js → _12mbgs5._.js} +2 -2
  125. package/app/apps/web/.next/server/chunks/ssr/apps_web_0.mu7c7._.js +1 -1
  126. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_day_[date]_day-view_tsx_0wjoe1u._.js +1 -1
  127. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_insights_page_tsx_071kwyw._.js +3 -0
  128. package/app/apps/web/.next/server/chunks/ssr/apps_web_app_sessions_sessions-grid_tsx_0bri5x.._.js +3 -0
  129. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0b-4-4j._.js +1 -1
  130. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0c-tpft._.js +1 -1
  131. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0l6hsch._.js +6 -0
  132. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_0txtsv5._.js +1 -1
  133. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0n7_p1d._.js +1 -1
  134. package/app/apps/web/.next/server/chunks/ssr/apps_web_lib_changelog_ts_0nb5_-s._.js +1 -1
  135. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0cepfi8._.js +3 -0
  136. package/app/apps/web/.next/server/chunks/ssr/packages_entries_dist_node_0_271yr.js +1 -1
  137. package/app/apps/web/.next/server/chunks/ssr/packages_parser_dist_0mx_509._.js +1 -1
  138. package/app/apps/web/.next/server/middleware-build-manifest.js +8 -8
  139. package/app/apps/web/.next/server/middleware-manifest.json +5 -5
  140. package/app/apps/web/.next/server/pages/500.html +1 -1
  141. package/app/apps/web/.next/server/server-reference-manifest.js +1 -1
  142. package/app/apps/web/.next/server/server-reference-manifest.json +1 -1
  143. package/app/apps/web/.next/static/chunks/02bv_ejzhhzvp.js +10 -0
  144. package/app/apps/web/.next/static/chunks/{07lbm-sdh9c2j.js → 0389wb_f0j78j.js} +1 -1
  145. package/app/apps/web/.next/static/chunks/03gy1evbn94qu.js +6 -0
  146. package/app/apps/web/.next/static/chunks/03rd.3cc_t~yf.js +4 -0
  147. package/app/apps/web/.next/static/chunks/{12jvvc_w.cb45.css → 083~hzkv577.5.css} +1 -1
  148. package/app/apps/web/.next/static/chunks/{0osdgl2.9cl1q.js → 0d2x-wfu.m0-z.js} +1 -1
  149. package/app/apps/web/.next/static/chunks/{0lziv8y_6yp5w.js → 0e_fzy0n8ij-o.js} +1 -1
  150. package/app/apps/web/.next/static/chunks/{0l4e3as18eexy.js → 0g223u9amib0q.js} +1 -1
  151. package/app/apps/web/.next/static/chunks/{0h3.-_s-k0mj3.js → 0i.hc~2bj0_3d.js} +1 -1
  152. package/app/apps/web/.next/static/chunks/0m7dd-2zd9vl2.js +4 -0
  153. package/app/apps/web/.next/static/chunks/0nqklmbs5s3ax.js +6 -0
  154. package/app/apps/web/.next/static/chunks/{073081qv4uyn8.js → 0p--hzrway8~3.js} +1 -1
  155. package/app/apps/web/.next/static/chunks/0q8dg4txlzno0.js +1 -0
  156. package/app/apps/web/.next/static/chunks/{0gblp_v.kiu-..js → 0qv-gw888_5nu.js} +1 -1
  157. package/app/apps/web/.next/static/chunks/{14bjditmmrn7l.js → 0tirv_vo6q8uc.js} +1 -1
  158. package/app/apps/web/.next/static/chunks/{0qmh5da6gfm56.js → 0vz4~a~~hq399.js} +1 -1
  159. package/app/apps/web/.next/static/chunks/{13po66tt.68f3.js → 0wwcx42ng8i2e.js} +3 -3
  160. package/app/apps/web/.next/static/chunks/{0n0n.0279ot6c.js → 14vx702c32kn6.js} +1 -1
  161. package/app/apps/web/.next/static/chunks/{turbopack-0lb9w090w5ezx.js → turbopack-0mxu3~9vk.brh.js} +1 -1
  162. package/app/apps/web/app/api/digest/_shared/digest-route-helpers.ts +178 -0
  163. package/app/apps/web/app/api/digest/day/[date]/route.ts +34 -128
  164. package/app/apps/web/app/api/digest/month/[yearMonth]/route.ts +36 -121
  165. package/app/apps/web/app/api/digest/week/[startDate]/route.ts +36 -123
  166. package/app/apps/web/app/api/events/route.ts +47 -4
  167. package/app/apps/web/app/globals.css +23 -0
  168. package/app/apps/web/app/insights/page.tsx +20 -5
  169. package/app/apps/web/app/layout.tsx +1 -0
  170. package/app/apps/web/app/page.tsx +1 -1
  171. package/app/apps/web/app/parallelism/burst-detail-modal.tsx +637 -0
  172. package/app/apps/web/app/parallelism/concurrency-info-modal.tsx +228 -0
  173. package/app/apps/web/app/parallelism/gantt-chart-utils.ts +49 -0
  174. package/app/apps/web/app/parallelism/gantt-chart.tsx +16 -902
  175. package/app/apps/web/app/projects/[slug]/page.tsx +1 -1
  176. package/app/apps/web/app/sessions/[id]/session-view/colors.ts +31 -0
  177. package/app/apps/web/app/sessions/[id]/session-view/debug-list.tsx +78 -0
  178. package/app/apps/web/app/sessions/[id]/session-view/drawer.tsx +414 -0
  179. package/app/apps/web/app/sessions/[id]/session-view/entry-day-strip.tsx +124 -0
  180. package/app/apps/web/app/sessions/[id]/session-view/header-stats.tsx +217 -0
  181. package/app/apps/web/app/sessions/[id]/session-view/helpers.ts +135 -0
  182. package/app/apps/web/app/sessions/[id]/session-view/subagent-drawer.tsx +339 -0
  183. package/app/apps/web/app/sessions/[id]/session-view/token-stats.tsx +111 -0
  184. package/app/apps/web/app/sessions/[id]/session-view/tooltip.tsx +63 -0
  185. package/app/apps/web/app/sessions/[id]/session-view/types.ts +46 -0
  186. package/app/apps/web/app/sessions/[id]/session-view/workflows-shared.tsx +104 -0
  187. package/app/apps/web/app/sessions/[id]/session-view.tsx +701 -1577
  188. package/app/apps/web/app/sessions/[id]/team-tab/minimap-hover-card.tsx +201 -0
  189. package/app/apps/web/app/sessions/[id]/team-tab/minimap-idle-band.tsx +53 -0
  190. package/app/apps/web/app/sessions/[id]/team-tab/minimap-shared.ts +20 -0
  191. package/app/apps/web/app/sessions/[id]/team-tab/team-minimap.tsx +10 -265
  192. package/app/apps/web/app/sessions/sessions-grid.tsx +43 -19
  193. package/app/apps/web/components/digest-actions.tsx +103 -0
  194. package/app/apps/web/components/job-queue-widget.tsx +1 -1
  195. package/app/apps/web/components/live-badge.tsx +11 -3
  196. package/app/apps/web/components/live-sessions-widget.tsx +9 -3
  197. package/app/apps/web/components/month-digest-view.tsx +19 -64
  198. package/app/apps/web/components/outcome-pill.tsx +24 -4
  199. package/app/apps/web/components/searchable-select.tsx +215 -0
  200. package/app/apps/web/components/tail-mode.tsx +39 -8
  201. package/app/apps/web/components/week-digest-view.tsx +16 -71
  202. package/app/apps/web/lib/changelog-data.generated.ts +1 -1
  203. package/app/apps/web/lib/entries-index.ts +7 -8
  204. package/app/apps/web/lib/entries.ts +0 -9
  205. package/app/apps/web/lib/format.test.ts +221 -0
  206. package/app/apps/web/lib/utilization-tone.test.ts +56 -0
  207. package/app/apps/web/package.json +1 -1
  208. package/app/apps/web/tsconfig.tsbuildinfo +1 -1
  209. package/dist/daemon-worker.js +83 -45
  210. package/dist/index.js +452 -198
  211. package/package.json +1 -1
  212. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0455vu3._.js +0 -5
  213. package/app/apps/web/.next/server/chunks/[root-of-the-server]__04j3tng._.js +0 -9
  214. package/app/apps/web/.next/server/chunks/[root-of-the-server]__0cnh3k4._.js +0 -9
  215. package/app/apps/web/.next/server/chunks/[root-of-the-server]__132eosx._.js +0 -9
  216. package/app/apps/web/.next/server/chunks/ssr/[root-of-the-server]__136-0me._.js +0 -8
  217. package/app/apps/web/.next/server/chunks/ssr/_0w.cv64._.js +0 -8
  218. package/app/apps/web/.next/server/chunks/ssr/apps_web_components_01d-iwx._.js +0 -6
  219. package/app/apps/web/.next/server/chunks/ssr/node_modules_lucide-react_dist_esm_icons_0-lmm4p._.js +0 -3
  220. package/app/apps/web/.next/static/chunks/00xkqnvk4-d1-.js +0 -10
  221. package/app/apps/web/.next/static/chunks/0htgc_hr1bw67.js +0 -1
  222. package/app/apps/web/.next/static/chunks/0nnjldasalwdu.js +0 -4
  223. package/app/apps/web/.next/static/chunks/0okg13phslr1..js +0 -4
  224. package/app/apps/web/.next/static/chunks/0vf-3rauafbek.js +0 -6
  225. package/app/apps/web/.next/static/chunks/0vl27-vjob2_9.js +0 -6
  226. /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_buildManifest.js +0 -0
  227. /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_clientMiddlewareManifest.js +0 -0
  228. /package/app/apps/web/.next/static/{7kQ9mNICnVHhf4-LmSGcy → VEfemGVAQ0xCHjj15orZU}/_ssgManifest.js +0 -0
@@ -0,0 +1,201 @@
1
+ "use client";
2
+
3
+ import { formatGap } from "@/lib/format";
4
+ import type { TeamTrack, TeamTurn } from "./adapter";
5
+ import { formatTeammatePreview } from "./format";
6
+ import { formatEdge } from "./minimap-shared";
7
+
8
+ export type HoverState = {
9
+ turn: TeamTurn;
10
+ track: TeamTrack;
11
+ clientX: number;
12
+ clientY: number;
13
+ };
14
+
15
+ export function HoverCard({
16
+ hover,
17
+ multiDay,
18
+ }: {
19
+ hover: HoverState;
20
+ multiDay: boolean;
21
+ }) {
22
+ const { turn, track } = hover;
23
+ const summary = turn.megaRow.summary;
24
+ const tmMsg =
25
+ turn.userPrompt && "event" in turn.userPrompt
26
+ ? turn.userPrompt.event.teammateMessage
27
+ : undefined;
28
+ const userLabel = tmMsg ? `FROM ${tmMsg.teammateId}` : "HUMAN";
29
+ const userText = tmMsg
30
+ ? formatTeammatePreview(tmMsg)
31
+ : turn.userPrompt && turn.userPrompt.kind === "user"
32
+ ? (turn.userPrompt.displayPreview ?? turn.userPrompt.event.preview ?? "")
33
+ : "";
34
+ const firstAgent = summary.firstAgentPreview ?? "";
35
+ const finalAgent = summary.finalAgentPreview ?? "";
36
+ const showFinal = finalAgent && finalAgent !== firstAgent;
37
+ const tools = summary.toolNames.slice(0, 4);
38
+
39
+ // Position card near the cursor, biased above-and-right to avoid covering
40
+ // the bar itself. Clamp to viewport via simple offsets.
41
+ const left = Math.min(window.innerWidth - 360, hover.clientX + 12);
42
+ const top = Math.max(8, hover.clientY - 12);
43
+
44
+ return (
45
+ <div
46
+ style={{
47
+ position: "fixed",
48
+ left,
49
+ top,
50
+ width: 340,
51
+ background: "var(--af-surface)",
52
+ border: "1px solid var(--af-border-subtle)",
53
+ borderLeft: `3px solid ${track.color}`,
54
+ borderRadius: 4,
55
+ padding: "8px 10px",
56
+ fontSize: 10,
57
+ lineHeight: 1.4,
58
+ boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
59
+ zIndex: 100,
60
+ pointerEvents: "none",
61
+ }}
62
+ >
63
+ <div
64
+ style={{
65
+ display: "flex",
66
+ justifyContent: "space-between",
67
+ fontSize: 9,
68
+ fontFamily: "ui-monospace, monospace",
69
+ color: track.color,
70
+ fontWeight: 600,
71
+ textTransform: "uppercase",
72
+ letterSpacing: "0.04em",
73
+ marginBottom: 4,
74
+ }}
75
+ >
76
+ <span>{track.isLead ? "LEAD" : track.label}</span>
77
+ <span style={{ color: "var(--af-text-tertiary)", fontWeight: 500 }}>
78
+ {formatEdge(turn.startMs, multiDay)} → {formatEdge(turn.endMs, multiDay)} · {formatGap(turn.durationMs)}
79
+ </span>
80
+ </div>
81
+ <div
82
+ style={{
83
+ fontSize: 9,
84
+ color: "var(--af-text-tertiary)",
85
+ marginBottom: 6,
86
+ }}
87
+ >
88
+ {summary.agentMessages} msg · {summary.toolCalls} tools
89
+ {summary.errors > 0 ? ` · ${summary.errors} err` : ""}
90
+ </div>
91
+ {userText && (
92
+ <div style={{ marginBottom: 5 }}>
93
+ <div
94
+ style={{
95
+ fontSize: 8,
96
+ color: tmMsg ? "var(--af-text-tertiary)" : track.color,
97
+ fontWeight: 600,
98
+ textTransform: "uppercase",
99
+ letterSpacing: "0.04em",
100
+ marginBottom: 1,
101
+ fontStyle: tmMsg ? "italic" : undefined,
102
+ }}
103
+ >
104
+ {userLabel}
105
+ </div>
106
+ <div
107
+ style={{
108
+ color: tmMsg ? "var(--af-text-tertiary)" : "var(--af-text)",
109
+ display: "-webkit-box",
110
+ WebkitLineClamp: 2,
111
+ WebkitBoxOrient: "vertical",
112
+ overflow: "hidden",
113
+ }}
114
+ >
115
+ {userText}
116
+ </div>
117
+ </div>
118
+ )}
119
+ {firstAgent && (
120
+ <div style={{ marginBottom: 5 }}>
121
+ <div
122
+ style={{
123
+ fontSize: 8,
124
+ color: track.color,
125
+ fontWeight: 600,
126
+ textTransform: "uppercase",
127
+ letterSpacing: "0.04em",
128
+ marginBottom: 1,
129
+ }}
130
+ >
131
+ AGENT
132
+ </div>
133
+ <div
134
+ style={{
135
+ color: "var(--af-text)",
136
+ display: "-webkit-box",
137
+ WebkitLineClamp: 3,
138
+ WebkitBoxOrient: "vertical",
139
+ overflow: "hidden",
140
+ }}
141
+ >
142
+ {firstAgent}
143
+ </div>
144
+ </div>
145
+ )}
146
+ {tools.length > 0 && (
147
+ <div
148
+ style={{
149
+ display: "flex",
150
+ gap: 3,
151
+ flexWrap: "wrap",
152
+ marginBottom: showFinal ? 5 : 0,
153
+ }}
154
+ >
155
+ {tools.map((t) => (
156
+ <span
157
+ key={t.name}
158
+ style={{
159
+ fontSize: 8,
160
+ padding: "1px 5px",
161
+ background: "var(--af-surface-hover)",
162
+ borderRadius: 2,
163
+ color: "var(--af-text-tertiary)",
164
+ }}
165
+ >
166
+ {t.name}
167
+ {t.count > 1 ? ` ×${t.count}` : ""}
168
+ </span>
169
+ ))}
170
+ </div>
171
+ )}
172
+ {showFinal && (
173
+ <div>
174
+ <div
175
+ style={{
176
+ fontSize: 8,
177
+ color: track.color,
178
+ fontWeight: 600,
179
+ textTransform: "uppercase",
180
+ letterSpacing: "0.04em",
181
+ marginBottom: 1,
182
+ }}
183
+ >
184
+ RESULT
185
+ </div>
186
+ <div
187
+ style={{
188
+ color: "var(--af-text)",
189
+ display: "-webkit-box",
190
+ WebkitLineClamp: 2,
191
+ WebkitBoxOrient: "vertical",
192
+ overflow: "hidden",
193
+ }}
194
+ >
195
+ {finalAgent}
196
+ </div>
197
+ </div>
198
+ )}
199
+ </div>
200
+ );
201
+ }
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import type { MinimapIdleBand } from "./adapter";
5
+ import { LABEL_WIDTH } from "./minimap-shared";
6
+
7
+ export function IdleBandOverlay({ band }: { band: MinimapIdleBand }) {
8
+ const [hovered, setHovered] = useState(false);
9
+ const left = band.xFracStart * 100;
10
+ const width = Math.max(0.5, (band.xFracEnd - band.xFracStart) * 100);
11
+ return (
12
+ <div
13
+ onMouseEnter={() => setHovered(true)}
14
+ onMouseLeave={() => setHovered(false)}
15
+ style={{
16
+ position: "absolute",
17
+ top: 0,
18
+ bottom: 0,
19
+ left: `calc(${LABEL_WIDTH}px + ${left}% * (100% - ${LABEL_WIDTH}px) / 100%)`,
20
+ width: `calc(${width}% * (100% - ${LABEL_WIDTH}px) / 100%)`,
21
+ background:
22
+ "repeating-linear-gradient(135deg, transparent 0, transparent 4px, var(--af-border-subtle) 4px, var(--af-border-subtle) 6px)",
23
+ zIndex: 1,
24
+ cursor: "help",
25
+ }}
26
+ >
27
+ {hovered && (
28
+ <div
29
+ style={{
30
+ position: "absolute",
31
+ left: "50%",
32
+ bottom: "calc(100% + 6px)",
33
+ transform: "translateX(-50%)",
34
+ background: "#0F172A",
35
+ color: "#F1F5F9",
36
+ borderRadius: 8,
37
+ padding: "6px 10px",
38
+ fontSize: 11,
39
+ pointerEvents: "none",
40
+ boxShadow: "0 4px 16px rgba(15,23,42,0.22)",
41
+ zIndex: 20,
42
+ whiteSpace: "nowrap",
43
+ }}
44
+ >
45
+ <div style={{ fontWeight: 600, marginBottom: 1 }}>Idle</div>
46
+ <div style={{ opacity: 0.78, fontFamily: "ui-monospace, monospace", fontSize: 10 }}>
47
+ {band.label}
48
+ </div>
49
+ </div>
50
+ )}
51
+ </div>
52
+ );
53
+ }
@@ -0,0 +1,20 @@
1
+ export const LANE_HEIGHT = 24;
2
+ export const LANE_GAP = 2;
3
+ export const LABEL_WIDTH = 100;
4
+ export const DEFAULT_VISIBLE_LANES = 4;
5
+
6
+ export function formatEdge(ms: number, multiDay: boolean): string {
7
+ const d = new Date(ms);
8
+ if (!multiDay) {
9
+ return d.toLocaleTimeString([], {
10
+ hour: "2-digit",
11
+ minute: "2-digit",
12
+ });
13
+ }
14
+ const date = d.toLocaleDateString([], { month: "short", day: "numeric" });
15
+ const time = d.toLocaleTimeString([], {
16
+ hour: "2-digit",
17
+ minute: "2-digit",
18
+ });
19
+ return `${date} ${time}`;
20
+ }
@@ -4,15 +4,19 @@ import Link from "next/link";
4
4
  import * as React from "react";
5
5
  import { useEffect, useState } from "react";
6
6
  import { ChevronLeft, ChevronRight } from "lucide-react";
7
- import { formatGap } from "@/lib/format";
8
7
  import type { TimelineData, TeamTrack, TeamTurn, XAnchor, MinimapIdleBand } from "./adapter";
9
8
  import { xOfMs, msOfXFrac } from "./adapter";
10
- import { formatTeammatePreview } from "./format";
9
+ import {
10
+ LANE_HEIGHT,
11
+ LANE_GAP,
12
+ LABEL_WIDTH,
13
+ DEFAULT_VISIBLE_LANES,
14
+ formatEdge,
15
+ } from "./minimap-shared";
16
+ import { IdleBandOverlay } from "./minimap-idle-band";
17
+ import { HoverCard, type HoverState } from "./minimap-hover-card";
11
18
 
12
- const LANE_HEIGHT = 24;
13
- const LANE_GAP = 2;
14
- const LABEL_WIDTH = 100;
15
- export const DEFAULT_VISIBLE_LANES = 4;
19
+ export { DEFAULT_VISIBLE_LANES };
16
20
 
17
21
  type Props = {
18
22
  data: TimelineData;
@@ -28,13 +32,6 @@ type Props = {
28
32
  visibleTrackIds: string[];
29
33
  };
30
34
 
31
- type HoverState = {
32
- turn: TeamTurn;
33
- track: TeamTrack;
34
- clientX: number;
35
- clientY: number;
36
- };
37
-
38
35
  export function TeamMinimap({
39
36
  data,
40
37
  playheadMs,
@@ -411,194 +408,6 @@ export function TeamMinimap({
411
408
  );
412
409
  }
413
410
 
414
- function HoverCard({
415
- hover,
416
- multiDay,
417
- }: {
418
- hover: HoverState;
419
- multiDay: boolean;
420
- }) {
421
- const { turn, track } = hover;
422
- const summary = turn.megaRow.summary;
423
- const tmMsg =
424
- turn.userPrompt && "event" in turn.userPrompt
425
- ? turn.userPrompt.event.teammateMessage
426
- : undefined;
427
- const userLabel = tmMsg ? `FROM ${tmMsg.teammateId}` : "HUMAN";
428
- const userText = tmMsg
429
- ? formatTeammatePreview(tmMsg)
430
- : turn.userPrompt && turn.userPrompt.kind === "user"
431
- ? (turn.userPrompt.displayPreview ?? turn.userPrompt.event.preview ?? "")
432
- : "";
433
- const firstAgent = summary.firstAgentPreview ?? "";
434
- const finalAgent = summary.finalAgentPreview ?? "";
435
- const showFinal = finalAgent && finalAgent !== firstAgent;
436
- const tools = summary.toolNames.slice(0, 4);
437
-
438
- // Position card near the cursor, biased above-and-right to avoid covering
439
- // the bar itself. Clamp to viewport via simple offsets.
440
- const left = Math.min(window.innerWidth - 360, hover.clientX + 12);
441
- const top = Math.max(8, hover.clientY - 12);
442
-
443
- return (
444
- <div
445
- style={{
446
- position: "fixed",
447
- left,
448
- top,
449
- width: 340,
450
- background: "var(--af-surface)",
451
- border: "1px solid var(--af-border-subtle)",
452
- borderLeft: `3px solid ${track.color}`,
453
- borderRadius: 4,
454
- padding: "8px 10px",
455
- fontSize: 10,
456
- lineHeight: 1.4,
457
- boxShadow: "0 8px 24px rgba(0,0,0,0.25)",
458
- zIndex: 100,
459
- pointerEvents: "none",
460
- }}
461
- >
462
- <div
463
- style={{
464
- display: "flex",
465
- justifyContent: "space-between",
466
- fontSize: 9,
467
- fontFamily: "ui-monospace, monospace",
468
- color: track.color,
469
- fontWeight: 600,
470
- textTransform: "uppercase",
471
- letterSpacing: "0.04em",
472
- marginBottom: 4,
473
- }}
474
- >
475
- <span>{track.isLead ? "LEAD" : track.label}</span>
476
- <span style={{ color: "var(--af-text-tertiary)", fontWeight: 500 }}>
477
- {formatEdge(turn.startMs, multiDay)} → {formatEdge(turn.endMs, multiDay)} · {formatGap(turn.durationMs)}
478
- </span>
479
- </div>
480
- <div
481
- style={{
482
- fontSize: 9,
483
- color: "var(--af-text-tertiary)",
484
- marginBottom: 6,
485
- }}
486
- >
487
- {summary.agentMessages} msg · {summary.toolCalls} tools
488
- {summary.errors > 0 ? ` · ${summary.errors} err` : ""}
489
- </div>
490
- {userText && (
491
- <div style={{ marginBottom: 5 }}>
492
- <div
493
- style={{
494
- fontSize: 8,
495
- color: tmMsg ? "var(--af-text-tertiary)" : track.color,
496
- fontWeight: 600,
497
- textTransform: "uppercase",
498
- letterSpacing: "0.04em",
499
- marginBottom: 1,
500
- fontStyle: tmMsg ? "italic" : undefined,
501
- }}
502
- >
503
- {userLabel}
504
- </div>
505
- <div
506
- style={{
507
- color: tmMsg ? "var(--af-text-tertiary)" : "var(--af-text)",
508
- display: "-webkit-box",
509
- WebkitLineClamp: 2,
510
- WebkitBoxOrient: "vertical",
511
- overflow: "hidden",
512
- }}
513
- >
514
- {userText}
515
- </div>
516
- </div>
517
- )}
518
- {firstAgent && (
519
- <div style={{ marginBottom: 5 }}>
520
- <div
521
- style={{
522
- fontSize: 8,
523
- color: track.color,
524
- fontWeight: 600,
525
- textTransform: "uppercase",
526
- letterSpacing: "0.04em",
527
- marginBottom: 1,
528
- }}
529
- >
530
- AGENT
531
- </div>
532
- <div
533
- style={{
534
- color: "var(--af-text)",
535
- display: "-webkit-box",
536
- WebkitLineClamp: 3,
537
- WebkitBoxOrient: "vertical",
538
- overflow: "hidden",
539
- }}
540
- >
541
- {firstAgent}
542
- </div>
543
- </div>
544
- )}
545
- {tools.length > 0 && (
546
- <div
547
- style={{
548
- display: "flex",
549
- gap: 3,
550
- flexWrap: "wrap",
551
- marginBottom: showFinal ? 5 : 0,
552
- }}
553
- >
554
- {tools.map((t) => (
555
- <span
556
- key={t.name}
557
- style={{
558
- fontSize: 8,
559
- padding: "1px 5px",
560
- background: "var(--af-surface-hover)",
561
- borderRadius: 2,
562
- color: "var(--af-text-tertiary)",
563
- }}
564
- >
565
- {t.name}
566
- {t.count > 1 ? ` ×${t.count}` : ""}
567
- </span>
568
- ))}
569
- </div>
570
- )}
571
- {showFinal && (
572
- <div>
573
- <div
574
- style={{
575
- fontSize: 8,
576
- color: track.color,
577
- fontWeight: 600,
578
- textTransform: "uppercase",
579
- letterSpacing: "0.04em",
580
- marginBottom: 1,
581
- }}
582
- >
583
- RESULT
584
- </div>
585
- <div
586
- style={{
587
- color: "var(--af-text)",
588
- display: "-webkit-box",
589
- WebkitLineClamp: 2,
590
- WebkitBoxOrient: "vertical",
591
- overflow: "hidden",
592
- }}
593
- >
594
- {finalAgent}
595
- </div>
596
- </div>
597
- )}
598
- </div>
599
- );
600
- }
601
-
602
411
  function pageBtnStyle(disabled: boolean): React.CSSProperties {
603
412
  return {
604
413
  background: "transparent",
@@ -616,67 +425,3 @@ function pageBtnStyle(disabled: boolean): React.CSSProperties {
616
425
  };
617
426
  }
618
427
 
619
- function formatEdge(ms: number, multiDay: boolean): string {
620
- const d = new Date(ms);
621
- if (!multiDay) {
622
- return d.toLocaleTimeString([], {
623
- hour: "2-digit",
624
- minute: "2-digit",
625
- });
626
- }
627
- const date = d.toLocaleDateString([], { month: "short", day: "numeric" });
628
- const time = d.toLocaleTimeString([], {
629
- hour: "2-digit",
630
- minute: "2-digit",
631
- });
632
- return `${date} ${time}`;
633
- }
634
-
635
- function IdleBandOverlay({ band }: { band: MinimapIdleBand }) {
636
- const [hovered, setHovered] = useState(false);
637
- const left = band.xFracStart * 100;
638
- const width = Math.max(0.5, (band.xFracEnd - band.xFracStart) * 100);
639
- return (
640
- <div
641
- onMouseEnter={() => setHovered(true)}
642
- onMouseLeave={() => setHovered(false)}
643
- style={{
644
- position: "absolute",
645
- top: 0,
646
- bottom: 0,
647
- left: `calc(${LABEL_WIDTH}px + ${left}% * (100% - ${LABEL_WIDTH}px) / 100%)`,
648
- width: `calc(${width}% * (100% - ${LABEL_WIDTH}px) / 100%)`,
649
- background:
650
- "repeating-linear-gradient(135deg, transparent 0, transparent 4px, var(--af-border-subtle) 4px, var(--af-border-subtle) 6px)",
651
- zIndex: 1,
652
- cursor: "help",
653
- }}
654
- >
655
- {hovered && (
656
- <div
657
- style={{
658
- position: "absolute",
659
- left: "50%",
660
- bottom: "calc(100% + 6px)",
661
- transform: "translateX(-50%)",
662
- background: "#0F172A",
663
- color: "#F1F5F9",
664
- borderRadius: 8,
665
- padding: "6px 10px",
666
- fontSize: 11,
667
- pointerEvents: "none",
668
- boxShadow: "0 4px 16px rgba(15,23,42,0.22)",
669
- zIndex: 20,
670
- whiteSpace: "nowrap",
671
- }}
672
- >
673
- <div style={{ fontWeight: 600, marginBottom: 1 }}>Idle</div>
674
- <div style={{ opacity: 0.78, fontFamily: "ui-monospace, monospace", fontSize: 10 }}>
675
- {band.label}
676
- </div>
677
- </div>
678
- )}
679
- </div>
680
- );
681
- }
682
-
@@ -19,6 +19,7 @@ import { TeamBadge } from "@/components/team-badge";
19
19
  import { AgentBadge } from "@/components/agent-badge";
20
20
  import { DataTable, type Column } from "@/components/data-table";
21
21
  import { useViewToggle } from "@/components/view-toggle";
22
+ import { SearchableSelect } from "@/components/searchable-select";
22
23
  import { OutcomePill, outcomePriority } from "@/components/outcome-pill";
23
24
 
24
25
  export type SessionRow = {
@@ -152,17 +153,16 @@ export function SessionsGrid({ rows }: { rows: SessionRow[] }) {
152
153
  style={{ width: "100%", paddingLeft: 32 }}
153
154
  />
154
155
  </div>
155
- <select
156
+ <SearchableSelect
156
157
  value={project}
157
- onChange={(e) => setProject(e.target.value)}
158
- style={{ maxWidth: 260 }}
159
- >
160
- {projects.map((p) => (
161
- <option key={p} value={p}>
162
- {p === "all" ? "All projects" : prettyProjectName(p)}
163
- </option>
164
- ))}
165
- </select>
158
+ onChange={setProject}
159
+ title="Filter by project"
160
+ searchPlaceholder="Filter projects…"
161
+ options={projects.map((p) => ({
162
+ value: p,
163
+ label: p === "all" ? "All projects" : prettyProjectName(p),
164
+ }))}
165
+ />
166
166
  {agentOptions.length > 1 && (
167
167
  <select
168
168
  value={agent}
@@ -400,6 +400,7 @@ function OutcomeCell({ row }: { row: SessionRow }) {
400
400
  }
401
401
 
402
402
  function SessionCard({ row }: { row: SessionRow }) {
403
+ const router = useRouter();
403
404
  const { session: s, briefSummary, outcome, latestLocalDay, enrichmentStatus } = row;
404
405
  const totalTokens =
405
406
  s.totalUsage.input + s.totalUsage.output + s.totalUsage.cacheRead + s.totalUsage.cacheWrite;
@@ -441,7 +442,7 @@ function SessionCard({ row }: { row: SessionRow }) {
441
442
  }}
442
443
  title={s.projectName}
443
444
  >
444
- <LiveBadge mtimeIso={s.lastTimestamp} />
445
+ <LiveBadge mtimeIso={s.lastTimestamp} activityMs={s.lastActivityMs} />
445
446
  <span
446
447
  style={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
447
448
  >
@@ -468,14 +469,37 @@ function SessionCard({ row }: { row: SessionRow }) {
468
469
  {outcome ? (
469
470
  <OutcomePill outcome={outcome} size="md" agent={s.agent} />
470
471
  ) : showPending ? (
471
- <OutcomePill
472
- outcome={null}
473
- pending
474
- sessionId={s.id}
475
- localDay={latestLocalDay!}
476
- size="md"
477
- agent={s.agent}
478
- />
472
+ // The card body is a <Link>, so the pending pill can't be its own
473
+ // <Link> (nested <a> → hydration error). Render it as a non-anchor
474
+ // and drive the /digest navigation from here instead.
475
+ <span
476
+ role="link"
477
+ tabIndex={0}
478
+ title={`Generate ${latestLocalDay} digest →`}
479
+ onClick={(e) => {
480
+ e.preventDefault();
481
+ e.stopPropagation();
482
+ router.push(`/digest/${latestLocalDay}`);
483
+ }}
484
+ onKeyDown={(e) => {
485
+ if (e.key === "Enter" || e.key === " ") {
486
+ e.preventDefault();
487
+ e.stopPropagation();
488
+ router.push(`/digest/${latestLocalDay}`);
489
+ }
490
+ }}
491
+ style={{ cursor: "pointer", display: "inline-flex" }}
492
+ >
493
+ <OutcomePill
494
+ outcome={null}
495
+ pending
496
+ sessionId={s.id}
497
+ localDay={latestLocalDay!}
498
+ size="md"
499
+ agent={s.agent}
500
+ noLink
501
+ />
502
+ </span>
479
503
  ) : null}
480
504
  </div>
481
505
  )}