@tonyclaw/agent-inspector 3.0.3 → 3.0.4

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 (35) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-CufpM7_P.js → CompareDrawer-Bs2kzUSp.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-BUnlrfiS.js +106 -0
  4. package/.output/public/assets/{ReplayDialog-ClDmxv57.js → ReplayDialog-B6xy7pl5.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-DcuIghmp.js → RequestAnatomy-BSvSmpCO.js} +1 -1
  6. package/.output/public/assets/{ResponseView-C7Tl47j3.js → ResponseView-BFnUva7Z.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-DBK_R540.js → StreamingChunkSequence-u7ZaJOqn.js} +1 -1
  8. package/.output/public/assets/{_sessionId-D8Ke-EcI.js → _sessionId-CH4nsMOA.js} +1 -1
  9. package/.output/public/assets/{index-CpWH7b2O.js → index-B5HS6AxR.js} +1 -1
  10. package/.output/public/assets/index-BqVL284D.js +1 -0
  11. package/.output/public/assets/index-DJW6qbnH.css +1 -0
  12. package/.output/public/assets/{json-viewer-BEifdKM_.js → json-viewer-5Tj5vkqe.js} +1 -1
  13. package/.output/public/assets/{main-DDfExS3_.js → main-ChxZWG8K.js} +2 -2
  14. package/.output/server/{_sessionId-Di7z893V.mjs → _sessionId-B3r-VBdM.mjs} +2 -2
  15. package/.output/server/_ssr/{CompareDrawer-ABv-8FMB.mjs → CompareDrawer-DAYrKHKe.mjs} +2 -2
  16. package/.output/server/_ssr/{ProxyViewerContainer-CGX-1aUr.mjs → ProxyViewerContainer-CYmNmQK9.mjs} +332 -148
  17. package/.output/server/_ssr/{ReplayDialog-DD31ftzG.mjs → ReplayDialog-CdKvdXxk.mjs} +3 -3
  18. package/.output/server/_ssr/{RequestAnatomy-54kNHAjH.mjs → RequestAnatomy-BtmTUq7O.mjs} +2 -2
  19. package/.output/server/_ssr/{ResponseView-CRnibtlX.mjs → ResponseView-DuD_z5hd.mjs} +2 -2
  20. package/.output/server/_ssr/{StreamingChunkSequence-Cb5WGfMm.mjs → StreamingChunkSequence-DpIV2wt_.mjs} +2 -2
  21. package/.output/server/_ssr/{index-BJ-8DomR.mjs → index-B4_8vH8k.mjs} +2 -2
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-DB5188I7.mjs → json-viewer-gcNJEB7y.mjs} +2 -2
  24. package/.output/server/_ssr/{router-BKf70uDr.mjs → router-CYjIsElo.mjs} +4 -4
  25. package/.output/server/{_tanstack-start-manifest_v-DUuf3Rgf.mjs → _tanstack-start-manifest_v-BRdOxY6q.mjs} +1 -1
  26. package/.output/server/index.mjs +67 -67
  27. package/package.json +1 -1
  28. package/src/components/ProxyViewer.tsx +92 -25
  29. package/src/components/ProxyViewerContainer.tsx +154 -55
  30. package/src/components/providers/ProviderCard.tsx +83 -36
  31. package/src/components/proxy-viewer/ConversationGroup.tsx +1 -1
  32. package/src/components/proxy-viewer/ConversationHeader.tsx +4 -5
  33. package/.output/public/assets/ProxyViewerContainer-Dppr2zGE.js +0 -106
  34. package/.output/public/assets/index-BskTYcAH.css +0 -1
  35. package/.output/public/assets/index-DRHfTEM5.js +0 -1
@@ -1,5 +1,5 @@
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, d as RuntimeConfigSchema, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, n as DeleteInspectorGroupsResponseSchema, o as GroupEvidenceExportResultSchema, q as DeleteInspectorGroupResponseSchema, z as createPendingProviderTestResults, B as ProviderTestResultsSchema, E as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, F as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, l as AlertSummarySchema, m as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, u as maskApiKey, v as PATH_V1_MESSAGES, w as PATH_V1_CHAT_COMPLETIONS, x as PATH_V1_RESPONSES, r as providerHasContextMetadata, t as findProviderModelMetadata, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, H as resolveProviderContextWindow, j as getSessionPath, y as previewUpstreamUrl, K as KnowledgeCandidateSchema, J as isPlainRecord, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-BKf70uDr.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, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, n as DeleteInspectorGroupsResponseSchema, o as GroupEvidenceExportResultSchema, q as DeleteInspectorGroupResponseSchema, z as createPendingProviderTestResults, B as ProviderTestResultsSchema, E as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, F as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, l as AlertSummarySchema, m as AlertListResponseSchema, G as GroupEvidenceReadResponseSchema, u as maskApiKey, v as PATH_V1_MESSAGES, w as PATH_V1_CHAT_COMPLETIONS, x as PATH_V1_RESPONSES, r as providerHasContextMetadata, t as findProviderModelMetadata, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, H as resolveProviderContextWindow, j as getSessionPath, y as previewUpstreamUrl, K as KnowledgeCandidateSchema, J as isPlainRecord, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-CYjIsElo.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";
@@ -9,7 +9,7 @@ import { c as cva } from "../_libs/class-variance-authority.mjs";
9
9
  import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
10
10
  import { d as diffJson, a as diffLines } from "../_libs/diff.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 { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, S as Search, U as Upload, D as Download, F as FileBraces, c as Trash2, P as Plus, A as ArrowLeft, d as ChevronUp, e as Copy, f as ChevronsLeft, g as ChevronLeft, h as ChevronRight, i as ChevronsRight, j as Clapperboard, k as Flag, l as Siren, m as Timer, G as Gauge, L as LoaderCircle, R as RefreshCw, n as Layers, o as Settings, M as Monitor, p as Scan, q as CircleAlert, r as Terminal, s as ArrowUpRight, t as ArrowDownRight, u as Cpu, v as FolderOpen, B as Bell, w as Clock3, x as ShieldAlert, E as EyeOff, y as Eye, z as ExternalLink, H as RotateCw, J as Clock, K as Pencil, N as MessageSquare, Z as Zap, W as Wrench, O as Brain, Q as Minus, V as CircleCheckBig, Y as CircleQuestionMark, _ as Server, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as CircleX, a3 as ShieldCheck, a4 as Save, a5 as FileSearch, a6 as CloudUpload, a7 as OctagonAlert, a8 as Radio, a9 as Globe, aa as FileTerminal, ab as ChevronsUp, ac as ChevronsDown, ad as FileDiff, ae as History, af as RotateCcw, ag as GitCompareArrows, ah as ArrowUp, ai as ArrowDown, aj as Ellipsis, ak as CodeXml, al as Blocks, am as MousePointerClick, an as FileText, ao as Rows3, ap as Columns2 } from "../_libs/lucide-react.mjs";
12
+ import { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, R as RefreshCw, c as Trash2, S as Search, U as Upload, D as Download, F as FileBraces, P as Plus, A as ArrowLeft, d as ChevronUp, e as Copy, f as ChevronsLeft, g as ChevronLeft, h as ChevronRight, i as ChevronsRight, j as Clapperboard, k as Flag, l as Siren, m as Timer, G as Gauge, L as LoaderCircle, n as Layers, o as Settings, M as Monitor, p as Scan, q as CircleAlert, r as Terminal, s as ArrowUpRight, t as ArrowDownRight, u as Cpu, v as FolderOpen, B as Bell, w as Clock3, x as ShieldAlert, E as EyeOff, y as Eye, z as ExternalLink, H as RotateCw, J as Clock, K as Pencil, N as MessageSquare, Z as Zap, W as Wrench, O as Brain, Q as Minus, V as CircleCheckBig, Y as CircleQuestionMark, _ as Server, $ as Lock, a0 as Wifi, a1 as WifiOff, a2 as CircleX, a3 as ShieldCheck, a4 as Save, a5 as FileSearch, a6 as CloudUpload, a7 as OctagonAlert, a8 as Radio, a9 as Globe, aa as FileTerminal, ab as ChevronsUp, ac as ChevronsDown, ad as FileDiff, ae as History, af as RotateCcw, ag as GitCompareArrows, ah as ArrowUp, ai as ArrowDown, aj as Ellipsis, ak as CodeXml, al as Blocks, am as MousePointerClick, an as FileText, ao as Rows3, ap 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
14
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
15
15
  import { R as Root2$1, L as List, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
@@ -1650,7 +1650,7 @@ function ConversationHeader({
1650
1650
  type: "button",
1651
1651
  onClick: handleClearClick,
1652
1652
  "aria-label": `Clear group (${totalCalls} request${totalCalls !== 1 ? "s" : ""})`,
1653
- title: "Clear this group",
1653
+ title: "Clear this group from the current view",
1654
1654
  className: HEADER_ACTION_BUTTON_CLASS$1,
1655
1655
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" })
1656
1656
  }
@@ -1677,8 +1677,8 @@ function ConversationHeader({
1677
1677
  {
1678
1678
  open: confirmOpen,
1679
1679
  onOpenChange: setConfirmOpen,
1680
- title: "Clear this group?",
1681
- description: `This will remove ${totalCalls} request${totalCalls !== 1 ? "s" : ""} from this conversation. This action cannot be undone.`,
1680
+ title: "Clear this group from the current view?",
1681
+ description: `This will hide ${totalCalls} request${totalCalls !== 1 ? "s" : ""} from the current view. Stored logs remain reloadable.`,
1682
1682
  confirmLabel: "Clear",
1683
1683
  variant: "destructive",
1684
1684
  onConfirm: () => {
@@ -2302,27 +2302,27 @@ function useCopyFeedback(text) {
2302
2302
  return { copied, copy };
2303
2303
  }
2304
2304
  const LazyCompareDrawer = reactExports.lazy(
2305
- () => import("./CompareDrawer-ABv-8FMB.mjs").then((m) => ({ default: m.CompareDrawer }))
2305
+ () => import("./CompareDrawer-DAYrKHKe.mjs").then((m) => ({ default: m.CompareDrawer }))
2306
2306
  );
2307
2307
  const LazyReplayDialog = reactExports.lazy(
2308
- () => import("./ReplayDialog-DD31ftzG.mjs").then((m) => ({ default: m.ReplayDialog }))
2308
+ () => import("./ReplayDialog-CdKvdXxk.mjs").then((m) => ({ default: m.ReplayDialog }))
2309
2309
  );
2310
2310
  const LazyRequestAnatomy = reactExports.lazy(
2311
- () => import("./RequestAnatomy-54kNHAjH.mjs").then((m) => ({ default: m.RequestAnatomy }))
2311
+ () => import("./RequestAnatomy-BtmTUq7O.mjs").then((m) => ({ default: m.RequestAnatomy }))
2312
2312
  );
2313
2313
  const LazyResponseView = reactExports.lazy(
2314
- () => import("./ResponseView-CRnibtlX.mjs").then((m) => ({ default: m.ResponseView }))
2314
+ () => import("./ResponseView-DuD_z5hd.mjs").then((m) => ({ default: m.ResponseView }))
2315
2315
  );
2316
2316
  const LazyStreamingChunkSequence = reactExports.lazy(
2317
- () => import("./StreamingChunkSequence-Cb5WGfMm.mjs").then((m) => ({
2317
+ () => import("./StreamingChunkSequence-DpIV2wt_.mjs").then((m) => ({
2318
2318
  default: m.StreamingChunkSequence
2319
2319
  }))
2320
2320
  );
2321
2321
  const LazyJsonViewer = reactExports.lazy(
2322
- () => import("./json-viewer-DB5188I7.mjs").then((m) => ({ default: m.JsonViewer }))
2322
+ () => import("./json-viewer-gcNJEB7y.mjs").then((m) => ({ default: m.JsonViewer }))
2323
2323
  );
2324
2324
  const LazyJsonViewerFromString = reactExports.lazy(
2325
- () => import("./json-viewer-DB5188I7.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2325
+ () => import("./json-viewer-gcNJEB7y.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2326
2326
  );
2327
2327
  const HIGHLIGHT_DURATION_MS = 1200;
2328
2328
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -8327,6 +8327,40 @@ function ProviderEndpointSummary({
8327
8327
  children
8328
8328
  ] });
8329
8329
  }
8330
+ function providerHasEndpoint(value) {
8331
+ return value !== void 0 && value !== "";
8332
+ }
8333
+ function providerTestTargets(provider) {
8334
+ const targets = [];
8335
+ if (providerHasEndpoint(provider.anthropicBaseUrl)) {
8336
+ targets.push("Anthropic");
8337
+ }
8338
+ if (providerHasEndpoint(provider.openaiBaseUrl)) {
8339
+ targets.push("Chat");
8340
+ }
8341
+ if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
8342
+ targets.push("Responses");
8343
+ }
8344
+ return targets;
8345
+ }
8346
+ function ProviderTestCoverage({ provider }) {
8347
+ const targets = providerTestTargets(provider);
8348
+ if (targets.length === 0) {
8349
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Test requires at least one configured endpoint." });
8350
+ }
8351
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
8352
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Test covers" }),
8353
+ targets.map((target) => /* @__PURE__ */ jsxRuntimeExports.jsx(
8354
+ "span",
8355
+ {
8356
+ className: "rounded border border-border/70 bg-muted/25 px-1.5 py-0.5 text-foreground/80",
8357
+ children: target
8358
+ },
8359
+ target
8360
+ )),
8361
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "non-stream + stream" })
8362
+ ] });
8363
+ }
8330
8364
  function getErrorIcon(type) {
8331
8365
  const iconProps = { className: "size-3", strokeWidth: 2 };
8332
8366
  switch (type) {
@@ -8650,30 +8684,30 @@ function ProviderCard({
8650
8684
  }
8651
8685
  )
8652
8686
  ] }),
8653
- provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx(
8687
+ providerHasEndpoint(provider.anthropicBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(
8654
8688
  ProviderEndpointSummary,
8655
8689
  {
8656
- label: "Anthropic",
8690
+ label: "Test: Anthropic",
8657
8691
  baseUrl: provider.anthropicBaseUrl,
8658
8692
  endpoint: PATH_V1_MESSAGES,
8659
8693
  providerName: provider.name,
8660
8694
  children: testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.anthropic })
8661
8695
  }
8662
8696
  ),
8663
- provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx(
8697
+ providerHasEndpoint(provider.openaiBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(
8664
8698
  ProviderEndpointSummary,
8665
8699
  {
8666
- label: "OpenAI Chat",
8700
+ label: "Test: Chat",
8667
8701
  baseUrl: provider.openaiBaseUrl,
8668
8702
  endpoint: PATH_V1_CHAT_COMPLETIONS,
8669
8703
  providerName: provider.name,
8670
8704
  children: testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.openaiChat })
8671
8705
  }
8672
8706
  ),
8673
- provider.openaiResponsesBaseUrl !== void 0 && provider.openaiResponsesBaseUrl !== "" && /* @__PURE__ */ jsxRuntimeExports.jsx(
8707
+ providerHasEndpoint(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(
8674
8708
  ProviderEndpointSummary,
8675
8709
  {
8676
- label: "OpenAI Responses",
8710
+ label: "Test: Responses",
8677
8711
  baseUrl: provider.openaiResponsesBaseUrl,
8678
8712
  endpoint: PATH_V1_RESPONSES,
8679
8713
  providerName: provider.name,
@@ -8717,49 +8751,52 @@ function ProviderCard({
8717
8751
  ] }, modelName)) })
8718
8752
  ] }) })
8719
8753
  ] }),
8720
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 pt-1 border-t", children: [
8721
- onTest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
8722
- Button,
8723
- {
8724
- variant: "outline",
8725
- size: "sm",
8726
- onClick: () => onTest(provider.id),
8727
- className: "text-xs h-7 gap-1",
8728
- disabled: isTesting ?? false,
8729
- children: [
8730
- /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: `size-3 ${isTesting ?? false ? "animate-spin" : ""}` }),
8731
- isTesting ?? false ? testingTimeLeft !== void 0 ? `Testing (${testingTimeLeft}s)` : "Testing..." : "Test"
8732
- ]
8733
- }
8734
- ),
8735
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
8736
- Button,
8737
- {
8738
- variant: "outline",
8739
- size: "sm",
8740
- onClick: () => onEdit(provider),
8741
- className: "text-xs h-7 gap-1",
8742
- disabled: isTesting ?? false,
8743
- children: [
8744
- /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
8745
- "Edit"
8746
- ]
8747
- }
8748
- ),
8749
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
8750
- Button,
8751
- {
8752
- variant: "outline",
8753
- size: "sm",
8754
- onClick: () => onDelete(provider.id),
8755
- className: "text-xs h-7 gap-1 text-destructive hover:text-destructive",
8756
- disabled: isTesting ?? false,
8757
- children: [
8758
- /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3" }),
8759
- "Delete"
8760
- ]
8761
- }
8762
- )
8754
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 pt-1 border-t sm:flex-row sm:items-center sm:justify-between", children: [
8755
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestCoverage, { provider }),
8756
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
8757
+ onTest !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
8758
+ Button,
8759
+ {
8760
+ variant: "outline",
8761
+ size: "sm",
8762
+ onClick: () => onTest(provider.id),
8763
+ className: "text-xs h-7 gap-1",
8764
+ disabled: isTesting ?? false,
8765
+ children: [
8766
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: `size-3 ${isTesting ?? false ? "animate-spin" : ""}` }),
8767
+ isTesting ?? false ? testingTimeLeft !== void 0 ? `Testing (${testingTimeLeft}s)` : "Testing..." : "Test"
8768
+ ]
8769
+ }
8770
+ ),
8771
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
8772
+ Button,
8773
+ {
8774
+ variant: "outline",
8775
+ size: "sm",
8776
+ onClick: () => onEdit(provider),
8777
+ className: "text-xs h-7 gap-1",
8778
+ disabled: isTesting ?? false,
8779
+ children: [
8780
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Pencil, { className: "size-3" }),
8781
+ "Edit"
8782
+ ]
8783
+ }
8784
+ ),
8785
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
8786
+ Button,
8787
+ {
8788
+ variant: "outline",
8789
+ size: "sm",
8790
+ onClick: () => onDelete(provider.id),
8791
+ className: "text-xs h-7 gap-1 text-destructive hover:text-destructive",
8792
+ disabled: isTesting ?? false,
8793
+ children: [
8794
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3" }),
8795
+ "Delete"
8796
+ ]
8797
+ }
8798
+ )
8799
+ ] })
8763
8800
  ] })
8764
8801
  ]
8765
8802
  }
@@ -13704,6 +13741,7 @@ function ProxyViewer({
13704
13741
  onSessionChange,
13705
13742
  onModelChange,
13706
13743
  onClearAll,
13744
+ onReloadSession,
13707
13745
  onClearGroup,
13708
13746
  onNotify,
13709
13747
  onImportLogs,
@@ -13829,6 +13867,8 @@ function ProxyViewer({
13829
13867
  const comparisonPredecessors = reactExports.useMemo(() => buildValidPredecessors(groups), [groups]);
13830
13868
  const exportActionVisibility = getExportActionVisibility(captureMode, logs.length);
13831
13869
  const isPinnedSessionPage = pinnedSessionId !== void 0;
13870
+ const canClearCurrentView = logs.length > 0;
13871
+ const reloadSessionLabel = logs.length === 0 ? "Load" : "Reload";
13832
13872
  const sessionInputValue = selectedSession === "__all__" ? "" : selectedSession;
13833
13873
  const sessionSelectValue = selectedSession === "__all__" || sessions.includes(selectedSession) ? selectedSession : "__typed__";
13834
13874
  const activeScopeLabel = hasSessionContext && sessionContextScope !== null ? truncateSessionId(sessionContextScope.label) : selectedSession === "__all__" ? "All sessions" : truncateSessionId(selectedSession);
@@ -13864,26 +13904,62 @@ function ProxyViewer({
13864
13904
  [onSessionChange]
13865
13905
  );
13866
13906
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-background min-h-screen w-full text-foreground", children: [
13867
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid min-h-screen w-full grid-cols-1 lg:grid-cols-[340px_minmax(0,1fr)] xl:grid-cols-[360px_minmax(0,1fr)]", children: [
13868
- /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "bg-background/95 border-b border-white/[0.045] lg:sticky lg:top-0 lg:h-screen lg:overflow-y-auto lg:border-b-0 lg:border-r lg:border-white/[0.055]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 p-4", children: [
13907
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid min-h-screen w-full grid-cols-1 lg:grid-cols-[minmax(0,1fr)_340px] xl:grid-cols-[minmax(0,1fr)_360px]", children: [
13908
+ /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "bg-background/95 order-1 border-b border-white/[0.045] lg:sticky lg:top-0 lg:order-2 lg:h-screen lg:overflow-y-auto lg:border-b-0 lg:border-l lg:border-white/[0.055]", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 p-4", children: [
13869
13909
  /* @__PURE__ */ jsxRuntimeExports.jsx(BrandHeader, { compact: true, crabEntrancePhase }),
13870
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarPanel, { label: "Session", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-3 gap-2", children: [
13871
- /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMetric, { label: "Requests", value: logs.length }),
13872
- /* @__PURE__ */ jsxRuntimeExports.jsx(
13873
- SidebarMetric,
13874
- {
13875
- label: "Input",
13876
- value: formatTokens(hasSessionContext ? sessionContextTotalIn : totalIn)
13877
- }
13878
- ),
13879
- /* @__PURE__ */ jsxRuntimeExports.jsx(
13880
- SidebarMetric,
13881
- {
13882
- label: "Output",
13883
- value: formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)
13884
- }
13885
- )
13886
- ] }) }),
13910
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarPanel, { label: "Session", children: [
13911
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
13912
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMetric, { label: "Requests", value: logs.length }),
13913
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarMetric, { label: "Sessions", value: sessions.length }),
13914
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13915
+ SidebarMetric,
13916
+ {
13917
+ label: "Input",
13918
+ value: formatTokens(hasSessionContext ? sessionContextTotalIn : totalIn)
13919
+ }
13920
+ ),
13921
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
13922
+ SidebarMetric,
13923
+ {
13924
+ label: "Output",
13925
+ value: formatTokens(hasSessionContext ? sessionContextTotalOut : totalOut)
13926
+ }
13927
+ )
13928
+ ] }),
13929
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2", children: [
13930
+ onReloadSession !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
13931
+ "button",
13932
+ {
13933
+ type: "button",
13934
+ onClick: onReloadSession,
13935
+ disabled: isLoading,
13936
+ className: "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
13937
+ title: "Load stored logs for this session",
13938
+ children: [
13939
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: cn("size-3.5", isLoading && "animate-spin") }),
13940
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: reloadSessionLabel })
13941
+ ]
13942
+ }
13943
+ ),
13944
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
13945
+ "button",
13946
+ {
13947
+ type: "button",
13948
+ onClick: onClearAll,
13949
+ disabled: !canClearCurrentView,
13950
+ className: cn(
13951
+ "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 min-w-0 items-center justify-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
13952
+ onReloadSession === void 0 && "col-span-2"
13953
+ ),
13954
+ title: "Clear the current view. Stored logs remain reloadable.",
13955
+ children: [
13956
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
13957
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear view" })
13958
+ ]
13959
+ }
13960
+ )
13961
+ ] })
13962
+ ] }),
13887
13963
  /* @__PURE__ */ jsxRuntimeExports.jsx(SidebarPanel, { label: "Workspace", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-black/20 rounded-md px-3 py-2 shadow-[inset_0_1px_0_rgba(255,255,255,0.035)]", children: [
13888
13964
  /* @__PURE__ */ jsxRuntimeExports.jsx(
13889
13965
  "div",
@@ -13957,7 +14033,7 @@ function ProxyViewer({
13957
14033
  ] })
13958
14034
  ] }) })
13959
14035
  ] }) }),
13960
- /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "min-w-0", children: [
14036
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("main", { className: "order-2 min-w-0 lg:order-1", children: [
13961
14037
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-background/95 shadow-[0_12px_36px_rgba(0,0,0,0.18),inset_0_-1px_0_rgba(255,255,255,0.045)] sticky top-0 z-30 px-4 py-3 lg:px-5", children: [
13962
14038
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
13963
14039
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-[180px] flex-1", children: [
@@ -14022,16 +14098,31 @@ function ProxyViewer({
14022
14098
  ] })
14023
14099
  }
14024
14100
  ),
14101
+ onReloadSession !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
14102
+ "button",
14103
+ {
14104
+ type: "button",
14105
+ onClick: onReloadSession,
14106
+ disabled: isLoading,
14107
+ className: "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
14108
+ title: "Reload this session from stored logs",
14109
+ children: [
14110
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: cn("size-3.5", isLoading && "animate-spin") }),
14111
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: reloadSessionLabel })
14112
+ ]
14113
+ }
14114
+ ),
14025
14115
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
14026
14116
  "button",
14027
14117
  {
14028
14118
  type: "button",
14029
14119
  onClick: onClearAll,
14030
- className: "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground",
14031
- title: "Clear all logs",
14120
+ disabled: !canClearCurrentView,
14121
+ className: "bg-white/[0.035] hover:bg-white/[0.065] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
14122
+ title: "Clear the current view. Stored logs remain reloadable.",
14032
14123
  children: [
14033
14124
  /* @__PURE__ */ jsxRuntimeExports.jsx(Trash2, { className: "size-3.5" }),
14034
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear" })
14125
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Clear view" })
14035
14126
  ]
14036
14127
  }
14037
14128
  )
@@ -14044,24 +14135,45 @@ function ProxyViewer({
14044
14135
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-4 lg:px-5", children: [
14045
14136
  /* @__PURE__ */ jsxRuntimeExports.jsx(SessionOpeningSlate, { stats: slateStats }),
14046
14137
  logs.length === 0 ? selectedSession !== "__all__" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_18px_52px_rgba(0,0,0,0.18)] mx-auto max-w-xl space-y-4 rounded-[8px] px-4 py-8", children: [
14047
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: isLoading ? "Loading session data..." : "Session not found" }),
14138
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-medium", children: isLoading ? "Loading session data..." : "No logs loaded for this session" }),
14048
14139
  /* @__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) }),
14049
14140
  sessionMemberships.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-left", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SessionMembershipPanel, { memberships: sessionMemberships }) }),
14050
- !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-xs", children: [
14051
- "This session may have been cleared or never existed.",
14052
- " ",
14053
- hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx("a", { href: "/", className: "underline transition-colors hover:text-foreground", children: "Back to all sessions" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
14054
- "button",
14055
- {
14056
- type: "button",
14057
- onClick: () => onSessionChange("__all__"),
14058
- className: "underline transition-colors hover:text-foreground",
14059
- children: "Show all sessions"
14060
- }
14061
- )
14141
+ !isLoading && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-3 text-xs", children: [
14142
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Stored logs may still be available." }),
14143
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-center justify-center gap-2", children: [
14144
+ onReloadSession !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
14145
+ "button",
14146
+ {
14147
+ type: "button",
14148
+ onClick: onReloadSession,
14149
+ className: "bg-white/[0.045] hover:bg-white/[0.075] inline-flex h-8 items-center gap-1.5 rounded-md px-3 text-xs text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] transition-colors hover:text-foreground",
14150
+ children: [
14151
+ /* @__PURE__ */ jsxRuntimeExports.jsx(RefreshCw, { className: "size-3.5" }),
14152
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Load session" })
14153
+ ]
14154
+ }
14155
+ ),
14156
+ hideSessionFilter ? /* @__PURE__ */ jsxRuntimeExports.jsx(
14157
+ "a",
14158
+ {
14159
+ href: "/",
14160
+ className: "inline-flex h-8 items-center rounded-md px-3 text-xs text-muted-foreground underline transition-colors hover:text-foreground",
14161
+ children: "Back to all sessions"
14162
+ }
14163
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
14164
+ "button",
14165
+ {
14166
+ type: "button",
14167
+ onClick: () => onSessionChange("__all__"),
14168
+ className: "inline-flex h-8 items-center rounded-md px-3 text-xs text-muted-foreground underline transition-colors hover:text-foreground",
14169
+ children: "Show all sessions"
14170
+ }
14171
+ )
14172
+ ] })
14062
14173
  ] })
14063
14174
  ] }) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_18px_52px_rgba(0,0,0,0.18)] mx-auto max-w-2xl space-y-4 rounded-[8px] px-4 py-8", children: [
14064
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No requests captured yet." }),
14175
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm", children: "No logs loaded in the current view." }),
14176
+ sessions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mx-auto max-w-xl text-xs leading-relaxed text-muted-foreground", children: "Pick a session from the filter panel to load stored logs on demand." }),
14065
14177
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center gap-2", children: [
14066
14178
  /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "ANTHROPIC_BASE_URL=http://localhost:9527/proxy <your-tool>" }),
14067
14179
  /* @__PURE__ */ jsxRuntimeExports.jsx(CopyableCommand, { command: "OPENAI_BASE_URL=http://localhost:9527/proxy <your-tool>" })
@@ -14127,6 +14239,7 @@ const LogCursorPageSchema = object({
14127
14239
  oldestLogId: number().int().positive().nullable(),
14128
14240
  newestLogId: number().int().positive().nullable()
14129
14241
  });
14242
+ const SessionsResponseSchema = array(string());
14130
14243
  function buildSessionMembershipEvidence(groups, sessionId) {
14131
14244
  const memberships = [];
14132
14245
  for (const group of groups) {
@@ -14172,6 +14285,8 @@ const DEBOUNCE_MS = 100;
14172
14285
  const HASH_SCROLL_ATTEMPTS = 12;
14173
14286
  const HASH_HIGHLIGHT_MS = 1800;
14174
14287
  const SESSION_PAGE_LIMIT = 100;
14288
+ const SESSION_RELOAD_PAGE_LIMIT = 500;
14289
+ const MAX_SESSION_RELOAD_PAGES = 200;
14175
14290
  function buildLogsStreamUrl(sessionId) {
14176
14291
  const params = new URLSearchParams();
14177
14292
  if (sessionId !== void 0) {
@@ -14181,11 +14296,11 @@ function buildLogsStreamUrl(sessionId) {
14181
14296
  const query = params.toString();
14182
14297
  return query.length > 0 ? `/api/logs/stream?${query}` : "/api/logs/stream";
14183
14298
  }
14184
- function buildSessionLogsPageUrl(sessionId, request) {
14299
+ function buildSessionLogsPageUrl(sessionId, request, limit = SESSION_PAGE_LIMIT) {
14185
14300
  const params = new URLSearchParams({
14186
14301
  cursor: "1",
14187
14302
  compact: "1",
14188
- limit: String(SESSION_PAGE_LIMIT),
14303
+ limit: String(limit),
14189
14304
  sessionId
14190
14305
  });
14191
14306
  switch (request.kind) {
@@ -14220,12 +14335,23 @@ function mergeLogsById(previousLogs, incomingLogs) {
14220
14335
  }
14221
14336
  return [...byId.values()].sort((left, right) => left.id - right.id);
14222
14337
  }
14338
+ function mergeSessionIds(previousSessions, incomingSessions) {
14339
+ const sessions = /* @__PURE__ */ new Set();
14340
+ for (const session of previousSessions) {
14341
+ if (session !== "") sessions.add(session);
14342
+ }
14343
+ for (const session of incomingSessions) {
14344
+ if (session !== "") sessions.add(session);
14345
+ }
14346
+ return [...sessions].sort();
14347
+ }
14223
14348
  function ProxyViewerContainer({
14224
14349
  initialSessionId
14225
14350
  } = {}) {
14226
14351
  const [allLogs, setAllLogs] = reactExports.useState([]);
14227
14352
  const [selectedSession, setSelectedSession] = reactExports.useState(initialSessionId ?? "__all__");
14228
14353
  const [selectedModel, setSelectedModel] = reactExports.useState("__all__");
14354
+ const [knownSessions, setKnownSessions] = reactExports.useState([]);
14229
14355
  const { notice, showToast, dismissToast } = useTransientToast();
14230
14356
  const [streamInitialized, setStreamInitialized] = reactExports.useState(initialSessionId === void 0);
14231
14357
  const [logPage, setLogPage] = reactExports.useState(null);
@@ -14249,8 +14375,17 @@ function ProxyViewerContainer({
14249
14375
  () => filterLogs(allLogs, selectedSession, selectedModel),
14250
14376
  [allLogs, selectedSession, selectedModel]
14251
14377
  );
14252
- const sessions = reactExports.useMemo(() => extractSessions(allLogs), [allLogs]);
14378
+ const sessions = reactExports.useMemo(
14379
+ () => mergeSessionIds(knownSessions, extractSessions(allLogs)),
14380
+ [allLogs, knownSessions]
14381
+ );
14253
14382
  const models = reactExports.useMemo(() => extractModels(allLogs), [allLogs]);
14383
+ const refreshSessions = reactExports.useCallback(() => {
14384
+ void fetchJson("/api/sessions", SessionsResponseSchema).then((sessionIds) => {
14385
+ setKnownSessions((prev) => mergeSessionIds(prev, sessionIds));
14386
+ }).catch(() => {
14387
+ });
14388
+ }, []);
14254
14389
  const flushUpdates = reactExports.useCallback(() => {
14255
14390
  flushTimerRef.current = null;
14256
14391
  const updates = pendingUpdatesRef.current;
@@ -14301,9 +14436,14 @@ function ProxyViewerContainer({
14301
14436
  logIndexRef.current = buildLogIndex(nextLogs);
14302
14437
  return nextLogs;
14303
14438
  });
14439
+ setKnownSessions((prev) => mergeSessionIds(prev, extractSessions(update.logs)));
14304
14440
  setStreamInitialized(true);
14305
14441
  dismissToast();
14306
14442
  } else if (update.type === "update") {
14443
+ setKnownSessions((prev) => {
14444
+ if (update.log.sessionId === null || update.log.sessionId === "") return prev;
14445
+ return mergeSessionIds(prev, [update.log.sessionId]);
14446
+ });
14307
14447
  scheduleUpdate(update.log);
14308
14448
  }
14309
14449
  } catch {
@@ -14346,6 +14486,56 @@ function ProxyViewerContainer({
14346
14486
  },
14347
14487
  [dismissToast, initialSessionId, showErrorToast]
14348
14488
  );
14489
+ const reloadSessionLogs = reactExports.useCallback(
14490
+ (sessionId) => {
14491
+ const requestId = sessionPageRequestIdRef.current + 1;
14492
+ sessionPageRequestIdRef.current = requestId;
14493
+ setSessionPageLoading(true);
14494
+ dismissToast();
14495
+ void (async () => {
14496
+ const collected = [];
14497
+ let page = await fetchJson(
14498
+ buildSessionLogsPageUrl(sessionId, { kind: "oldest" }, SESSION_RELOAD_PAGE_LIMIT),
14499
+ LogCursorPageSchema
14500
+ );
14501
+ collected.push(...page.logs);
14502
+ let loadedPages = 1;
14503
+ while (page.hasNewer && page.newestLogId !== null && loadedPages < MAX_SESSION_RELOAD_PAGES) {
14504
+ page = await fetchJson(
14505
+ buildSessionLogsPageUrl(
14506
+ sessionId,
14507
+ { kind: "newer", afterLogId: page.newestLogId },
14508
+ SESSION_RELOAD_PAGE_LIMIT
14509
+ ),
14510
+ LogCursorPageSchema
14511
+ );
14512
+ collected.push(...page.logs);
14513
+ loadedPages += 1;
14514
+ }
14515
+ if (sessionPageRequestIdRef.current !== requestId) return;
14516
+ setAllLogs((prev) => {
14517
+ const next = mergeLogsById(prev, collected);
14518
+ logIndexRef.current = buildLogIndex(next);
14519
+ return next;
14520
+ });
14521
+ setKnownSessions((prev) => mergeSessionIds(prev, [sessionId]));
14522
+ setSessionPageLoading(false);
14523
+ if (page.hasNewer) {
14524
+ showToast({
14525
+ message: `Loaded ${String(collected.length)} logs for this session. More history is available.`,
14526
+ tone: "success"
14527
+ });
14528
+ } else {
14529
+ dismissToast();
14530
+ }
14531
+ })().catch((err) => {
14532
+ if (sessionPageRequestIdRef.current !== requestId) return;
14533
+ setSessionPageLoading(false);
14534
+ showErrorToast(err instanceof Error ? err.message : "Failed to reload session logs");
14535
+ });
14536
+ },
14537
+ [dismissToast, showErrorToast, showToast]
14538
+ );
14349
14539
  reactExports.useEffect(() => {
14350
14540
  if (initialSessionId === void 0) {
14351
14541
  setSessionMemberships([]);
@@ -14367,10 +14557,18 @@ function ProxyViewerContainer({
14367
14557
  cancelled = true;
14368
14558
  };
14369
14559
  }, [initialSessionId]);
14560
+ reactExports.useEffect(() => {
14561
+ refreshSessions();
14562
+ }, [refreshSessions]);
14370
14563
  reactExports.useEffect(() => {
14371
14564
  if (initialSessionId === void 0) return;
14372
14565
  loadSessionPage({ kind: "newest" });
14373
14566
  }, [initialSessionId, loadSessionPage]);
14567
+ reactExports.useEffect(() => {
14568
+ if (initialSessionId !== void 0) return;
14569
+ if (selectedSession === "__all__") return;
14570
+ reloadSessionLogs(selectedSession);
14571
+ }, [initialSessionId, reloadSessionLogs, selectedSession]);
14374
14572
  reactExports.useEffect(() => {
14375
14573
  if (initialSessionId !== void 0) return void 0;
14376
14574
  connectSSE();
@@ -14438,57 +14636,42 @@ function ProxyViewerContainer({
14438
14636
  };
14439
14637
  }, [logs.length]);
14440
14638
  const handleClearAll = reactExports.useCallback(() => {
14441
- if (initialSessionId !== void 0 && allLogs.length === 0) return;
14442
- void (async () => {
14443
- try {
14444
- const body = initialSessionId === void 0 ? void 0 : JSON.stringify({ ids: allLogs.map((log) => log.id) });
14445
- const res = await fetch("/api/logs", {
14446
- method: "DELETE",
14447
- ...body === void 0 ? {} : {
14448
- headers: { "Content-Type": "application/json" },
14449
- body
14450
- }
14451
- });
14452
- if (!res.ok) {
14453
- showErrorToast("Failed to clear logs");
14454
- return;
14455
- }
14456
- logIndexRef.current.clear();
14457
- setAllLogs([]);
14458
- dismissToast();
14459
- } catch (err) {
14460
- showErrorToast(err instanceof Error ? err.message : "Unknown error clearing logs");
14461
- }
14462
- })();
14463
- }, [allLogs, dismissToast, initialSessionId, showErrorToast]);
14639
+ const clearedSessions = extractSessions(allLogs);
14640
+ logIndexRef.current.clear();
14641
+ setAllLogs([]);
14642
+ setLogPage(null);
14643
+ setKnownSessions((prev) => mergeSessionIds(prev, clearedSessions));
14644
+ showToast({
14645
+ message: "Cleared the current view. Stored logs are still reloadable.",
14646
+ tone: "success"
14647
+ });
14648
+ }, [allLogs, showToast]);
14464
14649
  const handleClearGroup = reactExports.useCallback(
14465
14650
  (ids) => {
14466
14651
  if (ids.length === 0) return;
14467
- void (async () => {
14468
- try {
14469
- const res = await fetch("/api/logs", {
14470
- method: "DELETE",
14471
- headers: { "Content-Type": "application/json" },
14472
- body: JSON.stringify({ ids })
14473
- });
14474
- if (!res.ok) {
14475
- showErrorToast("Failed to clear group");
14476
- return;
14477
- }
14478
- const idSet = new Set(ids);
14479
- setAllLogs((prev) => {
14480
- const remaining = prev.filter((l) => !idSet.has(l.id));
14481
- logIndexRef.current = buildLogIndex(remaining);
14482
- return remaining;
14483
- });
14484
- dismissToast();
14485
- } catch (err) {
14486
- showErrorToast(err instanceof Error ? err.message : "Unknown error clearing group");
14487
- }
14488
- })();
14652
+ const idSet = new Set(ids);
14653
+ setAllLogs((prev) => {
14654
+ const removed = prev.filter((log) => idSet.has(log.id));
14655
+ const remaining = prev.filter((log) => !idSet.has(log.id));
14656
+ setKnownSessions((known) => mergeSessionIds(known, extractSessions(removed)));
14657
+ logIndexRef.current = buildLogIndex(remaining);
14658
+ return remaining;
14659
+ });
14660
+ showToast({ message: "Cleared this group from the current view.", tone: "success" });
14489
14661
  },
14490
- [dismissToast, showErrorToast]
14662
+ [showToast]
14491
14663
  );
14664
+ const handleReloadSession = reactExports.useCallback(() => {
14665
+ if (initialSessionId !== void 0) {
14666
+ loadSessionPage({ kind: "newest" });
14667
+ return;
14668
+ }
14669
+ if (selectedSession === "__all__") {
14670
+ refreshSessions();
14671
+ return;
14672
+ }
14673
+ reloadSessionLogs(selectedSession);
14674
+ }, [initialSessionId, loadSessionPage, refreshSessions, reloadSessionLogs, selectedSession]);
14492
14675
  const handleImportLogs = reactExports.useCallback(
14493
14676
  async (file) => {
14494
14677
  const formData = new FormData();
@@ -14572,10 +14755,11 @@ function ProxyViewerContainer({
14572
14755
  onSessionChange: setSelectedSession,
14573
14756
  onModelChange: setSelectedModel,
14574
14757
  onClearAll: handleClearAll,
14758
+ onReloadSession: initialSessionId !== void 0 || selectedSession !== "__all__" ? handleReloadSession : void 0,
14575
14759
  onClearGroup: handleClearGroup,
14576
14760
  onNotify: showToast,
14577
14761
  onImportLogs: initialSessionId === void 0 ? handleImportLogs : void 0,
14578
- isLoading: initialSessionId === void 0 ? !streamInitialized : sessionPageLoading,
14762
+ isLoading: initialSessionId === void 0 ? !streamInitialized || sessionPageLoading : sessionPageLoading,
14579
14763
  pagination,
14580
14764
  sessionMemberships,
14581
14765
  viewMode,