@tonyclaw/agent-inspector 2.0.43 → 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 (39) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-CxM1gCfL.js → CompareDrawer-VDpcSNGM.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-fsXSjqtm.js +117 -0
  4. package/.output/public/assets/{ReplayDialog-UseUkucS.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-JSQEPeNS.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-BpGVJcpB.js → main-DmzLt6ti.js} +2 -2
  12. package/.output/server/_libs/lucide-react.mjs +219 -178
  13. package/.output/server/{_sessionId-DGn-TENM.mjs → _sessionId-CCN1UoEf.mjs} +3 -3
  14. package/.output/server/_ssr/{CompareDrawer-CFElCSYY.mjs → CompareDrawer-DkSY3vxP.mjs} +4 -4
  15. package/.output/server/_ssr/{ProxyViewerContainer-B7SR9mrD.mjs → ProxyViewerContainer-OTh1V_Kh.mjs} +798 -394
  16. package/.output/server/_ssr/{ReplayDialog-DfKapj0k.mjs → ReplayDialog-DhI6yxrR.mjs} +5 -5
  17. package/.output/server/_ssr/{RequestAnatomy-CUxTCg31.mjs → RequestAnatomy-DN1SJVVL.mjs} +4 -4
  18. package/.output/server/_ssr/{ResponseView-BXY0w197.mjs → ResponseView-DW-kpJL5.mjs} +4 -4
  19. package/.output/server/_ssr/{StreamingChunkSequence-CzMnES9H.mjs → StreamingChunkSequence-BAE57Fae.mjs} +4 -4
  20. package/.output/server/_ssr/{index-CVlT-REW.mjs → index-C9ryJEna.mjs} +3 -3
  21. package/.output/server/_ssr/index.mjs +2 -2
  22. package/.output/server/_ssr/{router-B2d1LUx6.mjs → router-DIIJGyML.mjs} +458 -10
  23. package/.output/server/{_tanstack-start-manifest_v-Drpi28BM.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 +869 -363
  28. package/src/components/ProxyViewerContainer.tsx +135 -3
  29. package/src/components/proxy-viewer/LogEntryHeader.tsx +12 -6
  30. package/src/components/ui/crab-logo.tsx +0 -50
  31. package/src/proxy/logIndex.ts +188 -1
  32. package/src/proxy/store.ts +405 -3
  33. package/src/routes/api/logs.ts +36 -0
  34. package/.output/public/assets/ProxyViewerContainer-TtRG-0E7.js +0 -117
  35. package/.output/public/assets/RequestAnatomy-aPxgEJ2L.js +0 -1
  36. package/.output/public/assets/ResponseView-DA-F4F97.js +0 -1
  37. package/.output/public/assets/_sessionId-BEXuCWq5.js +0 -1
  38. package/.output/public/assets/index-Bhsa_2xX.js +0 -1
  39. package/.output/public/assets/index-DOWlRJ0W.css +0 -1
@@ -1,20 +1,20 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.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, 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 } from "./router-B2d1LUx6.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
@@ -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-CFElCSYY.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-DfKapj0k.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-CUxTCg31.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-BXY0w197.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-CzMnES9H.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) : "—"
@@ -5345,74 +5352,7 @@ function CrabLogo({ className }) {
5345
5352
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
5346
5353
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
5347
5354
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
5348
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" }),
5349
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5350
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5351
- "path",
5352
- {
5353
- 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",
5354
- fill: "#2f6b3f",
5355
- stroke: "none",
5356
- opacity: "0.95"
5357
- }
5358
- ),
5359
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5360
- "path",
5361
- {
5362
- 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",
5363
- fill: "#6fb36f",
5364
- stroke: "none",
5365
- opacity: "0.95"
5366
- }
5367
- ),
5368
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M8.4 16 L8.4 18.4", stroke: "#d6b45f", strokeWidth: "0.34" }),
5369
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "10.6", cy: "17.75", r: "0.36", fill: "#c2412d", stroke: "none" })
5370
- ] }),
5371
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5372
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5373
- "path",
5374
- {
5375
- d: "M8 16.5 L11.9 13.6 L15.8 16.8 C13.3 18.4 10.4 18.3 8 16.5 Z",
5376
- fill: "#2f6b3f",
5377
- stroke: "none",
5378
- opacity: "0.97"
5379
- }
5380
- ),
5381
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5382
- "path",
5383
- {
5384
- 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",
5385
- fill: "#6fb36f",
5386
- stroke: "none",
5387
- opacity: "0.95"
5388
- }
5389
- ),
5390
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M11.9 14.3 L11.9 17.8", stroke: "#d6b45f", strokeWidth: "0.42" }),
5391
- /* @__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" }),
5392
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "15.6", cy: "16.75", r: "0.43", fill: "#c2412d", stroke: "none" })
5393
- ] }),
5394
- /* @__PURE__ */ jsxRuntimeExports.jsxs("g", { children: [
5395
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5396
- "path",
5397
- {
5398
- 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",
5399
- fill: "#2f6b3f",
5400
- stroke: "none",
5401
- opacity: "0.95"
5402
- }
5403
- ),
5404
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5405
- "path",
5406
- {
5407
- 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",
5408
- fill: "#6fb36f",
5409
- stroke: "none",
5410
- opacity: "0.95"
5411
- }
5412
- ),
5413
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M16.2 16.3 L16.2 18.6", stroke: "#d6b45f", strokeWidth: "0.34" }),
5414
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "18.35", cy: "18.15", r: "0.36", fill: "#c2412d", stroke: "none" })
5415
- ] })
5355
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
5416
5356
  ]
5417
5357
  }
5418
5358
  );
@@ -10260,6 +10200,114 @@ function getFirstUserAgent(logs) {
10260
10200
  }
10261
10201
  return null;
10262
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
+ }
10263
10311
  function resolveSessionContextScope({
10264
10312
  pinnedSessionId,
10265
10313
  selectedSession,
@@ -10351,6 +10399,25 @@ function formatMetadataValue(value) {
10351
10399
  if (value === null) return null;
10352
10400
  return JSON.stringify(value);
10353
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
+ }
10354
10421
  function getMemberMetadataRows(member) {
10355
10422
  const rows = [];
10356
10423
  for (const key of MEMBER_METADATA_KEYS) {
@@ -10362,19 +10429,60 @@ function getMemberMetadataRows(member) {
10362
10429
  return rows;
10363
10430
  }
10364
10431
  function SessionMembershipPanel({
10365
- memberships
10432
+ memberships,
10433
+ defaultOpen = false
10366
10434
  }) {
10435
+ const [open, setOpen] = reactExports.useState(defaultOpen);
10367
10436
  if (memberships.length === 0) return null;
10368
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 rounded-md border border-border bg-muted/10 px-3 py-2 text-xs", children: [
10369
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
10370
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-foreground", children: "Group Member Evidence" }),
10371
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "font-mono text-[10px] text-muted-foreground", children: [
10372
- memberships.length,
10373
- " group",
10374
- memberships.length === 1 ? "" : "s"
10375
- ] })
10376
- ] }),
10377
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2", children: memberships.map((membership) => {
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) => {
10378
10486
  const member = membership.member;
10379
10487
  const metadataRows = getMemberMetadataRows(member);
10380
10488
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
@@ -10425,6 +10533,105 @@ function SessionMembershipPanel({
10425
10533
  }) })
10426
10534
  ] });
10427
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
+ }
10428
10635
  function formatRequestCount(count) {
10429
10636
  return `${count} ${count === 1 ? "request" : "requests"}`;
10430
10637
  }
@@ -10510,36 +10717,7 @@ function CopyableCommand({ command }) {
10510
10717
  onClick: handleCopy,
10511
10718
  className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 cursor-pointer",
10512
10719
  "aria-label": "Copy command",
10513
- children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(
10514
- "svg",
10515
- {
10516
- width: "16",
10517
- height: "16",
10518
- viewBox: "0 0 24 24",
10519
- fill: "none",
10520
- stroke: "currentColor",
10521
- strokeWidth: "2",
10522
- strokeLinecap: "round",
10523
- strokeLinejoin: "round",
10524
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M20 6 9 17l-5-5" })
10525
- }
10526
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
10527
- "svg",
10528
- {
10529
- width: "16",
10530
- height: "16",
10531
- viewBox: "0 0 24 24",
10532
- fill: "none",
10533
- stroke: "currentColor",
10534
- strokeWidth: "2",
10535
- strokeLinecap: "round",
10536
- strokeLinejoin: "round",
10537
- children: [
10538
- /* @__PURE__ */ jsxRuntimeExports.jsx("rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2" }),
10539
- /* @__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" })
10540
- ]
10541
- }
10542
- )
10720
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-4" })
10543
10721
  }
10544
10722
  )
10545
10723
  ] });
@@ -10554,6 +10732,98 @@ function McpReadyBadge() {
10554
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." })
10555
10733
  ] }) });
10556
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
+ }
10557
10827
  function SessionContextLogButton({ label, logId }) {
10558
10828
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
10559
10829
  "button",
@@ -10738,6 +11008,15 @@ function SessionContextSummaryPanel({
10738
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)) })
10739
11009
  ] });
10740
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
+ }
10741
11020
  function SessionContextBar({
10742
11021
  label,
10743
11022
  logs,
@@ -10748,11 +11027,16 @@ function SessionContextBar({
10748
11027
  showBackLink
10749
11028
  }) {
10750
11029
  const [copied, setCopied] = reactExports.useState(false);
11030
+ const [detailsOpen, setDetailsOpen] = reactExports.useState(false);
10751
11031
  const timeRange = reactExports.useMemo(
10752
11032
  () => formatTimeRange(logs, timeDisplayFormat),
10753
11033
  [logs, timeDisplayFormat]
10754
11034
  );
10755
11035
  const userAgent = reactExports.useMemo(() => getFirstUserAgent(logs), [logs]);
11036
+ const contextOverview = reactExports.useMemo(
11037
+ () => formatSessionContextOverview(contextSummary),
11038
+ [contextSummary]
11039
+ );
10756
11040
  const handleCopyLink = reactExports.useCallback(() => {
10757
11041
  void copyTextToClipboard(window.location.href).then((success) => {
10758
11042
  if (!success) return;
@@ -10760,8 +11044,8 @@ function SessionContextBar({
10760
11044
  setTimeout(() => setCopied(false), 2e3);
10761
11045
  });
10762
11046
  }, []);
10763
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
10764
- /* @__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: [
10765
11049
  showBackLink && /* @__PURE__ */ jsxRuntimeExports.jsx(
10766
11050
  "a",
10767
11051
  {
@@ -10772,7 +11056,7 @@ function SessionContextBar({
10772
11056
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
10773
11057
  }
10774
11058
  ),
10775
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
11059
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-[180px] flex-1", children: [
10776
11060
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
10777
11061
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: label, children: truncateSessionId(label) }),
10778
11062
  userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -10799,6 +11083,20 @@ function SessionContextBar({
10799
11083
  ] })
10800
11084
  ] })
10801
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
+ ),
10802
11100
  /* @__PURE__ */ jsxRuntimeExports.jsx(
10803
11101
  "button",
10804
11102
  {
@@ -10811,7 +11109,7 @@ function SessionContextBar({
10811
11109
  }
10812
11110
  )
10813
11111
  ] }),
10814
- /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
11112
+ detailsOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(SessionContextSummaryPanel, { summary: contextSummary })
10815
11113
  ] });
10816
11114
  }
10817
11115
  function ProxyViewer({
@@ -10831,6 +11129,7 @@ function ProxyViewer({
10831
11129
  slowResponseThresholdSeconds,
10832
11130
  timeDisplayFormat,
10833
11131
  isLoading = false,
11132
+ pagination,
10834
11133
  sessionMemberships = [],
10835
11134
  hideSessionFilter = false,
10836
11135
  pinnedSessionId
@@ -10904,6 +11203,17 @@ function ProxyViewer({
10904
11203
  const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
10905
11204
  const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
10906
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
+ );
10907
11217
  const handleCompareWithPrevious = reactExports.useCallback(
10908
11218
  (log) => {
10909
11219
  const predecessor = comparisonPredecessors.get(log.id);
@@ -10911,220 +11221,207 @@ function ProxyViewer({
10911
11221
  },
10912
11222
  [comparisonPredecessors]
10913
11223
  );
10914
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6", children: [
10915
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "sticky top-0 z-30 bg-background pt-6", children: [
10916
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pb-8", children: [
10917
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
10918
- /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
10919
- /* @__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
+ ),
10920
11352
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
10921
- "span",
11353
+ "button",
10922
11354
  {
10923
- className: "flex shrink-0 items-end gap-1 group cursor-default",
10924
- "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",
10925
11359
  children: [
10926
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
10927
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
10928
- const color = [
10929
- "text-amber-500",
10930
- "text-rose-500",
10931
- "text-sky-500",
10932
- "text-emerald-500",
10933
- "text-violet-500",
10934
- "text-orange-500",
10935
- "text-cyan-500",
10936
- "text-pink-500",
10937
- "text-lime-500",
10938
- "text-blue-500",
10939
- "text-yellow-500",
10940
- "text-fuchsia-500"
10941
- ][i];
10942
- const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
10943
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
10944
- Crab,
10945
- {
10946
- className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
10947
- style: {
10948
- transitionDelay: `${i * 50}ms`,
10949
- ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
10950
- }
10951
- },
10952
- i
10953
- );
10954
- }) })
11360
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
11361
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear" })
10955
11362
  ]
10956
11363
  }
10957
- ),
10958
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
10959
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "Agent Inspector" }),
10960
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
10961
- "v",
10962
- packageJson.version
10963
- ] })
10964
- ] }),
10965
- /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
10966
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
11364
+ )
10967
11365
  ] }),
10968
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-[calc(100vw-7rem)] text-sm font-semibold text-muted-foreground", children: "Local First. Evidence First." }),
10969
- /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
10970
- ] }),
10971
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "justify-self-end flex items-center gap-1", children: [
10972
- /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
10973
- /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {})
10974
- ] })
10975
- ] }),
10976
- hasSessionContext && sessionContextScope !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
10977
- SessionContextBar,
10978
- {
10979
- label: sessionContextScope.label,
10980
- logs: sessionContextLogs,
10981
- totalIn: sessionContextTotalIn,
10982
- totalOut: sessionContextTotalOut,
10983
- timeDisplayFormat,
10984
- contextSummary: sessionContextSummary,
10985
- showBackLink: sessionContextScope.showBackLink
10986
- }
10987
- ),
10988
- hasSessionContext && pinnedSessionId !== void 0 && sessionMemberships.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(SessionMembershipPanel, { memberships: sessionMemberships }),
10989
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
10990
- !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
10991
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
10992
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
10993
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
10994
- sessions.map((s) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: s, children: truncateSessionId(s) }, s))
10995
- ] })
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 })
10996
11368
  ] }),
10997
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedModel, onValueChange: onModelChange, children: [
10998
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[250px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All models" }) }),
10999
- /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
11000
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All models" }),
11001
- models.map((m) => /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: m, children: m }, m))
11002
- ] })
11003
- ] }),
11004
- captureMode === "full" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
11005
- /* @__PURE__ */ jsxRuntimeExports.jsx(
11006
- "button",
11007
- {
11008
- type: "button",
11009
- onClick: () => onViewModeChange("simple"),
11010
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "simple" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
11011
- children: "Simple"
11012
- }
11013
- ),
11014
- /* @__PURE__ */ jsxRuntimeExports.jsx(
11015
- "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",
11016
11397
  {
11017
- type: "button",
11018
- onClick: () => onViewModeChange("full"),
11019
- className: `h-8 px-3 cursor-pointer transition-colors text-xs ${viewMode === "full" ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
11020
- 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
+ ]
11021
11420
  }
11022
11421
  )
11023
- ] }) : /* @__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" }),
11024
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1" }),
11025
- !hasSessionContext && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground text-xs font-mono", children: [
11026
- logs.length,
11027
- " request",
11028
- logs.length !== 1 ? "s" : "",
11029
- totalIn > 0 || totalOut > 0 ? ` - ${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out` : ""
11030
- ] }),
11031
- exportActionVisibility.redacted && /* @__PURE__ */ jsxRuntimeExports.jsx(
11032
- "button",
11033
- {
11034
- type: "button",
11035
- onClick: () => {
11036
- void handleExport("redacted");
11037
- },
11038
- disabled: exporting !== null,
11039
- 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",
11040
- title: "Export redacted logs as JSON ZIP",
11041
- children: exporting === "redacted" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11042
- /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3.5" }),
11043
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Export" })
11044
- ] })
11045
- }
11046
- ),
11047
- exportActionVisibility.raw && /* @__PURE__ */ jsxRuntimeExports.jsx(
11048
- "button",
11049
- {
11050
- type: "button",
11051
- onClick: () => {
11052
- void handleExport("raw");
11053
- },
11054
- disabled: exporting !== null,
11055
- 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",
11056
- title: "Export raw logs without redaction",
11057
- children: exporting === "raw" ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Exporting..." }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11058
- /* @__PURE__ */ jsxRuntimeExports.jsx(FileBraces, { className: "size-3.5" }),
11059
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Raw" })
11060
- ] })
11061
- }
11062
- ),
11063
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
11064
- "button",
11065
- {
11066
- type: "button",
11067
- onClick: onClearAll,
11068
- 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",
11069
- title: "Clear all logs",
11070
- children: [
11071
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
11072
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear" })
11073
- ]
11074
- }
11075
- )
11076
- ] }),
11077
- 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 })
11078
- ] }),
11079
- /* @__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: [
11080
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: isLoading ? "Loading session data..." : "Session not found" }),
11081
- /* @__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) }),
11082
- sessionMemberships.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mx-auto max-w-3xl text-left", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SessionMembershipPanel, { memberships: sessionMemberships }) }),
11083
- !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs", children: [
11084
- "This session may have been cleared or never existed.",
11085
- " ",
11086
- hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "underline hover:text-foreground transition-colors", children: "Back to all sessions" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
11087
- "button",
11088
- {
11089
- type: "button",
11090
- onClick: () => onSessionChange("__all__"),
11091
- className: "underline hover:text-foreground transition-colors",
11092
- children: "Show all sessions"
11093
- }
11094
- )
11422
+ ] })
11095
11423
  ] })
11096
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
11097
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
11098
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs", children: "Route AI coding tools through the proxy:" }),
11099
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
11100
- /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "ANTHROPIC_BASE_URL=http://localhost:25947/proxy <your-tool>" }),
11101
- /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "OPENAI_BASE_URL=http://localhost:25947/proxy <your-tool>" })
11102
- ] }),
11103
- /* @__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." })
11104
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
11105
- "div",
11106
- {
11107
- ref: logListWrapperRef,
11108
- tabIndex: 0,
11109
- className: "flex flex-col gap-2 focus:outline-none",
11110
- children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: logListRef, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
11111
- ConversationGroupList,
11112
- {
11113
- groups,
11114
- viewMode,
11115
- strip,
11116
- slowResponseThresholdSeconds,
11117
- cacheTrends,
11118
- onCompareWithPrevious: handleCompareWithPrevious,
11119
- comparisonPredecessors,
11120
- onClearGroup,
11121
- standalone: groups.length === 1,
11122
- hasPinnedSessionContext: hasSessionContext,
11123
- timeDisplayFormat
11124
- }
11125
- ) })
11126
- }
11127
- ) }),
11424
+ ] }),
11128
11425
  comparePair !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(reactExports.Suspense, { fallback: null, children: /* @__PURE__ */ jsxRuntimeExports.jsx(LazyCompareDrawer, { left: comparePair[0], right: comparePair[1], onClose: closeCompare }) })
11129
11426
  ] });
11130
11427
  }
@@ -11138,6 +11435,15 @@ const SSEUpdateSchema = union([
11138
11435
  log: CapturedLogSchema
11139
11436
  })
11140
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
+ });
11141
11447
  function buildSessionMembershipEvidence(groups, sessionId) {
11142
11448
  const memberships = [];
11143
11449
  for (const group of groups) {
@@ -11183,6 +11489,7 @@ const DEBOUNCE_MS = 50;
11183
11489
  const HASH_SCROLL_ATTEMPTS = 12;
11184
11490
  const HASH_HIGHLIGHT_MS = 1800;
11185
11491
  const MAX_CLIENT_LOGS = 500;
11492
+ const SESSION_PAGE_LIMIT = 100;
11186
11493
  function buildLogsStreamUrl(sessionId) {
11187
11494
  const params = new URLSearchParams();
11188
11495
  if (sessionId !== void 0) {
@@ -11192,6 +11499,26 @@ function buildLogsStreamUrl(sessionId) {
11192
11499
  const query = params.toString();
11193
11500
  return query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream";
11194
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()}`;
11521
+ }
11195
11522
  function buildLogIndex(logs) {
11196
11523
  const idx = /* @__PURE__ */ new Map();
11197
11524
  for (let i = 0; i < logs.length; i++) {
@@ -11213,10 +11540,13 @@ function ProxyViewerContainer({
11213
11540
  const [viewMode, setViewMode] = reactExports.useState("simple");
11214
11541
  const [error, setError] = reactExports.useState(null);
11215
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);
11216
11545
  const [sessionMemberships, setSessionMemberships] = reactExports.useState([]);
11217
11546
  const eventSourceRef = reactExports.useRef(null);
11218
11547
  const reconnectTimeoutRef = reactExports.useRef(null);
11219
11548
  const handledHashRef = reactExports.useRef(null);
11549
+ const sessionPageRequestIdRef = reactExports.useRef(0);
11220
11550
  const logIndexRef = reactExports.useRef(/* @__PURE__ */ new Map());
11221
11551
  const pendingUpdatesRef = reactExports.useRef([]);
11222
11552
  const flushTimerRef = reactExports.useRef(null);
@@ -11301,6 +11631,32 @@ function ProxyViewerContainer({
11301
11631
  reconnectTimeoutRef.current = setTimeout(connectSSE, 3e3);
11302
11632
  };
11303
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
+ );
11304
11660
  reactExports.useEffect(() => {
11305
11661
  if (initialSessionId === void 0) {
11306
11662
  setSessionMemberships([]);
@@ -11323,6 +11679,11 @@ function ProxyViewerContainer({
11323
11679
  };
11324
11680
  }, [initialSessionId]);
11325
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;
11326
11687
  connectSSE();
11327
11688
  return () => {
11328
11689
  if (eventSourceRef.current) {
@@ -11338,7 +11699,7 @@ function ProxyViewerContainer({
11338
11699
  flushTimerRef.current = null;
11339
11700
  }
11340
11701
  };
11341
- }, [connectSSE]);
11702
+ }, [connectSSE, initialSessionId]);
11342
11703
  reactExports.useEffect(() => {
11343
11704
  const hash = window.location.hash;
11344
11705
  if (!hash.startsWith("#log-")) return;
@@ -11437,6 +11798,48 @@ function ProxyViewerContainer({
11437
11798
  })();
11438
11799
  }, []);
11439
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
+ ]);
11440
11843
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
11441
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 }),
11442
11845
  /* @__PURE__ */ jsxRuntimeExports.jsx(OnboardingBanner, {}),
@@ -11452,7 +11855,8 @@ function ProxyViewerContainer({
11452
11855
  onModelChange: setSelectedModel,
11453
11856
  onClearAll: handleClearAll,
11454
11857
  onClearGroup: handleClearGroup,
11455
- isLoading: initialSessionId !== void 0 && !streamInitialized,
11858
+ isLoading: initialSessionId === void 0 ? !streamInitialized : sessionPageLoading,
11859
+ pagination,
11456
11860
  sessionMemberships,
11457
11861
  viewMode,
11458
11862
  onViewModeChange: setViewMode,