@tonyclaw/llm-inspector 1.18.0 → 1.18.2

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 (44) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/CompareDrawer-C-4ypEWs.js +1 -0
  3. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +101 -0
  4. package/.output/public/assets/ReplayDialog-CyBKOgba.js +1 -0
  5. package/.output/public/assets/RequestAnatomy-C0IrVQ3q.js +1 -0
  6. package/.output/public/assets/ResponseView-MogToC4i.js +1 -0
  7. package/.output/public/assets/StreamingChunkSequence-ClhUhT-s.js +1 -0
  8. package/.output/public/assets/_sessionId-BO47oA3Z.js +1 -0
  9. package/.output/public/assets/index-BRvz6-L6.css +1 -0
  10. package/.output/public/assets/index-Btw8ec7-.js +1 -0
  11. package/.output/public/assets/{json-viewer-D-z1r1Pp.js → json-viewer-BicGakI5.js} +1 -1
  12. package/.output/public/assets/{main-CZJ63sQh.js → main-Be2qqUUW.js} +8 -7
  13. package/.output/server/_libs/lucide-react.mjs +23 -17
  14. package/.output/server/_sessionId-DhKJIdQC.mjs +122 -0
  15. package/.output/server/_ssr/{CompareDrawer-BJr-913n.mjs → CompareDrawer-BGUgukJ8.mjs} +57 -57
  16. package/.output/server/_ssr/{index-C7I_Qgt0.mjs → ProxyViewerContainer--3K3o3Sm.mjs} +277 -191
  17. package/.output/server/_ssr/{ReplayDialog-BwmToGuR.mjs → ReplayDialog-Bo86xZI4.mjs} +57 -57
  18. package/.output/server/_ssr/{RequestAnatomy-BmMiPRPB.mjs → RequestAnatomy-jRU5qgwB.mjs} +4 -4
  19. package/.output/server/_ssr/{ResponseView-ZB9-8Raw.mjs → ResponseView-DdO_-79a.mjs} +5 -5
  20. package/.output/server/_ssr/{StreamingChunkSequence-DWm4CQWC.mjs → StreamingChunkSequence-BigLwhh4.mjs} +57 -57
  21. package/.output/server/_ssr/index-BHG6vOnr.mjs +117 -0
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-D9XETzwp.mjs → json-viewer-B4c_WjXD.mjs} +4 -4
  24. package/.output/server/_ssr/{router-711KpGkz.mjs → router-DVixpJO-.mjs} +79 -22
  25. package/.output/server/_tanstack-start-manifest_v-BbvWUF4v.mjs +4 -0
  26. package/.output/server/index.mjs +70 -56
  27. package/README.md +109 -59
  28. package/package.json +1 -1
  29. package/src/assets/logos/mcp.png +0 -0
  30. package/src/components/ProxyViewer.tsx +240 -64
  31. package/src/components/ProxyViewerContainer.tsx +42 -11
  32. package/src/components/proxy-viewer/ConversationGroup.tsx +1 -8
  33. package/src/components/proxy-viewer/ConversationHeader.tsx +35 -7
  34. package/src/components/ui/mcp-logo.tsx +20 -0
  35. package/src/lib/sessionUrl.ts +44 -0
  36. package/src/routes/session/$sessionId.tsx +23 -0
  37. package/.output/public/assets/CompareDrawer-BpwZCB6M.js +0 -1
  38. package/.output/public/assets/ReplayDialog-Clratkzl.js +0 -1
  39. package/.output/public/assets/RequestAnatomy-EtiX0r_G.js +0 -1
  40. package/.output/public/assets/ResponseView-CJqxo-EN.js +0 -1
  41. package/.output/public/assets/StreamingChunkSequence-BIbRqQiV.js +0 -1
  42. package/.output/public/assets/index-B-0F9n1w.js +0 -101
  43. package/.output/public/assets/index-DoGvsnbA.css +0 -1
  44. package/.output/server/_tanstack-start-manifest_v-noQw0Vmw.mjs +0 -4
@@ -1,115 +1,19 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, R as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, d as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, a as AnthropicRequestSchema } from "./router-711KpGkz.mjs";
2
+ import { C as CapturedLogSchema, D as DEFAULT_SLOW_RESPONSE_THRESHOLD_SECONDS, a as RuntimeConfigSchema, r as requestFormatForPath, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, d as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, g as getSessionPath, e as ProviderConfigSchema, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, O as OpenAIRequestSchema, A as AnthropicResponseSchema$1, b as AnthropicRequestSchema } from "./router-DVixpJO-.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$1, C as Content, a as Close, b as Title, P as Portal$1, O as Overlay } from "../_libs/radix-ui__react-dialog.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";
9
9
  import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
10
  import { u 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, D as Download, S as Settings, a as ChevronDown, U as Upload, b as Scan, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, T as Trash2, i as TriangleAlert, E as EyeOff, j as Eye, k as ExternalLink, R as RotateCw, l as Pencil, G as GitCompareArrows, m as Minus, n as CircleCheckBig, O as OctagonAlert, W as Wrench, o as Globe, F as FileTerminal, p as Radio, q as ChevronsUp, r as ChevronsDown, s as RotateCcw, t as CircleQuestionMark, u as Server, v as Gauge, w as Lock, x as Wifi, y as WifiOff, A as ArrowUp, z as ArrowDown, B as Rows3, H as Columns2 } from "../_libs/lucide-react.mjs";
12
+ import { C as Check, X, P as Plus, D as Download, S as Settings, A as ArrowLeft, a as Copy, b as ChevronDown, U as Upload, c as Scan, d as CircleAlert, e as ChevronUp, L as LoaderCircle, f as ChevronRight, g as User, h as Clock, M as MessageSquare, Z as Zap, E as ExternalLink, T as Trash2, i as TriangleAlert, j as EyeOff, k as Eye, R as RotateCw, l as Pencil, G as GitCompareArrows, m as Minus, n as CircleCheckBig, O as OctagonAlert, W as Wrench, o as Globe, F as FileTerminal, p as Radio, q as ChevronsUp, r as ChevronsDown, s as RotateCcw, t as CircleQuestionMark, u as Server, v as Gauge, w as Lock, x as Wifi, y as WifiOff, z as ArrowUp, B as ArrowDown, H as Rows3, I as Columns2 } from "../_libs/lucide-react.mjs";
13
13
  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";
14
- import { R as Root2$1, L as List, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
15
- import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
14
+ 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";
15
+ import { R as Root2$1, L as List, T as Trigger$3, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
16
16
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
17
- import "../_libs/tanstack__react-router.mjs";
18
- import "../_libs/tiny-warning.mjs";
19
- import "../_libs/tanstack__router-core.mjs";
20
- import "../_libs/cookie-es.mjs";
21
- import "../_libs/tanstack__history.mjs";
22
- import "../_libs/tiny-invariant.mjs";
23
- import "../_libs/seroval.mjs";
24
- import "../_libs/seroval-plugins.mjs";
25
- import "node:stream/web";
26
- import "node:stream";
27
- import "../_libs/react-dom.mjs";
28
- import "util";
29
- import "async_hooks";
30
- import "stream";
31
- import "crypto";
32
- import "../_libs/isbot.mjs";
33
- import "node:fs";
34
- import "node:fs/promises";
35
- import "node:buffer";
36
- import "node:path";
37
- import "../_libs/conf.mjs";
38
- import "node:util";
39
- import "node:process";
40
- import "node:crypto";
41
- import "node:assert";
42
- import "../_libs/dot-prop.mjs";
43
- import "../_libs/env-paths.mjs";
44
- import "node:os";
45
- import "../_libs/atomically.mjs";
46
- import "../_libs/stubborn-fs.mjs";
47
- import "../_libs/stubborn-utils.mjs";
48
- import "../_libs/when-exit.mjs";
49
- import "../_libs/ajv.mjs";
50
- import "../_libs/fast-deep-equal.mjs";
51
- import "../_libs/json-schema-traverse.mjs";
52
- import "../_libs/fast-uri.mjs";
53
- import "../_libs/ajv-formats.mjs";
54
- import "../_libs/debounce-fn.mjs";
55
- import "../_libs/mimic-function.mjs";
56
- import "../_libs/semver.mjs";
57
- import "../_libs/uint8array-extras.mjs";
58
- import "node:child_process";
59
- import "node:worker_threads";
60
- import "../_libs/modelcontextprotocol__server.mjs";
61
- import "../_libs/use-sync-external-store.mjs";
62
- import "../_libs/dequal.mjs";
63
- import "../_libs/readable-stream.mjs";
64
- import "events";
65
- import "node:string_decoder";
66
- import "../_libs/process-nextick-args.mjs";
67
- import "../_libs/isarray.mjs";
68
- import "../_libs/safe-buffer.mjs";
69
- import "buffer";
70
- import "../_libs/core-util-is.mjs";
71
- import "../_libs/inherits.mjs";
72
- import "../_libs/util-deprecate.mjs";
73
- import "../_libs/lie.mjs";
74
- import "../_libs/immediate.mjs";
75
- import "../_libs/setimmediate.mjs";
76
- import "../_libs/pako.mjs";
77
- import "../_libs/radix-ui__primitive.mjs";
78
- import "../_libs/radix-ui__react-compose-refs.mjs";
79
- import "../_libs/radix-ui__react-context.mjs";
80
- import "../_libs/radix-ui__react-id.mjs";
81
- import "../_libs/@radix-ui/react-use-layout-effect+[...].mjs";
82
- import "../_libs/@radix-ui/react-use-controllable-state+[...].mjs";
83
- import "../_libs/@radix-ui/react-dismissable-layer+[...].mjs";
84
- import "../_libs/radix-ui__react-primitive.mjs";
85
- import "../_libs/@radix-ui/react-use-callback-ref+[...].mjs";
86
- import "../_libs/@radix-ui/react-use-escape-keydown+[...].mjs";
87
- import "../_libs/radix-ui__react-focus-scope.mjs";
88
- import "../_libs/radix-ui__react-portal.mjs";
89
- import "../_libs/radix-ui__react-presence.mjs";
90
- import "../_libs/radix-ui__react-focus-guards.mjs";
91
- import "../_libs/react-remove-scroll.mjs";
92
- import "tslib";
93
- import "../_libs/react-remove-scroll-bar.mjs";
94
- import "../_libs/react-style-singleton.mjs";
95
- import "../_libs/get-nonce.mjs";
96
- import "../_libs/use-sidecar.mjs";
97
- import "../_libs/use-callback-ref.mjs";
98
- import "../_libs/aria-hidden.mjs";
99
- import "../_libs/tanstack__virtual-core.mjs";
100
- import "../_libs/radix-ui__number.mjs";
101
- import "../_libs/radix-ui__react-collection.mjs";
102
- import "../_libs/radix-ui__react-direction.mjs";
103
- import "../_libs/radix-ui__react-popper.mjs";
104
- import "../_libs/floating-ui__react-dom.mjs";
105
- import "../_libs/floating-ui__dom.mjs";
106
- import "../_libs/floating-ui__core.mjs";
107
- import "../_libs/floating-ui__utils.mjs";
108
- import "../_libs/radix-ui__react-arrow.mjs";
109
- import "../_libs/radix-ui__react-use-size.mjs";
110
- import "../_libs/radix-ui__react-use-previous.mjs";
111
- import "../_libs/@radix-ui/react-visually-hidden+[...].mjs";
112
- import "../_libs/radix-ui__react-roving-focus.mjs";
113
17
  const ApiErrorSchema = object({
114
18
  error: string()
115
19
  });
@@ -371,7 +275,7 @@ function getStatusCategory(status) {
371
275
  if (status >= 500) return "server_error";
372
276
  return "pending";
373
277
  }
374
- const version = "1.18.0";
278
+ const version = "1.18.2";
375
279
  const packageJson = {
376
280
  version
377
281
  };
@@ -418,12 +322,12 @@ function Dialog({
418
322
  function DialogTrigger({
419
323
  ...props
420
324
  }) {
421
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$1, { "data-slot": "dialog-trigger", ...props });
325
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$2, { "data-slot": "dialog-trigger", ...props });
422
326
  }
423
327
  function DialogPortal({
424
328
  ...props
425
329
  }) {
426
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { "data-slot": "dialog-portal", ...props });
330
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { "data-slot": "dialog-portal", ...props });
427
331
  }
428
332
  function DialogOverlay({
429
333
  className,
@@ -590,6 +494,13 @@ function ConversationHeader({
590
494
  if (onClear === void 0) return;
591
495
  setConfirmOpen(true);
592
496
  };
497
+ const handleOpenInNewTab = reactExports.useCallback(
498
+ (e) => {
499
+ e.stopPropagation();
500
+ window.open(getSessionPath(conversationId), "_blank", "noopener,noreferrer");
501
+ },
502
+ [conversationId]
503
+ );
593
504
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
594
505
  "div",
595
506
  {
@@ -619,7 +530,7 @@ function ConversationHeader({
619
530
  {
620
531
  className: "text-purple-400/90 font-mono text-xs font-semibold shrink-0",
621
532
  title: conversationId,
622
- children: conversationId.length > 24 ? conversationId.slice(0, 12) + "" + conversationId.slice(-12) : conversationId
533
+ children: conversationId.startsWith("PID:") || conversationId.includes("|") ? conversationId : conversationId.length > 24 ? conversationId.slice(0, 12) + "..." + conversationId.slice(-12) : conversationId
623
534
  }
624
535
  ),
625
536
  userAgent !== null && userAgent !== void 0 && userAgent !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -671,6 +582,22 @@ function ConversationHeader({
671
582
  ] })
672
583
  ] }),
673
584
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-1 min-w-0" }),
585
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
586
+ "button",
587
+ {
588
+ type: "button",
589
+ onClick: handleOpenInNewTab,
590
+ onKeyDown: (e) => {
591
+ if (e.key === "Enter" || e.key === " ") {
592
+ handleOpenInNewTab(e);
593
+ }
594
+ },
595
+ "aria-label": `Open session ${conversationId} in a new tab`,
596
+ title: "Open this session in a new tab",
597
+ className: "text-muted-foreground hover:text-foreground transition-colors shrink-0 inline-flex items-center justify-center size-8 rounded hover:bg-muted cursor-pointer",
598
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3.5" })
599
+ }
600
+ ),
674
601
  onClear !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
675
602
  "button",
676
603
  {
@@ -1120,8 +1047,8 @@ const crabVariants = [
1120
1047
  Crab11,
1121
1048
  Crab12
1122
1049
  ];
1123
- function getCrabVariant(index2) {
1124
- return crabVariants[Math.abs(index2) % crabVariants.length] ?? Crab1;
1050
+ function getCrabVariant(index) {
1051
+ return crabVariants[Math.abs(index) % crabVariants.length] ?? Crab1;
1125
1052
  }
1126
1053
  function getInteriorCrabVariantIndex(seed) {
1127
1054
  const interiorCount = crabVariants.length - 2;
@@ -1229,7 +1156,7 @@ function Tooltip({ ...props }) {
1229
1156
  return /* @__PURE__ */ jsxRuntimeExports.jsx(Root3, { "data-slot": "tooltip", ...props });
1230
1157
  }
1231
1158
  function TooltipTrigger({ ...props }) {
1232
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$3, { "data-slot": "tooltip-trigger", ...props });
1159
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Trigger$1, { "data-slot": "tooltip-trigger", ...props });
1233
1160
  }
1234
1161
  function TooltipContent({
1235
1162
  className,
@@ -1237,7 +1164,7 @@ function TooltipContent({
1237
1164
  children,
1238
1165
  ...props
1239
1166
  }) {
1240
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$2, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1167
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Portal$1, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
1241
1168
  Content2$1,
1242
1169
  {
1243
1170
  "data-slot": "tooltip-content",
@@ -1396,7 +1323,7 @@ function TabsTrigger({
1396
1323
  ...props
1397
1324
  }) {
1398
1325
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
1399
- Trigger$2,
1326
+ Trigger$3,
1400
1327
  {
1401
1328
  "data-slot": "tabs-trigger",
1402
1329
  className: cn(
@@ -1424,27 +1351,27 @@ function TabsContent({
1424
1351
  );
1425
1352
  }
1426
1353
  const LazyCompareDrawer = reactExports.lazy(
1427
- () => import("./CompareDrawer-BJr-913n.mjs").then((m) => ({ default: m.CompareDrawer }))
1354
+ () => import("./CompareDrawer-BGUgukJ8.mjs").then((m) => ({ default: m.CompareDrawer }))
1428
1355
  );
1429
1356
  const LazyReplayDialog = reactExports.lazy(
1430
- () => import("./ReplayDialog-BwmToGuR.mjs").then((m) => ({ default: m.ReplayDialog }))
1357
+ () => import("./ReplayDialog-Bo86xZI4.mjs").then((m) => ({ default: m.ReplayDialog }))
1431
1358
  );
1432
1359
  const LazyRequestAnatomy = reactExports.lazy(
1433
- () => import("./RequestAnatomy-BmMiPRPB.mjs").then((m) => ({ default: m.RequestAnatomy }))
1360
+ () => import("./RequestAnatomy-jRU5qgwB.mjs").then((m) => ({ default: m.RequestAnatomy }))
1434
1361
  );
1435
1362
  const LazyResponseView = reactExports.lazy(
1436
- () => import("./ResponseView-ZB9-8Raw.mjs").then((m) => ({ default: m.ResponseView }))
1363
+ () => import("./ResponseView-DdO_-79a.mjs").then((m) => ({ default: m.ResponseView }))
1437
1364
  );
1438
1365
  const LazyStreamingChunkSequence = reactExports.lazy(
1439
- () => import("./StreamingChunkSequence-DWm4CQWC.mjs").then((m) => ({
1366
+ () => import("./StreamingChunkSequence-BigLwhh4.mjs").then((m) => ({
1440
1367
  default: m.StreamingChunkSequence
1441
1368
  }))
1442
1369
  );
1443
1370
  const LazyJsonViewer = reactExports.lazy(
1444
- () => import("./json-viewer-D9XETzwp.mjs").then((m) => ({ default: m.JsonViewer }))
1371
+ () => import("./json-viewer-B4c_WjXD.mjs").then((m) => ({ default: m.JsonViewer }))
1445
1372
  );
1446
1373
  const LazyJsonViewerFromString = reactExports.lazy(
1447
- () => import("./json-viewer-D9XETzwp.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1374
+ () => import("./json-viewer-B4c_WjXD.mjs").then((m) => ({ default: m.JsonViewerFromString }))
1448
1375
  );
1449
1376
  const HIGHLIGHT_DURATION_MS = 1200;
1450
1377
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -2244,12 +2171,12 @@ const anthropicLogFormatAdapter = {
2244
2171
  }
2245
2172
  }
2246
2173
  if (Array.isArray(body.messages)) {
2247
- body.messages.forEach((message, index2) => {
2174
+ body.messages.forEach((message, index) => {
2248
2175
  if (message === null || typeof message !== "object") return;
2249
2176
  const m = message;
2250
2177
  const role = m.role === "user" || m.role === "assistant" ? m.role : "user";
2251
2178
  const text = contentToText$1(m.content);
2252
- segments.push(segment$1(role, `[${index2}] ${role}`, text, `/messages/${index2}`));
2179
+ segments.push(segment$1(role, `[${index}] ${role}`, text, `/messages/${index}`));
2253
2180
  });
2254
2181
  }
2255
2182
  if (Array.isArray(body.tools) && body.tools.length > 0) {
@@ -2350,12 +2277,12 @@ const openAILogFormatAdapter = {
2350
2277
  const body = parsed;
2351
2278
  const segments = [];
2352
2279
  if (Array.isArray(body.messages)) {
2353
- body.messages.forEach((message, index2) => {
2280
+ body.messages.forEach((message, index) => {
2354
2281
  if (message === null || typeof message !== "object") return;
2355
2282
  const m = message;
2356
2283
  const role = m.role === "user" || m.role === "assistant" || m.role === "system" || m.role === "tool" ? m.role : "user";
2357
2284
  const text = messageToText(m);
2358
- segments.push(segment(role, `[${index2}] ${role}`, text, `/messages/${index2}`));
2285
+ segments.push(segment(role, `[${index}] ${role}`, text, `/messages/${index}`));
2359
2286
  });
2360
2287
  }
2361
2288
  if (Array.isArray(body.tools) && body.tools.length > 0) {
@@ -2859,9 +2786,9 @@ function buildTurnGroups(logs) {
2859
2786
  function buildValidPredecessors(groups) {
2860
2787
  const predecessors = /* @__PURE__ */ new Map();
2861
2788
  for (const group of groups) {
2862
- for (let index2 = 1; index2 < group.logs.length; index2 += 1) {
2863
- const current = group.logs[index2];
2864
- const previous = group.logs[index2 - 1];
2789
+ for (let index = 1; index < group.logs.length; index += 1) {
2790
+ const current = group.logs[index];
2791
+ const previous = group.logs[index - 1];
2865
2792
  if (current === void 0 || previous === void 0) continue;
2866
2793
  const currentFormat = resolveLogFormat(current);
2867
2794
  const previousFormat = resolveLogFormat(previous);
@@ -3166,12 +3093,11 @@ const ConversationGroup = reactExports.memo(function({
3166
3093
  const mixed = hasMixedApiFormat(group.logs);
3167
3094
  const isLoading = group.logs.some((log) => log.responseStatus === null);
3168
3095
  const turnGroups = reactExports.useMemo(() => buildTurnGroups(group.logs), [group.logs]);
3169
- const displayId = group.conversationId.startsWith("PID:") || group.conversationId.includes("|") ? group.conversationId : group.conversationId.length > 24 ? group.conversationId.slice(0, 12) + "…" + group.conversationId.slice(-12) : group.conversationId;
3170
3096
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2", children: [
3171
3097
  !standalone && /* @__PURE__ */ jsxRuntimeExports.jsx(
3172
3098
  ConversationHeader,
3173
3099
  {
3174
- conversationId: displayId,
3100
+ conversationId: group.conversationId,
3175
3101
  startTime,
3176
3102
  endTime,
3177
3103
  totalCalls: group.logs.length,
@@ -3230,6 +3156,18 @@ function CrabLogo({ className }) {
3230
3156
  }
3231
3157
  );
3232
3158
  }
3159
+ const McpLogoPng = "data:image/png;base64,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";
3160
+ function McpLogo({ className }) {
3161
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3162
+ "img",
3163
+ {
3164
+ src: McpLogoPng,
3165
+ alt: "Model Context Protocol",
3166
+ "aria-hidden": "true",
3167
+ className: cn("inline-block size-8 object-contain invert", className)
3168
+ }
3169
+ );
3170
+ }
3233
3171
  function Select({
3234
3172
  ...props
3235
3173
  }) {
@@ -3489,13 +3427,13 @@ function ImportWizardDialog({
3489
3427
  scan();
3490
3428
  }
3491
3429
  }, [open, scan]);
3492
- const toggleProvider = reactExports.useCallback((index2) => {
3430
+ const toggleProvider = reactExports.useCallback((index) => {
3493
3431
  setSelected((prev) => {
3494
3432
  const next = new Set(prev);
3495
- if (next.has(index2)) {
3496
- next.delete(index2);
3433
+ if (next.has(index)) {
3434
+ next.delete(index);
3497
3435
  } else {
3498
- next.add(index2);
3436
+ next.add(index);
3499
3437
  }
3500
3438
  return next;
3501
3439
  });
@@ -4020,10 +3958,10 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
4020
3958
  const modelRowRefs = reactExports.useRef([]);
4021
3959
  reactExports.useEffect(() => {
4022
3960
  if (openModelDropdown === null) return;
4023
- const index2 = openModelDropdown;
3961
+ const index = openModelDropdown;
4024
3962
  function handleClick(e) {
4025
3963
  if (!(e.target instanceof Node)) return;
4026
- const ref = modelRowRefs.current[index2];
3964
+ const ref = modelRowRefs.current[index];
4027
3965
  if (ref !== null && ref !== void 0 && !ref.contains(e.target)) {
4028
3966
  setOpenModelDropdown(null);
4029
3967
  }
@@ -5132,8 +5070,8 @@ function focusAndScroll(el) {
5132
5070
  el.focus({ preventScroll: true });
5133
5071
  el.scrollIntoView({ block: "nearest", behavior: "smooth" });
5134
5072
  }
5135
- function safeItemAt(items, index2) {
5136
- const el = items[index2];
5073
+ function safeItemAt(items, index) {
5074
+ const el = items[index];
5137
5075
  return el ?? null;
5138
5076
  }
5139
5077
  function isEditableTarget(target) {
@@ -5263,6 +5201,25 @@ function computeTokenSummary(logs) {
5263
5201
  }
5264
5202
  return { totalIn, totalOut };
5265
5203
  }
5204
+ function formatTimeRange(logs) {
5205
+ const first = logs[0];
5206
+ const last = logs[logs.length - 1];
5207
+ if (first === void 0 || last === void 0) return null;
5208
+ const format = (iso) => new Date(iso).toLocaleTimeString([], {
5209
+ hour: "2-digit",
5210
+ minute: "2-digit",
5211
+ second: "2-digit"
5212
+ });
5213
+ return `${format(first.timestamp)} - ${format(last.timestamp)}`;
5214
+ }
5215
+ function getFirstUserAgent(logs) {
5216
+ for (const log of logs) {
5217
+ if (log.userAgent !== null && log.userAgent !== void 0 && log.userAgent !== "") {
5218
+ return log.userAgent;
5219
+ }
5220
+ }
5221
+ return null;
5222
+ }
5266
5223
  function CopyableCommand({ command }) {
5267
5224
  const [copied, setCopied] = reactExports.useState(false);
5268
5225
  const handleCopy = reactExports.useCallback(() => {
@@ -5314,6 +5271,82 @@ function CopyableCommand({ command }) {
5314
5271
  )
5315
5272
  ] });
5316
5273
  }
5274
+ function McpReadyBadge() {
5275
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
5276
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-7 items-center gap-2 rounded-md border border-cyan-400/30 bg-cyan-500/10 px-2.5 font-mono text-[11px] font-medium text-cyan-300 shadow-[0_0_16px_rgba(34,211,238,0.08)]", children: [
5277
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "size-1.5 rounded-full bg-emerald-300 shadow-[0_0_8px_rgba(110,231,183,0.8)]" }),
5278
+ "MCP Ready",
5279
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden text-cyan-200/70 sm:inline", children: "/api/mcp" })
5280
+ ] }) }),
5281
+ /* @__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." })
5282
+ ] }) });
5283
+ }
5284
+ function SessionContextBar({
5285
+ sessionId,
5286
+ logs,
5287
+ totalIn,
5288
+ totalOut
5289
+ }) {
5290
+ const [copied, setCopied] = reactExports.useState(false);
5291
+ const timeRange = reactExports.useMemo(() => formatTimeRange(logs), [logs]);
5292
+ const userAgent = reactExports.useMemo(() => getFirstUserAgent(logs), [logs]);
5293
+ const handleCopyLink = reactExports.useCallback(() => {
5294
+ void window.navigator.clipboard.writeText(window.location.href).then(() => {
5295
+ setCopied(true);
5296
+ setTimeout(() => setCopied(false), 2e3);
5297
+ });
5298
+ }, []);
5299
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4 flex items-center gap-3 border border-border rounded-md bg-muted/20 px-3 py-2 text-xs", children: [
5300
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5301
+ "a",
5302
+ {
5303
+ href: "/",
5304
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5305
+ "aria-label": "Back to all sessions",
5306
+ title: "Back to all sessions",
5307
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ArrowLeft, { className: "size-3.5" })
5308
+ }
5309
+ ),
5310
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 flex-1", children: [
5311
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
5312
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono font-semibold text-purple-400/90 truncate", title: sessionId, children: truncateSessionId(sessionId) }),
5313
+ userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(
5314
+ "span",
5315
+ {
5316
+ className: "font-mono text-muted-foreground truncate max-w-[220px]",
5317
+ title: userAgent,
5318
+ children: userAgent
5319
+ }
5320
+ )
5321
+ ] }),
5322
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-muted-foreground", children: [
5323
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
5324
+ logs.length,
5325
+ " request",
5326
+ logs.length !== 1 ? "s" : ""
5327
+ ] }),
5328
+ timeRange !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: timeRange }),
5329
+ (totalIn > 0 || totalOut > 0) && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono", children: [
5330
+ formatTokens(totalIn),
5331
+ " in / ",
5332
+ formatTokens(totalOut),
5333
+ " out"
5334
+ ] })
5335
+ ] })
5336
+ ] }),
5337
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5338
+ "button",
5339
+ {
5340
+ type: "button",
5341
+ onClick: handleCopyLink,
5342
+ className: "inline-flex size-8 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
5343
+ "aria-label": copied ? "Copied session link" : "Copy session link",
5344
+ title: copied ? "Copied session link" : "Copy session link",
5345
+ children: copied ? /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Copy, { className: "size-3.5" })
5346
+ }
5347
+ )
5348
+ ] });
5349
+ }
5317
5350
  function ProxyViewer({
5318
5351
  logs,
5319
5352
  sessions,
@@ -5327,7 +5360,9 @@ function ProxyViewer({
5327
5360
  viewMode,
5328
5361
  onViewModeChange,
5329
5362
  strip,
5330
- slowResponseThresholdSeconds
5363
+ slowResponseThresholdSeconds,
5364
+ hideSessionFilter = false,
5365
+ pinnedSessionId
5331
5366
  }) {
5332
5367
  const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
5333
5368
  const [exporting, setExporting] = reactExports.useState(false);
@@ -5349,6 +5384,14 @@ function ProxyViewer({
5349
5384
  clearTimeout(t2);
5350
5385
  };
5351
5386
  }, []);
5387
+ reactExports.useEffect(() => {
5388
+ if (pinnedSessionId === void 0) {
5389
+ document.title = "LLM Inspector";
5390
+ return;
5391
+ }
5392
+ const requestLabel = logs.length === 1 ? "1 req" : `${logs.length} req`;
5393
+ document.title = `${truncateSessionId(pinnedSessionId)} - ${requestLabel} - LLM Inspector`;
5394
+ }, [logs.length, pinnedSessionId]);
5352
5395
  const handleExport = reactExports.useCallback(async () => {
5353
5396
  setExporting(true);
5354
5397
  try {
@@ -5374,51 +5417,66 @@ function ProxyViewer({
5374
5417
  [comparisonPredecessors]
5375
5418
  );
5376
5419
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1400px] xl:max-w-[1600px] 2xl:max-w-[1800px] mx-auto px-6 pb-6", children: [
5377
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-end pt-6 pb-8 relative", children: [
5378
- /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-lg font-bold flex items-end gap-2 absolute left-1/2 -translate-x-1/2 whitespace-nowrap", children: [
5379
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-end gap-1 group cursor-default", "aria-hidden": "true", children: [
5380
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
5381
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex items-end gap-0.5", children: crabVariants.map((Crab, i) => {
5382
- const color = [
5383
- "text-amber-500",
5384
- "text-rose-500",
5385
- "text-sky-500",
5386
- "text-emerald-500",
5387
- "text-violet-500",
5388
- "text-orange-500",
5389
- "text-cyan-500",
5390
- "text-pink-500",
5391
- "text-lime-500",
5392
- "text-blue-500",
5393
- "text-yellow-500",
5394
- "text-fuchsia-500"
5395
- ][i];
5396
- const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
5397
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
5398
- Crab,
5399
- {
5400
- className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
5401
- style: {
5402
- transitionDelay: `${i * 50}ms`,
5403
- ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
5404
- }
5405
- },
5406
- i
5407
- );
5408
- }) })
5420
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[1fr_auto_1fr] items-start gap-3 pt-6 pb-8", children: [
5421
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
5422
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "flex min-w-0 flex-col items-center gap-2 text-center", children: [
5423
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex max-w-[calc(100vw-7rem)] items-end gap-2 whitespace-nowrap", children: [
5424
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex shrink-0 items-end gap-1 group cursor-default", "aria-hidden": "true", children: [
5425
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 transition-all duration-300 group-hover:scale-125 group-hover:-translate-y-1.5" }),
5426
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "hidden items-end gap-0.5 sm:flex", children: crabVariants.map((Crab, i) => {
5427
+ const color = [
5428
+ "text-amber-500",
5429
+ "text-rose-500",
5430
+ "text-sky-500",
5431
+ "text-emerald-500",
5432
+ "text-violet-500",
5433
+ "text-orange-500",
5434
+ "text-cyan-500",
5435
+ "text-pink-500",
5436
+ "text-lime-500",
5437
+ "text-blue-500",
5438
+ "text-yellow-500",
5439
+ "text-fuchsia-500"
5440
+ ][i];
5441
+ const entranceClass = crabEntrancePhase === "hidden" ? "opacity-0 scale-0" : crabEntrancePhase === "playing" ? "animate-crab-piano-pop" : "";
5442
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
5443
+ Crab,
5444
+ {
5445
+ className: `size-5 ${color} transition-all duration-300 ease-out group-hover:scale-125 group-hover:-translate-y-1 ${entranceClass}`,
5446
+ style: {
5447
+ transitionDelay: `${i * 50}ms`,
5448
+ ...crabEntrancePhase === "playing" ? { animationDelay: `${i * 400}ms` } : {}
5449
+ }
5450
+ },
5451
+ i
5452
+ );
5453
+ }) })
5454
+ ] }),
5455
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-baseline gap-2 pl-1", children: [
5456
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate text-lg font-bold", children: "LLM Inspector" }),
5457
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "shrink-0 font-mono text-xs font-semibold text-muted-foreground", children: [
5458
+ "v",
5459
+ packageJson.version
5460
+ ] })
5461
+ ] }),
5462
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4 shrink-0 text-muted-foreground/70", "aria-hidden": "true" }),
5463
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpLogo, { className: "size-10 shrink-0" })
5409
5464
  ] }),
5410
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-baseline gap-2", children: [
5411
- "LLM Inspector",
5412
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-muted-foreground font-mono", children: [
5413
- "v",
5414
- packageJson.version
5415
- ] })
5416
- ] })
5465
+ /* @__PURE__ */ jsxRuntimeExports.jsx(McpReadyBadge, {})
5417
5466
  ] }),
5418
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
5467
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "justify-self-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SettingsDialog, {}) })
5419
5468
  ] }),
5469
+ pinnedSessionId !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
5470
+ SessionContextBar,
5471
+ {
5472
+ sessionId: pinnedSessionId,
5473
+ logs,
5474
+ totalIn,
5475
+ totalOut
5476
+ }
5477
+ ),
5420
5478
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mb-4", children: [
5421
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
5479
+ !hideSessionFilter && /* @__PURE__ */ jsxRuntimeExports.jsxs(Select, { value: selectedSession, onValueChange: onSessionChange, children: [
5422
5480
  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectTrigger, { className: "flex-1 max-w-[350px] text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue, { placeholder: "All sessions" }) }),
5423
5481
  /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { children: [
5424
5482
  /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "__all__", children: "All sessions" }),
@@ -5486,7 +5544,23 @@ function ProxyViewer({
5486
5544
  }
5487
5545
  )
5488
5546
  ] }),
5489
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: logs.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
5547
+ /* @__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: [
5548
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: "Session not found" }),
5549
+ /* @__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) }),
5550
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs", children: [
5551
+ "This session may have been cleared or never existed.",
5552
+ " ",
5553
+ hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "underline hover:text-foreground transition-colors", children: "Back to all sessions" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
5554
+ "button",
5555
+ {
5556
+ type: "button",
5557
+ onClick: () => onSessionChange("__all__"),
5558
+ className: "underline hover:text-foreground transition-colors",
5559
+ children: "Show all sessions"
5560
+ }
5561
+ )
5562
+ ] })
5563
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center text-muted-foreground py-16 space-y-4", children: [
5490
5564
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
5491
5565
  /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs", children: "Route AI coding tools through the proxy:" }),
5492
5566
  /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "LLM_BASE_URL=http://localhost:25947/proxy <your-tool>" })
@@ -5549,9 +5623,16 @@ function filterLogs(logs, selectedSession, selectedModel) {
5549
5623
  });
5550
5624
  }
5551
5625
  const DEBOUNCE_MS = 50;
5552
- function ProxyViewerContainer() {
5626
+ function buildLogsStreamUrl(sessionId) {
5627
+ if (sessionId === void 0) return "/api/logs/stream";
5628
+ const params = new URLSearchParams({ sessionId });
5629
+ return `/api/logs/stream?${params.toString()}`;
5630
+ }
5631
+ function ProxyViewerContainer({
5632
+ initialSessionId
5633
+ } = {}) {
5553
5634
  const [allLogs, setAllLogs] = reactExports.useState([]);
5554
- const [selectedSession, setSelectedSession] = reactExports.useState("__all__");
5635
+ const [selectedSession, setSelectedSession] = reactExports.useState(initialSessionId ?? "__all__");
5555
5636
  const [selectedModel, setSelectedModel] = reactExports.useState("__all__");
5556
5637
  const [viewMode, setViewMode] = reactExports.useState("simple");
5557
5638
  const [error, setError] = reactExports.useState(null);
@@ -5598,7 +5679,7 @@ function ProxyViewerContainer() {
5598
5679
  if (eventSourceRef.current) {
5599
5680
  eventSourceRef.current.close();
5600
5681
  }
5601
- const es = new EventSource("/api/logs/stream");
5682
+ const es = new EventSource(buildLogsStreamUrl(initialSessionId));
5602
5683
  eventSourceRef.current = es;
5603
5684
  es.onmessage = (event) => {
5604
5685
  try {
@@ -5639,7 +5720,7 @@ function ProxyViewerContainer() {
5639
5720
  }
5640
5721
  reconnectTimeoutRef.current = setTimeout(connectSSE, 3e3);
5641
5722
  };
5642
- }, [scheduleUpdate]);
5723
+ }, [initialSessionId, scheduleUpdate]);
5643
5724
  reactExports.useEffect(() => {
5644
5725
  connectSSE();
5645
5726
  return () => {
@@ -5658,9 +5739,17 @@ function ProxyViewerContainer() {
5658
5739
  };
5659
5740
  }, [connectSSE]);
5660
5741
  const handleClearAll = reactExports.useCallback(() => {
5742
+ if (initialSessionId !== void 0 && allLogs.length === 0) return;
5661
5743
  void (async () => {
5662
5744
  try {
5663
- const res = await fetch("/api/logs", { method: "DELETE" });
5745
+ const body = initialSessionId === void 0 ? void 0 : JSON.stringify({ ids: allLogs.map((log) => log.id) });
5746
+ const res = await fetch("/api/logs", {
5747
+ method: "DELETE",
5748
+ ...body === void 0 ? {} : {
5749
+ headers: { "Content-Type": "application/json" },
5750
+ body
5751
+ }
5752
+ });
5664
5753
  if (!res.ok) {
5665
5754
  setError("Failed to clear logs");
5666
5755
  return;
@@ -5672,7 +5761,7 @@ function ProxyViewerContainer() {
5672
5761
  setError(err instanceof Error ? err.message : "Unknown error clearing logs");
5673
5762
  }
5674
5763
  })();
5675
- }, []);
5764
+ }, [allLogs, initialSessionId]);
5676
5765
  const handleClearGroup = reactExports.useCallback((ids) => {
5677
5766
  if (ids.length === 0) return;
5678
5767
  void (async () => {
@@ -5722,19 +5811,17 @@ function ProxyViewerContainer() {
5722
5811
  viewMode,
5723
5812
  onViewModeChange: setViewMode,
5724
5813
  strip,
5725
- slowResponseThresholdSeconds
5814
+ slowResponseThresholdSeconds,
5815
+ hideSessionFilter: initialSessionId !== void 0,
5816
+ pinnedSessionId: initialSessionId
5726
5817
  }
5727
5818
  )
5728
5819
  ] });
5729
5820
  }
5730
- const SplitComponent = ProxyViewerContainer;
5731
- const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
5732
- __proto__: null,
5733
- component: SplitComponent
5734
- }, Symbol.toStringTag, { value: "Module" }));
5735
5821
  export {
5736
5822
  Badge as B,
5737
5823
  Dialog as D,
5824
+ ProxyViewerContainer as P,
5738
5825
  Tabs as T,
5739
5826
  getConversationId as a,
5740
5827
  DialogContent as b,
@@ -5754,6 +5841,5 @@ export {
5754
5841
  getStatusCategory as p,
5755
5842
  parseJsonText as q,
5756
5843
  resolveLogFormat as r,
5757
- safeJsonValue as s,
5758
- index as t
5844
+ safeJsonValue as s
5759
5845
  };