@tonyclaw/agent-inspector 3.0.22 → 3.0.24

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 (47) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-CaDAbX9q.js → CompareDrawer-BC52FYwn.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-WuChOrSS.js +127 -0
  4. package/.output/public/assets/{ReplayDialog-C5kb1Bj4.js → ReplayDialog-BcKsarKt.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-CbPNLnlL.js → RequestAnatomy-DR0uGpCj.js} +1 -1
  6. package/.output/public/assets/{ResponseView-DyzW0Zfo.js → ResponseView-kxAAPfmn.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-Cl8zJu0w.js → StreamingChunkSequence-YQR0PjQj.js} +1 -1
  8. package/.output/public/assets/{_sessionId-BeN8-5Ho.js → _sessionId-DRl-Y9om.js} +1 -1
  9. package/.output/public/assets/{index-BCHWOmF2.js → index-CQbrc-hi.js} +71 -7
  10. package/.output/public/assets/{index-DcFtdKF3.js → index-CcSIBI4L.js} +1 -1
  11. package/.output/public/assets/index-DQeXi4Rv.css +1 -0
  12. package/.output/public/assets/{index-Pz9T7cS9.js → index-ZrMgyEZV.js} +1 -1
  13. package/.output/public/assets/{json-viewer-PNvFrOUw.js → json-viewer-CS5pwnEA.js} +1 -1
  14. package/.output/server/_libs/lucide-react.mjs +266 -234
  15. package/.output/server/{_sessionId-CIvbZsmt.mjs → _sessionId-BLk2xPkR.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-CtWPZP5T.mjs → CompareDrawer-Cw7Dseo3.mjs} +3 -3
  17. package/.output/server/_ssr/{ProxyViewerContainer-Dnm-Hup3.mjs → ProxyViewerContainer-D8SB0zyQ.mjs} +1124 -877
  18. package/.output/server/_ssr/{ReplayDialog-BYii7Q2m.mjs → ReplayDialog-B-YMhZkE.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-CC6brPZr.mjs → RequestAnatomy-a9d8UTYZ.mjs} +2 -2
  20. package/.output/server/_ssr/{ResponseView-CwwkL_0d.mjs → ResponseView-PzaZS4CN.mjs} +3 -3
  21. package/.output/server/_ssr/{StreamingChunkSequence-CFsJzBLS.mjs → StreamingChunkSequence-C21_TCXk.mjs} +2 -2
  22. package/.output/server/_ssr/{index-B-Mm06YB.mjs → index-CrCeNP7H.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-D2MGmYZ0.mjs → json-viewer-Di2gxs9H.mjs} +3 -3
  25. package/.output/server/_ssr/{router-BwvN4eYE.mjs → router-BJzdUbcH.mjs} +296 -91
  26. package/.output/server/{_tanstack-start-manifest_v-Dpk8cafb.mjs → _tanstack-start-manifest_v-CGBTdAYC.mjs} +1 -1
  27. package/.output/server/index.mjs +64 -64
  28. package/package.json +6 -2
  29. package/src/components/ProxyViewer.tsx +136 -460
  30. package/src/components/ProxyViewerContainer.tsx +5 -47
  31. package/src/components/alerts/AlertsDialog.tsx +6 -2
  32. package/src/components/providers/ProviderCard.tsx +20 -579
  33. package/src/components/providers/ProviderTestResultsView.tsx +732 -0
  34. package/src/components/proxy-viewer/LogEntry.tsx +5 -0
  35. package/src/components/proxy-viewer/LogEntryHeader.tsx +7 -0
  36. package/src/components/proxy-viewer/ToolTraceEvents.tsx +33 -36
  37. package/src/components/proxy-viewer/TurnGroup.tsx +1 -5
  38. package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +46 -0
  39. package/src/components/proxy-viewer/proxyViewerLogic.ts +469 -0
  40. package/src/lib/themeMode.ts +45 -0
  41. package/src/lib/uiScale.ts +112 -0
  42. package/src/lib/useThemeMode.ts +40 -0
  43. package/src/lib/useUiScale.ts +52 -0
  44. package/src/routes/__root.tsx +78 -1
  45. package/styles/globals.css +305 -23
  46. package/.output/public/assets/ProxyViewerContainer-7ugZIMW0.js +0 -127
  47. package/.output/public/assets/index-SqCROi0s.css +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, R as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, q as DeleteInspectorGroupsResponseSchema, 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, m as extractStopReason, p as packageJson, P as ProviderConfigSchema, i as apiFormatForPath, r as GroupEvidenceExportResultSchema, t as DeleteInspectorGroupResponseSchema, N as PiAgentResponseSchema, l as isTurnBoundary, n as AlertSummarySchema, o as AlertListResponseSchema, I as InspectorGroupsListResponseSchema, G as GroupEvidenceReadResponseSchema, E as createPendingProviderTestResults, F as ProviderTestResultsSchema, H as createFailedProviderTestResults, M as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, J as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, T as TimeDisplayFormatSchema, v as maskApiKey, x as PATH_V1_MESSAGES, y as PATH_V1_CHAT_COMPLETIONS, z as PATH_V1_RESPONSES, u as providerHasContextMetadata, f as parseOpenAIResponse, g as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, h as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, e as AnthropicRequestSchema, s as safeGetOwnProperty, Q as resolveProviderContextWindow, j as getSessionPath, w as findProviderModelMetadata, B as previewUpstreamUrl, K as KnowledgeCandidateSchema, S as isPlainRecord, k as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-BwvN4eYE.mjs";
2
+ import { C as CapturedLogSchema, z as DeleteInspectorGroupsResponseSchema, 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, g as readStoredUiScalePreference, h as persistUiScalePreference, i as applyUiScalePreference, w as extractStopReason, p as packageJson, P as ProviderConfigSchema, U as UI_SCALE_STORAGE_KEY, j as normalizeUiScalePreference, q as apiFormatForPath, B as GroupEvidenceExportResultSchema, E as DeleteInspectorGroupResponseSchema, $ as PiAgentResponseSchema, v as isTurnBoundary, r as readStoredThemeMode, e as applyThemeMode, f as persistThemeMode, a2 as formatUiScale, x as AlertSummarySchema, y as AlertListResponseSchema, I as InspectorGroupsListResponseSchema, G as GroupEvidenceReadResponseSchema, V as createPendingProviderTestResults, W as ProviderTestResultsSchema, X as createFailedProviderTestResults, Y as MAX_SLOW_RESPONSE_THRESHOLD_SECONDS, Z as MAX_PROVIDER_TEST_TIMEOUT_SECONDS, _ as TimeDisplayFormatSchema, T as THEME_MODE_STORAGE_KEY, n as normalizeThemeMode, H as maskApiKey, M as PATH_V1_MESSAGES, N as PATH_V1_CHAT_COMPLETIONS, Q as PATH_V1_RESPONSES, F as providerHasContextMetadata, l as parseOpenAIResponse, m as parseOpenAIResponsesResponse, O as OpenAIRequestSchema, o as OpenAIResponsesRequestSchema, A as AnthropicResponseSchema$1, k as AnthropicRequestSchema, s as safeGetOwnProperty, a0 as resolveProviderContextWindow, t as getSessionPath, J as findProviderModelMetadata, S as previewUpstreamUrl, K as KnowledgeCandidateSchema, a1 as isPlainRecord, u as stripClaudeCodeBillingHeader, L as LogBodyChunkSchema } from "./router-BJzdUbcH.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";
@@ -7,7 +7,7 @@ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { c as cva } from "../_libs/class-variance-authority.mjs";
8
8
  import { u as useWindowVirtualizer, a as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
9
9
  import { d as diffJson, a as diffLines } from "../_libs/diff.mjs";
10
- import { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, P as PanelRightOpen, c as PanelRightClose, S as Search, U as Upload, D as Download, F as FileBraces, R as RefreshCw, d as Trash2, e as Plus, B as BrainCircuit, L as LoaderCircle, f as Send, A as ArrowLeft, g as ChevronUp, h as Copy, i as ChevronsLeft, j as ChevronLeft, k as ChevronRight, l as ChevronsRight, m as Clapperboard, n as Flag, o as Siren, p as Timer, G as Gauge, q as Layers, r as FlaskConical, s as Sparkles, t as Activity, u as Beaker, N as Network, v as Terminal, w as Settings, M as Monitor, x as Rocket, y as PackageCheck, H as History, z as Scan, E as CircleAlert, J as ArrowUpRight, K as ArrowDownRight, O as Cpu, Q as FolderOpen, V as Bell, W as Clock3, Y as ShieldAlert, Z as EyeOff, _ as Eye, $ as ExternalLink, a0 as Clock, a1 as RotateCw, a2 as Pencil, a3 as MessageSquare, a4 as Zap, a5 as Wrench, a6 as Brain, a7 as CircleCheckBig, a8 as Minus, a9 as CircleQuestionMark, aa as Server, ab as Lock, ac as Wifi, ad as WifiOff, ae as CircleX, af as ShieldCheck, ag as Save, ah as FileSearch, ai as CloudUpload, aj as OctagonAlert, ak as Radio, al as Globe, am as FileTerminal, an as ChevronsUp, ao as ChevronsDown, ap as FileDiff, aq as RotateCcw, ar as GitCompareArrows, as as ArrowUp, at as ArrowDown, au as Ellipsis, av as CodeXml, aw as Blocks, ax as MousePointerClick, ay as FileText, az as Rows3, aA as Columns2 } from "../_libs/lucide-react.mjs";
10
+ import { T as TriangleAlert, C as CircleCheck, X, I as Info, a as ChevronDown, b as Check, P as PanelRightOpen, c as PanelRightClose, S as Search, U as Upload, D as Download, F as FileBraces, R as RefreshCw, d as Trash2, e as Plus, B as BrainCircuit, L as LoaderCircle, f as Send, A as ArrowLeft, g as ChevronUp, h as Copy, i as ChevronsLeft, j as ChevronLeft, k as ChevronRight, l as ChevronsRight, m as Clapperboard, n as Flag, o as Siren, p as Timer, G as Gauge, q as Sun, E as Eye, M as Moon, r as ALargeSmall, s as Layers, t as FlaskConical, u as Sparkles, v as Activity, w as Beaker, N as Network, x as Terminal, y as Settings, z as Monitor, H as Rocket, J as PackageCheck, K as History, O as Scan, Q as CircleAlert, V as ArrowUpRight, W as ArrowDownRight, Y as Cpu, Z as FolderOpen, _ as Bell, $ as Clock3, a0 as ShieldAlert, a1 as EyeOff, a2 as ExternalLink, a3 as Clock, a4 as RotateCw, a5 as Pencil, a6 as MessageSquare, a7 as Zap, a8 as Wrench, a9 as Brain, aa as CircleCheckBig, ab as Minus, ac as CircleX, ad as ShieldCheck, ae as Save, af as FileSearch, ag as CloudUpload, ah as CircleQuestionMark, ai as Server, aj as Lock, ak as Wifi, al as WifiOff, am as OctagonAlert, an as Radio, ao as Globe, ap as FileTerminal, aq as ChevronsUp, ar as ChevronsDown, as as FileDiff, at as RotateCcw, au as GitCompareArrows, av as ArrowUp, aw as ArrowDown, ax as Ellipsis, ay as CodeXml, az as Blocks, aA as MousePointerClick, aB as FileText, aC as Rows3, aD as Columns2 } from "../_libs/lucide-react.mjs";
11
11
  import { u as union, o as object, e as array, l as literal, n as number, d as boolean, b as string, _ as _enum } from "../_libs/zod.mjs";
12
12
  import { S as Select$1, a as SelectTrigger$1, b as SelectIcon, c as SelectValue$1, d as SelectPortal, e as SelectContent$1, f as SelectViewport, g as SelectItem$1, h as SelectItemIndicator, i as SelectItemText, j as SelectScrollUpButton$1, k as SelectScrollDownButton$1 } from "../_libs/radix-ui__react-select.mjs";
13
13
  import { D as Dialog$1, a as DialogContent$1, b as DialogClose, c as DialogTitle$1, d as DialogDescription$1, e as DialogPortal$1, f as DialogOverlay$1, g as DialogTrigger$1 } from "../_libs/radix-ui__react-dialog.mjs";
@@ -625,6 +625,59 @@ function getStatusCategory(status) {
625
625
  if (status >= 500) return "server_error";
626
626
  return "pending";
627
627
  }
628
+ function useThemeMode() {
629
+ const [themeMode, setThemeModeState] = reactExports.useState(readStoredThemeMode);
630
+ const setThemeMode = reactExports.useCallback((nextThemeMode) => {
631
+ setThemeModeState(nextThemeMode);
632
+ applyThemeMode(nextThemeMode);
633
+ persistThemeMode(nextThemeMode);
634
+ }, []);
635
+ reactExports.useEffect(() => {
636
+ applyThemeMode(themeMode);
637
+ }, [themeMode]);
638
+ reactExports.useEffect(() => {
639
+ function handleStorage(event) {
640
+ if (event.key !== THEME_MODE_STORAGE_KEY) return;
641
+ const nextThemeMode = normalizeThemeMode(event.newValue);
642
+ setThemeModeState(nextThemeMode);
643
+ applyThemeMode(nextThemeMode);
644
+ }
645
+ window.addEventListener("storage", handleStorage);
646
+ return () => window.removeEventListener("storage", handleStorage);
647
+ }, []);
648
+ return { themeMode, setThemeMode };
649
+ }
650
+ function useUiScale() {
651
+ const [uiScalePreference, setUiScalePreferenceState] = reactExports.useState(
652
+ readStoredUiScalePreference
653
+ );
654
+ const [resolvedUiScale, setResolvedUiScale] = reactExports.useState(1);
655
+ const setUiScalePreference = reactExports.useCallback((nextPreference) => {
656
+ setUiScalePreferenceState(nextPreference);
657
+ persistUiScalePreference(nextPreference);
658
+ setResolvedUiScale(applyUiScalePreference(nextPreference));
659
+ }, []);
660
+ reactExports.useEffect(() => {
661
+ const refreshScale = () => {
662
+ setResolvedUiScale(applyUiScalePreference(uiScalePreference));
663
+ };
664
+ refreshScale();
665
+ if (uiScalePreference !== "auto") return;
666
+ window.addEventListener("resize", refreshScale);
667
+ return () => window.removeEventListener("resize", refreshScale);
668
+ }, [uiScalePreference]);
669
+ reactExports.useEffect(() => {
670
+ const handleStorage = (event) => {
671
+ if (event.key !== UI_SCALE_STORAGE_KEY) return;
672
+ const nextPreference = normalizeUiScalePreference(event.newValue);
673
+ setUiScalePreferenceState(nextPreference);
674
+ setResolvedUiScale(applyUiScalePreference(nextPreference));
675
+ };
676
+ window.addEventListener("storage", handleStorage);
677
+ return () => window.removeEventListener("storage", handleStorage);
678
+ }, []);
679
+ return { uiScalePreference, resolvedUiScale, setUiScalePreference };
680
+ }
628
681
  async function fetcher(url) {
629
682
  return fetchJson(
630
683
  url,
@@ -2472,27 +2525,27 @@ function useCopyFeedback(text) {
2472
2525
  return { copied, copy };
2473
2526
  }
2474
2527
  const LazyCompareDrawer = reactExports.lazy(
2475
- () => import("./CompareDrawer-CtWPZP5T.mjs").then((m) => ({ default: m.CompareDrawer }))
2528
+ () => import("./CompareDrawer-Cw7Dseo3.mjs").then((m) => ({ default: m.CompareDrawer }))
2476
2529
  );
2477
2530
  const LazyReplayDialog = reactExports.lazy(
2478
- () => import("./ReplayDialog-BYii7Q2m.mjs").then((m) => ({ default: m.ReplayDialog }))
2531
+ () => import("./ReplayDialog-B-YMhZkE.mjs").then((m) => ({ default: m.ReplayDialog }))
2479
2532
  );
2480
2533
  const LazyRequestAnatomy = reactExports.lazy(
2481
- () => import("./RequestAnatomy-CC6brPZr.mjs").then((m) => ({ default: m.RequestAnatomy }))
2534
+ () => import("./RequestAnatomy-a9d8UTYZ.mjs").then((m) => ({ default: m.RequestAnatomy }))
2482
2535
  );
2483
2536
  const LazyResponseView = reactExports.lazy(
2484
- () => import("./ResponseView-CwwkL_0d.mjs").then((m) => ({ default: m.ResponseView }))
2537
+ () => import("./ResponseView-PzaZS4CN.mjs").then((m) => ({ default: m.ResponseView }))
2485
2538
  );
2486
2539
  const LazyStreamingChunkSequence = reactExports.lazy(
2487
- () => import("./StreamingChunkSequence-CFsJzBLS.mjs").then((m) => ({
2540
+ () => import("./StreamingChunkSequence-C21_TCXk.mjs").then((m) => ({
2488
2541
  default: m.StreamingChunkSequence
2489
2542
  }))
2490
2543
  );
2491
2544
  const LazyJsonViewer = reactExports.lazy(
2492
- () => import("./json-viewer-D2MGmYZ0.mjs").then((m) => ({ default: m.JsonViewer }))
2545
+ () => import("./json-viewer-Di2gxs9H.mjs").then((m) => ({ default: m.JsonViewer }))
2493
2546
  );
2494
2547
  const LazyJsonViewerFromString = reactExports.lazy(
2495
- () => import("./json-viewer-D2MGmYZ0.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2548
+ () => import("./json-viewer-Di2gxs9H.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2496
2549
  );
2497
2550
  const HIGHLIGHT_DURATION_MS = 1200;
2498
2551
  const MAX_HIGHLIGHT_ATTEMPTS = 12;
@@ -2951,6 +3004,58 @@ function Badge({
2951
3004
  }
2952
3005
  );
2953
3006
  }
3007
+ const VISIBLE_TOOL_LIMIT = 3;
3008
+ function ToolTraceEventChip({ event }) {
3009
+ const handleClick = (clickEvent) => {
3010
+ clickEvent.stopPropagation();
3011
+ dispatchLogFocusRequest({
3012
+ logId: event.logId,
3013
+ tab: "response",
3014
+ toolCallIndex: event.index
3015
+ });
3016
+ };
3017
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
3018
+ "button",
3019
+ {
3020
+ type: "button",
3021
+ className: "inline-flex h-5 min-w-0 max-w-[8.5rem] items-center rounded px-1.5 text-[10px] text-muted-foreground transition-colors hover:bg-background/65 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
3022
+ onClick: handleClick,
3023
+ onKeyDown: (keyEvent) => keyEvent.stopPropagation(),
3024
+ "aria-label": `Open response tool call ${event.name}`,
3025
+ title: event.name,
3026
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono font-semibold", children: event.name })
3027
+ }
3028
+ );
3029
+ }
3030
+ function ToolTraceEvents({ events }) {
3031
+ if (events.length === 0) return null;
3032
+ const visibleEvents = events.slice(0, VISIBLE_TOOL_LIMIT);
3033
+ const hiddenCount = Math.max(0, events.length - visibleEvents.length);
3034
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
3035
+ "span",
3036
+ {
3037
+ className: "inline-flex h-7 min-w-0 max-w-full items-center gap-1 overflow-hidden rounded-md border border-cyan-300/10 bg-cyan-300/[0.055] px-1 sm:max-w-[26rem]",
3038
+ role: "group",
3039
+ "aria-label": `${events.length} response tool ${events.length === 1 ? "call" : "calls"}`,
3040
+ onClick: (clickEvent) => clickEvent.stopPropagation(),
3041
+ onKeyDown: (keyEvent) => keyEvent.stopPropagation(),
3042
+ children: [
3043
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-5 shrink-0 items-center gap-1 px-1 font-mono text-[9px] font-semibold uppercase text-cyan-300", children: [
3044
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
3045
+ "Call ",
3046
+ events.length
3047
+ ] }),
3048
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 flex-1 items-center gap-0.5 overflow-hidden", children: [
3049
+ visibleEvents.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventChip, { event }, event.id)),
3050
+ hiddenCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-5 shrink-0 items-center rounded px-1 font-mono text-[10px] font-semibold text-muted-foreground", children: [
3051
+ "+",
3052
+ hiddenCount
3053
+ ] })
3054
+ ] })
3055
+ ]
3056
+ }
3057
+ );
3058
+ }
2954
3059
  const STATUS_BADGE_CLASSES = {
2955
3060
  success: "bg-slate-300/[0.08] text-slate-100 border-transparent",
2956
3061
  client_error: "bg-amber-300/[0.10] text-amber-100 border-transparent",
@@ -3011,6 +3116,7 @@ const LogEntryHeader = reactExports.memo(function({
3011
3116
  displayNumber,
3012
3117
  messageCount = null,
3013
3118
  toolCount = null,
3119
+ toolEvents = [],
3014
3120
  expanded,
3015
3121
  onToggle,
3016
3122
  cacheTrend = null,
@@ -3195,6 +3301,7 @@ const LogEntryHeader = reactExports.memo(function({
3195
3301
  /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
3196
3302
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: toolCount })
3197
3303
  ] }),
3304
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEvents, { events: toolEvents }),
3198
3305
  log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
3199
3306
  "span",
3200
3307
  {
@@ -3869,7 +3976,8 @@ const LogEntry = reactExports.memo(function({
3869
3976
  timeDisplayFormat,
3870
3977
  cacheTrend = null,
3871
3978
  onCompareWithPrevious,
3872
- showClientMetadata = true
3979
+ showClientMetadata = true,
3980
+ toolEvents = []
3873
3981
  }) {
3874
3982
  const [expanded, setExpanded] = reactExports.useState(false);
3875
3983
  const [replayOpen, setReplayOpen] = reactExports.useState(false);
@@ -4217,6 +4325,7 @@ const LogEntry = reactExports.memo(function({
4217
4325
  displayNumber,
4218
4326
  messageCount: requestAnalysis.messageCount,
4219
4327
  toolCount: requestAnalysis.toolCount,
4328
+ toolEvents,
4220
4329
  expanded,
4221
4330
  onToggle: handleToggleExpanded,
4222
4331
  cacheTrend,
@@ -4600,52 +4709,6 @@ function ThreadConnector({
4600
4709
  ) })
4601
4710
  ] });
4602
4711
  }
4603
- const VISIBLE_TOOL_LIMIT = 4;
4604
- function ToolTraceEventChip({ event }) {
4605
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
4606
- "button",
4607
- {
4608
- type: "button",
4609
- className: "inline-flex h-6 max-w-[11rem] min-w-0 items-center gap-1 rounded-md bg-white/[0.028] px-1.5 text-[11px] text-muted-foreground transition-colors hover:bg-sky-400/[0.08] hover:text-sky-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
4610
- onClick: () => dispatchLogFocusRequest({
4611
- logId: event.logId,
4612
- tab: "response",
4613
- toolCallIndex: event.index
4614
- }),
4615
- "aria-label": `Open response tool call ${event.name}`,
4616
- children: [
4617
- /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3 shrink-0 text-sky-400/65" }),
4618
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate font-mono font-semibold", children: event.name })
4619
- ]
4620
- },
4621
- event.id
4622
- );
4623
- }
4624
- function ToolTraceEvents({
4625
- events,
4626
- warnings = []
4627
- }) {
4628
- if (events.length === 0 && warnings.length === 0) return null;
4629
- const visibleEvents = events.slice(0, VISIBLE_TOOL_LIMIT);
4630
- const hiddenCount = Math.max(0, events.length - visibleEvents.length);
4631
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mx-3 mb-2 flex min-w-0 items-center gap-1.5 overflow-hidden rounded-md bg-black/[0.10] px-2 py-1 shadow-[inset_0_1px_0_rgba(255,255,255,0.025)]", children: [
4632
- events.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-md px-1.5 font-mono text-[10px] font-semibold uppercase tracking-[0.12em] text-sky-300/70", children: [
4633
- /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
4634
- events.length
4635
- ] }),
4636
- warnings.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-6 shrink-0 items-center gap-1 rounded-md border border-amber-300/20 bg-amber-300/[0.08] px-1.5 font-mono text-[10px] font-semibold text-amber-100", children: [
4637
- /* @__PURE__ */ jsxRuntimeExports.jsx(TriangleAlert, { className: "size-3" }),
4638
- warnings.length
4639
- ] }),
4640
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1 overflow-hidden", children: [
4641
- visibleEvents.map((event) => /* @__PURE__ */ jsxRuntimeExports.jsx(ToolTraceEventChip, { event }, event.id)),
4642
- hiddenCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex h-6 shrink-0 items-center rounded-md bg-white/[0.025] px-1.5 font-mono text-[11px] font-semibold text-muted-foreground", children: [
4643
- "+",
4644
- hiddenCount
4645
- ] })
4646
- ] })
4647
- ] });
4648
- }
4649
4712
  const PREVIEW_LIMIT = 180;
4650
4713
  const toolTraceCache = /* @__PURE__ */ new WeakMap();
4651
4714
  function responseMayContainToolTrace(log, format) {
@@ -5161,29 +5224,21 @@ const TurnGroup = reactExports.memo(function TurnGroup2({
5161
5224
  onToggle: toggleCollapse
5162
5225
  }
5163
5226
  ),
5164
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-[8px]", bgClass), children: [
5165
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5166
- LogEntry,
5167
- {
5168
- log,
5169
- displayNumber: entry.sessionLogNumber,
5170
- viewMode,
5171
- strip,
5172
- slowResponseThresholdSeconds,
5173
- timeDisplayFormat,
5174
- cacheTrend: cacheTrends?.get(log.id) ?? null,
5175
- showClientMetadata: showLogClientMetadata,
5176
- onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
5177
- }
5178
- ),
5179
- /* @__PURE__ */ jsxRuntimeExports.jsx(
5180
- ToolTraceEvents,
5181
- {
5182
- events: toolEventsByLogId.get(log.id) ?? [],
5183
- warnings: log.warnings ?? []
5184
- }
5185
- )
5186
- ] })
5227
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("flex-1 min-w-0 mb-0.5 rounded-[8px]", bgClass), children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5228
+ LogEntry,
5229
+ {
5230
+ log,
5231
+ displayNumber: entry.sessionLogNumber,
5232
+ viewMode,
5233
+ strip,
5234
+ slowResponseThresholdSeconds,
5235
+ timeDisplayFormat,
5236
+ cacheTrend: cacheTrends?.get(log.id) ?? null,
5237
+ showClientMetadata: showLogClientMetadata,
5238
+ toolEvents: toolEventsByLogId.get(log.id),
5239
+ onCompareWithPrevious: comparisonPredecessors.has(log.id) ? onCompareWithPrevious : void 0
5240
+ }
5241
+ ) })
5187
5242
  ] }, log.id);
5188
5243
  })
5189
5244
  )
@@ -5314,7 +5369,7 @@ function formatElapsed$1(ms) {
5314
5369
  if (ms < 1e3) return `${String(ms)}ms`;
5315
5370
  return `${(ms / 1e3).toFixed(1)}s`;
5316
5371
  }
5317
- function formatTimeRange$1(startedAt, endedAt, timeDisplayFormat) {
5372
+ function formatTimeRange$2(startedAt, endedAt, timeDisplayFormat) {
5318
5373
  if (startedAt === null || endedAt === null) return null;
5319
5374
  return formatTimestampRange(startedAt, endedAt, timeDisplayFormat);
5320
5375
  }
@@ -5801,7 +5856,7 @@ function AgentTraceSummary({
5801
5856
  );
5802
5857
  const showElapsedSummary = showRollupMetrics || summary.maxElapsedMs !== null;
5803
5858
  const timeRange = reactExports.useMemo(
5804
- () => formatTimeRange$1(summary.startedAt, summary.endedAt, timeDisplayFormat),
5859
+ () => formatTimeRange$2(summary.startedAt, summary.endedAt, timeDisplayFormat),
5805
5860
  [summary.endedAt, summary.startedAt, timeDisplayFormat]
5806
5861
  );
5807
5862
  const createCandidates = reactExports.useCallback(() => {
@@ -6167,152 +6222,451 @@ const ConversationGroup = reactExports.memo(function({
6167
6222
  ] })
6168
6223
  ] });
6169
6224
  });
6170
- function CrabLogo({ className }) {
6171
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6172
- "svg",
6173
- {
6174
- viewBox: "0 0 24 24",
6175
- fill: "none",
6176
- stroke: "currentColor",
6177
- strokeWidth: "1.5",
6178
- strokeLinecap: "round",
6179
- strokeLinejoin: "round",
6180
- "aria-hidden": "true",
6181
- className: cn("inline-block size-5", className),
6182
- children: [
6183
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z" }),
6184
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
6185
- /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
6186
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6187
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
6188
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6189
- /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6190
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
6191
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
6192
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
6193
- /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
6194
- ]
6195
- }
6196
- );
6225
+ function shouldShowRawExport(captureMode) {
6226
+ return captureMode === "full";
6197
6227
  }
6198
- const McpLogoPng = "data:image/png;base64,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";
6199
- function McpLogo({ className }) {
6200
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6201
- "img",
6202
- {
6203
- src: McpLogoPng,
6204
- alt: "Model Context Protocol",
6205
- "aria-hidden": "true",
6206
- className: cn("inline-block size-8 object-contain invert", className)
6207
- }
6208
- );
6228
+ function getExportActionVisibility(captureMode, logCount) {
6229
+ const hasLogs = logCount > 0;
6230
+ return {
6231
+ redacted: hasLogs,
6232
+ raw: hasLogs && shouldShowRawExport(captureMode)
6233
+ };
6209
6234
  }
6210
- function Select({
6211
- ...props
6212
- }) {
6213
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Select$1, { "data-slot": "select", ...props });
6235
+ function formatTimeRange$1(logs, timeDisplayFormat) {
6236
+ const first = logs[0];
6237
+ const last = logs[logs.length - 1];
6238
+ if (first === void 0 || last === void 0) return null;
6239
+ return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
6214
6240
  }
6215
- function SelectValue({
6216
- ...props
6217
- }) {
6218
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue$1, { "data-slot": "select-value", ...props });
6241
+ function hasLogFailure(log) {
6242
+ if (log.error !== null && log.error !== void 0 && log.error.length > 0) return true;
6243
+ return log.responseStatus !== null && log.responseStatus >= 400;
6219
6244
  }
6220
- function SelectTrigger({
6221
- className,
6222
- size = "default",
6223
- children,
6224
- ...props
6225
- }) {
6226
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6227
- SelectTrigger$1,
6228
- {
6229
- "data-slot": "select-trigger",
6230
- "data-size": size,
6231
- className: cn(
6232
- "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
6233
- className
6234
- ),
6235
- ...props,
6236
- children: [
6237
- children,
6238
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectIcon, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 opacity-50" }) })
6239
- ]
6245
+ function getModelLabel(logs) {
6246
+ const models = /* @__PURE__ */ new Set();
6247
+ for (const log of logs) {
6248
+ if (log.model !== null && log.model.length > 0) {
6249
+ models.add(log.model);
6240
6250
  }
6241
- );
6251
+ }
6252
+ const sorted = [...models].sort();
6253
+ if (sorted.length === 0) return "Model unknown";
6254
+ if (sorted.length === 1) return sorted[0] ?? "Model unknown";
6255
+ return `${sorted.length} models`;
6242
6256
  }
6243
- function SelectContent({
6244
- className,
6245
- children,
6246
- position = "popper",
6247
- align = "center",
6248
- ...props
6249
- }) {
6250
- return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6251
- SelectContent$1,
6252
- {
6253
- "data-slot": "select-content",
6254
- className: cn(
6255
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
6256
- position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
6257
- className
6258
- ),
6259
- position,
6260
- align,
6261
- ...props,
6262
- children: [
6263
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
6264
- /* @__PURE__ */ jsxRuntimeExports.jsx(
6265
- SelectViewport,
6266
- {
6267
- className: cn(
6268
- "p-1",
6269
- position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
6270
- ),
6271
- children
6272
- }
6273
- ),
6274
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
6275
- ]
6257
+ function getProviderLabel(logs) {
6258
+ const providers = /* @__PURE__ */ new Set();
6259
+ for (const log of logs) {
6260
+ if (log.providerName !== null && log.providerName !== void 0 && log.providerName.length > 0) {
6261
+ providers.add(log.providerName);
6276
6262
  }
6277
- ) });
6263
+ }
6264
+ const sorted = [...providers].sort();
6265
+ if (sorted.length === 0) return "Provider unknown";
6266
+ if (sorted.length === 1) return sorted[0] ?? "Provider unknown";
6267
+ return `${sorted.length} providers`;
6278
6268
  }
6279
- function SelectItem({
6280
- className,
6281
- children,
6282
- ...props
6283
- }) {
6284
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6285
- SelectItem$1,
6286
- {
6287
- "data-slot": "select-item",
6288
- className: cn(
6289
- "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
6290
- className
6291
- ),
6292
- ...props,
6293
- children: [
6294
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemIndicator, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) }) }),
6295
- /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemText, { children })
6296
- ]
6297
- }
6298
- );
6269
+ function formatElapsedMs(ms) {
6270
+ if (ms < 1e3) return `${ms}ms`;
6271
+ if (ms < 6e4) {
6272
+ const seconds2 = ms / 1e3;
6273
+ return `${seconds2 < 10 ? seconds2.toFixed(1) : seconds2.toFixed(0)}s`;
6274
+ }
6275
+ const totalSeconds = Math.round(ms / 1e3);
6276
+ const minutes = Math.floor(totalSeconds / 60);
6277
+ const seconds = totalSeconds % 60;
6278
+ return `${minutes}m ${seconds}s`;
6299
6279
  }
6300
- function SelectScrollUpButton({
6301
- className,
6302
- ...props
6303
- }) {
6304
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
6305
- SelectScrollUpButton$1,
6306
- {
6307
- "data-slot": "select-scroll-up-button",
6308
- className: cn("flex cursor-default items-center justify-center py-1", className),
6309
- ...props,
6310
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4" })
6311
- }
6312
- );
6280
+ function formatTokenPair(inputTokens, outputTokens) {
6281
+ return `${formatTokens(inputTokens)} in / ${formatTokens(outputTokens)} out`;
6313
6282
  }
6314
- function SelectScrollDownButton({
6315
- className,
6283
+ function countSessionTurns(logs, conversationGroups) {
6284
+ if (logs.length === 0) return 0;
6285
+ if (conversationGroups.length === 0) return buildTurnGroups(logs).length;
6286
+ let turns = 0;
6287
+ for (const group of conversationGroups) {
6288
+ turns += buildTurnGroups(group.logs).length;
6289
+ }
6290
+ return turns;
6291
+ }
6292
+ function summarizeFinishReason({
6293
+ stopReason,
6294
+ pending,
6295
+ lastFailureLogNumber
6296
+ }) {
6297
+ if (pending > 0) {
6298
+ return {
6299
+ label: "In flight",
6300
+ detail: `${pending} request${pending === 1 ? "" : "s"} pending`,
6301
+ tone: "cool"
6302
+ };
6303
+ }
6304
+ if (lastFailureLogNumber !== null && stopReason === null) {
6305
+ return {
6306
+ label: "Error",
6307
+ detail: `Last failed request #${lastFailureLogNumber}`,
6308
+ tone: "danger"
6309
+ };
6310
+ }
6311
+ switch (stopReason) {
6312
+ case "end_turn":
6313
+ return { label: "End turn", detail: "Assistant completed the turn", tone: "neutral" };
6314
+ case "stop":
6315
+ return { label: "Stop", detail: "Model reached a stop condition", tone: "neutral" };
6316
+ case "length":
6317
+ return { label: "Output limit", detail: "Turn ended at max output tokens", tone: "watch" };
6318
+ case "tool_use":
6319
+ return { label: "Tool call", detail: "Assistant requested a tool", tone: "cool" };
6320
+ case null:
6321
+ return { label: "Unknown", detail: "No finish reason captured", tone: "neutral" };
6322
+ }
6323
+ }
6324
+ function isClientAbortError(error) {
6325
+ if (error === null) return false;
6326
+ const lower = error.toLowerCase();
6327
+ return lower.includes("abort") || lower.includes("cancel") || lower.includes("client disconnected") || lower.includes("stream disconnected") || lower.includes("connection closed");
6328
+ }
6329
+ function summarizeSessionOutcome({
6330
+ pending,
6331
+ failures,
6332
+ lengthStops,
6333
+ stopReason,
6334
+ lastFailure
6335
+ }) {
6336
+ if (lastFailure !== null && (lastFailure.status === 499 || isClientAbortError(lastFailure.error))) {
6337
+ return {
6338
+ label: "Client aborted",
6339
+ detail: `Request #${lastFailure.logNumber} ended before completion`,
6340
+ tone: "watch"
6341
+ };
6342
+ }
6343
+ if (pending > 0) {
6344
+ return {
6345
+ label: "In flight",
6346
+ detail: `${pending} request${pending === 1 ? "" : "s"} still running`,
6347
+ tone: "cool"
6348
+ };
6349
+ }
6350
+ if (lastFailure !== null && lastFailure.status !== null && lastFailure.status >= 500) {
6351
+ return {
6352
+ label: "Provider error",
6353
+ detail: `Request #${lastFailure.logNumber} returned HTTP ${lastFailure.status}`,
6354
+ tone: "danger"
6355
+ };
6356
+ }
6357
+ if (failures > 0 && lastFailure !== null) {
6358
+ return {
6359
+ label: "Failed",
6360
+ detail: `Request #${lastFailure.logNumber} needs attention`,
6361
+ tone: "danger"
6362
+ };
6363
+ }
6364
+ if (lengthStops > 0 || stopReason === "length") {
6365
+ return {
6366
+ label: "Output limited",
6367
+ detail: "Model stopped at the output token limit",
6368
+ tone: "watch"
6369
+ };
6370
+ }
6371
+ switch (stopReason) {
6372
+ case "end_turn":
6373
+ case "stop":
6374
+ return { label: "Completed", detail: "Assistant completed normally", tone: "neutral" };
6375
+ case "tool_use":
6376
+ return { label: "Tool requested", detail: "Assistant handed off to a tool", tone: "cool" };
6377
+ case null:
6378
+ return { label: "Unknown", detail: "No terminal signal captured", tone: "neutral" };
6379
+ }
6380
+ }
6381
+ function buildSessionSlateStats({
6382
+ logs,
6383
+ conversationGroups,
6384
+ isLoading,
6385
+ timeDisplayFormat,
6386
+ slowResponseThresholdSeconds
6387
+ }) {
6388
+ let failures = 0;
6389
+ let pending = 0;
6390
+ let slow = 0;
6391
+ let lengthStops = 0;
6392
+ let totalIn = 0;
6393
+ let totalOut = 0;
6394
+ let terminalStopReason = null;
6395
+ let lastFailureLogNumber = null;
6396
+ let lastFailure = null;
6397
+ let slowestMs = null;
6398
+ let slowestLogNumber = null;
6399
+ let heaviestTokens = 0;
6400
+ let heaviestInputTokens = 0;
6401
+ let heaviestOutputTokens = 0;
6402
+ let heaviestLogNumber = null;
6403
+ const slowThresholdMs = slowResponseThresholdSeconds * 1e3;
6404
+ for (let index = 0; index < logs.length; index += 1) {
6405
+ const log = logs[index];
6406
+ if (log === void 0) continue;
6407
+ const logNumber = index + 1;
6408
+ const stopReason = extractStopReason(log);
6409
+ if (stopReason !== null) terminalStopReason = stopReason;
6410
+ if (stopReason === "length") lengthStops += 1;
6411
+ if (hasLogFailure(log)) {
6412
+ failures += 1;
6413
+ lastFailureLogNumber = logNumber;
6414
+ lastFailure = {
6415
+ logNumber,
6416
+ status: log.responseStatus,
6417
+ error: log.error ?? null
6418
+ };
6419
+ }
6420
+ if (log.responseStatus === null) pending += 1;
6421
+ if (slowResponseThresholdSeconds > 0 && log.elapsedMs !== null && log.elapsedMs >= slowThresholdMs) {
6422
+ slow += 1;
6423
+ }
6424
+ if (log.inputTokens !== null) totalIn += log.inputTokens;
6425
+ if (log.outputTokens !== null) totalOut += log.outputTokens;
6426
+ if (log.elapsedMs !== null && (slowestMs === null || log.elapsedMs > slowestMs)) {
6427
+ slowestMs = log.elapsedMs;
6428
+ slowestLogNumber = logNumber;
6429
+ }
6430
+ const inputTokens = log.inputTokens ?? 0;
6431
+ const outputTokens = log.outputTokens ?? 0;
6432
+ const requestTokens = inputTokens + outputTokens;
6433
+ if (requestTokens > heaviestTokens) {
6434
+ heaviestTokens = requestTokens;
6435
+ heaviestInputTokens = inputTokens;
6436
+ heaviestOutputTokens = outputTokens;
6437
+ heaviestLogNumber = logNumber;
6438
+ }
6439
+ }
6440
+ const status = isLoading || pending > 0 && failures === 0 ? "rolling" : failures > 0 ? "failed" : slow > 0 || lengthStops > 0 ? "watch" : "clear";
6441
+ const statusLabel2 = status === "rolling" ? "Rolling" : status === "failed" ? "Failure" : status === "watch" ? "Watch" : "Clear";
6442
+ const finish = summarizeFinishReason({
6443
+ stopReason: terminalStopReason,
6444
+ pending,
6445
+ lastFailureLogNumber
6446
+ });
6447
+ const outcome = summarizeSessionOutcome({
6448
+ pending,
6449
+ failures,
6450
+ lengthStops,
6451
+ stopReason: terminalStopReason,
6452
+ lastFailure
6453
+ });
6454
+ return {
6455
+ status,
6456
+ statusLabel: statusLabel2,
6457
+ modelLabel: getModelLabel(logs),
6458
+ providerLabel: getProviderLabel(logs),
6459
+ requests: logs.length,
6460
+ turns: countSessionTurns(logs, conversationGroups),
6461
+ failures,
6462
+ pending,
6463
+ slow,
6464
+ lengthStops,
6465
+ timeRange: formatTimeRange$1(logs, timeDisplayFormat),
6466
+ tokenLabel: `${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`,
6467
+ finishLabel: finish.label,
6468
+ finishDetail: finish.detail,
6469
+ finishTone: finish.tone,
6470
+ outcomeLabel: outcome.label,
6471
+ outcomeDetail: outcome.detail,
6472
+ outcomeTone: outcome.tone,
6473
+ slowestLabel: slowestMs === null ? "n/a" : formatElapsedMs(slowestMs),
6474
+ slowestDetail: slowestLogNumber === null ? "Latency pending" : `Request #${slowestLogNumber}`,
6475
+ heaviestDetail: heaviestLogNumber === null ? "Tokens pending" : `#${heaviestLogNumber} ${formatTokenPair(heaviestInputTokens, heaviestOutputTokens)}`
6476
+ };
6477
+ }
6478
+ function resolveSessionContextScope({
6479
+ pinnedSessionId,
6480
+ selectedSession,
6481
+ sessions,
6482
+ logs
6483
+ }) {
6484
+ if (pinnedSessionId !== void 0) {
6485
+ const scopedLogs = logs.filter((log) => log.sessionId === pinnedSessionId);
6486
+ if (scopedLogs.length === 0) return null;
6487
+ return { label: pinnedSessionId, logs: scopedLogs, showBackLink: true };
6488
+ }
6489
+ if (selectedSession !== "__all__") {
6490
+ const scopedLogs = logs.filter((log) => log.sessionId === selectedSession);
6491
+ if (scopedLogs.length === 0) return null;
6492
+ return { label: selectedSession, logs: scopedLogs, showBackLink: true };
6493
+ }
6494
+ const singleSessionId = sessions.length === 1 ? sessions[0] ?? null : null;
6495
+ if (singleSessionId !== null) {
6496
+ const allVisibleLogsBelongToSession = logs.every((log) => log.sessionId === singleSessionId);
6497
+ if (allVisibleLogsBelongToSession) {
6498
+ return { label: singleSessionId, logs: [...logs], showBackLink: false };
6499
+ }
6500
+ }
6501
+ const allVisibleLogsAreSessionless = sessions.length === 0 && logs.length > 0 && logs.every((log) => log.sessionId === null);
6502
+ if (allVisibleLogsAreSessionless) {
6503
+ return { label: "Visible requests", logs: [...logs], showBackLink: false };
6504
+ }
6505
+ return null;
6506
+ }
6507
+ function formatPaginationStatus({
6508
+ logs,
6509
+ pagination
6510
+ }) {
6511
+ if (pagination.isLoading && logs.length === 0) return "Loading logs...";
6512
+ if (logs.length === 0) return "No logs on this page";
6513
+ const first = logs[0];
6514
+ const last = logs[logs.length - 1];
6515
+ const range = first === void 0 || last === void 0 ? "Current page" : `#${first.id}-#${last.id}`;
6516
+ if (pagination.total === null) {
6517
+ return `Showing ${logs.length} logs (${range})`;
6518
+ }
6519
+ if (pagination.total <= logs.length) {
6520
+ return `Showing all ${pagination.total} logs`;
6521
+ }
6522
+ return `Showing ${logs.length} of ${pagination.total} logs (${range})`;
6523
+ }
6524
+ function CrabLogo({ className }) {
6525
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6526
+ "svg",
6527
+ {
6528
+ viewBox: "0 0 24 24",
6529
+ fill: "none",
6530
+ stroke: "currentColor",
6531
+ strokeWidth: "1.5",
6532
+ strokeLinecap: "round",
6533
+ strokeLinejoin: "round",
6534
+ "aria-hidden": "true",
6535
+ className: cn("inline-block size-5", className),
6536
+ children: [
6537
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 13 C5 9 8 7 12 7 C16 7 19 9 19 13 C19 16 16 18 12 18 C8 18 5 16 5 13 Z" }),
6538
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M5 11 C3.5 9.5 1.5 10 2 12.5 C2.5 14 4 13.5 5 12.5" }),
6539
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M19 11 C20.5 9.5 22.5 10 22 12.5 C21.5 14 20 13.5 19 12.5" }),
6540
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "10", y1: "7", x2: "9.5", y2: "5" }),
6541
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "14", y1: "7", x2: "14.5", y2: "5" }),
6542
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "9.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6543
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "14.5", cy: "4.5", r: "0.9", fill: "currentColor", stroke: "none" }),
6544
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6.5", y1: "16", x2: "4.5", y2: "19.5" }),
6545
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "9", y1: "17.5", x2: "8", y2: "20.5" }),
6546
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "15", y1: "17.5", x2: "16", y2: "20.5" }),
6547
+ /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "17.5", y1: "16", x2: "19.5", y2: "19.5" })
6548
+ ]
6549
+ }
6550
+ );
6551
+ }
6552
+ const McpLogoPng = "data:image/png;base64,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";
6553
+ function McpLogo({ className }) {
6554
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
6555
+ "img",
6556
+ {
6557
+ src: McpLogoPng,
6558
+ alt: "Model Context Protocol",
6559
+ "aria-hidden": "true",
6560
+ className: cn("inline-block size-8 object-contain invert", className)
6561
+ }
6562
+ );
6563
+ }
6564
+ function Select({
6565
+ ...props
6566
+ }) {
6567
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Select$1, { "data-slot": "select", ...props });
6568
+ }
6569
+ function SelectValue({
6570
+ ...props
6571
+ }) {
6572
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectValue$1, { "data-slot": "select-value", ...props });
6573
+ }
6574
+ function SelectTrigger({
6575
+ className,
6576
+ size = "default",
6577
+ children,
6578
+ ...props
6579
+ }) {
6580
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6581
+ SelectTrigger$1,
6582
+ {
6583
+ "data-slot": "select-trigger",
6584
+ "data-size": size,
6585
+ className: cn(
6586
+ "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
6587
+ className
6588
+ ),
6589
+ ...props,
6590
+ children: [
6591
+ children,
6592
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectIcon, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-4 opacity-50" }) })
6593
+ ]
6594
+ }
6595
+ );
6596
+ }
6597
+ function SelectContent({
6598
+ className,
6599
+ children,
6600
+ position = "popper",
6601
+ align = "center",
6602
+ ...props
6603
+ }) {
6604
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(SelectPortal, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6605
+ SelectContent$1,
6606
+ {
6607
+ "data-slot": "select-content",
6608
+ className: cn(
6609
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
6610
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
6611
+ className
6612
+ ),
6613
+ position,
6614
+ align,
6615
+ ...props,
6616
+ children: [
6617
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollUpButton, {}),
6618
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
6619
+ SelectViewport,
6620
+ {
6621
+ className: cn(
6622
+ "p-1",
6623
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
6624
+ ),
6625
+ children
6626
+ }
6627
+ ),
6628
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectScrollDownButton, {})
6629
+ ]
6630
+ }
6631
+ ) });
6632
+ }
6633
+ function SelectItem({
6634
+ className,
6635
+ children,
6636
+ ...props
6637
+ }) {
6638
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
6639
+ SelectItem$1,
6640
+ {
6641
+ "data-slot": "select-item",
6642
+ className: cn(
6643
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
6644
+ className
6645
+ ),
6646
+ ...props,
6647
+ children: [
6648
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemIndicator, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Check, { className: "size-4" }) }) }),
6649
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItemText, { children })
6650
+ ]
6651
+ }
6652
+ );
6653
+ }
6654
+ function SelectScrollUpButton({
6655
+ className,
6656
+ ...props
6657
+ }) {
6658
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
6659
+ SelectScrollUpButton$1,
6660
+ {
6661
+ "data-slot": "select-scroll-up-button",
6662
+ className: cn("flex cursor-default items-center justify-center py-1", className),
6663
+ ...props,
6664
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronUp, { className: "size-4" })
6665
+ }
6666
+ );
6667
+ }
6668
+ function SelectScrollDownButton({
6669
+ className,
6316
6670
  ...props
6317
6671
  }) {
6318
6672
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -6719,7 +7073,7 @@ function AlertsDialog() {
6719
7073
  const criticalCount = summary?.critical ?? 0;
6720
7074
  const warningCount = summary?.warning ?? 0;
6721
7075
  const noticeCount = summary?.notice ?? 0;
6722
- const triggerTone = criticalCount > 0 ? "bg-red-500" : warningCount > 0 ? "bg-amber-400" : "bg-sky-400";
7076
+ const triggerTone = criticalCount > 0 ? "bg-red-600 text-white" : warningCount > 0 ? "bg-amber-400 text-zinc-950" : "bg-sky-400 text-zinc-950";
6723
7077
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(Dialog, { open, onOpenChange: setOpen, children: [
6724
7078
  /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
6725
7079
  Button,
@@ -6736,7 +7090,7 @@ function AlertsDialog() {
6736
7090
  "span",
6737
7091
  {
6738
7092
  className: cn(
6739
- "absolute -right-1 -top-1 min-w-4 rounded-full px-1 text-[10px] font-semibold leading-4 text-white",
7093
+ "absolute -right-1 -top-1 min-w-4 rounded-full px-1 text-[10px] font-semibold leading-4",
6740
7094
  triggerTone
6741
7095
  ),
6742
7096
  children: formatCount(totalCount)
@@ -8181,31 +8535,18 @@ function ImportWizardDialog({
8181
8535
  ] })
8182
8536
  ] }) });
8183
8537
  }
8184
- const KNOWN_PROVIDER_DOCS = {
8185
- deepseek: "https://api-docs.deepseek.com/zh-cn/"
8186
- };
8187
8538
  function hasSuccessField(result) {
8188
8539
  return Object.prototype.hasOwnProperty.call(result, "success");
8189
8540
  }
8190
8541
  function isNotConfiguredState(result) {
8191
8542
  return Object.prototype.hasOwnProperty.call(result, "notConfigured");
8192
8543
  }
8544
+ function providerHasEndpoint$1(value) {
8545
+ return value !== void 0 && value !== "";
8546
+ }
8193
8547
  function hasText(value) {
8194
8548
  return value !== void 0 && value.trim() !== "";
8195
8549
  }
8196
- function formatHeaders(headers) {
8197
- if (headers === void 0) {
8198
- return void 0;
8199
- }
8200
- const lines = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
8201
- if (lines.length === 0) {
8202
- return void 0;
8203
- }
8204
- return lines.join("\n");
8205
- }
8206
- function hasHeaderDetails(headers) {
8207
- return formatHeaders(headers) !== void 0;
8208
- }
8209
8550
  function formatElapsed(ms) {
8210
8551
  if (ms < 1e3) return `${ms}ms`;
8211
8552
  return `${(ms / 1e3).toFixed(1)}s`;
@@ -8214,155 +8555,395 @@ function formatTokenRate(tokensPerSecond) {
8214
8555
  if (tokensPerSecond >= 10) return `${tokensPerSecond.toFixed(0)}/s`;
8215
8556
  return `${tokensPerSecond.toFixed(1)}/s`;
8216
8557
  }
8217
- function hasDiagnosticDetails(result) {
8218
- const debug = result.debug;
8219
- return hasText(result.error?.hint) || hasText(result.error?.details) || hasText(result.rawResponse) || hasText(debug?.requestUrl) || hasText(debug?.requestBody) || hasText(debug?.responseBody) || hasHeaderDetails(debug?.requestHeaders) || hasHeaderDetails(debug?.responseHeaders) || debug?.responseStatus !== void 0;
8558
+ function errorLabel(type) {
8559
+ switch (type) {
8560
+ case "timeout":
8561
+ return "Timeout";
8562
+ case "network_unreachable":
8563
+ return "Offline";
8564
+ case "connection_refused":
8565
+ return "Refused";
8566
+ case "auth_failed":
8567
+ return "Auth";
8568
+ case "rate_limited":
8569
+ return "Rate limit";
8570
+ case "server_error":
8571
+ return "Server";
8572
+ case "invalid_response":
8573
+ return "Invalid";
8574
+ case "unknown":
8575
+ return "Failed";
8576
+ }
8577
+ }
8578
+ function compactSuccessMetric(result) {
8579
+ if (result.firstChunkMs !== void 0 && result.firstChunkMs !== null) {
8580
+ return formatElapsed(result.firstChunkMs);
8581
+ }
8582
+ if (result.latencyMs !== void 0) return formatElapsed(result.latencyMs);
8583
+ if (result.totalStreamMs !== void 0 && result.totalStreamMs !== null) {
8584
+ return formatElapsed(result.totalStreamMs);
8585
+ }
8586
+ if (result.tokensPerSecond !== void 0 && result.tokensPerSecond !== null && result.tokensPerSecond > 0) {
8587
+ return formatTokenRate(result.tokensPerSecond);
8588
+ }
8589
+ return null;
8590
+ }
8591
+ function presentProviderProbe(result) {
8592
+ if (isNotConfiguredState(result)) {
8593
+ return { tone: "quiet", label: "N/A", metric: null, hasDetails: false };
8594
+ }
8595
+ if (!hasSuccessField(result)) {
8596
+ return { tone: "pending", label: "Running", metric: null, hasDetails: false };
8597
+ }
8598
+ if (result.success) {
8599
+ return {
8600
+ tone: "success",
8601
+ label: "Pass",
8602
+ metric: compactSuccessMetric(result),
8603
+ hasDetails: true
8604
+ };
8605
+ }
8606
+ return {
8607
+ tone: "failure",
8608
+ label: errorLabel(result.error?.type ?? "unknown"),
8609
+ metric: null,
8610
+ hasDetails: true
8611
+ };
8612
+ }
8613
+ function configuredFormatResults(provider, testResults) {
8614
+ if (testResults === void 0) return [];
8615
+ const results = [];
8616
+ if (providerHasEndpoint$1(provider.anthropicBaseUrl)) results.push(testResults.anthropic);
8617
+ if (providerHasEndpoint$1(provider.openaiBaseUrl)) results.push(testResults.openaiChat);
8618
+ if (providerHasEndpoint$1(provider.openaiResponsesBaseUrl)) {
8619
+ results.push(testResults.openaiResponses);
8620
+ }
8621
+ return results;
8622
+ }
8623
+ function summarizeProviderTestResults(provider, testResults) {
8624
+ const summary = { total: 0, passed: 0, failed: 0, testing: 0 };
8625
+ for (const results of configuredFormatResults(provider, testResults)) {
8626
+ for (const state of [results.nonStreaming, results.streaming]) {
8627
+ if (isNotConfiguredState(state)) continue;
8628
+ summary.total += 1;
8629
+ if (!hasSuccessField(state)) {
8630
+ summary.testing += 1;
8631
+ } else if (state.success) {
8632
+ summary.passed += 1;
8633
+ } else {
8634
+ summary.failed += 1;
8635
+ }
8636
+ }
8637
+ }
8638
+ return summary;
8639
+ }
8640
+ function formatHeaders(headers) {
8641
+ if (headers === void 0) return void 0;
8642
+ const lines = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
8643
+ return lines.length === 0 ? void 0 : lines.join("\n");
8220
8644
  }
8221
8645
  function DiagnosticSection({
8222
8646
  title,
8223
8647
  children
8224
8648
  }) {
8225
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-1", children: [
8226
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium text-foreground/80", children: title }),
8649
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-1.5", children: [
8650
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-medium text-foreground/80", children: title }),
8227
8651
  children
8228
8652
  ] });
8229
8653
  }
8230
8654
  function DiagnosticPre({ value }) {
8231
- return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-40 overflow-auto whitespace-pre-wrap break-all rounded-md bg-background/80 p-2 font-mono text-[11px] leading-relaxed text-foreground/80", children: value });
8655
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-all rounded-md bg-muted/40 p-3 font-mono text-[11px] leading-relaxed text-foreground/80", children: value });
8656
+ }
8657
+ function ResultMetric({ label, value }) {
8658
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 rounded-md bg-muted/35 px-3 py-2", children: [
8659
+ /* @__PURE__ */ jsxRuntimeExports.jsx("dt", { className: "text-[10px] text-muted-foreground", children: label }),
8660
+ /* @__PURE__ */ jsxRuntimeExports.jsx("dd", { className: "mt-1 truncate font-mono text-xs font-semibold text-foreground", title: value, children: value })
8661
+ ] });
8662
+ }
8663
+ function ProviderTestMetrics({ result }) {
8664
+ const hasTokens = result.inputTokens !== void 0 || result.outputTokens !== void 0;
8665
+ const hasCache = result.cacheCreationInputTokens !== void 0 || result.cacheReadInputTokens !== void 0;
8666
+ const hasMetrics = hasText(result.model) || hasTokens || hasCache || result.latencyMs !== void 0 || result.firstChunkMs !== void 0 && result.firstChunkMs !== null || result.totalStreamMs !== void 0 && result.totalStreamMs !== null || result.tokensPerSecond !== void 0 && result.tokensPerSecond !== null && result.tokensPerSecond > 0;
8667
+ if (!hasMetrics) return null;
8668
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("dl", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4", children: [
8669
+ hasText(result.model) && /* @__PURE__ */ jsxRuntimeExports.jsx(ResultMetric, { label: "Model", value: result.model }),
8670
+ result.latencyMs !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ResultMetric, { label: "Latency", value: formatElapsed(result.latencyMs) }),
8671
+ result.firstChunkMs !== void 0 && result.firstChunkMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ResultMetric, { label: "First chunk", value: formatElapsed(result.firstChunkMs) }),
8672
+ result.totalStreamMs !== void 0 && result.totalStreamMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(ResultMetric, { label: "Stream total", value: formatElapsed(result.totalStreamMs) }),
8673
+ result.tokensPerSecond !== void 0 && result.tokensPerSecond !== null && result.tokensPerSecond > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(
8674
+ ResultMetric,
8675
+ {
8676
+ label: "Token rate",
8677
+ value: `${formatTokenRate(result.tokensPerSecond)} tok`
8678
+ }
8679
+ ),
8680
+ hasTokens && /* @__PURE__ */ jsxRuntimeExports.jsx(
8681
+ ResultMetric,
8682
+ {
8683
+ label: "Tokens in / out",
8684
+ value: `${String(result.inputTokens ?? 0)} / ${String(result.outputTokens ?? 0)}`
8685
+ }
8686
+ ),
8687
+ hasCache && /* @__PURE__ */ jsxRuntimeExports.jsx(
8688
+ ResultMetric,
8689
+ {
8690
+ label: "Cache create / read",
8691
+ value: `${String(result.cacheCreationInputTokens ?? 0)} / ${String(result.cacheReadInputTokens ?? 0)}`
8692
+ }
8693
+ )
8694
+ ] });
8232
8695
  }
8233
- function ProviderTestDetails({ result }) {
8696
+ function ProviderTestDiagnostics({ result }) {
8234
8697
  const debug = result.debug;
8235
8698
  const requestUrl = debug?.requestUrl;
8236
8699
  const requestMethod = debug?.requestMethod ?? "POST";
8237
8700
  const requestHeaders = formatHeaders(debug?.requestHeaders);
8238
8701
  const responseHeaders = formatHeaders(debug?.responseHeaders);
8239
- const responseStatus = debug?.responseStatus !== void 0 ? `${debug.responseStatus}${hasText(debug.responseStatusText) ? ` ${debug.responseStatusText}` : ""}` : void 0;
8702
+ const responseStatus = debug?.responseStatus === void 0 ? void 0 : `${String(debug.responseStatus)}${hasText(debug.responseStatusText) ? ` ${debug.responseStatusText}` : ""}`;
8240
8703
  const responseBody = debug?.responseBody ?? result.rawResponse;
8241
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-h-80 max-w-full space-y-2 overflow-auto rounded-md border bg-muted/50 p-2 text-xs text-muted-foreground shadow-sm sm:w-[520px]", children: [
8242
- hasText(result.error?.hint) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Hint", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { children: result.error.hint }) }),
8243
- hasText(result.error?.details) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Error", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: result.error.details }) }),
8244
- hasText(requestUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md bg-background/80 p-2 font-mono text-[11px] break-all text-foreground/80", children: [
8245
- requestMethod,
8246
- " ",
8247
- requestUrl
8248
- ] }) }),
8249
- requestHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request Headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: requestHeaders }) }),
8250
- hasText(debug?.requestBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request Body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: debug.requestBody }) }),
8251
- hasText(responseStatus) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Status", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-md bg-background/80 p-2 font-mono text-[11px] text-foreground/80", children: responseStatus }) }),
8252
- responseHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseHeaders }) }),
8253
- hasText(responseBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response Body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseBody }) })
8704
+ const hasDiagnostics = hasText(result.error?.hint) || hasText(result.error?.details) || hasText(requestUrl) || requestHeaders !== void 0 || hasText(debug?.requestBody) || hasText(responseStatus) || responseHeaders !== void 0 || hasText(responseBody);
8705
+ if (!hasDiagnostics) return null;
8706
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 border-t border-border/70 pt-4 text-xs text-muted-foreground", children: [
8707
+ hasText(result.error?.hint) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Hint", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "break-words text-foreground/80", children: result.error.hint }) }),
8708
+ hasText(result.error?.details) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Error details", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: result.error.details }) }),
8709
+ hasText(requestUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: `${requestMethod} ${requestUrl}` }) }),
8710
+ requestHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: requestHeaders }) }),
8711
+ hasText(debug?.requestBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Request body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: debug.requestBody }) }),
8712
+ hasText(responseStatus) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response status", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseStatus }) }),
8713
+ responseHeaders !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response headers", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseHeaders }) }),
8714
+ hasText(responseBody) && /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticSection, { title: "Response body", children: /* @__PURE__ */ jsxRuntimeExports.jsx(DiagnosticPre, { value: responseBody }) })
8254
8715
  ] });
8255
8716
  }
8256
- function ProviderFormatTestStatus({
8257
- results
8717
+ function ProviderTestResultDialog({
8718
+ result,
8719
+ probeLabel,
8720
+ children
8258
8721
  }) {
8259
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid min-w-0 gap-2 sm:grid-cols-2", children: [
8260
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 rounded-md border border-border/60 bg-muted/20 px-2 py-1.5", children: [
8261
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground", children: "Non-stream" }),
8262
- /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: results.nonStreaming })
8263
- ] }),
8264
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 rounded-md border border-border/60 bg-muted/20 px-2 py-1.5", children: [
8265
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-muted-foreground", children: "Stream" }),
8266
- /* @__PURE__ */ jsxRuntimeExports.jsx(TestStatus, { result: results.streaming })
8722
+ const errorMessage = result.error?.message ?? "Connection failed";
8723
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(Dialog, { children: [
8724
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTrigger, { asChild: true, children }),
8725
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogContent, { className: "max-h-[82vh] max-w-2xl gap-0 overflow-hidden p-0", children: [
8726
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(DialogHeader, { className: "border-b border-border/70 px-5 py-4 pr-12", children: [
8727
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
8728
+ result.success ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-4 text-cyan-300" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-4 text-red-300" }),
8729
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogTitle, { className: "text-base", children: probeLabel })
8730
+ ] }),
8731
+ /* @__PURE__ */ jsxRuntimeExports.jsx(DialogDescription, { children: result.success ? "Connection probe passed." : errorMessage })
8732
+ ] }),
8733
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 overflow-y-auto p-5", children: [
8734
+ !result.success && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded-md border border-red-400/20 bg-red-400/[0.06] px-3 py-2 text-sm text-red-200", children: [
8735
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-medium", children: errorLabel(result.error?.type ?? "unknown") }),
8736
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-1 break-words text-xs text-muted-foreground", children: errorMessage })
8737
+ ] }),
8738
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestMetrics, { result }),
8739
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestDiagnostics, { result })
8740
+ ] })
8267
8741
  ] })
8268
8742
  ] });
8269
8743
  }
8270
- function ProviderFormatStatusBlock({
8271
- label,
8272
- children
8744
+ function errorIcon(type) {
8745
+ const className = "size-3 shrink-0";
8746
+ switch (type) {
8747
+ case "timeout":
8748
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className });
8749
+ case "network_unreachable":
8750
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(WifiOff, { className });
8751
+ case "connection_refused":
8752
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Wifi, { className });
8753
+ case "auth_failed":
8754
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Lock, { className });
8755
+ case "rate_limited":
8756
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { className });
8757
+ case "server_error":
8758
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { className });
8759
+ case "invalid_response":
8760
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { className });
8761
+ case "unknown":
8762
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className });
8763
+ }
8764
+ }
8765
+ const PROBE_TONE_CLASSES = {
8766
+ success: "bg-cyan-300/[0.07] text-cyan-100 hover:bg-cyan-300/[0.11]",
8767
+ failure: "bg-red-400/[0.07] text-red-200 hover:bg-red-400/[0.11]",
8768
+ pending: "bg-muted/40 text-foreground/80",
8769
+ quiet: "bg-transparent text-muted-foreground/70"
8770
+ };
8771
+ function ProviderProbeStatus({
8772
+ result,
8773
+ probeLabel
8273
8774
  }) {
8274
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0 space-y-2 rounded-md border border-border/60 bg-muted/15 p-2", children: [
8275
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-medium text-foreground/80", children: label }),
8276
- children
8775
+ const presentation = presentProviderProbe(result);
8776
+ const icon = isNotConfiguredState(result) ? /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3 shrink-0" }) : !hasSuccessField(result) ? /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: "size-3 shrink-0 animate-spin" }) : result.success ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3 shrink-0" }) : errorIcon(result.error?.type ?? "unknown");
8777
+ const content = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
8778
+ icon,
8779
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: presentation.label }),
8780
+ presentation.metric !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-auto shrink-0 font-mono text-[10px] opacity-75", children: presentation.metric })
8277
8781
  ] });
8278
- }
8279
- function providerHasEndpoint(value) {
8280
- return value !== void 0 && value !== "";
8281
- }
8282
- function providerTestTargets(provider) {
8283
- const targets = [];
8284
- if (providerHasEndpoint(provider.anthropicBaseUrl)) {
8285
- targets.push("Anthropic");
8286
- }
8287
- if (providerHasEndpoint(provider.openaiBaseUrl)) {
8288
- targets.push("Chat");
8289
- }
8290
- if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
8291
- targets.push("Responses");
8782
+ const className = cn(
8783
+ "inline-flex h-7 w-full min-w-0 items-center gap-1.5 rounded-md px-2 text-left text-xs transition-colors",
8784
+ PROBE_TONE_CLASSES[presentation.tone]
8785
+ );
8786
+ if (hasSuccessField(result) && presentation.hasDetails) {
8787
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestResultDialog, { result, probeLabel, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
8788
+ "button",
8789
+ {
8790
+ type: "button",
8791
+ className,
8792
+ "aria-label": `Open ${probeLabel} result: ${presentation.label}`,
8793
+ children: content
8794
+ }
8795
+ ) });
8292
8796
  }
8293
- return targets;
8797
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className, children: content });
8798
+ }
8799
+ function ProbePlaceholder({ label }) {
8800
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex h-7 w-full min-w-0 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground/70", children: [
8801
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3 shrink-0" }),
8802
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: label })
8803
+ ] });
8294
8804
  }
8295
- function CapabilityChip({
8805
+ function ProviderFormatRow({
8296
8806
  label,
8297
- enabled,
8298
- tone
8807
+ results,
8808
+ configured = true
8299
8809
  }) {
8300
- const className = enabled ? "border-border/70 bg-muted/25 text-foreground/80" : "border-border/50 bg-transparent text-muted-foreground/70";
8301
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: `inline-flex items-center gap-1 rounded border px-1.5 py-0.5 ${className}`, children: [
8302
- enabled ? /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3" }),
8303
- label
8810
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid min-w-0 grid-cols-[minmax(6.5rem,1fr)_minmax(6rem,0.9fr)_minmax(6rem,0.9fr)] items-center gap-2 border-t border-border/60 px-2 py-2", children: [
8811
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 truncate text-xs font-medium text-foreground/85", title: label, children: label }),
8812
+ configured && results !== void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderProbeStatus, { result: results.nonStreaming, probeLabel: `${label} non-stream` }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ProbePlaceholder, { label: configured ? "Not tested" : "Off" }),
8813
+ configured && results !== void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderProbeStatus, { result: results.streaming, probeLabel: `${label} stream` }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ProbePlaceholder, { label: configured ? "Not tested" : "Off" })
8304
8814
  ] });
8305
8815
  }
8306
- function ProviderCapabilitySummary({ provider }) {
8307
- const hasAnthropic = providerHasEndpoint(provider.anthropicBaseUrl);
8308
- const hasChat = providerHasEndpoint(provider.openaiBaseUrl);
8309
- const hasResponses = providerHasEndpoint(provider.openaiResponsesBaseUrl);
8816
+ function ProviderTestMatrix({
8817
+ provider,
8818
+ results
8819
+ }) {
8820
+ const hasAnthropic = providerHasEndpoint$1(provider.anthropicBaseUrl);
8821
+ const hasChat = providerHasEndpoint$1(provider.openaiBaseUrl);
8822
+ const hasResponses = providerHasEndpoint$1(provider.openaiResponsesBaseUrl);
8310
8823
  if (!hasAnthropic && !hasChat && !hasResponses) {
8311
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Test requires at least one configured endpoint." });
8312
- }
8313
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
8314
- hasAnthropic && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "Anthropic", enabled: true }),
8315
- hasChat && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "OpenAI Chat", enabled: true }),
8316
- hasResponses && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "OpenAI Responses", enabled: true })
8317
- ] });
8318
- }
8319
- function configuredFormatResults(provider, testResults) {
8320
- if (testResults === void 0) return [];
8321
- const results = [];
8322
- if (providerHasEndpoint(provider.anthropicBaseUrl)) {
8323
- results.push(testResults.anthropic);
8824
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "No testable endpoint configured." });
8324
8825
  }
8325
- if (providerHasEndpoint(provider.openaiBaseUrl)) {
8326
- results.push(testResults.openaiChat);
8327
- }
8328
- if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
8329
- results.push(testResults.openaiResponses);
8330
- }
8331
- return results;
8826
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 overflow-x-auto rounded-md border border-border/70", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-[20rem]", children: [
8827
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-[minmax(6.5rem,1fr)_minmax(6rem,0.9fr)_minmax(6rem,0.9fr)] gap-2 bg-muted/25 px-2 py-1.5 text-[10px] text-muted-foreground", children: [
8828
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Protocol" }),
8829
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Non-stream" }),
8830
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Stream" })
8831
+ ] }),
8832
+ hasAnthropic && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatRow, { label: "Anthropic", results: results?.anthropic }),
8833
+ hasChat && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatRow, { label: "OpenAI Chat", results: results?.openaiChat }),
8834
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
8835
+ ProviderFormatRow,
8836
+ {
8837
+ label: "OpenAI Responses",
8838
+ results: results?.openaiResponses,
8839
+ configured: hasResponses
8840
+ }
8841
+ )
8842
+ ] }) });
8332
8843
  }
8333
- function summarizeTestResults(provider, testResults) {
8844
+ function summarizeFormat(results) {
8334
8845
  const summary = { total: 0, passed: 0, failed: 0, testing: 0 };
8335
- for (const results of configuredFormatResults(provider, testResults)) {
8336
- for (const state of [results.nonStreaming, results.streaming]) {
8337
- if (isNotConfiguredState(state)) continue;
8338
- summary.total += 1;
8339
- if (hasSuccessField(state)) {
8340
- if (state.success) {
8341
- summary.passed += 1;
8342
- } else {
8343
- summary.failed += 1;
8344
- }
8345
- } else {
8346
- summary.testing += 1;
8347
- }
8348
- }
8846
+ for (const state of [results.nonStreaming, results.streaming]) {
8847
+ if (isNotConfiguredState(state)) continue;
8848
+ summary.total += 1;
8849
+ if (!hasSuccessField(state)) summary.testing += 1;
8850
+ else if (state.success) summary.passed += 1;
8851
+ else summary.failed += 1;
8349
8852
  }
8350
8853
  return summary;
8351
8854
  }
8855
+ function FormatSummaryBadge({
8856
+ label,
8857
+ results
8858
+ }) {
8859
+ const summary = summarizeFormat(results);
8860
+ const className = summary.failed > 0 ? "border-red-400/20 bg-red-400/[0.06] text-red-200" : summary.testing > 0 ? "border-amber-300/20 bg-amber-300/[0.06] text-amber-100" : summary.total > 0 && summary.passed === summary.total ? "border-cyan-300/20 bg-cyan-300/[0.06] text-cyan-100" : "border-border/60 bg-transparent text-muted-foreground";
8861
+ const value = summary.failed > 0 ? `${summary.failed} fail` : summary.testing > 0 ? "running" : summary.total === 0 ? "off" : `${summary.passed}/${summary.total}`;
8862
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: cn("rounded border px-1.5 py-0.5 text-[10px]", className), children: [
8863
+ label,
8864
+ " ",
8865
+ value
8866
+ ] });
8867
+ }
8868
+ function ModelResultRow({
8869
+ provider,
8870
+ modelName,
8871
+ results,
8872
+ expanded,
8873
+ onToggle
8874
+ }) {
8875
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
8876
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
8877
+ "button",
8878
+ {
8879
+ type: "button",
8880
+ onClick: onToggle,
8881
+ className: "flex w-full min-w-0 flex-wrap items-center gap-2 px-2 py-2 text-left transition-colors hover:bg-muted/20",
8882
+ "aria-expanded": expanded,
8883
+ children: [
8884
+ expanded ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5 shrink-0 text-muted-foreground" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5 shrink-0 text-muted-foreground" }),
8885
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-[8rem] flex-1 truncate text-xs font-medium", title: modelName, children: modelName }),
8886
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex flex-wrap items-center justify-end gap-1", children: [
8887
+ providerHasEndpoint$1(provider.anthropicBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(FormatSummaryBadge, { label: "A", results: results.anthropic }),
8888
+ providerHasEndpoint$1(provider.openaiBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(FormatSummaryBadge, { label: "Chat", results: results.openaiChat }),
8889
+ providerHasEndpoint$1(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx(FormatSummaryBadge, { label: "Resp", results: results.openaiResponses })
8890
+ ] })
8891
+ ]
8892
+ }
8893
+ ),
8894
+ expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-2 pb-3", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestMatrix, { provider, results }) })
8895
+ ] });
8896
+ }
8897
+ function ProviderModelTestResultsView({
8898
+ provider,
8899
+ testResults
8900
+ }) {
8901
+ const entries = Object.entries(testResults.models ?? {});
8902
+ const [showModels, setShowModels] = reactExports.useState(false);
8903
+ const [expandedModelName, setExpandedModelName] = reactExports.useState(null);
8904
+ if (entries.length === 0) return null;
8905
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-2", children: [
8906
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
8907
+ "button",
8908
+ {
8909
+ type: "button",
8910
+ onClick: () => setShowModels((value) => !value),
8911
+ className: "flex w-full items-center gap-1.5 text-left text-xs font-medium text-foreground/80 transition-colors hover:text-foreground",
8912
+ "aria-expanded": showModels,
8913
+ children: [
8914
+ showModels ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3.5" }),
8915
+ "Model probes",
8916
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded bg-muted/50 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground", children: entries.length })
8917
+ ]
8918
+ }
8919
+ ),
8920
+ showModels && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 divide-y divide-border/60 rounded-md border border-border/70", children: entries.map(([modelName, modelResults]) => /* @__PURE__ */ jsxRuntimeExports.jsx(
8921
+ ModelResultRow,
8922
+ {
8923
+ provider,
8924
+ modelName,
8925
+ results: modelResults,
8926
+ expanded: expandedModelName === modelName,
8927
+ onToggle: () => setExpandedModelName((current) => current === modelName ? null : modelName)
8928
+ },
8929
+ modelName
8930
+ )) })
8931
+ ] });
8932
+ }
8352
8933
  function ProviderTestPill({
8353
8934
  provider,
8354
8935
  testResults,
8355
8936
  isTesting
8356
8937
  }) {
8357
- const targets = providerTestTargets(provider);
8358
- const summary = summarizeTestResults(provider, testResults);
8938
+ const hasEndpoint = providerHasEndpoint$1(provider.anthropicBaseUrl) || providerHasEndpoint$1(provider.openaiBaseUrl) || providerHasEndpoint$1(provider.openaiResponsesBaseUrl);
8939
+ const summary = summarizeProviderTestResults(provider, testResults);
8359
8940
  if (isTesting) {
8360
8941
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100", children: [
8361
8942
  /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: "size-3 animate-spin" }),
8362
8943
  "Testing"
8363
8944
  ] });
8364
8945
  }
8365
- if (targets.length === 0) {
8946
+ if (!hasEndpoint) {
8366
8947
  return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground", children: "No endpoint" });
8367
8948
  }
8368
8949
  if (testResults === void 0 || summary.total === 0) {
@@ -8371,15 +8952,12 @@ function ProviderTestPill({
8371
8952
  if (summary.failed > 0) {
8372
8953
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-red-400/25 bg-red-500/[0.08] px-2 py-1 text-xs text-red-200", children: [
8373
8954
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { className: "size-3" }),
8374
- summary.passed,
8375
- "/",
8376
- summary.total,
8377
- " passed"
8955
+ summary.failed,
8956
+ " failed"
8378
8957
  ] });
8379
8958
  }
8380
8959
  if (summary.testing > 0 || summary.passed < summary.total) {
8381
8960
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "rounded border border-amber-300/25 bg-amber-400/[0.08] px-2 py-1 text-xs text-amber-100", children: [
8382
- "Partial ",
8383
8961
  summary.passed,
8384
8962
  "/",
8385
8963
  summary.total
@@ -8387,12 +8965,36 @@ function ProviderTestPill({
8387
8965
  }
8388
8966
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100", children: [
8389
8967
  /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }),
8390
- "OK ",
8391
8968
  summary.passed,
8392
8969
  "/",
8393
8970
  summary.total
8394
8971
  ] });
8395
8972
  }
8973
+ const KNOWN_PROVIDER_DOCS = {
8974
+ deepseek: "https://api-docs.deepseek.com/zh-cn/"
8975
+ };
8976
+ function providerHasEndpoint(value) {
8977
+ return value !== void 0 && value !== "";
8978
+ }
8979
+ function CapabilityChip({ label }) {
8980
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded border border-border/70 bg-muted/25 px-1.5 py-0.5 text-foreground/80", children: [
8981
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }),
8982
+ label
8983
+ ] });
8984
+ }
8985
+ function ProviderCapabilitySummary({ provider }) {
8986
+ const hasAnthropic = providerHasEndpoint(provider.anthropicBaseUrl);
8987
+ const hasChat = providerHasEndpoint(provider.openaiBaseUrl);
8988
+ const hasResponses = providerHasEndpoint(provider.openaiResponsesBaseUrl);
8989
+ if (!hasAnthropic && !hasChat && !hasResponses) {
8990
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Test requires at least one configured endpoint." });
8991
+ }
8992
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [
8993
+ hasAnthropic && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "Anthropic" }),
8994
+ hasChat && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "OpenAI Chat" }),
8995
+ hasResponses && /* @__PURE__ */ jsxRuntimeExports.jsx(CapabilityChip, { label: "OpenAI Responses" })
8996
+ ] });
8997
+ }
8396
8998
  function compactModelLabel(provider) {
8397
8999
  const models = provider.models ?? [];
8398
9000
  if (models.length === 0) return "No models configured";
@@ -8422,143 +9024,6 @@ function ModelChip({ provider, model }) {
8422
9024
  ] }) })
8423
9025
  ] });
8424
9026
  }
8425
- function getErrorIcon(type) {
8426
- const iconProps = { className: "size-3", strokeWidth: 2 };
8427
- switch (type) {
8428
- case "timeout":
8429
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { ...iconProps });
8430
- case "network_unreachable":
8431
- return /* @__PURE__ */ jsxRuntimeExports.jsx(WifiOff, { ...iconProps });
8432
- case "connection_refused":
8433
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Wifi, { ...iconProps });
8434
- case "auth_failed":
8435
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Lock, { ...iconProps });
8436
- case "rate_limited":
8437
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Gauge, { ...iconProps });
8438
- case "server_error":
8439
- return /* @__PURE__ */ jsxRuntimeExports.jsx(Server, { ...iconProps });
8440
- case "invalid_response":
8441
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleQuestionMark, { ...iconProps });
8442
- case "unknown":
8443
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { ...iconProps });
8444
- default:
8445
- return /* @__PURE__ */ jsxRuntimeExports.jsx(CircleAlert, { ...iconProps });
8446
- }
8447
- }
8448
- function TestStatus({ result }) {
8449
- const [showDetails, setShowDetails] = reactExports.useState(false);
8450
- if (!hasSuccessField(result)) {
8451
- if (isNotConfiguredState(result)) {
8452
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground shrink-0", children: [
8453
- /* @__PURE__ */ jsxRuntimeExports.jsx(Minus, { className: "size-3" }),
8454
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Not configured" })
8455
- ] });
8456
- }
8457
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground shrink-0", children: [
8458
- /* @__PURE__ */ jsxRuntimeExports.jsx(RotateCw, { className: "size-3 animate-spin" }),
8459
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Testing..." })
8460
- ] });
8461
- }
8462
- if (result.success) {
8463
- const tokenParts = [];
8464
- const timingParts = [];
8465
- if (result.inputTokens !== void 0) {
8466
- tokenParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8467
- result.inputTokens,
8468
- " in"
8469
- ] }, "in"));
8470
- }
8471
- if (result.outputTokens !== void 0) {
8472
- tokenParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8473
- result.outputTokens,
8474
- " out"
8475
- ] }, "out"));
8476
- }
8477
- if (result.cacheCreationInputTokens !== void 0 && result.cacheCreationInputTokens > 0) {
8478
- tokenParts.push(
8479
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
8480
- "KV Cache +",
8481
- result.cacheCreationInputTokens
8482
- ] }, "cache-create")
8483
- );
8484
- }
8485
- if (result.cacheReadInputTokens !== void 0 && result.cacheReadInputTokens > 0) {
8486
- tokenParts.push(
8487
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
8488
- "KV Cache ~",
8489
- result.cacheReadInputTokens
8490
- ] }, "cache-read")
8491
- );
8492
- }
8493
- if (result.firstChunkMs !== void 0 && result.firstChunkMs !== null) {
8494
- timingParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8495
- "first ",
8496
- formatElapsed(result.firstChunkMs)
8497
- ] }, "first"));
8498
- }
8499
- if (result.totalStreamMs !== void 0 && result.totalStreamMs !== null) {
8500
- timingParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8501
- "total ",
8502
- formatElapsed(result.totalStreamMs)
8503
- ] }, "total"));
8504
- }
8505
- if (result.tokensPerSecond !== void 0 && result.tokensPerSecond !== null && result.tokensPerSecond > 0) {
8506
- timingParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
8507
- formatTokenRate(result.tokensPerSecond),
8508
- " tok/s"
8509
- ] }, "rate"));
8510
- }
8511
- const displayTokens = [];
8512
- for (let i = 0; i < tokenParts.length; i++) {
8513
- if (i > 0) displayTokens.push(", ");
8514
- displayTokens.push(tokenParts[i]);
8515
- }
8516
- const displayTiming = [];
8517
- for (let i = 0; i < timingParts.length; i++) {
8518
- if (i > 0) displayTiming.push(", ");
8519
- displayTiming.push(timingParts[i]);
8520
- }
8521
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-1 text-xs text-cyan-100", children: [
8522
- /* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3 text-cyan-300" }),
8523
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connected" }),
8524
- tokenParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
8525
- "(",
8526
- displayTokens,
8527
- ")"
8528
- ] }),
8529
- timingParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "hidden text-muted-foreground lg:inline", children: [
8530
- "(",
8531
- displayTiming,
8532
- ")"
8533
- ] })
8534
- ] });
8535
- }
8536
- const error = result.error;
8537
- const errorMessage = error?.message ?? "Connection failed";
8538
- const errorType = error?.type ?? "unknown";
8539
- const hasDetails = hasDiagnosticDetails(result);
8540
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1 shrink-0", children: [
8541
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-red-600 max-w-[200px]", children: [
8542
- getErrorIcon(errorType),
8543
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: errorMessage }),
8544
- hasDetails && /* @__PURE__ */ jsxRuntimeExports.jsxs(
8545
- "button",
8546
- {
8547
- type: "button",
8548
- onClick: () => setShowDetails((value) => !value),
8549
- "aria-expanded": showDetails,
8550
- "aria-label": showDetails ? "Hide provider test diagnostics" : "Show provider test diagnostics",
8551
- className: "inline-flex h-5 shrink-0 items-center gap-1 rounded border border-border/70 bg-background/70 px-1.5 text-[10px] text-muted-foreground transition-colors hover:text-foreground",
8552
- children: [
8553
- showDetails ? /* @__PURE__ */ jsxRuntimeExports.jsx(EyeOff, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3" }),
8554
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: showDetails ? "Hide" : "Details" })
8555
- ]
8556
- }
8557
- )
8558
- ] }),
8559
- showDetails && hasDetails && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestDetails, { result })
8560
- ] });
8561
- }
8562
9027
  function formatTimeAgo(isoString) {
8563
9028
  const now = Date.now();
8564
9029
  const then = new Date(isoString).getTime();
@@ -8603,7 +9068,6 @@ function ProviderCard({
8603
9068
  const [showApiKey, setShowApiKey] = reactExports.useState(false);
8604
9069
  const [copied, setCopied] = reactExports.useState(false);
8605
9070
  const [showDetails, setShowDetails] = reactExports.useState(false);
8606
- const [showModelResults, setShowModelResults] = reactExports.useState(false);
8607
9071
  function handleCopy() {
8608
9072
  void copyTextToClipboard(provider.apiKey).then((success) => {
8609
9073
  if (!success) return;
@@ -8622,6 +9086,7 @@ function ProviderCard({
8622
9086
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
8623
9087
  "div",
8624
9088
  {
9089
+ "data-provider-id": provider.id,
8625
9090
  className: `flex flex-col gap-3 rounded-[8px] border border-border bg-card p-4 shadow-sm transition-all duration-500 ${isHighlighted === true ? "ring-2 ring-primary shadow-md" : ""}`,
8626
9091
  children: [
8627
9092
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-2", children: [
@@ -8720,51 +9185,11 @@ function ProviderCard({
8720
9185
  )
8721
9186
  ] }),
8722
9187
  provider.models !== void 0 && provider.models.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-wrap gap-1", children: provider.models.map((model) => /* @__PURE__ */ jsxRuntimeExports.jsx(ModelChip, { provider, model }, model)) }),
8723
- (providerHasEndpoint(provider.anthropicBaseUrl) || providerHasEndpoint(provider.openaiBaseUrl) || providerHasEndpoint(provider.openaiResponsesBaseUrl)) && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-2 lg:grid-cols-3", children: [
8724
- providerHasEndpoint(provider.anthropicBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsxs(ProviderFormatStatusBlock, { label: "Anthropic", children: [
8725
- testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.anthropic }),
8726
- testResults === void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Not tested yet" })
8727
- ] }),
8728
- providerHasEndpoint(provider.openaiBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsxs(ProviderFormatStatusBlock, { label: "OpenAI Chat", children: [
8729
- testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.openaiChat }),
8730
- testResults === void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Not tested yet" })
8731
- ] }),
8732
- providerHasEndpoint(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsxs(ProviderFormatStatusBlock, { label: "OpenAI Responses", children: [
8733
- testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: testResults.openaiResponses }),
8734
- testResults === void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "Not tested yet" })
8735
- ] }),
8736
- !providerHasEndpoint(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "min-w-0 rounded-md border border-border/50 bg-transparent p-2 text-xs text-muted-foreground/75", children: "OpenAI Responses not configured" })
9188
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "space-y-2", children: [
9189
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-xs font-medium text-foreground/80", children: "Connection probes" }),
9190
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestMatrix, { provider, results: testResults })
8737
9191
  ] }),
8738
- testResults?.models !== void 0 && Object.keys(testResults.models).length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
8739
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
8740
- "button",
8741
- {
8742
- type: "button",
8743
- onClick: () => setShowModelResults((value) => !value),
8744
- className: "flex items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground",
8745
- children: [
8746
- showModelResults ? /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "size-3" }),
8747
- "Model Test Results (",
8748
- Object.keys(testResults.models).length,
8749
- ")"
8750
- ]
8751
- }
8752
- ),
8753
- showModelResults && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2 overflow-x-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("table", { className: "w-full border-collapse text-xs", children: [
8754
- /* @__PURE__ */ jsxRuntimeExports.jsx("thead", { children: /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border", children: [
8755
- /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-2 py-1 text-left font-medium text-muted-foreground", children: "Model" }),
8756
- providerHasEndpoint(provider.anthropicBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-2 py-1 text-left font-medium text-muted-foreground", children: "Anthropic" }),
8757
- providerHasEndpoint(provider.openaiBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-2 py-1 text-left font-medium text-muted-foreground", children: "Chat" }),
8758
- providerHasEndpoint(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("th", { className: "px-2 py-1 text-left font-medium text-muted-foreground", children: "Responses" })
8759
- ] }) }),
8760
- /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(testResults.models).map(([modelName, modelResult]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-border/50 last:border-0", children: [
8761
- /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-1 font-medium", children: modelName }),
8762
- providerHasEndpoint(provider.anthropicBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-1 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: modelResult.anthropic }) }),
8763
- providerHasEndpoint(provider.openaiBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-1 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: modelResult.openaiChat }) }),
8764
- providerHasEndpoint(provider.openaiResponsesBaseUrl) && /* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "px-2 py-1 align-top", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderFormatTestStatus, { results: modelResult.openaiResponses }) })
8765
- ] }, modelName)) })
8766
- ] }) })
8767
- ] })
9192
+ testResults !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderModelTestResultsView, { provider, testResults })
8768
9193
  ] }),
8769
9194
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2 border-t pt-1 sm:flex-row sm:items-center sm:justify-between", children: [
8770
9195
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -13341,288 +13766,41 @@ function truncateSessionId(id) {
13341
13766
  return `${id.slice(0, 12)}...${id.slice(-12)}`;
13342
13767
  }
13343
13768
  function buildDisplayNumberByLogId(groups) {
13344
- const result = /* @__PURE__ */ new Map();
13345
- for (const group of groups) {
13346
- for (let index = 0; index < group.logs.length; index += 1) {
13347
- const log = group.logs[index];
13348
- if (log === void 0) continue;
13349
- result.set(log.id, index + 1);
13350
- }
13351
- }
13352
- return result;
13353
- }
13354
- function displayNumberForLog(log, displayNumberByLogId) {
13355
- return displayNumberByLogId.get(log.id) ?? log.id;
13356
- }
13357
- function shouldShowRawExport(captureMode) {
13358
- return captureMode === "full";
13359
- }
13360
- function getExportActionVisibility(captureMode, logCount) {
13361
- const hasLogs = logCount > 0;
13362
- return {
13363
- redacted: hasLogs,
13364
- raw: hasLogs && shouldShowRawExport(captureMode)
13365
- };
13366
- }
13367
- function computeTokenSummary(logs) {
13368
- let totalIn = 0;
13369
- let totalOut = 0;
13370
- for (const log of logs) {
13371
- if (log.inputTokens !== null) totalIn += log.inputTokens;
13372
- if (log.outputTokens !== null) totalOut += log.outputTokens;
13373
- }
13374
- return { totalIn, totalOut };
13375
- }
13376
- function formatTimeRange(logs, timeDisplayFormat) {
13377
- const first = logs[0];
13378
- const last = logs[logs.length - 1];
13379
- if (first === void 0 || last === void 0) return null;
13380
- return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
13381
- }
13382
- function getFirstUserAgent(logs) {
13383
- for (const log of logs) {
13384
- if (log.userAgent !== null && log.userAgent !== void 0 && log.userAgent !== "") {
13385
- return log.userAgent;
13386
- }
13387
- }
13388
- return null;
13389
- }
13390
- function hasLogFailure(log) {
13391
- if (log.error !== null && log.error !== void 0 && log.error.length > 0) return true;
13392
- return log.responseStatus !== null && log.responseStatus >= 400;
13393
- }
13394
- function getModelLabel(logs) {
13395
- const models = /* @__PURE__ */ new Set();
13396
- for (const log of logs) {
13397
- if (log.model !== null && log.model.length > 0) {
13398
- models.add(log.model);
13399
- }
13400
- }
13401
- const sorted = [...models].sort();
13402
- if (sorted.length === 0) return "Model unknown";
13403
- if (sorted.length === 1) return sorted[0] ?? "Model unknown";
13404
- return `${sorted.length} models`;
13405
- }
13406
- function getProviderLabel(logs) {
13407
- const providers = /* @__PURE__ */ new Set();
13408
- for (const log of logs) {
13409
- if (log.providerName !== null && log.providerName !== void 0 && log.providerName.length > 0) {
13410
- providers.add(log.providerName);
13411
- }
13412
- }
13413
- const sorted = [...providers].sort();
13414
- if (sorted.length === 0) return "Provider unknown";
13415
- if (sorted.length === 1) return sorted[0] ?? "Provider unknown";
13416
- return `${sorted.length} providers`;
13417
- }
13418
- function formatElapsedMs(ms) {
13419
- if (ms < 1e3) return `${ms}ms`;
13420
- if (ms < 6e4) {
13421
- const seconds2 = ms / 1e3;
13422
- return `${seconds2 < 10 ? seconds2.toFixed(1) : seconds2.toFixed(0)}s`;
13423
- }
13424
- const totalSeconds = Math.round(ms / 1e3);
13425
- const minutes = Math.floor(totalSeconds / 60);
13426
- const seconds = totalSeconds % 60;
13427
- return `${minutes}m ${seconds}s`;
13428
- }
13429
- function formatTokenPair(inputTokens, outputTokens) {
13430
- return `${formatTokens(inputTokens)} in / ${formatTokens(outputTokens)} out`;
13431
- }
13432
- function countSessionTurns(logs, conversationGroups) {
13433
- if (logs.length === 0) return 0;
13434
- if (conversationGroups.length === 0) return buildTurnGroups(logs).length;
13435
- let turns = 0;
13436
- for (const group of conversationGroups) {
13437
- turns += buildTurnGroups(group.logs).length;
13438
- }
13439
- return turns;
13440
- }
13441
- function summarizeFinishReason({
13442
- stopReason,
13443
- pending,
13444
- lastFailureLogNumber
13445
- }) {
13446
- if (pending > 0) {
13447
- return {
13448
- label: "In flight",
13449
- detail: `${pending} request${pending === 1 ? "" : "s"} pending`,
13450
- tone: "cool"
13451
- };
13452
- }
13453
- if (lastFailureLogNumber !== null && stopReason === null) {
13454
- return {
13455
- label: "Error",
13456
- detail: `Last failed request #${lastFailureLogNumber}`,
13457
- tone: "danger"
13458
- };
13459
- }
13460
- switch (stopReason) {
13461
- case "end_turn":
13462
- return { label: "End turn", detail: "Assistant completed the turn", tone: "neutral" };
13463
- case "stop":
13464
- return { label: "Stop", detail: "Model reached a stop condition", tone: "neutral" };
13465
- case "length":
13466
- return { label: "Output limit", detail: "Turn ended at max output tokens", tone: "watch" };
13467
- case "tool_use":
13468
- return { label: "Tool call", detail: "Assistant requested a tool", tone: "cool" };
13469
- case null:
13470
- return { label: "Unknown", detail: "No finish reason captured", tone: "neutral" };
13471
- }
13472
- }
13473
- function isClientAbortError(error) {
13474
- if (error === null) return false;
13475
- const lower = error.toLowerCase();
13476
- return lower.includes("abort") || lower.includes("cancel") || lower.includes("client disconnected") || lower.includes("stream disconnected") || lower.includes("connection closed");
13477
- }
13478
- function summarizeSessionOutcome({
13479
- pending,
13480
- failures,
13481
- lengthStops,
13482
- stopReason,
13483
- lastFailure
13484
- }) {
13485
- if (lastFailure !== null && (lastFailure.status === 499 || isClientAbortError(lastFailure.error))) {
13486
- return {
13487
- label: "Client aborted",
13488
- detail: `Request #${lastFailure.logNumber} ended before completion`,
13489
- tone: "watch"
13490
- };
13491
- }
13492
- if (pending > 0) {
13493
- return {
13494
- label: "In flight",
13495
- detail: `${pending} request${pending === 1 ? "" : "s"} still running`,
13496
- tone: "cool"
13497
- };
13498
- }
13499
- if (lastFailure !== null && lastFailure.status !== null && lastFailure.status >= 500) {
13500
- return {
13501
- label: "Provider error",
13502
- detail: `Request #${lastFailure.logNumber} returned HTTP ${lastFailure.status}`,
13503
- tone: "danger"
13504
- };
13505
- }
13506
- if (failures > 0 && lastFailure !== null) {
13507
- return {
13508
- label: "Failed",
13509
- detail: `Request #${lastFailure.logNumber} needs attention`,
13510
- tone: "danger"
13511
- };
13512
- }
13513
- if (lengthStops > 0 || stopReason === "length") {
13514
- return {
13515
- label: "Output limited",
13516
- detail: "Model stopped at the output token limit",
13517
- tone: "watch"
13518
- };
13519
- }
13520
- switch (stopReason) {
13521
- case "end_turn":
13522
- case "stop":
13523
- return { label: "Completed", detail: "Assistant completed normally", tone: "neutral" };
13524
- case "tool_use":
13525
- return { label: "Tool requested", detail: "Assistant handed off to a tool", tone: "cool" };
13526
- case null:
13527
- return { label: "Unknown", detail: "No terminal signal captured", tone: "neutral" };
13769
+ const result = /* @__PURE__ */ new Map();
13770
+ for (const group of groups) {
13771
+ for (let index = 0; index < group.logs.length; index += 1) {
13772
+ const log = group.logs[index];
13773
+ if (log === void 0) continue;
13774
+ result.set(log.id, index + 1);
13775
+ }
13528
13776
  }
13777
+ return result;
13529
13778
  }
13530
- function buildSessionSlateStats({
13531
- logs,
13532
- conversationGroups,
13533
- isLoading,
13534
- timeDisplayFormat,
13535
- slowResponseThresholdSeconds
13536
- }) {
13537
- let failures = 0;
13538
- let pending = 0;
13539
- let slow = 0;
13540
- let lengthStops = 0;
13779
+ function displayNumberForLog(log, displayNumberByLogId) {
13780
+ return displayNumberByLogId.get(log.id) ?? log.id;
13781
+ }
13782
+ function computeTokenSummary(logs) {
13541
13783
  let totalIn = 0;
13542
13784
  let totalOut = 0;
13543
- let terminalStopReason = null;
13544
- let lastFailureLogNumber = null;
13545
- let lastFailure = null;
13546
- let slowestMs = null;
13547
- let slowestLogNumber = null;
13548
- let heaviestTokens = 0;
13549
- let heaviestInputTokens = 0;
13550
- let heaviestOutputTokens = 0;
13551
- let heaviestLogNumber = null;
13552
- const slowThresholdMs = slowResponseThresholdSeconds * 1e3;
13553
- for (let index = 0; index < logs.length; index += 1) {
13554
- const log = logs[index];
13555
- if (log === void 0) continue;
13556
- const logNumber = index + 1;
13557
- const stopReason = extractStopReason(log);
13558
- if (stopReason !== null) terminalStopReason = stopReason;
13559
- if (stopReason === "length") lengthStops += 1;
13560
- if (hasLogFailure(log)) {
13561
- failures += 1;
13562
- lastFailureLogNumber = logNumber;
13563
- lastFailure = {
13564
- logNumber,
13565
- status: log.responseStatus,
13566
- error: log.error ?? null
13567
- };
13568
- }
13569
- if (log.responseStatus === null) pending += 1;
13570
- if (slowResponseThresholdSeconds > 0 && log.elapsedMs !== null && log.elapsedMs >= slowThresholdMs) {
13571
- slow += 1;
13572
- }
13785
+ for (const log of logs) {
13573
13786
  if (log.inputTokens !== null) totalIn += log.inputTokens;
13574
13787
  if (log.outputTokens !== null) totalOut += log.outputTokens;
13575
- if (log.elapsedMs !== null && (slowestMs === null || log.elapsedMs > slowestMs)) {
13576
- slowestMs = log.elapsedMs;
13577
- slowestLogNumber = logNumber;
13578
- }
13579
- const inputTokens = log.inputTokens ?? 0;
13580
- const outputTokens = log.outputTokens ?? 0;
13581
- const requestTokens = inputTokens + outputTokens;
13582
- if (requestTokens > heaviestTokens) {
13583
- heaviestTokens = requestTokens;
13584
- heaviestInputTokens = inputTokens;
13585
- heaviestOutputTokens = outputTokens;
13586
- heaviestLogNumber = logNumber;
13788
+ }
13789
+ return { totalIn, totalOut };
13790
+ }
13791
+ function getFirstUserAgent(logs) {
13792
+ for (const log of logs) {
13793
+ if (log.userAgent !== null && log.userAgent !== void 0 && log.userAgent !== "") {
13794
+ return log.userAgent;
13587
13795
  }
13588
13796
  }
13589
- const status = isLoading || pending > 0 && failures === 0 ? "rolling" : failures > 0 ? "failed" : slow > 0 || lengthStops > 0 ? "watch" : "clear";
13590
- const statusLabel2 = status === "rolling" ? "Rolling" : status === "failed" ? "Failure" : status === "watch" ? "Watch" : "Clear";
13591
- const finish = summarizeFinishReason({
13592
- stopReason: terminalStopReason,
13593
- pending,
13594
- lastFailureLogNumber
13595
- });
13596
- const outcome = summarizeSessionOutcome({
13597
- pending,
13598
- failures,
13599
- lengthStops,
13600
- stopReason: terminalStopReason,
13601
- lastFailure
13602
- });
13603
- return {
13604
- status,
13605
- statusLabel: statusLabel2,
13606
- modelLabel: getModelLabel(logs),
13607
- providerLabel: getProviderLabel(logs),
13608
- requests: logs.length,
13609
- turns: countSessionTurns(logs, conversationGroups),
13610
- failures,
13611
- pending,
13612
- slow,
13613
- lengthStops,
13614
- timeRange: formatTimeRange(logs, timeDisplayFormat),
13615
- tokenLabel: `${formatTokens(totalIn)} in / ${formatTokens(totalOut)} out`,
13616
- finishLabel: finish.label,
13617
- finishDetail: finish.detail,
13618
- finishTone: finish.tone,
13619
- outcomeLabel: outcome.label,
13620
- outcomeDetail: outcome.detail,
13621
- outcomeTone: outcome.tone,
13622
- slowestLabel: slowestMs === null ? "n/a" : formatElapsedMs(slowestMs),
13623
- slowestDetail: slowestLogNumber === null ? "Latency pending" : `Request #${slowestLogNumber}`,
13624
- heaviestDetail: heaviestLogNumber === null ? "Tokens pending" : `#${heaviestLogNumber} ${formatTokenPair(heaviestInputTokens, heaviestOutputTokens)}`
13625
- };
13797
+ return null;
13798
+ }
13799
+ function formatTimeRange(logs, timeDisplayFormat) {
13800
+ const first = logs[0];
13801
+ const last = logs[logs.length - 1];
13802
+ if (first === void 0 || last === void 0) return null;
13803
+ return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
13626
13804
  }
13627
13805
  function slateMetricToneClass(tone) {
13628
13806
  switch (tone) {
@@ -13746,35 +13924,6 @@ function SessionOpeningSlate({ stats }) {
13746
13924
  ] })
13747
13925
  ] });
13748
13926
  }
13749
- function resolveSessionContextScope({
13750
- pinnedSessionId,
13751
- selectedSession,
13752
- sessions,
13753
- logs
13754
- }) {
13755
- if (pinnedSessionId !== void 0) {
13756
- const scopedLogs = logs.filter((log) => log.sessionId === pinnedSessionId);
13757
- if (scopedLogs.length === 0) return null;
13758
- return { label: pinnedSessionId, logs: scopedLogs, showBackLink: true };
13759
- }
13760
- if (selectedSession !== "__all__") {
13761
- const scopedLogs = logs.filter((log) => log.sessionId === selectedSession);
13762
- if (scopedLogs.length === 0) return null;
13763
- return { label: selectedSession, logs: scopedLogs, showBackLink: true };
13764
- }
13765
- const singleSessionId = sessions.length === 1 ? sessions[0] ?? null : null;
13766
- if (singleSessionId !== null) {
13767
- const allVisibleLogsBelongToSession = logs.every((log) => log.sessionId === singleSessionId);
13768
- if (allVisibleLogsBelongToSession) {
13769
- return { label: singleSessionId, logs: [...logs], showBackLink: false };
13770
- }
13771
- }
13772
- const allVisibleLogsAreSessionless = sessions.length === 0 && logs.length > 0 && logs.every((log) => log.sessionId === null);
13773
- if (allVisibleLogsAreSessionless) {
13774
- return { label: "Visible requests", logs: [...logs], showBackLink: false };
13775
- }
13776
- return null;
13777
- }
13778
13927
  function formatPercent(value) {
13779
13928
  if (value === null) return "Unknown";
13780
13929
  const percent = value * 100;
@@ -13971,23 +14120,6 @@ function SessionMembershipPanel({
13971
14120
  }) })
13972
14121
  ] });
13973
14122
  }
13974
- function formatPaginationStatus({
13975
- logs,
13976
- pagination
13977
- }) {
13978
- if (pagination.isLoading && logs.length === 0) return "Loading logs...";
13979
- if (logs.length === 0) return "No logs on this page";
13980
- const first = logs[0];
13981
- const last = logs[logs.length - 1];
13982
- const range = first === void 0 || last === void 0 ? "Current page" : `#${first.id}-#${last.id}`;
13983
- if (pagination.total === null) {
13984
- return `Showing ${logs.length} logs (${range})`;
13985
- }
13986
- if (pagination.total <= logs.length) {
13987
- return `Showing all ${pagination.total} logs`;
13988
- }
13989
- return `Showing ${logs.length} of ${pagination.total} logs (${range})`;
13990
- }
13991
14123
  function LogPaginationButton({
13992
14124
  label,
13993
14125
  title,
@@ -14261,11 +14393,107 @@ function CrabFamily({
14261
14393
  }) })
14262
14394
  ] });
14263
14395
  }
14396
+ function ThemeModeToggle() {
14397
+ const { themeMode, setThemeMode } = useThemeMode();
14398
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14399
+ "div",
14400
+ {
14401
+ className: "border-border/70 bg-muted/70 inline-flex h-8 items-center rounded-md border p-0.5 shadow-sm",
14402
+ "aria-label": "Theme mode",
14403
+ role: "group",
14404
+ children: [
14405
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14406
+ "button",
14407
+ {
14408
+ type: "button",
14409
+ onClick: () => setThemeMode("light"),
14410
+ className: cn(
14411
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14412
+ themeMode === "light" && "bg-background text-foreground shadow-sm"
14413
+ ),
14414
+ "aria-label": "Use light mode",
14415
+ "aria-pressed": themeMode === "light",
14416
+ title: "Light mode",
14417
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sun, { className: "size-3.5" })
14418
+ }
14419
+ ),
14420
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14421
+ "button",
14422
+ {
14423
+ type: "button",
14424
+ onClick: () => setThemeMode("eye-care"),
14425
+ className: cn(
14426
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14427
+ themeMode === "eye-care" && "bg-background text-foreground shadow-sm"
14428
+ ),
14429
+ "aria-label": "Use eye care mode",
14430
+ "aria-pressed": themeMode === "eye-care",
14431
+ title: "Eye care mode",
14432
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3.5" })
14433
+ }
14434
+ ),
14435
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14436
+ "button",
14437
+ {
14438
+ type: "button",
14439
+ onClick: () => setThemeMode("dark"),
14440
+ className: cn(
14441
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14442
+ themeMode === "dark" && "bg-background text-foreground shadow-sm"
14443
+ ),
14444
+ "aria-label": "Use dark mode",
14445
+ "aria-pressed": themeMode === "dark",
14446
+ title: "Dark mode",
14447
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Moon, { className: "size-3.5" })
14448
+ }
14449
+ )
14450
+ ]
14451
+ }
14452
+ );
14453
+ }
14454
+ function UiScaleControl({
14455
+ preference,
14456
+ resolvedScale,
14457
+ onChange
14458
+ }) {
14459
+ const resolvedLabel = formatUiScale(resolvedScale);
14460
+ const currentLabel = preference === "auto" ? `Auto (${resolvedLabel})` : `${preference}%`;
14461
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14462
+ Select,
14463
+ {
14464
+ value: preference,
14465
+ onValueChange: (value) => onChange(normalizeUiScalePreference(value)),
14466
+ children: [
14467
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
14468
+ SelectTrigger,
14469
+ {
14470
+ size: "sm",
14471
+ className: "bg-white/[0.035] hover:bg-white/[0.065] h-8 min-w-[4rem] justify-center gap-1 border-0 px-2 py-0 text-muted-foreground shadow-[inset_0_0_0_1px_rgba(255,255,255,0.06)] [&>svg:last-child]:hidden",
14472
+ "aria-label": `Interface size: ${currentLabel}`,
14473
+ children: [
14474
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ALargeSmall, { className: "size-3.5" }),
14475
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] font-semibold tabular-nums", children: resolvedLabel })
14476
+ ]
14477
+ }
14478
+ ),
14479
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { align: "end", children: [
14480
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "auto", children: "Auto" }),
14481
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "100", children: "100%" }),
14482
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "125", children: "125%" }),
14483
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "150", children: "150%" })
14484
+ ] })
14485
+ ]
14486
+ }
14487
+ );
14488
+ }
14264
14489
  function BrandHeader({
14265
14490
  compact,
14266
14491
  crabEntrancePhase,
14267
14492
  currentSessionId,
14268
- logCount
14493
+ logCount,
14494
+ uiScalePreference,
14495
+ resolvedUiScale,
14496
+ onUiScaleChange
14269
14497
  }) {
14270
14498
  if (compact) {
14271
14499
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-white/[0.035] shadow-[inset_0_1px_0_rgba(255,255,255,0.045),0_16px_44px_rgba(0,0,0,0.18)] rounded-[8px] p-3", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [
@@ -14283,6 +14511,15 @@ function BrandHeader({
14283
14511
  ] })
14284
14512
  ] }),
14285
14513
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
14514
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeModeToggle, {}),
14515
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14516
+ UiScaleControl,
14517
+ {
14518
+ preference: uiScalePreference,
14519
+ resolvedScale: resolvedUiScale,
14520
+ onChange: onUiScaleChange
14521
+ }
14522
+ ),
14286
14523
  /* @__PURE__ */ jsxRuntimeExports.jsx(AlertsDialog, {}),
14287
14524
  /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
14288
14525
  /* @__PURE__ */ jsxRuntimeExports.jsx(AgentLabDialog, { currentSessionId, logCount }),
@@ -14308,6 +14545,15 @@ function BrandHeader({
14308
14545
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-full text-sm font-semibold text-muted-foreground", children: "Local First. Evidence First." })
14309
14546
  ] }),
14310
14547
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 justify-end gap-1", children: [
14548
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeModeToggle, {}),
14549
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14550
+ UiScaleControl,
14551
+ {
14552
+ preference: uiScalePreference,
14553
+ resolvedScale: resolvedUiScale,
14554
+ onChange: onUiScaleChange
14555
+ }
14556
+ ),
14311
14557
  /* @__PURE__ */ jsxRuntimeExports.jsx(AlertsDialog, {}),
14312
14558
  /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
14313
14559
  /* @__PURE__ */ jsxRuntimeExports.jsx(AgentLabDialog, { currentSessionId, logCount }),
@@ -14881,6 +15127,7 @@ function ProxyViewer({
14881
15127
  }) {
14882
15128
  const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
14883
15129
  const { providers } = useProviders();
15130
+ const { uiScalePreference, resolvedUiScale, setUiScalePreference } = useUiScale();
14884
15131
  const [exporting, setExporting] = reactExports.useState(null);
14885
15132
  const [exportError, setExportError] = reactExports.useState(null);
14886
15133
  const [importing, setImporting] = reactExports.useState(false);
@@ -15122,7 +15369,7 @@ function ProxyViewer({
15122
15369
  onClick: toggleDetailsPanelCollapsed,
15123
15370
  className: cn(
15124
15371
  "group fixed top-1/2 right-3 z-40 inline-flex h-14 w-7 -translate-y-1/2 items-center justify-center rounded-l-md border border-white/[0.08] bg-background/90 text-muted-foreground shadow-[0_12px_34px_rgba(0,0,0,0.26),inset_0_1px_0_rgba(255,255,255,0.06)] backdrop-blur transition-[right,background-color,color,border-color] duration-200 hover:border-white/[0.16] hover:bg-white/[0.07] hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
15125
- detailsPanelCollapsed ? "right-3" : "lg:right-[326px] xl:right-[346px]"
15372
+ detailsPanelCollapsed ? "right-3" : "lg:right-[calc(21.25rem-0.875rem)] xl:right-[calc(22.5rem-0.875rem)]"
15126
15373
  ),
15127
15374
  "aria-label": detailsPanelCollapsed ? "Show details panel" : "Hide details panel",
15128
15375
  title: detailsPanelCollapsed ? "Show details panel" : "Hide details panel",
@@ -15138,7 +15385,7 @@ function ProxyViewer({
15138
15385
  {
15139
15386
  className: cn(
15140
15387
  "grid w-full min-w-0 grid-cols-1 lg:min-h-0 lg:flex-1 lg:overflow-hidden",
15141
- detailsPanelCollapsed ? "lg:grid-cols-[minmax(0,1fr)]" : "lg:grid-cols-[minmax(0,1fr)_340px] xl:grid-cols-[minmax(0,1fr)_360px]"
15388
+ detailsPanelCollapsed ? "lg:grid-cols-[minmax(0,1fr)]" : "lg:grid-cols-[minmax(0,1fr)_21.25rem] xl:grid-cols-[minmax(0,1fr)_22.5rem]"
15142
15389
  ),
15143
15390
  children: [
15144
15391
  !detailsPanelCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx("aside", { className: "bg-background/95 order-1 border-b border-white/[0.045] lg:order-2 lg:h-full lg:overflow-y-auto lg:border-b-0 lg:border-l lg:border-white/[0.055] inspector-scrollbar", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4 p-4", children: [
@@ -15148,7 +15395,10 @@ function ProxyViewer({
15148
15395
  compact: true,
15149
15396
  crabEntrancePhase,
15150
15397
  currentSessionId: labSessionId,
15151
- logCount: logs.length
15398
+ logCount: logs.length,
15399
+ uiScalePreference,
15400
+ resolvedUiScale,
15401
+ onUiScaleChange: setUiScalePreference
15152
15402
  }
15153
15403
  ),
15154
15404
  /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarPanel, { label: "Session", children: [
@@ -15489,6 +15739,32 @@ function ProxyViewer({
15489
15739
  ) })
15490
15740
  ] });
15491
15741
  }
15742
+ function filterLogs(logs, selectedSession, selectedModel) {
15743
+ if (selectedSession === "__all__" && selectedModel === "__all__") return logs;
15744
+ return logs.filter((l) => {
15745
+ if (selectedSession !== "__all__" && l.sessionId !== selectedSession) return false;
15746
+ if (selectedModel !== "__all__" && l.model !== selectedModel) return false;
15747
+ return true;
15748
+ });
15749
+ }
15750
+ function mergeLogsById(previousLogs, incomingLogs) {
15751
+ if (previousLogs.length === 0) return [...incomingLogs];
15752
+ if (incomingLogs.length === 0) return [...previousLogs];
15753
+ const byId = /* @__PURE__ */ new Map();
15754
+ for (const log of previousLogs) byId.set(log.id, log);
15755
+ for (const log of incomingLogs) byId.set(log.id, log);
15756
+ return [...byId.values()].toSorted((left, right) => left.id - right.id);
15757
+ }
15758
+ function mergeSessionIds(previousSessions, incomingSessions) {
15759
+ const merged = /* @__PURE__ */ new Set();
15760
+ for (const session of previousSessions) {
15761
+ if (session !== "") merged.add(session);
15762
+ }
15763
+ for (const session of incomingSessions) {
15764
+ if (session !== "") merged.add(session);
15765
+ }
15766
+ return [...merged].sort();
15767
+ }
15492
15768
  const SSEUpdateSchema = union([
15493
15769
  object({
15494
15770
  type: literal("init"),
@@ -15549,14 +15825,6 @@ function extractModels(logs) {
15549
15825
  }
15550
15826
  return [...set];
15551
15827
  }
15552
- function filterLogs(logs, selectedSession, selectedModel) {
15553
- if (selectedSession === "__all__" && selectedModel === "__all__") return logs;
15554
- return logs.filter((l) => {
15555
- if (selectedSession !== "__all__" && l.sessionId !== selectedSession) return false;
15556
- if (selectedModel !== "__all__" && l.model !== selectedModel) return false;
15557
- return true;
15558
- });
15559
- }
15560
15828
  const DEBOUNCE_MS = 100;
15561
15829
  const HASH_SCROLL_ATTEMPTS = 12;
15562
15830
  const HASH_HIGHLIGHT_MS = 1800;
@@ -15641,27 +15909,6 @@ async function deleteAllGroupsForFullHistoryDelete() {
15641
15909
  return { deleted: 0, ok: false };
15642
15910
  }
15643
15911
  }
15644
- function mergeLogsById(previousLogs, incomingLogs) {
15645
- if (incomingLogs.length === 0) return [...previousLogs];
15646
- const byId = /* @__PURE__ */ new Map();
15647
- for (const log of previousLogs) {
15648
- byId.set(log.id, log);
15649
- }
15650
- for (const log of incomingLogs) {
15651
- byId.set(log.id, log);
15652
- }
15653
- return [...byId.values()].sort((left, right) => left.id - right.id);
15654
- }
15655
- function mergeSessionIds(previousSessions, incomingSessions) {
15656
- const sessions = /* @__PURE__ */ new Set();
15657
- for (const session of previousSessions) {
15658
- if (session !== "") sessions.add(session);
15659
- }
15660
- for (const session of incomingSessions) {
15661
- if (session !== "") sessions.add(session);
15662
- }
15663
- return [...sessions].sort();
15664
- }
15665
15912
  function ProxyViewerContainer({
15666
15913
  initialSessionId
15667
15914
  } = {}) {