@tonyclaw/agent-inspector 2.0.42 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-Cfqxlo-U.js → CompareDrawer-VDpcSNGM.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-fsXSjqtm.js +117 -0
  4. package/.output/public/assets/{ReplayDialog-daRidZo_.js → ReplayDialog-CoDgTzHR.js} +1 -1
  5. package/.output/public/assets/RequestAnatomy-t3plgkPE.js +1 -0
  6. package/.output/public/assets/ResponseView-BYuKiNeG.js +1 -0
  7. package/.output/public/assets/{StreamingChunkSequence-nK-0LpbM.js → StreamingChunkSequence-DOdU38Is.js} +1 -1
  8. package/.output/public/assets/_sessionId-ew5QlyZ2.js +1 -0
  9. package/.output/public/assets/index-DFICWD6o.js +1 -0
  10. package/.output/public/assets/index-zLLCkAnF.css +1 -0
  11. package/.output/public/assets/{main-DOy_Q96H.js → main-DmzLt6ti.js} +2 -2
  12. package/.output/server/_libs/lucide-react.mjs +219 -178
  13. package/.output/server/{_sessionId-DtYRZHlM.mjs → _sessionId-CCN1UoEf.mjs} +3 -3
  14. package/.output/server/_ssr/{CompareDrawer-fy1ARwtx.mjs → CompareDrawer-DkSY3vxP.mjs} +4 -4
  15. package/.output/server/_ssr/{ProxyViewerContainer-9GJtCWCq.mjs → ProxyViewerContainer-OTh1V_Kh.mjs} +1034 -448
  16. package/.output/server/_ssr/{ReplayDialog-BrS7syOE.mjs → ReplayDialog-DhI6yxrR.mjs} +5 -5
  17. package/.output/server/_ssr/{RequestAnatomy-BtWt1cWY.mjs → RequestAnatomy-DN1SJVVL.mjs} +4 -4
  18. package/.output/server/_ssr/{ResponseView-CkKGYE6m.mjs → ResponseView-DW-kpJL5.mjs} +4 -4
  19. package/.output/server/_ssr/{StreamingChunkSequence-C7rB3osr.mjs → StreamingChunkSequence-BAE57Fae.mjs} +4 -4
  20. package/.output/server/_ssr/{index-D24WforP.mjs → index-C9ryJEna.mjs} +3 -3
  21. package/.output/server/_ssr/index.mjs +2 -2
  22. package/.output/server/_ssr/{router-CVqVjBzJ.mjs → router-DIIJGyML.mjs} +458 -10
  23. package/.output/server/{_tanstack-start-manifest_v-DSikvwru.mjs → _tanstack-start-manifest_v-C_v9-E5d.mjs} +1 -1
  24. package/.output/server/index.mjs +63 -63
  25. package/package.json +1 -1
  26. package/src/components/OnboardingBanner.tsx +74 -68
  27. package/src/components/ProxyViewer.tsx +942 -297
  28. package/src/components/ProxyViewerContainer.tsx +199 -5
  29. package/src/components/groups/GroupsDialog.tsx +11 -10
  30. package/src/components/proxy-viewer/LogEntry.tsx +107 -56
  31. package/src/components/proxy-viewer/LogEntryHeader.tsx +12 -6
  32. package/src/components/ui/crab-logo.tsx +0 -50
  33. package/src/components/ui/dialog.tsx +1 -1
  34. package/src/proxy/logIndex.ts +188 -1
  35. package/src/proxy/store.ts +405 -3
  36. package/src/routes/api/logs.ts +36 -0
  37. package/.output/public/assets/ProxyViewerContainer-CE9pAX4c.js +0 -117
  38. package/.output/public/assets/RequestAnatomy-CJ7EPQGQ.js +0 -1
  39. package/.output/public/assets/ResponseView-xBmr54hB.js +0 -1
  40. package/.output/public/assets/_sessionId-CbWEG5ej.js +0 -1
  41. package/.output/public/assets/index-BfGJEb-2.css +0 -1
  42. package/.output/public/assets/index-D1MkoT4l.js +0 -1
@@ -1,20 +1,20 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, v as packageJson, i as GroupEvidenceExportResultSchema, j as DeleteInspectorGroupResponseSchema, d as RuntimeConfigSchema, P as ProviderConfigSchema, f as apiFormatForPath, n as createPendingProviderTestResults, o as ProviderTestResultsSchema, q as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, r as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, m as maskApiKey, k as providerHasContextMetadata, l as findProviderModelMetadata, g as getSessionPath, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, t as resolveProviderContextWindow, K as KnowledgeCandidateSchema, h as stripClaudeCodeBillingHeader, u as isPlainRecord, G as GroupEvidenceReadResponseSchema, I as InspectorGroupsListResponseSchema } from "./router-CVqVjBzJ.mjs";
2
+ import { I as InspectorGroupsListResponseSchema, C as CapturedLogSchema, D as DEFAULT_CAPTURE_MODE, a as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, b as DEFAULT_PROVIDER_TEST_TIMEOUT_SECONDS, c as DEFAULT_TIME_DISPLAY_FORMAT, d as RuntimeConfigSchema, v as packageJson, P as ProviderConfigSchema, f as apiFormatForPath, i as GroupEvidenceExportResultSchema, j as DeleteInspectorGroupResponseSchema, n as createPendingProviderTestResults, o as ProviderTestResultsSchema, q as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, r as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, g as getSessionPath, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, t as resolveProviderContextWindow, m as maskApiKey, k as providerHasContextMetadata, l as findProviderModelMetadata, K as KnowledgeCandidateSchema, h as stripClaudeCodeBillingHeader, u as isPlainRecord, G as GroupEvidenceReadResponseSchema } from "./router-DIIJGyML.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { J as JSZip } from "../_libs/jszip.mjs";
5
5
  import { c as clsx } from "../_libs/clsx.mjs";
6
6
  import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { c as cva } from "../_libs/class-variance-authority.mjs";
8
- import { R as Root, T as Trigger$2, C as Content, a as Close, b as Title, P as Portal$2, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
8
+ import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as Portal$2, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
9
9
  import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
10
  import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
11
11
  import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
12
- import { C as Check, X, P as Plus, D as Download, F as FileBraces, T as Trash2, L as Layers, a as LoaderCircle, R as RefreshCw, S as Settings, A as ArrowLeft, b as Copy, c as ChevronDown, U as Upload, d as Scan, e as CircleAlert, f as Terminal, g as ChevronUp, E as EyeOff, h as Eye, i as ExternalLink, j as RotateCw, k as Clock, l as ChevronRight, m as Pencil, n as ArrowUpRight, o as ArrowDownRight, p as User, M as MessageSquare, Z as Zap, W as Wrench, q as TriangleAlert, B as Brain, r as Minus, s as CircleCheckBig, t as CircleX, u as ShieldCheck, v as Save, w as FileSearch, x as CircleCheck, y as CloudUpload, O as OctagonAlert, z as Radio, G as Gauge, H as Globe, I as FileTerminal, J as ChevronsUp, K as ChevronsDown, N as FileDiff, Q as History, V as RotateCcw, Y as Search, _ as GitCompareArrows, $ as CircleQuestionMark, a0 as Server, a1 as Lock, a2 as Wifi, a3 as WifiOff, a4 as ArrowUp, a5 as ArrowDown, a6 as Ellipsis, a7 as CodeXml, a8 as Blocks, a9 as MousePointerClick, aa as FileText, ab as Rows3, ac as Columns2 } from "../_libs/lucide-react.mjs";
12
+ import { I as Info, C as ChevronDown, a as Check, X, D as Download, F as FileBraces, T as Trash2, P as Plus, A as ArrowLeft, b as ChevronUp, c as Copy, d as ChevronsLeft, e as ChevronLeft, f as ChevronRight, g as ChevronsRight, h as Clapperboard, S as Siren, G as Gauge, L as Layers, i as LoaderCircle, R as RefreshCw, j as Settings, U as Upload, k as Scan, l as CircleAlert, m as Terminal, n as ArrowUpRight, o as ArrowDownRight, p as User, q as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, W as Wrench, r as TriangleAlert, B as Brain, s as EyeOff, t as Eye, u as RotateCw, v as Pencil, w as CircleX, x as ShieldCheck, y as Save, z as FileSearch, H as CircleCheck, J as CloudUpload, O as OctagonAlert, K as Radio, N as Globe, Q as FileTerminal, V as ChevronsUp, Y as ChevronsDown, _ as FileDiff, $ as History, a0 as RotateCcw, a1 as Search, a2 as GitCompareArrows, a3 as Minus, a4 as CircleCheckBig, a5 as ArrowUp, a6 as ArrowDown, a7 as CircleQuestionMark, a8 as Server, a9 as Lock, aa as Wifi, ab as WifiOff, ac as Ellipsis, ad as CodeXml, ae as Blocks, af as MousePointerClick, ag as FileText, ah as Rows3, ai as Columns2 } from "../_libs/lucide-react.mjs";
13
13
  import { M as Markdown } from "../_libs/react-markdown.mjs";
14
14
  import { u as union, d as object, a as array, l as literal, b as string, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
15
- import { P as Provider, R as Root3, T as Trigger$1, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
16
15
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
17
- import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
16
+ import { R as Root2$1, L as List, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
17
+ import { P as Provider, R as Root3, T as Trigger$3, a as Portal$1, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
18
18
  import { R as Root$1, C as CollapsibleContent$1, a as CollapsibleTrigger$1 } from "../_libs/radix-ui__react-collapsible.mjs";
19
19
  import { R as Root$2, V as Viewport$1, C as Corner, S as ScrollAreaScrollbar, a as ScrollAreaThumb } from "../_libs/radix-ui__react-scroll-area.mjs";
20
20
  const ApiErrorSchema = object({
@@ -196,69 +196,70 @@ function useOnboarding() {
196
196
  }
197
197
  function OnboardingBanner() {
198
198
  const { hasSeenOnboarding, captureMode, isLoading, markSeen } = useOnboarding();
199
+ const [expanded, setExpanded] = reactExports.useState(false);
199
200
  if (isLoading || hasSeenOnboarding) return null;
200
201
  const isFullMode = captureMode === "full";
201
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
202
+ const modeLabel = isFullMode ? "Full" : "Simple";
203
+ const detailClass = expanded ? "grid" : "hidden sm:grid";
204
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
202
205
  "div",
203
206
  {
204
207
  role: "note",
205
208
  "aria-label": "Onboarding tip",
206
- className: "mx-4 mt-2 mb-1 flex items-start gap-3 rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm",
207
- children: [
209
+ className: "mx-4 mt-2 mb-1 rounded-md border border-amber-500/30 bg-[#130d04] px-3 py-2 text-sm shadow-[0_12px_40px_rgba(0,0,0,0.16)]",
210
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3", children: [
211
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Info, { className: "mt-0.5 size-4 shrink-0 text-amber-300", "aria-hidden": "true" }),
208
212
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
209
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-1 font-medium text-amber-600 dark:text-amber-400", children: isFullMode ? "Agent Inspector is ready in Full mode" : "Agent Inspector is ready in Simple mode" }),
210
- /* @__PURE__ */ jsxRuntimeExports.jsxs("ol", { className: "space-y-0.5 text-xs leading-relaxed text-muted-foreground", children: [
211
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
212
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "1. Start in the right mode" }),
213
- ": this process is currently running in",
214
- " ",
215
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: captureMode }),
216
- ". Mode is fixed at startup."
217
- ] }),
218
- isFullMode ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
219
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
220
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "2. Full diagnostics" }),
221
- ": raw headers, raw response, and SSE chunks are captured for deep investigations."
222
- ] }),
223
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
224
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "3. Daily work" }),
225
- ": restart with",
226
- " ",
227
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "agent-inspector --mode simple" }),
228
- " to reduce capture overhead."
229
- ] })
230
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
231
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
232
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "2. Simple capture" }),
233
- ": requests, responses, tools, timing, tokens, replay, and memory candidates are available with lower overhead."
234
- ] }),
235
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
236
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "3. Need raw diagnostics?" }),
237
- " Restart with",
238
- " ",
239
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "agent-inspector --mode full" }),
240
- " for raw headers, raw response, and SSE chunks."
241
- ] })
242
- ] }),
243
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
244
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Agent setup" }),
245
- ": run ",
246
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "agent-inspector onboard --status" }),
247
- ", then use ",
248
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "/agent-inspector:onboard" }),
249
- " in Claude Code or ask Codex to use",
250
- " ",
251
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "agent-inspector-onboard" }),
252
- "."
253
- ] }),
254
- /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { children: [
255
- /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Container networking" }),
256
- ": if the AI tool runs outside this Inspector container or host, replace ",
257
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "localhost" }),
258
- " with a reachable address."
259
- ] })
260
- ] })
213
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
214
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium text-amber-200", children: "Inspector ready" }),
215
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded border border-amber-400/30 bg-amber-400/10 px-1.5 py-0.5 font-mono text-[10px] uppercase text-amber-100", children: modeLabel }),
216
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden truncate text-xs text-muted-foreground sm:inline", children: isFullMode ? "Raw headers, raw response, and SSE chunks are being captured." : "Request, response, tool, timing, token, replay, and memory signals are active." })
217
+ ] }),
218
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 text-xs leading-relaxed text-muted-foreground sm:hidden", children: isFullMode ? "Full diagnostics are active." : "Core capture is active with lower overhead." }),
219
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
220
+ "div",
221
+ {
222
+ className: `${detailClass} mt-2 gap-x-4 gap-y-1 text-xs leading-relaxed text-muted-foreground sm:grid-cols-3`,
223
+ children: [
224
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
225
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Mode" }),
226
+ ": ",
227
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: captureMode }),
228
+ " is fixed until restart."
229
+ ] }),
230
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
231
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Diagnostics" }),
232
+ ": use",
233
+ " ",
234
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: isFullMode ? "agent-inspector --mode simple" : "agent-inspector --mode full" }),
235
+ "."
236
+ ] }),
237
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
238
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: "Network" }),
239
+ ": replace ",
240
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { children: "localhost" }),
241
+ " when containers differ."
242
+ ] })
243
+ ]
244
+ }
245
+ )
261
246
  ] }),
247
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
248
+ "button",
249
+ {
250
+ type: "button",
251
+ onClick: () => setExpanded((value) => !value),
252
+ className: "inline-flex h-8 shrink-0 items-center gap-1 rounded-md border border-amber-500/30 px-2 text-xs text-amber-200 transition-colors hover:bg-amber-500/10 sm:hidden",
253
+ "aria-expanded": expanded,
254
+ "aria-label": expanded ? "Hide onboarding details" : "Show onboarding details",
255
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
256
+ ChevronDown,
257
+ {
258
+ className: `size-3.5 transition-transform ${expanded ? "rotate-180" : ""}`
259
+ }
260
+ )
261
+ }
262
+ ),
262
263
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
263
264
  "button",
264
265
  {
@@ -266,7 +267,7 @@ function OnboardingBanner() {
266
267
  onClick: () => {
267
268
  void markSeen();
268
269
  },
269
- className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-amber-500/40 px-3 text-xs text-amber-700 transition-colors hover:bg-amber-500/10 dark:text-amber-300",
270
+ className: "hidden h-8 shrink-0 items-center gap-1.5 rounded-md border border-amber-500/40 px-3 text-xs text-amber-200 transition-colors hover:bg-amber-500/10 sm:inline-flex",
270
271
  "aria-label": "Dismiss onboarding tip",
271
272
  children: [
272
273
  /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }),
@@ -286,7 +287,7 @@ function OnboardingBanner() {
286
287
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(X, { className: "size-3.5" })
287
288
  }
288
289
  )
289
- ]
290
+ ] })
290
291
  }
291
292
  );
292
293
  }
@@ -613,7 +614,7 @@ function Dialog({
613
614
  function DialogTrigger({
614
615
  ...props
615
616
  }) {
616
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$2, { "data-slot": "dialog-trigger", ...props });
617
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$1, { "data-slot": "dialog-trigger", ...props });
617
618
  }
618
619
  function DialogPortal({
619
620
  ...props
@@ -629,7 +630,7 @@ function DialogOverlay({
629
630
  {
630
631
  "data-slot": "dialog-overlay",
631
632
  className: cn(
632
- "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
633
+ "fixed inset-0 z-40 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
633
634
  className
634
635
  ),
635
636
  ...props
@@ -1749,7 +1750,7 @@ function Tooltip({ ...props }) {
1749
1750
  return /* @__PURE__ */ jsxRuntimeExports.jsx(Root3, { "data-slot": "tooltip", ...props });
1750
1751
  }
1751
1752
  function TooltipTrigger({ ...props }) {
1752
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$1, { "data-slot": "tooltip-trigger", ...props });
1753
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$3, { "data-slot": "tooltip-trigger", ...props });
1753
1754
  }
1754
1755
  function TooltipContent({
1755
1756
  className,
@@ -1885,7 +1886,7 @@ function TabsTrigger({
1885
1886
  ...props
1886
1887
  }) {
1887
1888
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
1888
- Trigger$3,
1889
+ Trigger$2,
1889
1890
  {
1890
1891
  "data-slot": "tabs-trigger",
1891
1892
  className: cn(
@@ -1937,19 +1938,19 @@ function useCopyFeedback(text) {
1937
1938
  return { copied, copy };
1938
1939
  }
1939
1940
  const LazyCompareDrawer = reactExports.lazy(
1940
- () => import("./CompareDrawer-fy1ARwtx.mjs").then((m) => ({ default: m.CompareDrawer }))
1941
+ () => import("./CompareDrawer-DkSY3vxP.mjs").then((m) => ({ default: m.CompareDrawer }))
1941
1942
  );
1942
1943
  const LazyReplayDialog = reactExports.lazy(
1943
- () => import("./ReplayDialog-BrS7syOE.mjs").then((m) => ({ default: m.ReplayDialog }))
1944
+ () => import("./ReplayDialog-DhI6yxrR.mjs").then((m) => ({ default: m.ReplayDialog }))
1944
1945
  );
1945
1946
  const LazyRequestAnatomy = reactExports.lazy(
1946
- () => import("./RequestAnatomy-BtWt1cWY.mjs").then((m) => ({ default: m.RequestAnatomy }))
1947
+ () => import("./RequestAnatomy-DN1SJVVL.mjs").then((m) => ({ default: m.RequestAnatomy }))
1947
1948
  );
1948
1949
  const LazyResponseView = reactExports.lazy(
1949
- () => import("./ResponseView-CkKGYE6m.mjs").then((m) => ({ default: m.ResponseView }))
1950
+ () => import("./ResponseView-DW-kpJL5.mjs").then((m) => ({ default: m.ResponseView }))
1950
1951
  );
1951
1952
  const LazyStreamingChunkSequence = reactExports.lazy(
1952
- () => import("./StreamingChunkSequence-C7rB3osr.mjs").then((m) => ({
1953
+ () => import("./StreamingChunkSequence-BAE57Fae.mjs").then((m) => ({
1953
1954
  default: m.StreamingChunkSequence
1954
1955
  }))
1955
1956
  );
@@ -2402,7 +2403,7 @@ const LogEntryHeader = reactExports.memo(function({
2402
2403
  "data-nav-id": `log-${log.id}`,
2403
2404
  "data-nav-action": expanded ? "collapse" : "expand",
2404
2405
  className: cn(
2405
- "flex items-center gap-2 px-3 py-1 cursor-pointer transition-colors",
2406
+ "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1 px-3 py-1.5 cursor-pointer transition-colors",
2406
2407
  "hover:bg-muted/50",
2407
2408
  "select-none",
2408
2409
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:outline-none"
@@ -2491,24 +2492,30 @@ const LogEntryHeader = reactExports.memo(function({
2491
2492
  ] }) }),
2492
2493
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Output token throughput after first chunk" })
2493
2494
  ] }),
2494
- hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-xs shrink-0", children: [
2495
+ hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-center gap-1 text-xs sm:shrink-0", children: [
2495
2496
  /* @__PURE__ */ jsxRuntimeExports.jsx(Zap, { className: "size-3 text-muted-foreground" }),
2496
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums", children: [
2497
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-x-1 font-mono tabular-nums", children: [
2497
2498
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
2498
2499
  "span",
2499
2500
  {
2500
- className: log.inputTokens !== null ? "text-blue-400" : "text-muted-foreground",
2501
+ className: cn(
2502
+ "whitespace-nowrap",
2503
+ log.inputTokens !== null ? "text-blue-400" : "text-muted-foreground"
2504
+ ),
2501
2505
  children: [
2502
2506
  "IN ",
2503
2507
  log.inputTokens !== null ? formatTokens(log.inputTokens) : "—"
2504
2508
  ]
2505
2509
  }
2506
2510
  ),
2507
- " / ",
2511
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: "/" }),
2508
2512
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
2509
2513
  "span",
2510
2514
  {
2511
- className: log.outputTokens !== null ? "text-amber-400" : "text-muted-foreground",
2515
+ className: cn(
2516
+ "whitespace-nowrap",
2517
+ log.outputTokens !== null ? "text-amber-400" : "text-muted-foreground"
2518
+ ),
2512
2519
  children: [
2513
2520
  "OUT ",
2514
2521
  log.outputTokens !== null ? formatTokens(log.outputTokens) : "—"
@@ -3505,6 +3512,9 @@ function resolveFocusedTab(tab, anatomySegments) {
3505
3512
  return "request";
3506
3513
  }
3507
3514
  }
3515
+ function shouldHydrateLogBody(log) {
3516
+ return log.bodyContentMode === "compact" || log.bodyContentMode === "truncated";
3517
+ }
3508
3518
  function DiffToggleButton({
3509
3519
  active,
3510
3520
  onClick
@@ -3561,40 +3571,43 @@ const LogEntry = reactExports.memo(function({
3561
3571
  const [requestDiff, setRequestDiff] = reactExports.useState(false);
3562
3572
  const [activeTab, setActiveTab] = reactExports.useState("request");
3563
3573
  const [expandToPath, setExpandToPath] = reactExports.useState(null);
3574
+ const [hydratedLog, setHydratedLog] = reactExports.useState(null);
3575
+ const [bodyHydrationState, setBodyHydrationState] = reactExports.useState("idle");
3564
3576
  const requestJsonRef = reactExports.useRef(null);
3565
- const resolvedFormat = resolveLogFormat(log);
3577
+ const displayLog = hydratedLog ?? log;
3578
+ const resolvedFormat = resolveLogFormat(displayLog);
3566
3579
  const adapter = getLogFormatAdapter(resolvedFormat);
3567
3580
  const requestAnalysis = reactExports.useMemo(
3568
- () => adapter.analyzeRequest(log.rawRequestBody),
3569
- [adapter, log.rawRequestBody]
3581
+ () => adapter.analyzeRequest(displayLog.rawRequestBody),
3582
+ [adapter, displayLog.rawRequestBody]
3570
3583
  );
3571
3584
  const responseAnalysis = reactExports.useMemo(
3572
- () => adapter.analyzeResponse(log.responseText),
3573
- [adapter, log.responseText]
3585
+ () => adapter.analyzeResponse(displayLog.responseText),
3586
+ [adapter, displayLog.responseText]
3574
3587
  );
3575
3588
  const strippedRequestBody = reactExports.useMemo(() => {
3576
- if (!strip || resolvedFormat !== "anthropic" || log.rawRequestBody === null) {
3589
+ if (!strip || resolvedFormat !== "anthropic" || displayLog.rawRequestBody === null) {
3577
3590
  return null;
3578
3591
  }
3579
- return stripClaudeCodeBillingHeader(log.rawRequestBody).body;
3580
- }, [log.rawRequestBody, resolvedFormat, strip]);
3581
- const displayedRequestBody = strippedRequestBody ?? log.rawRequestBody;
3592
+ return stripClaudeCodeBillingHeader(displayLog.rawRequestBody).body;
3593
+ }, [displayLog.rawRequestBody, resolvedFormat, strip]);
3594
+ const displayedRequestBody = strippedRequestBody ?? displayLog.rawRequestBody;
3582
3595
  const requestExpansion = useJsonBulkExpansion(displayedRequestBody);
3583
- const rawRequestExpansion = useJsonBulkExpansion(log.rawRequestBody);
3584
- const responseExpansion = useJsonBulkExpansion(log.responseText);
3596
+ const rawRequestExpansion = useJsonBulkExpansion(displayLog.rawRequestBody);
3597
+ const responseExpansion = useJsonBulkExpansion(displayLog.responseText);
3585
3598
  const headersText = reactExports.useMemo(
3586
- () => log.headers && Object.keys(log.headers).length > 0 ? JSON.stringify(log.headers, null, 2) : null,
3587
- [log.headers]
3599
+ () => displayLog.headers && Object.keys(displayLog.headers).length > 0 ? JSON.stringify(displayLog.headers, null, 2) : null,
3600
+ [displayLog.headers]
3588
3601
  );
3589
3602
  const rawHeadersText = reactExports.useMemo(
3590
- () => log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? JSON.stringify(log.rawHeaders, null, 2) : null,
3591
- [log.rawHeaders]
3603
+ () => displayLog.rawHeaders && Object.keys(displayLog.rawHeaders).length > 0 ? JSON.stringify(displayLog.rawHeaders, null, 2) : null,
3604
+ [displayLog.rawHeaders]
3592
3605
  );
3593
3606
  const requestCopy = useCopyFeedback(displayedRequestBody);
3594
- const rawRequestCopy = useCopyFeedback(log.rawRequestBody);
3607
+ const rawRequestCopy = useCopyFeedback(displayLog.rawRequestBody);
3595
3608
  const headersCopy = useCopyFeedback(headersText);
3596
3609
  const rawHeadersCopy = useCopyFeedback(rawHeadersText);
3597
- const responseCopy = useCopyFeedback(log.responseText);
3610
+ const responseCopy = useCopyFeedback(displayLog.responseText);
3598
3611
  const tabActions = reactExports.useMemo(
3599
3612
  () => ({
3600
3613
  request: {
@@ -3614,15 +3627,15 @@ const LogEntry = reactExports.memo(function({
3614
3627
  resolvedFormat,
3615
3628
  viewMode,
3616
3629
  strip,
3617
- log.rawRequestBody !== null
3630
+ displayLog.rawRequestBody !== null
3618
3631
  ) ? { active: requestDiff, onToggle: () => setRequestDiff(!requestDiff) } : void 0,
3619
3632
  diffWithPrevious: onCompareWithPrevious === void 0 ? void 0 : () => {
3620
- onCompareWithPrevious(log);
3633
+ onCompareWithPrevious(displayLog);
3621
3634
  }
3622
3635
  },
3623
3636
  "raw-request": {
3624
3637
  copyLabel: "Copy raw request",
3625
- copyText: log.rawRequestBody,
3638
+ copyText: displayLog.rawRequestBody,
3626
3639
  copyCopied: rawRequestCopy.copied,
3627
3640
  onCopy: rawRequestCopy.copy,
3628
3641
  expansion: {
@@ -3648,7 +3661,7 @@ const LogEntry = reactExports.memo(function({
3648
3661
  },
3649
3662
  raw: {
3650
3663
  copyLabel: "Copy response",
3651
- copyText: log.responseText,
3664
+ copyText: displayLog.responseText,
3652
3665
  copyCopied: responseCopy.copied,
3653
3666
  onCopy: responseCopy.copy,
3654
3667
  expansion: {
@@ -3663,14 +3676,15 @@ const LogEntry = reactExports.memo(function({
3663
3676
  requestCopy,
3664
3677
  requestExpansion,
3665
3678
  requestDiff,
3666
- log.rawRequestBody,
3679
+ displayLog,
3680
+ displayLog.rawRequestBody,
3667
3681
  rawRequestCopy,
3668
3682
  rawRequestExpansion,
3669
3683
  headersText,
3670
3684
  headersCopy,
3671
3685
  rawHeadersText,
3672
3686
  rawHeadersCopy,
3673
- log.responseText,
3687
+ displayLog.responseText,
3674
3688
  responseCopy,
3675
3689
  responseExpansion,
3676
3690
  resolvedFormat,
@@ -3687,7 +3701,7 @@ const LogEntry = reactExports.memo(function({
3687
3701
  () => parseRequestTools(requestExpansion.parsedData),
3688
3702
  [requestExpansion.parsedData]
3689
3703
  );
3690
- const warnings = log.warnings ?? [];
3704
+ const warnings = displayLog.warnings ?? [];
3691
3705
  const anatomyPaths = reactExports.useMemo(() => {
3692
3706
  if (anatomySegments === null) return void 0;
3693
3707
  return new Set(anatomySegments.map((s) => s.path));
@@ -3706,6 +3720,28 @@ const LogEntry = reactExports.memo(function({
3706
3720
  }
3707
3721
  setExpanded(nextExpanded);
3708
3722
  }, [anatomySegments, expanded]);
3723
+ reactExports.useEffect(() => {
3724
+ setHydratedLog(null);
3725
+ setBodyHydrationState("idle");
3726
+ }, [log.id]);
3727
+ reactExports.useEffect(() => {
3728
+ if (!expanded) return;
3729
+ if (!shouldHydrateLogBody(log)) return;
3730
+ if (hydratedLog !== null || bodyHydrationState !== "idle") return;
3731
+ let cancelled = false;
3732
+ setBodyHydrationState("loading");
3733
+ void fetchJson(`/api/logs/${String(log.id)}`, CapturedLogSchema).then((fullLog) => {
3734
+ if (cancelled) return;
3735
+ setHydratedLog(fullLog);
3736
+ setBodyHydrationState("idle");
3737
+ }).catch(() => {
3738
+ if (cancelled) return;
3739
+ setBodyHydrationState("failed");
3740
+ });
3741
+ return () => {
3742
+ cancelled = true;
3743
+ };
3744
+ }, [bodyHydrationState, expanded, hydratedLog, log.bodyContentMode, log.id]);
3709
3745
  reactExports.useEffect(() => {
3710
3746
  const handleLogFocusRequest = (event) => {
3711
3747
  const request = readLogFocusRequest(event);
@@ -3724,7 +3760,7 @@ const LogEntry = reactExports.memo(function({
3724
3760
  /* @__PURE__ */ jsxRuntimeExports.jsx(
3725
3761
  LogEntryHeader,
3726
3762
  {
3727
- log,
3763
+ log: displayLog,
3728
3764
  messageCount: requestAnalysis.messageCount,
3729
3765
  toolCount: requestAnalysis.toolCount,
3730
3766
  expanded,
@@ -3749,15 +3785,17 @@ const LogEntry = reactExports.memo(function({
3749
3785
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "raw", children: "Raw Response" }),
3750
3786
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsTrigger, { value: "parsed", children: "Response" })
3751
3787
  ] }),
3752
- shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: log.rawRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3788
+ shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-request", children: activeTab === "raw-request" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: displayLog.rawRequestBody === null ? /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No request body" }) : rawRequestExpansion.parsedData !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3753
3789
  LazyJsonViewer,
3754
3790
  {
3755
3791
  data: rawRequestExpansion.parsedData,
3756
3792
  bulkDepth: rawRequestExpansion.bulkDepth,
3757
3793
  bulkRevision: rawRequestExpansion.bulkRevision
3758
3794
  }
3759
- ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: log.rawRequestBody }) }) }),
3795
+ ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "font-mono text-xs whitespace-pre-wrap break-words", children: displayLog.rawRequestBody }) }) }),
3760
3796
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "request", children: activeTab === "request" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
3797
+ bodyHydrationState === "loading" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-3 rounded border border-border bg-muted/30 p-3 text-xs text-muted-foreground", children: "Loading full request and response bodies..." }),
3798
+ bodyHydrationState === "failed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-3 rounded border border-amber-500/30 bg-amber-500/10 p-3 text-xs text-amber-200", children: "Full request and response bodies could not be loaded. Compact metadata is still available." }),
3761
3799
  warnings.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 rounded border border-amber-500/30 bg-amber-500/10 p-3 text-xs", children: [
3762
3800
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 font-semibold text-amber-300", children: [
3763
3801
  /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3.5" }),
@@ -3775,7 +3813,7 @@ const LogEntry = reactExports.memo(function({
3775
3813
  requestDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3776
3814
  RequestDiffContent,
3777
3815
  {
3778
- rawBody: log.rawRequestBody,
3816
+ rawBody: displayLog.rawRequestBody,
3779
3817
  displayedBody: displayedRequestBody,
3780
3818
  emptyLabel: "No transformation applied; raw and sent request bodies are identical."
3781
3819
  }
@@ -3794,8 +3832,8 @@ const LogEntry = reactExports.memo(function({
3794
3832
  LazyRequestAnatomy,
3795
3833
  {
3796
3834
  parsed: requestExpansion.parsedData,
3797
- inputTokens: log.inputTokens ?? null,
3798
- model: log.model,
3835
+ inputTokens: displayLog.inputTokens ?? null,
3836
+ model: displayLog.model,
3799
3837
  segments: anatomySegments,
3800
3838
  onSegmentActivate: jumpToAnatomySegment
3801
3839
  }
@@ -3803,7 +3841,7 @@ const LogEntry = reactExports.memo(function({
3803
3841
  viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "headers", children: activeTab === "headers" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3", children: [
3804
3842
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end gap-2 mb-2", children: shouldShowHeadersDiffButton(
3805
3843
  viewMode,
3806
- log.rawHeaders !== void 0 && Object.keys(log.rawHeaders).length > 0
3844
+ displayLog.rawHeaders !== void 0 && Object.keys(displayLog.rawHeaders).length > 0
3807
3845
  ) && /* @__PURE__ */ jsxRuntimeExports.jsx(
3808
3846
  DiffToggleButton,
3809
3847
  {
@@ -3817,11 +3855,11 @@ const LogEntry = reactExports.memo(function({
3817
3855
  headersDiff ? /* @__PURE__ */ jsxRuntimeExports.jsx(
3818
3856
  HeadersDiffContent,
3819
3857
  {
3820
- rawHeaders: log.rawHeaders,
3821
- headers: log.headers,
3858
+ rawHeaders: displayLog.rawHeaders,
3859
+ headers: displayLog.headers,
3822
3860
  emptyLabel: "No transformation applied; raw and processed headers are identical."
3823
3861
  }
3824
- ) : log.headers && Object.keys(log.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
3862
+ ) : displayLog.headers && Object.keys(displayLog.headers).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(displayLog.headers).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
3825
3863
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
3826
3864
  key,
3827
3865
  ":"
@@ -3829,7 +3867,7 @@ const LogEntry = reactExports.memo(function({
3829
3867
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
3830
3868
  ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No headers captured" })
3831
3869
  ] }) }),
3832
- viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(log.rawHeaders).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
3870
+ viewMode === "full" && /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw-headers", children: activeTab === "raw-headers" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: displayLog.rawHeaders && Object.keys(displayLog.rawHeaders).length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1 font-mono text-xs", children: Object.entries(displayLog.rawHeaders).sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
3833
3871
  /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-blue-600 dark:text-blue-400 font-semibold shrink-0", children: [
3834
3872
  key,
3835
3873
  ":"
@@ -3837,44 +3875,44 @@ const LogEntry = reactExports.memo(function({
3837
3875
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
3838
3876
  ] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No raw headers captured" }) }) }),
3839
3877
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: activeTab === "raw" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 pt-1 pb-3 space-y-3", children: [
3840
- log.error !== void 0 && log.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
3878
+ displayLog.error !== void 0 && displayLog.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
3841
3879
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
3842
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: log.error })
3880
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: displayLog.error })
3843
3881
  ] }),
3844
- log.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3882
+ displayLog.responseText !== null ? /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3845
3883
  LazyJsonViewerFromString,
3846
3884
  {
3847
- text: log.responseText,
3885
+ text: displayLog.responseText,
3848
3886
  defaultExpandDepth: 0,
3849
3887
  bulkDepth: responseExpansion.bulkDepth,
3850
3888
  bulkRevision: responseExpansion.bulkRevision
3851
3889
  }
3852
3890
  ) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No response" }),
3853
- log.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3891
+ displayLog.streaming === true && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3854
3892
  LazyStreamingChunkSequence,
3855
3893
  {
3856
- logId: log.id,
3857
- truncated: log.streamingChunksPath !== null
3894
+ logId: displayLog.id,
3895
+ truncated: displayLog.streamingChunksPath !== null
3858
3896
  }
3859
3897
  ) })
3860
3898
  ] }) }),
3861
3899
  /* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "parsed", children: activeTab === "parsed" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-4 pt-1 pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: /* @__PURE__ */ jsxRuntimeExports.jsx(TabFallback, {}), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
3862
3900
  LazyResponseView,
3863
3901
  {
3864
- responseText: log.responseText,
3865
- responseStatus: log.responseStatus,
3866
- streaming: log.streaming,
3867
- inputTokens: log.inputTokens,
3868
- outputTokens: log.outputTokens,
3869
- cacheCreationInputTokens: log.cacheCreationInputTokens,
3870
- cacheReadInputTokens: log.cacheReadInputTokens,
3902
+ responseText: displayLog.responseText,
3903
+ responseStatus: displayLog.responseStatus,
3904
+ streaming: displayLog.streaming,
3905
+ inputTokens: displayLog.inputTokens,
3906
+ outputTokens: displayLog.outputTokens,
3907
+ cacheCreationInputTokens: displayLog.cacheCreationInputTokens,
3908
+ cacheReadInputTokens: displayLog.cacheReadInputTokens,
3871
3909
  apiFormat: resolvedFormat,
3872
- error: log.error
3910
+ error: displayLog.error
3873
3911
  }
3874
3912
  ) }) }) })
3875
3913
  ] }) })
3876
3914
  ] }),
3877
- /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyReplayDialog, { log, open: replayOpen, onOpenChange: setReplayOpen }) })
3915
+ /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyReplayDialog, { log: displayLog, open: replayOpen, onOpenChange: setReplayOpen }) })
3878
3916
  ] });
3879
3917
  });
3880
3918
  function ThreadConnector({
@@ -5314,74 +5352,7 @@ function CrabLogo({ className }) {
5314
5352
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
5315
5353
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
5316
5354
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
5317
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" }),
5318
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5319
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5320
- "path",
5321
- {
5322
- d: "M5.6 17.7 L8.4 15.4 L10.8 18 C9.1 18.9 7.2 18.8 5.6 17.7 Z",
5323
- fill: "#2f6b3f",
5324
- stroke: "none",
5325
- opacity: "0.95"
5326
- }
5327
- ),
5328
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5329
- "path",
5330
- {
5331
- d: "M6.9 17.5 L8.4 16 L9.8 17.7 C8.9 18.1 7.8 18.1 6.9 17.5 Z",
5332
- fill: "#6fb36f",
5333
- stroke: "none",
5334
- opacity: "0.95"
5335
- }
5336
- ),
5337
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8.4 16 L8.4 18.4", stroke: "#d6b45f", strokeWidth: "0.34" }),
5338
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.6", cy: "17.75", r: "0.36", fill: "#c2412d", stroke: "none" })
5339
- ] }),
5340
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5341
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5342
- "path",
5343
- {
5344
- d: "M8 16.5 L11.9 13.6 L15.8 16.8 C13.3 18.4 10.4 18.3 8 16.5 Z",
5345
- fill: "#2f6b3f",
5346
- stroke: "none",
5347
- opacity: "0.97"
5348
- }
5349
- ),
5350
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5351
- "path",
5352
- {
5353
- d: "M9.8 16.3 L11.9 14.4 L14.3 16.5 C12.8 17.4 11.3 17.4 9.8 16.3 Z",
5354
- fill: "#6fb36f",
5355
- stroke: "none",
5356
- opacity: "0.95"
5357
- }
5358
- ),
5359
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M11.9 14.3 L11.9 17.8", stroke: "#d6b45f", strokeWidth: "0.42" }),
5360
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M9.8 16.3 C11.2 15.8 12.9 15.9 14.3 16.5", stroke: "#9fca78", strokeWidth: "0.36" }),
5361
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "15.6", cy: "16.75", r: "0.43", fill: "#c2412d", stroke: "none" })
5362
- ] }),
5363
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5364
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5365
- "path",
5366
- {
5367
- d: "M13.6 18.1 L16.2 15.7 L18.5 18.2 C17.1 19.1 15.2 19 13.6 18.1 Z",
5368
- fill: "#2f6b3f",
5369
- stroke: "none",
5370
- opacity: "0.95"
5371
- }
5372
- ),
5373
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5374
- "path",
5375
- {
5376
- d: "M14.8 17.9 L16.2 16.3 L17.5 18 C16.6 18.4 15.7 18.4 14.8 17.9 Z",
5377
- fill: "#6fb36f",
5378
- stroke: "none",
5379
- opacity: "0.95"
5380
- }
5381
- ),
5382
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M16.2 16.3 L16.2 18.6", stroke: "#d6b45f", strokeWidth: "0.34" }),
5383
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "18.35", cy: "18.15", r: "0.36", fill: "#c2412d", stroke: "none" })
5384
- ] })
5355
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
5385
5356
  ]
5386
5357
  }
5387
5358
  );
@@ -6217,21 +6188,24 @@ function MemberRow({ row }) {
6217
6188
  const errors = session?.errorCount ?? null;
6218
6189
  const tokens = session?.tokenUsage.total ?? null;
6219
6190
  const firstChunkMs = log?.firstChunkMs ?? null;
6191
+ const sessionLabel = firstNonEmpty(member.label, member.sessionId);
6220
6192
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border last:border-0", children: [
6221
6193
  /* @__PURE__ */ jsxRuntimeExports.jsxs("td", { className: "max-w-56 px-3 py-2", children: [
6222
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
6223
- /* @__PURE__ */ jsxRuntimeExports.jsx(
6224
- "a",
6225
- {
6226
- href: sessionPath,
6227
- target: "_blank",
6228
- rel: "noreferrer",
6229
- className: "min-w-0 truncate font-mono text-xs text-primary hover:underline",
6230
- children: firstNonEmpty(member.label, member.sessionId)
6231
- }
6232
- ),
6233
- /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3 shrink-0 text-muted-foreground" })
6234
- ] }),
6194
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
6195
+ "a",
6196
+ {
6197
+ href: sessionPath,
6198
+ target: "_blank",
6199
+ rel: "noreferrer",
6200
+ className: "inline-flex max-w-full min-w-0 items-center gap-2 rounded-sm font-mono text-xs text-primary outline-none hover:underline focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
6201
+ "aria-label": `Open session ${sessionLabel}`,
6202
+ title: `Open session ${sessionLabel}`,
6203
+ children: [
6204
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: sessionLabel }),
6205
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3 shrink-0 text-muted-foreground" })
6206
+ ]
6207
+ }
6208
+ ),
6235
6209
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 truncate text-[11px] text-muted-foreground", children: displayText(member.agent) })
6236
6210
  ] }),
6237
6211
  /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "max-w-52 px-3 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "block truncate", children: providerModel(member) }) }),
@@ -10226,6 +10200,114 @@ function getFirstUserAgent(logs) {
10226
10200
  }
10227
10201
  return null;
10228
10202
  }
10203
+ function hasLogFailure(log) {
10204
+ if (log.error !== null && log.error !== void 0 && log.error.length > 0) return true;
10205
+ return log.responseStatus !== null && log.responseStatus >= 400;
10206
+ }
10207
+ function getModelLabel(logs) {
10208
+ const models = /* @__PURE__ */ new Set();
10209
+ for (const log of logs) {
10210
+ if (log.model !== null && log.model.length > 0) {
10211
+ models.add(log.model);
10212
+ }
10213
+ }
10214
+ const sorted = [...models].sort();
10215
+ if (sorted.length === 0) return "Model unknown";
10216
+ if (sorted.length === 1) return sorted[0] ?? "Model unknown";
10217
+ return `${sorted.length} models`;
10218
+ }
10219
+ function buildSessionSlateStats({
10220
+ logs,
10221
+ isLoading,
10222
+ timeDisplayFormat,
10223
+ slowResponseThresholdSeconds
10224
+ }) {
10225
+ let failures = 0;
10226
+ let slow = 0;
10227
+ let totalIn = 0;
10228
+ let totalOut = 0;
10229
+ const slowThresholdMs = slowResponseThresholdSeconds * 1e3;
10230
+ for (const log of logs) {
10231
+ if (hasLogFailure(log)) failures += 1;
10232
+ if (slowResponseThresholdSeconds > 0 && log.elapsedMs !== null && log.elapsedMs >= slowThresholdMs) {
10233
+ slow += 1;
10234
+ }
10235
+ if (log.inputTokens !== null) totalIn += log.inputTokens;
10236
+ if (log.outputTokens !== null) totalOut += log.outputTokens;
10237
+ }
10238
+ const status = isLoading ? "rolling" : failures > 0 ? "failed" : slow > 0 ? "watch" : "clear";
10239
+ const statusLabel2 = status === "rolling" ? "Rolling" : status === "failed" ? "Failure" : status === "watch" ? "Watch" : "Clear";
10240
+ return {
10241
+ status,
10242
+ statusLabel: statusLabel2,
10243
+ modelLabel: getModelLabel(logs),
10244
+ requests: logs.length,
10245
+ failures,
10246
+ slow,
10247
+ timeRange: formatTimeRange(logs, timeDisplayFormat),
10248
+ tokenLabel: `${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`
10249
+ };
10250
+ }
10251
+ function slateStatusClass(status) {
10252
+ switch (status) {
10253
+ case "rolling":
10254
+ return "border-cyan-400/35 bg-cyan-500/10 text-cyan-200";
10255
+ case "failed":
10256
+ return "border-red-400/35 bg-red-500/10 text-red-100";
10257
+ case "watch":
10258
+ return "border-amber-300/35 bg-amber-400/10 text-amber-100";
10259
+ case "clear":
10260
+ return "border-emerald-400/30 bg-emerald-500/10 text-emerald-100";
10261
+ }
10262
+ }
10263
+ function SessionOpeningSlate({ stats }) {
10264
+ if (stats.requests === 0) return null;
10265
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 rounded-md border border-border bg-[#0c0d0f] shadow-[0_14px_50px_rgba(0,0,0,0.25)]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-0 md:grid-cols-[minmax(220px,1.2fr)_repeat(4,minmax(120px,1fr))]", children: [
10266
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "col-span-2 border-b border-border/70 px-4 py-3 md:col-span-1 md:border-b-0 md:border-r", children: [
10267
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-[10px] font-semibold uppercase text-muted-foreground", children: [
10268
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Clapperboard, { className: "size-3.5 text-cyan-300" }),
10269
+ "Opening Slate"
10270
+ ] }),
10271
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 min-w-0 truncate font-mono text-sm font-semibold text-foreground", children: stats.modelLabel }),
10272
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 truncate font-mono text-[11px] text-muted-foreground", children: stats.timeRange ?? "Timeline pending" })
10273
+ ] }),
10274
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-r border-b border-border/70 px-4 py-3 md:border-b-0", children: [
10275
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Status" }),
10276
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10277
+ "div",
10278
+ {
10279
+ className: cn(
10280
+ "mt-2 inline-flex h-7 items-center rounded-md border px-2.5 font-mono text-xs font-semibold",
10281
+ slateStatusClass(stats.status)
10282
+ ),
10283
+ children: stats.statusLabel
10284
+ }
10285
+ )
10286
+ ] }),
10287
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-b border-border/70 px-4 py-3 md:border-b-0 md:border-r", children: [
10288
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Requests" }),
10289
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 font-mono text-xl font-semibold text-foreground", children: stats.requests })
10290
+ ] }),
10291
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-r border-border/70 px-4 py-3 md:border-r", children: [
10292
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-[10px] uppercase text-muted-foreground", children: [
10293
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Siren, { className: "size-3 text-red-300" }),
10294
+ "Failure"
10295
+ ] }),
10296
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 font-mono text-xl font-semibold text-red-100", children: stats.failures })
10297
+ ] }),
10298
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3", children: [
10299
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-[10px] uppercase text-muted-foreground", children: [
10300
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { className: "size-3 text-amber-200" }),
10301
+ "Tempo"
10302
+ ] }),
10303
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 font-mono text-sm font-semibold text-foreground", children: [
10304
+ stats.slow,
10305
+ " slow"
10306
+ ] }),
10307
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 truncate font-mono text-[11px] text-muted-foreground", children: stats.tokenLabel })
10308
+ ] })
10309
+ ] }) });
10310
+ }
10229
10311
  function resolveSessionContextScope({
10230
10312
  pinnedSessionId,
10231
10313
  selectedSession,
@@ -10298,6 +10380,258 @@ function formatContextWindow(summary) {
10298
10380
  if (tokens === null) return "Context window unknown";
10299
10381
  return `Context window ${formatContextWindowTokens(tokens)}`;
10300
10382
  }
10383
+ const MEMBER_METADATA_KEYS = [
10384
+ "score",
10385
+ "decision",
10386
+ "final_status",
10387
+ "validation_pass_ratio",
10388
+ "semantic_memory_score",
10389
+ "opencode_exit",
10390
+ "actual_model",
10391
+ "run_dir",
10392
+ "workspace",
10393
+ "project_dir",
10394
+ "work_dir"
10395
+ ];
10396
+ function formatMetadataValue(value) {
10397
+ if (typeof value === "string") return value.length > 0 ? value : null;
10398
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
10399
+ if (value === null) return null;
10400
+ return JSON.stringify(value);
10401
+ }
10402
+ function formatMemberMetadata(member, key) {
10403
+ if (member === null) return null;
10404
+ const value = member.metadata[key];
10405
+ if (value === void 0) return null;
10406
+ return formatMetadataValue(value);
10407
+ }
10408
+ function evidenceChipClass(value) {
10409
+ const normalized = value.toLowerCase();
10410
+ if (normalized.includes("reject") || normalized.includes("fail") || normalized.includes("error")) {
10411
+ return "border-red-400/35 bg-red-500/10 text-red-100";
10412
+ }
10413
+ if (normalized.includes("partial") || normalized.includes("pending") || normalized.includes("slow")) {
10414
+ return "border-amber-300/35 bg-amber-400/10 text-amber-100";
10415
+ }
10416
+ if (normalized.includes("pass") || normalized.includes("complete") || normalized.includes("accept")) {
10417
+ return "border-emerald-400/30 bg-emerald-500/10 text-emerald-100";
10418
+ }
10419
+ return "border-border/70 bg-background/70 text-muted-foreground";
10420
+ }
10421
+ function getMemberMetadataRows(member) {
10422
+ const rows = [];
10423
+ for (const key of MEMBER_METADATA_KEYS) {
10424
+ const value = member.metadata[key];
10425
+ if (value === void 0) continue;
10426
+ const formatted = formatMetadataValue(value);
10427
+ if (formatted !== null) rows.push({ key, value: formatted });
10428
+ }
10429
+ return rows;
10430
+ }
10431
+ function SessionMembershipPanel({
10432
+ memberships,
10433
+ defaultOpen = false
10434
+ }) {
10435
+ const [open, setOpen] = reactExports.useState(defaultOpen);
10436
+ if (memberships.length === 0) return null;
10437
+ const primary = memberships[0];
10438
+ const primaryMember = primary?.member ?? null;
10439
+ const score = formatMemberMetadata(primaryMember, "score");
10440
+ const decision = formatMemberMetadata(primaryMember, "decision");
10441
+ const finalStatus = formatMemberMetadata(primaryMember, "final_status");
10442
+ const summaryChips = [
10443
+ score === null ? null : { label: `score ${score}`, value: score },
10444
+ decision === null ? null : { label: decision, value: decision },
10445
+ finalStatus === null ? null : { label: finalStatus, value: finalStatus },
10446
+ primaryMember?.model === null || primaryMember?.model === void 0 ? null : { label: primaryMember.model, value: primaryMember.model }
10447
+ ].filter(
10448
+ (value) => value !== null && value.label.length > 0 && value.value.length > 0
10449
+ );
10450
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 overflow-hidden rounded-md border border-border bg-[#0c0d0f] text-xs", children: [
10451
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
10452
+ "button",
10453
+ {
10454
+ type: "button",
10455
+ onClick: () => setOpen((value) => !value),
10456
+ className: "flex w-full items-center gap-3 px-3 py-2 text-left transition-colors hover:bg-muted/30",
10457
+ "aria-expanded": open,
10458
+ children: [
10459
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
10460
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
10461
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-semibold text-foreground", children: "Case File" }),
10462
+ primary !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-muted-foreground", title: primary.groupTitle, children: primary.groupTitle }),
10463
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-[10px] uppercase text-muted-foreground", children: [
10464
+ memberships.length,
10465
+ " group",
10466
+ memberships.length === 1 ? "" : "s"
10467
+ ] })
10468
+ ] }),
10469
+ summaryChips.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: summaryChips.slice(0, 4).map((chip) => /* @__PURE__ */ jsxRuntimeExports.jsx(
10470
+ "span",
10471
+ {
10472
+ className: cn(
10473
+ "rounded border px-1.5 py-0.5 font-mono text-[10px]",
10474
+ evidenceChipClass(chip.value)
10475
+ ),
10476
+ children: chip.label
10477
+ },
10478
+ chip.label
10479
+ )) })
10480
+ ] }),
10481
+ open ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 shrink-0 text-muted-foreground" })
10482
+ ]
10483
+ }
10484
+ ),
10485
+ open && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2 border-t border-border/70 px-3 py-2", children: memberships.map((membership) => {
10486
+ const member = membership.member;
10487
+ const metadataRows = getMemberMetadataRows(member);
10488
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
10489
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [
10490
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10491
+ "span",
10492
+ {
10493
+ className: "truncate font-medium text-foreground",
10494
+ title: membership.groupTitle,
10495
+ children: membership.groupTitle
10496
+ }
10497
+ ),
10498
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] uppercase text-muted-foreground", children: membership.groupStatus }),
10499
+ member.status !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono text-[10px] text-muted-foreground", children: [
10500
+ "member ",
10501
+ member.status
10502
+ ] })
10503
+ ] }),
10504
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
10505
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", title: member.sessionId, children: truncateSessionId(member.sessionId) }),
10506
+ member.label !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: member.label }),
10507
+ member.provider !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: member.provider }),
10508
+ member.model !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono", children: member.model }),
10509
+ member.runId !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", title: member.runId, children: [
10510
+ "run ",
10511
+ truncateSessionId(member.runId)
10512
+ ] })
10513
+ ] }),
10514
+ metadataRows.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 grid gap-1 sm:grid-cols-2 xl:grid-cols-3", children: metadataRows.map((row) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
10515
+ "div",
10516
+ {
10517
+ className: "min-w-0 rounded border border-border/60 px-2 py-1",
10518
+ children: [
10519
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-[10px] text-muted-foreground", children: row.key }),
10520
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10521
+ "div",
10522
+ {
10523
+ className: "truncate font-mono text-[11px] text-foreground",
10524
+ title: row.value,
10525
+ children: row.value
10526
+ }
10527
+ )
10528
+ ]
10529
+ },
10530
+ row.key
10531
+ )) })
10532
+ ] }, `${membership.groupId}:${member.id}`);
10533
+ }) })
10534
+ ] });
10535
+ }
10536
+ function formatPaginationStatus({
10537
+ logs,
10538
+ pagination
10539
+ }) {
10540
+ if (pagination.isLoading && logs.length === 0) return "Loading logs...";
10541
+ if (logs.length === 0) return "No logs on this page";
10542
+ const first = logs[0];
10543
+ const last = logs[logs.length - 1];
10544
+ const range = first === void 0 || last === void 0 ? "Current page" : `#${first.id}-#${last.id}`;
10545
+ const total = pagination.total === null ? "logs" : `${pagination.total} logs`;
10546
+ return `Showing ${range} of ${total}`;
10547
+ }
10548
+ function LogPaginationButton({
10549
+ label,
10550
+ title,
10551
+ icon,
10552
+ disabled,
10553
+ onClick
10554
+ }) {
10555
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
10556
+ "button",
10557
+ {
10558
+ type: "button",
10559
+ onClick,
10560
+ disabled,
10561
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-45",
10562
+ title,
10563
+ children: [
10564
+ icon,
10565
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: label })
10566
+ ]
10567
+ }
10568
+ );
10569
+ }
10570
+ function LogPaginationBar({
10571
+ logs,
10572
+ pagination,
10573
+ embedded = false
10574
+ }) {
10575
+ if (pagination === void 0) return null;
10576
+ const disabled = pagination.isLoading;
10577
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
10578
+ "div",
10579
+ {
10580
+ className: cn(
10581
+ "flex min-h-9 flex-col gap-2 px-1 py-1 sm:flex-row sm:items-center",
10582
+ embedded ? "border-t border-border/70 pt-2" : "mb-3 rounded-md border border-border bg-muted/10 px-2"
10583
+ ),
10584
+ children: [
10585
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-full min-w-0 px-1 font-mono text-[11px] text-muted-foreground sm:flex-1", children: [
10586
+ formatPaginationStatus({ logs, pagination }),
10587
+ pagination.isLoading && logs.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-2", children: "Loading..." })
10588
+ ] }),
10589
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex w-full items-center gap-1 overflow-x-auto sm:w-auto", children: [
10590
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10591
+ LogPaginationButton,
10592
+ {
10593
+ label: "Oldest",
10594
+ title: "Load the oldest logs in this session",
10595
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsLeft, { className: "size-3.5" }),
10596
+ disabled: disabled || !pagination.hasOlder,
10597
+ onClick: pagination.onOldest
10598
+ }
10599
+ ),
10600
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10601
+ LogPaginationButton,
10602
+ {
10603
+ label: "Older",
10604
+ title: "Load older logs",
10605
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronLeft, { className: "size-3.5" }),
10606
+ disabled: disabled || !pagination.hasOlder,
10607
+ onClick: pagination.onOlder
10608
+ }
10609
+ ),
10610
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10611
+ LogPaginationButton,
10612
+ {
10613
+ label: "Newer",
10614
+ title: "Load newer logs",
10615
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" }),
10616
+ disabled: disabled || !pagination.hasNewer,
10617
+ onClick: pagination.onNewer
10618
+ }
10619
+ ),
10620
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
10621
+ LogPaginationButton,
10622
+ {
10623
+ label: "Newest",
10624
+ title: "Load the newest logs in this session",
10625
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronsRight, { className: "size-3.5" }),
10626
+ disabled: disabled || !pagination.hasNewer,
10627
+ onClick: pagination.onNewest
10628
+ }
10629
+ )
10630
+ ] })
10631
+ ]
10632
+ }
10633
+ );
10634
+ }
10301
10635
  function formatRequestCount(count) {
10302
10636
  return `${count} ${count === 1 ? "request" : "requests"}`;
10303
10637
  }
@@ -10383,36 +10717,7 @@ function CopyableCommand({ command }) {
10383
10717
  onClick: handleCopy,
10384
10718
  className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 cursor-pointer",
10385
10719
  "aria-label": "Copy command",
10386
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(
10387
- "svg",
10388
- {
10389
- width: "16",
10390
- height: "16",
10391
- viewBox: "0 0 24 24",
10392
- fill: "none",
10393
- stroke: "currentColor",
10394
- strokeWidth: "2",
10395
- strokeLinecap: "round",
10396
- strokeLinejoin: "round",
10397
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M20 6 9 17l-5-5" })
10398
- }
10399
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
10400
- "svg",
10401
- {
10402
- width: "16",
10403
- height: "16",
10404
- viewBox: "0 0 24 24",
10405
- fill: "none",
10406
- stroke: "currentColor",
10407
- strokeWidth: "2",
10408
- strokeLinecap: "round",
10409
- strokeLinejoin: "round",
10410
- children: [
10411
- /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
10412
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" })
10413
- ]
10414
- }
10415
- )
10720
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-4" })
10416
10721
  }
10417
10722
  )
10418
10723
  ] });
@@ -10427,6 +10732,98 @@ function McpReadyBadge() {
10427
10732
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { sideOffset: 8, className: "max-w-[320px] text-left leading-relaxed", children: "Coding agents can inspect logs, replay requests, test providers, and debug sessions through MCP at /api/mcp." })
10428
10733
  ] }) });
10429
10734
  }
10735
+ const CRAB_VARIANT_COLORS = [
10736
+ "text-amber-500",
10737
+ "text-rose-500",
10738
+ "text-sky-500",
10739
+ "text-emerald-500",
10740
+ "text-violet-500",
10741
+ "text-orange-500",
10742
+ "text-cyan-500",
10743
+ "text-pink-500",
10744
+ "text-lime-500",
10745
+ "text-blue-500",
10746
+ "text-yellow-500",
10747
+ "text-fuchsia-500"
10748
+ ];
10749
+ function CrabFamily({
10750
+ compact,
10751
+ entrancePhase
10752
+ }) {
10753
+ if (compact) {
10754
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-7 text-amber-500" });
10755
+ }
10756
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex shrink-0 items-end gap-1 group cursor-default", "aria-hidden": "true", children: [
10757
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
10758
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
10759
+ const color = CRAB_VARIANT_COLORS[i] ?? "text-amber-500";
10760
+ const entranceClass = entrancePhase === "hidden" ? "opacity-0 scale-0" : entrancePhase === "playing" ? "animate-crab-piano-pop" : "";
10761
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
10762
+ Crab,
10763
+ {
10764
+ className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
10765
+ style: {
10766
+ transitionDelay: `${i * 50}ms`,
10767
+ ...entrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
10768
+ }
10769
+ },
10770
+ i
10771
+ );
10772
+ }) })
10773
+ ] });
10774
+ }
10775
+ function BrandHeader({
10776
+ compact,
10777
+ crabEntrancePhase
10778
+ }) {
10779
+ if (compact) {
10780
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "pb-3", children: [
10781
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
10782
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
10783
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabFamily, { compact: true, entrancePhase: crabEntrancePhase }),
10784
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
10785
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [
10786
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-base font-bold", children: "Inspector" }),
10787
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "hidden shrink-0 font-mono text-[11px] font-semibold text-muted-foreground sm:inline", children: [
10788
+ "v",
10789
+ packageJson.version
10790
+ ] })
10791
+ ] }),
10792
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "hidden text-[11px] font-semibold text-muted-foreground sm:block", children: "Local First. Evidence First." })
10793
+ ] })
10794
+ ] }),
10795
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
10796
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
10797
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {})
10798
+ ] })
10799
+ ] }),
10800
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 hidden sm:block", children: /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {}) })
10801
+ ] });
10802
+ }
10803
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-6", children: [
10804
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
10805
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
10806
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap", children: [
10807
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabFamily, { compact: false, entrancePhase: crabEntrancePhase }),
10808
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
10809
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "Agent Inspector" }),
10810
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
10811
+ "v",
10812
+ packageJson.version
10813
+ ] })
10814
+ ] }),
10815
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
10816
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
10817
+ ] }),
10818
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-[calc(100vw-7rem)] text-sm font-semibold text-muted-foreground", children: "Local First. Evidence First." }),
10819
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
10820
+ ] }),
10821
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "justify-self-end flex items-center gap-1", children: [
10822
+ /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
10823
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {})
10824
+ ] })
10825
+ ] });
10826
+ }
10430
10827
  function SessionContextLogButton({ label, logId }) {
10431
10828
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
10432
10829
  "button",
@@ -10611,6 +11008,15 @@ function SessionContextSummaryPanel({
10611
11008
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "max-h-52 overflow-y-auto pr-1", children: summary.models.map((modelSummary) => /* @__PURE__ */ jsxRuntimeExports.jsx(SessionModelContextRow, { summary: modelSummary }, modelSummary.model)) })
10612
11009
  ] });
10613
11010
  }
11011
+ function formatSessionContextOverview(summary) {
11012
+ const riskCount = summary.models.filter((model) => model.latest.health.level === "risk").length;
11013
+ if (riskCount > 0) return `${riskCount} risk`;
11014
+ const optimizableCount = summary.models.filter(
11015
+ (model) => model.latest.health.level === "optimizable"
11016
+ ).length;
11017
+ if (optimizableCount > 0) return `${optimizableCount} optimizable`;
11018
+ return `${summary.models.length} model${summary.models.length === 1 ? "" : "s"}`;
11019
+ }
10614
11020
  function SessionContextBar({
10615
11021
  label,
10616
11022
  logs,
@@ -10621,11 +11027,16 @@ function SessionContextBar({
10621
11027
  showBackLink
10622
11028
  }) {
10623
11029
  const [copied, setCopied] = reactExports.useState(false);
11030
+ const [detailsOpen, setDetailsOpen] = reactExports.useState(false);
10624
11031
  const timeRange = reactExports.useMemo(
10625
11032
  () => formatTimeRange(logs, timeDisplayFormat),
10626
11033
  [logs, timeDisplayFormat]
10627
11034
  );
10628
11035
  const userAgent = reactExports.useMemo(() => getFirstUserAgent(logs), [logs]);
11036
+ const contextOverview = reactExports.useMemo(
11037
+ () => formatSessionContextOverview(contextSummary),
11038
+ [contextSummary]
11039
+ );
10629
11040
  const handleCopyLink = reactExports.useCallback(() => {
10630
11041
  void copyTextToClipboard(window.location.href).then((success) => {
10631
11042
  if (!success) return;
@@ -10633,8 +11044,8 @@ function SessionContextBar({
10633
11044
  setTimeout(() => setCopied(false), 2e3);
10634
11045
  });
10635
11046
  }, []);
10636
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
10637
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
11047
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-3 rounded-md border border-border bg-muted/15 px-3 py-2 text-xs", children: [
11048
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
10638
11049
  showBackLink && /* @__PURE__ */ jsxRuntimeExports.jsx(
10639
11050
  "a",
10640
11051
  {
@@ -10645,7 +11056,7 @@ function SessionContextBar({
10645
11056
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
10646
11057
  }
10647
11058
  ),
10648
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
11059
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-[180px] flex-1", children: [
10649
11060
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
10650
11061
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: label, children: truncateSessionId(label) }),
10651
11062
  userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -10672,6 +11083,20 @@ function SessionContextBar({
10672
11083
  ] })
10673
11084
  ] })
10674
11085
  ] }),
11086
+ contextSummary.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
11087
+ "button",
11088
+ {
11089
+ type: "button",
11090
+ onClick: () => setDetailsOpen((value) => !value),
11091
+ className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md border border-border bg-background/60 px-2.5 font-mono text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
11092
+ "aria-expanded": detailsOpen,
11093
+ title: detailsOpen ? "Hide context details" : "Show context details",
11094
+ children: [
11095
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: contextOverview }),
11096
+ detailsOpen ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" })
11097
+ ]
11098
+ }
11099
+ ),
10675
11100
  /* @__PURE__ */ jsxRuntimeExports.jsx(
10676
11101
  "button",
10677
11102
  {
@@ -10684,7 +11109,7 @@ function SessionContextBar({
10684
11109
  }
10685
11110
  )
10686
11111
  ] }),
10687
- /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
11112
+ detailsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
10688
11113
  ] });
10689
11114
  }
10690
11115
  function ProxyViewer({
@@ -10703,6 +11128,9 @@ function ProxyViewer({
10703
11128
  strip,
10704
11129
  slowResponseThresholdSeconds,
10705
11130
  timeDisplayFormat,
11131
+ isLoading = false,
11132
+ pagination,
11133
+ sessionMemberships = [],
10706
11134
  hideSessionFilter = false,
10707
11135
  pinnedSessionId
10708
11136
  }) {
@@ -10775,6 +11203,17 @@ function ProxyViewer({
10775
11203
  const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
10776
11204
  const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
10777
11205
  const exportActionVisibility = getExportActionVisibility(captureMode, logs.length);
11206
+ const isPinnedSessionPage = pinnedSessionId !== void 0;
11207
+ const activeScopeLabel = hasSessionContext && sessionContextScope !== null ? truncateSessionId(sessionContextScope.label) : selectedSession === "__all__" ? "All sessions" : truncateSessionId(selectedSession);
11208
+ const slateStats = reactExports.useMemo(
11209
+ () => buildSessionSlateStats({
11210
+ logs,
11211
+ isLoading,
11212
+ timeDisplayFormat,
11213
+ slowResponseThresholdSeconds
11214
+ }),
11215
+ [isLoading, logs, slowResponseThresholdSeconds, timeDisplayFormat]
11216
+ );
10778
11217
  const handleCompareWithPrevious = reactExports.useCallback(
10779
11218
  (log) => {
10780
11219
  const predecessor = comparisonPredecessors.get(log.id);
@@ -10782,218 +11221,207 @@ function ProxyViewer({
10782
11221
  },
10783
11222
  [comparisonPredecessors]
10784
11223
  );
10785
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6", children: [
10786
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sticky top-0 z-30 bg-background pt-6", children: [
10787
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8", children: [
10788
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
10789
- /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
10790
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap", children: [
11224
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-h-screen bg-[#070809] text-foreground", children: [
11225
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-auto grid min-h-screen max-w-[1920px] grid-cols-1 lg:grid-cols-[340px_minmax(0,1fr)] xl:grid-cols-[360px_minmax(0,1fr)]", children: [
11226
+ /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "border-b border-border bg-[#090a0c] lg:sticky lg:top-0 lg:h-screen lg:overflow-y-auto lg:border-b-0 lg:border-r", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 p-4", children: [
11227
+ /* @__PURE__ */ jsxRuntimeExports.jsx(BrandHeader, { compact: true, crabEntrancePhase }),
11228
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-2 border-y border-border/70 py-3", children: [
11229
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
11230
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-lg font-semibold text-foreground", children: logs.length }),
11231
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Requests" })
11232
+ ] }),
11233
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
11234
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-lg font-semibold text-foreground", children: formatTokens(hasSessionContext ? sessionContextTotalIn : totalIn) }),
11235
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Input" })
11236
+ ] }),
11237
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
11238
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-lg font-semibold text-foreground", children: formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut) }),
11239
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] uppercase text-muted-foreground", children: "Output" })
11240
+ ] })
11241
+ ] }),
11242
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
11243
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-semibold uppercase tracking-normal text-muted-foreground", children: "Workspace" }),
11244
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-border bg-[#0c0d0f] px-3 py-2", children: [
11245
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
11246
+ "div",
11247
+ {
11248
+ className: "font-mono text-xs font-semibold text-purple-300",
11249
+ title: activeScopeLabel,
11250
+ children: activeScopeLabel
11251
+ }
11252
+ ),
11253
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 text-[11px] text-muted-foreground", children: isPinnedSessionPage ? "Session workspace" : "Live workspace" })
11254
+ ] })
11255
+ ] }),
11256
+ hasSessionContext && sessionContextScope !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
11257
+ SessionContextBar,
11258
+ {
11259
+ label: sessionContextScope.label,
11260
+ logs: sessionContextLogs,
11261
+ totalIn: sessionContextTotalIn,
11262
+ totalOut: sessionContextTotalOut,
11263
+ timeDisplayFormat,
11264
+ contextSummary: sessionContextSummary,
11265
+ showBackLink: sessionContextScope.showBackLink
11266
+ }
11267
+ ),
11268
+ hasSessionContext && pinnedSessionId !== void 0 && sessionMemberships.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(SessionMembershipPanel, { memberships: sessionMemberships }),
11269
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
11270
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] font-semibold uppercase tracking-normal text-muted-foreground", children: "Filters" }),
11271
+ !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
11272
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "h-9 w-full text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
11273
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
11274
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
11275
+ sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
11276
+ ] })
11277
+ ] }),
11278
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
11279
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "h-9 w-full text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
11280
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
11281
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
11282
+ models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
11283
+ ] })
11284
+ ] }),
11285
+ captureMode === "full" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid h-9 grid-cols-2 overflow-hidden rounded-md border border-border", children: [
11286
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
11287
+ "button",
11288
+ {
11289
+ type: "button",
11290
+ onClick: () => onViewModeChange("simple"),
11291
+ className: cn(
11292
+ "text-xs transition-colors",
11293
+ viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"
11294
+ ),
11295
+ children: "Simple"
11296
+ }
11297
+ ),
11298
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
11299
+ "button",
11300
+ {
11301
+ type: "button",
11302
+ onClick: () => onViewModeChange("full"),
11303
+ className: cn(
11304
+ "text-xs transition-colors",
11305
+ viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"
11306
+ ),
11307
+ children: "Full"
11308
+ }
11309
+ )
11310
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-9 items-center rounded-md border border-border bg-muted px-3 text-xs text-foreground", children: "Simple" })
11311
+ ] })
11312
+ ] }) }),
11313
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "min-w-0", children: [
11314
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sticky top-0 z-30 border-b border-border bg-[#08090b]/95 px-4 py-3 shadow-[0_12px_40px_rgba(0,0,0,0.24)] backdrop-blur lg:px-5", children: [
11315
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
11316
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-[180px] flex-1", children: [
11317
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold text-foreground", children: "Logs" }),
11318
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-0.5 font-mono text-[11px] text-muted-foreground", children: activeScopeLabel })
11319
+ ] }),
11320
+ exportActionVisibility.redacted && /* @__PURE__ */ jsxRuntimeExports.jsx(
11321
+ "button",
11322
+ {
11323
+ type: "button",
11324
+ onClick: () => {
11325
+ void handleExport("redacted");
11326
+ },
11327
+ disabled: exporting !== null,
11328
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
11329
+ title: "Export redacted logs as JSON ZIP",
11330
+ children: exporting === "redacted" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11331
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
11332
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
11333
+ ] })
11334
+ }
11335
+ ),
11336
+ exportActionVisibility.raw && /* @__PURE__ */ jsxRuntimeExports.jsx(
11337
+ "button",
11338
+ {
11339
+ type: "button",
11340
+ onClick: () => {
11341
+ void handleExport("raw");
11342
+ },
11343
+ disabled: exporting !== null,
11344
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
11345
+ title: "Export raw logs without redaction",
11346
+ children: exporting === "raw" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11347
+ /* @__PURE__ */ jsxRuntimeExports.jsx(FileBraces, { className: "size-3.5" }),
11348
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Raw" })
11349
+ ] })
11350
+ }
11351
+ ),
10791
11352
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
10792
- "span",
11353
+ "button",
10793
11354
  {
10794
- className: "flex shrink-0 items-end gap-1 group cursor-default",
10795
- "aria-hidden": "true",
11355
+ type: "button",
11356
+ onClick: onClearAll,
11357
+ className: "inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
11358
+ title: "Clear all logs",
10796
11359
  children: [
10797
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
10798
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
10799
- const color = [
10800
- "text-amber-500",
10801
- "text-rose-500",
10802
- "text-sky-500",
10803
- "text-emerald-500",
10804
- "text-violet-500",
10805
- "text-orange-500",
10806
- "text-cyan-500",
10807
- "text-pink-500",
10808
- "text-lime-500",
10809
- "text-blue-500",
10810
- "text-yellow-500",
10811
- "text-fuchsia-500"
10812
- ][i];
10813
- const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
10814
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
10815
- Crab,
10816
- {
10817
- className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
10818
- style: {
10819
- transitionDelay: `${i * 50}ms`,
10820
- ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
10821
- }
10822
- },
10823
- i
10824
- );
10825
- }) })
11360
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
11361
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear" })
10826
11362
  ]
10827
11363
  }
10828
- ),
10829
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
10830
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "Agent Inspector" }),
10831
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
10832
- "v",
10833
- packageJson.version
10834
- ] })
10835
- ] }),
10836
- /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
10837
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
11364
+ )
10838
11365
  ] }),
10839
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-[calc(100vw-7rem)] text-sm font-semibold text-muted-foreground", children: "Local First. Evidence First." }),
10840
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
10841
- ] }),
10842
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "justify-self-end flex items-center gap-1", children: [
10843
- /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
10844
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {})
10845
- ] })
10846
- ] }),
10847
- hasSessionContext && sessionContextScope !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
10848
- SessionContextBar,
10849
- {
10850
- label: sessionContextScope.label,
10851
- logs: sessionContextLogs,
10852
- totalIn: sessionContextTotalIn,
10853
- totalOut: sessionContextTotalOut,
10854
- timeDisplayFormat,
10855
- contextSummary: sessionContextSummary,
10856
- showBackLink: sessionContextScope.showBackLink
10857
- }
10858
- ),
10859
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
10860
- !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
10861
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
10862
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
10863
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
10864
- sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
10865
- ] })
11366
+ /* @__PURE__ */ jsxRuntimeExports.jsx(LogPaginationBar, { logs, pagination, embedded: true }),
11367
+ exportError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: exportError })
10866
11368
  ] }),
10867
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
10868
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[250px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
10869
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
10870
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
10871
- models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
10872
- ] })
10873
- ] }),
10874
- captureMode === "full" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
10875
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10876
- "button",
10877
- {
10878
- type: "button",
10879
- onClick: () => onViewModeChange("simple"),
10880
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
10881
- children: "Simple"
10882
- }
10883
- ),
10884
- /* @__PURE__ */ jsxRuntimeExports.jsx(
10885
- "button",
11369
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-4 lg:px-5", children: [
11370
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SessionOpeningSlate, { stats: slateStats }),
11371
+ logs.length === 0 ? selectedSession !== "__all__" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-auto max-w-xl space-y-4 rounded-md border border-border bg-muted/10 px-4 py-8", children: [
11372
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: isLoading ? "Loading session data..." : "Session not found" }),
11373
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "inline-block max-w-full rounded bg-muted px-3 py-1 font-mono text-xs break-all", children: truncateSessionId(selectedSession) }),
11374
+ sessionMemberships.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-left", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SessionMembershipPanel, { memberships: sessionMemberships }) }),
11375
+ !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs", children: [
11376
+ "This session may have been cleared or never existed.",
11377
+ " ",
11378
+ hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "underline transition-colors hover:text-foreground", children: "Back to all sessions" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
11379
+ "button",
11380
+ {
11381
+ type: "button",
11382
+ onClick: () => onSessionChange("__all__"),
11383
+ className: "underline transition-colors hover:text-foreground",
11384
+ children: "Show all sessions"
11385
+ }
11386
+ )
11387
+ ] })
11388
+ ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-auto max-w-2xl space-y-4 rounded-md border border-border bg-muted/10 px-4 py-8", children: [
11389
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
11390
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
11391
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" }),
11392
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "OPENAI_BASE_URL=http://localhost:25947/proxy <your-tool>" })
11393
+ ] }),
11394
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground", children: "Container note: if your AI tool runs in a different container or host than Agent Inspector, replace localhost with an address reachable from that tool and verify the network/firewall path." })
11395
+ ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
11396
+ "div",
10886
11397
  {
10887
- type: "button",
10888
- onClick: () => onViewModeChange("full"),
10889
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
10890
- children: "Full"
11398
+ ref: logListWrapperRef,
11399
+ tabIndex: 0,
11400
+ className: "flex flex-col gap-2 focus:outline-none",
11401
+ children: [
11402
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logListRef, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
11403
+ ConversationGroupList,
11404
+ {
11405
+ groups,
11406
+ viewMode,
11407
+ strip,
11408
+ slowResponseThresholdSeconds,
11409
+ cacheTrends,
11410
+ onCompareWithPrevious: handleCompareWithPrevious,
11411
+ comparisonPredecessors,
11412
+ onClearGroup,
11413
+ standalone: groups.length === 1,
11414
+ hasPinnedSessionContext: hasSessionContext,
11415
+ timeDisplayFormat
11416
+ }
11417
+ ) }),
11418
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(LogPaginationBar, { logs, pagination }) })
11419
+ ]
10891
11420
  }
10892
11421
  )
10893
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex h-8 items-center rounded-md border border-border bg-muted px-3 text-xs text-foreground", children: "Simple" }),
10894
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
10895
- !hasSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
10896
- logs.length,
10897
- " request",
10898
- logs.length !== 1 ? "s" : "",
10899
- totalIn > 0 || totalOut > 0 ? ` - ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out` : ""
10900
- ] }),
10901
- exportActionVisibility.redacted && /* @__PURE__ */ jsxRuntimeExports.jsx(
10902
- "button",
10903
- {
10904
- type: "button",
10905
- onClick: () => {
10906
- void handleExport("redacted");
10907
- },
10908
- disabled: exporting !== null,
10909
- className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted",
10910
- title: "Export redacted logs as JSON ZIP",
10911
- children: exporting === "redacted" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
10912
- /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
10913
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
10914
- ] })
10915
- }
10916
- ),
10917
- exportActionVisibility.raw && /* @__PURE__ */ jsxRuntimeExports.jsx(
10918
- "button",
10919
- {
10920
- type: "button",
10921
- onClick: () => {
10922
- void handleExport("raw");
10923
- },
10924
- disabled: exporting !== null,
10925
- className: "h-8 px-2.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed inline-flex items-center gap-1.5 rounded-md hover:bg-muted",
10926
- title: "Export raw logs without redaction",
10927
- children: exporting === "raw" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
10928
- /* @__PURE__ */ jsxRuntimeExports.jsx(FileBraces, { className: "size-3.5" }),
10929
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Raw" })
10930
- ] })
10931
- }
10932
- ),
10933
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
10934
- "button",
10935
- {
10936
- type: "button",
10937
- onClick: onClearAll,
10938
- className: "h-8 px-3 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer inline-flex items-center gap-1.5 rounded-md hover:bg-muted",
10939
- title: "Clear all logs",
10940
- children: [
10941
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
10942
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear" })
10943
- ]
10944
- }
10945
- )
10946
- ] }),
10947
- exportError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mb-4 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: exportError })
10948
- ] }),
10949
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: logs.length === 0 ? selectedSession !== "__all__" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
10950
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: "Session not found" }),
10951
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs font-mono bg-muted px-3 py-1 rounded inline-block max-w-[500px] break-all", children: truncateSessionId(selectedSession) }),
10952
- /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs", children: [
10953
- "This session may have been cleared or never existed.",
10954
- " ",
10955
- hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "underline hover:text-foreground transition-colors", children: "Back to all sessions" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
10956
- "button",
10957
- {
10958
- type: "button",
10959
- onClick: () => onSessionChange("__all__"),
10960
- className: "underline hover:text-foreground transition-colors",
10961
- children: "Show all sessions"
10962
- }
10963
- )
11422
+ ] })
10964
11423
  ] })
10965
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
10966
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
10967
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs", children: "Route AI coding tools through the proxy:" }),
10968
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
10969
- /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" }),
10970
- /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "OPENAI_BASE_URL=http://localhost:25947/proxy <your-tool>" })
10971
- ] }),
10972
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground", children: "Container note: if your AI tool runs in a different container or host than Agent Inspector, replace localhost with an address reachable from that tool and verify the network/firewall path." })
10973
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
10974
- "div",
10975
- {
10976
- ref: logListWrapperRef,
10977
- tabIndex: 0,
10978
- className: "flex flex-col gap-2 focus:outline-none",
10979
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logListRef, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
10980
- ConversationGroupList,
10981
- {
10982
- groups,
10983
- viewMode,
10984
- strip,
10985
- slowResponseThresholdSeconds,
10986
- cacheTrends,
10987
- onCompareWithPrevious: handleCompareWithPrevious,
10988
- comparisonPredecessors,
10989
- onClearGroup,
10990
- standalone: groups.length === 1,
10991
- hasPinnedSessionContext: hasSessionContext,
10992
- timeDisplayFormat
10993
- }
10994
- ) })
10995
- }
10996
- ) }),
11424
+ ] }),
10997
11425
  comparePair !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyCompareDrawer, { left: comparePair[0], right: comparePair[1], onClose: closeCompare }) })
10998
11426
  ] });
10999
11427
  }
@@ -11007,6 +11435,34 @@ const SSEUpdateSchema = union([
11007
11435
  log: CapturedLogSchema
11008
11436
  })
11009
11437
  ]);
11438
+ const LogCursorPageSchema = object({
11439
+ logs: array(CapturedLogSchema),
11440
+ total: number().int().nonnegative(),
11441
+ limit: number().int().positive(),
11442
+ hasOlder: boolean(),
11443
+ hasNewer: boolean(),
11444
+ oldestLogId: number().int().positive().nullable(),
11445
+ newestLogId: number().int().positive().nullable()
11446
+ });
11447
+ function buildSessionMembershipEvidence(groups, sessionId) {
11448
+ const memberships = [];
11449
+ for (const group of groups) {
11450
+ for (const member of group.members) {
11451
+ if (member.sessionId !== sessionId) continue;
11452
+ memberships.push({
11453
+ groupId: group.id,
11454
+ groupTitle: group.title,
11455
+ groupKind: group.kind,
11456
+ groupStatus: group.status,
11457
+ groupTask: group.task,
11458
+ groupProject: group.project,
11459
+ groupEvidence: group.evidence,
11460
+ member
11461
+ });
11462
+ }
11463
+ }
11464
+ return memberships;
11465
+ }
11010
11466
  function extractSessions(logs) {
11011
11467
  const set = /* @__PURE__ */ new Set();
11012
11468
  for (const l of logs) {
@@ -11033,10 +11489,35 @@ const DEBOUNCE_MS = 50;
11033
11489
  const HASH_SCROLL_ATTEMPTS = 12;
11034
11490
  const HASH_HIGHLIGHT_MS = 1800;
11035
11491
  const MAX_CLIENT_LOGS = 500;
11492
+ const SESSION_PAGE_LIMIT = 100;
11036
11493
  function buildLogsStreamUrl(sessionId) {
11037
- if (sessionId === void 0) return "/api/logs/stream";
11038
- const params = new URLSearchParams({ sessionId });
11039
- return `/api/logs/stream?${params.toString()}`;
11494
+ const params = new URLSearchParams();
11495
+ if (sessionId !== void 0) {
11496
+ params.set("sessionId", sessionId);
11497
+ params.set("compact", "1");
11498
+ }
11499
+ const query = params.toString();
11500
+ return query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream";
11501
+ }
11502
+ function buildSessionLogsPageUrl(sessionId, request) {
11503
+ const params = new URLSearchParams({
11504
+ cursor: "1",
11505
+ compact: "1",
11506
+ limit: String(SESSION_PAGE_LIMIT),
11507
+ sessionId
11508
+ });
11509
+ switch (request.kind) {
11510
+ case "oldest":
11511
+ params.set("anchor", "oldest");
11512
+ break;
11513
+ case "older":
11514
+ params.set("beforeLogId", String(request.beforeLogId));
11515
+ break;
11516
+ case "newer":
11517
+ params.set("afterLogId", String(request.afterLogId));
11518
+ break;
11519
+ }
11520
+ return `/api/logs?${params.toString()}`;
11040
11521
  }
11041
11522
  function buildLogIndex(logs) {
11042
11523
  const idx = /* @__PURE__ */ new Map();
@@ -11058,9 +11539,14 @@ function ProxyViewerContainer({
11058
11539
  const [selectedModel, setSelectedModel] = reactExports.useState("__all__");
11059
11540
  const [viewMode, setViewMode] = reactExports.useState("simple");
11060
11541
  const [error, setError] = reactExports.useState(null);
11542
+ const [streamInitialized, setStreamInitialized] = reactExports.useState(initialSessionId === void 0);
11543
+ const [logPage, setLogPage] = reactExports.useState(null);
11544
+ const [sessionPageLoading, setSessionPageLoading] = reactExports.useState(initialSessionId !== void 0);
11545
+ const [sessionMemberships, setSessionMemberships] = reactExports.useState([]);
11061
11546
  const eventSourceRef = reactExports.useRef(null);
11062
11547
  const reconnectTimeoutRef = reactExports.useRef(null);
11063
11548
  const handledHashRef = reactExports.useRef(null);
11549
+ const sessionPageRequestIdRef = reactExports.useRef(0);
11064
11550
  const logIndexRef = reactExports.useRef(/* @__PURE__ */ new Map());
11065
11551
  const pendingUpdatesRef = reactExports.useRef([]);
11066
11552
  const flushTimerRef = reactExports.useRef(null);
@@ -11104,6 +11590,7 @@ function ProxyViewerContainer({
11104
11590
  if (eventSourceRef.current) {
11105
11591
  eventSourceRef.current.close();
11106
11592
  }
11593
+ setStreamInitialized(false);
11107
11594
  const es = new EventSource(buildLogsStreamUrl(initialSessionId));
11108
11595
  eventSourceRef.current = es;
11109
11596
  es.onmessage = (event) => {
@@ -11125,6 +11612,7 @@ function ProxyViewerContainer({
11125
11612
  const nextLogs = trimClientLogs(update.logs);
11126
11613
  logIndexRef.current = buildLogIndex(nextLogs);
11127
11614
  setAllLogs(nextLogs);
11615
+ setStreamInitialized(true);
11128
11616
  setError(null);
11129
11617
  } else if (update.type === "update") {
11130
11618
  scheduleUpdate(update.log);
@@ -11135,6 +11623,7 @@ function ProxyViewerContainer({
11135
11623
  };
11136
11624
  es.onerror = () => {
11137
11625
  setError("SSE connection lost, reconnecting...");
11626
+ setStreamInitialized(true);
11138
11627
  es.close();
11139
11628
  if (reconnectTimeoutRef.current !== null) {
11140
11629
  clearTimeout(reconnectTimeoutRef.current);
@@ -11142,7 +11631,59 @@ function ProxyViewerContainer({
11142
11631
  reconnectTimeoutRef.current = setTimeout(connectSSE, 3e3);
11143
11632
  };
11144
11633
  }, [initialSessionId, scheduleUpdate]);
11634
+ const loadSessionPage = reactExports.useCallback(
11635
+ (request) => {
11636
+ if (initialSessionId === void 0) return;
11637
+ const requestId = sessionPageRequestIdRef.current + 1;
11638
+ sessionPageRequestIdRef.current = requestId;
11639
+ setSessionPageLoading(true);
11640
+ setError(null);
11641
+ void fetchJson(buildSessionLogsPageUrl(initialSessionId, request), LogCursorPageSchema).then((page) => {
11642
+ if (sessionPageRequestIdRef.current !== requestId) return;
11643
+ const nextLogs = page.logs;
11644
+ logIndexRef.current = buildLogIndex(nextLogs);
11645
+ setAllLogs(nextLogs);
11646
+ setLogPage(page);
11647
+ setSelectedSession(initialSessionId);
11648
+ setSessionPageLoading(false);
11649
+ setError(null);
11650
+ }).catch((err) => {
11651
+ if (sessionPageRequestIdRef.current !== requestId) return;
11652
+ setAllLogs([]);
11653
+ setLogPage(null);
11654
+ setSessionPageLoading(false);
11655
+ setError(err instanceof Error ? err.message : "Failed to load session logs");
11656
+ });
11657
+ },
11658
+ [initialSessionId]
11659
+ );
11145
11660
  reactExports.useEffect(() => {
11661
+ if (initialSessionId === void 0) {
11662
+ setSessionMemberships([]);
11663
+ return;
11664
+ }
11665
+ let cancelled = false;
11666
+ void fetch("/api/groups").then(async (response) => {
11667
+ if (!response.ok) return null;
11668
+ const raw = await response.json();
11669
+ const parsed = InspectorGroupsListResponseSchema.safeParse(raw);
11670
+ return parsed.success ? parsed.data.groups : null;
11671
+ }).then((groups) => {
11672
+ if (cancelled || groups === null) return;
11673
+ setSessionMemberships(buildSessionMembershipEvidence(groups, initialSessionId));
11674
+ }).catch(() => {
11675
+ if (!cancelled) setSessionMemberships([]);
11676
+ });
11677
+ return () => {
11678
+ cancelled = true;
11679
+ };
11680
+ }, [initialSessionId]);
11681
+ reactExports.useEffect(() => {
11682
+ if (initialSessionId === void 0) return;
11683
+ loadSessionPage({ kind: "newest" });
11684
+ }, [initialSessionId, loadSessionPage]);
11685
+ reactExports.useEffect(() => {
11686
+ if (initialSessionId !== void 0) return void 0;
11146
11687
  connectSSE();
11147
11688
  return () => {
11148
11689
  if (eventSourceRef.current) {
@@ -11158,7 +11699,7 @@ function ProxyViewerContainer({
11158
11699
  flushTimerRef.current = null;
11159
11700
  }
11160
11701
  };
11161
- }, [connectSSE]);
11702
+ }, [connectSSE, initialSessionId]);
11162
11703
  reactExports.useEffect(() => {
11163
11704
  const hash = window.location.hash;
11164
11705
  if (!hash.startsWith("#log-")) return;
@@ -11257,6 +11798,48 @@ function ProxyViewerContainer({
11257
11798
  })();
11258
11799
  }, []);
11259
11800
  const { strip, captureMode, slowResponseThresholdSeconds, timeDisplayFormat } = useStripConfig();
11801
+ const loadOldestPage = reactExports.useCallback(() => {
11802
+ loadSessionPage({ kind: "oldest" });
11803
+ }, [loadSessionPage]);
11804
+ const loadNewestPage = reactExports.useCallback(() => {
11805
+ loadSessionPage({ kind: "newest" });
11806
+ }, [loadSessionPage]);
11807
+ const loadOlderPage = reactExports.useCallback(() => {
11808
+ const firstLog = allLogs[0];
11809
+ const beforeLogId = logPage?.oldestLogId ?? firstLog?.id ?? null;
11810
+ if (beforeLogId === null) return;
11811
+ loadSessionPage({ kind: "older", beforeLogId });
11812
+ }, [allLogs, loadSessionPage, logPage]);
11813
+ const loadNewerPage = reactExports.useCallback(() => {
11814
+ const lastLog = allLogs[allLogs.length - 1];
11815
+ const afterLogId = logPage?.newestLogId ?? lastLog?.id ?? null;
11816
+ if (afterLogId === null) return;
11817
+ loadSessionPage({ kind: "newer", afterLogId });
11818
+ }, [allLogs, loadSessionPage, logPage]);
11819
+ const pagination = reactExports.useMemo(() => {
11820
+ if (initialSessionId === void 0) return void 0;
11821
+ return {
11822
+ isLoading: sessionPageLoading,
11823
+ total: logPage?.total ?? null,
11824
+ pageSize: logPage?.limit ?? SESSION_PAGE_LIMIT,
11825
+ hasOlder: logPage?.hasOlder ?? false,
11826
+ hasNewer: logPage?.hasNewer ?? false,
11827
+ oldestLogId: logPage?.oldestLogId ?? null,
11828
+ newestLogId: logPage?.newestLogId ?? null,
11829
+ onOldest: loadOldestPage,
11830
+ onOlder: loadOlderPage,
11831
+ onNewer: loadNewerPage,
11832
+ onNewest: loadNewestPage
11833
+ };
11834
+ }, [
11835
+ initialSessionId,
11836
+ loadNewestPage,
11837
+ loadNewerPage,
11838
+ loadOlderPage,
11839
+ loadOldestPage,
11840
+ logPage,
11841
+ sessionPageLoading
11842
+ ]);
11260
11843
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11261
11844
  error !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fixed top-4 right-4 bg-destructive text-destructive-foreground px-4 py-2 rounded-md text-sm z-50", children: error }),
11262
11845
  /* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingBanner, {}),
@@ -11272,6 +11855,9 @@ function ProxyViewerContainer({
11272
11855
  onModelChange: setSelectedModel,
11273
11856
  onClearAll: handleClearAll,
11274
11857
  onClearGroup: handleClearGroup,
11858
+ isLoading: initialSessionId === void 0 ? !streamInitialized : sessionPageLoading,
11859
+ pagination,
11860
+ sessionMemberships,
11275
11861
  viewMode,
11276
11862
  onViewModeChange: setViewMode,
11277
11863
  captureMode,