@tonyclaw/agent-inspector 3.0.23 → 3.0.25

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 (49) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{CompareDrawer-CFedvZHU.js → CompareDrawer-BnvOPwpe.js} +1 -1
  3. package/.output/public/assets/ProxyViewerContainer-DcqFqo1m.js +127 -0
  4. package/.output/public/assets/{ReplayDialog-X-fRSKAu.js → ReplayDialog-BJgoQWdW.js} +1 -1
  5. package/.output/public/assets/{RequestAnatomy-S8qB5nVg.js → RequestAnatomy-CpBZEHnM.js} +1 -1
  6. package/.output/public/assets/{ResponseView-_rcOWSwJ.js → ResponseView-CWmyRb2M.js} +2 -2
  7. package/.output/public/assets/{StreamingChunkSequence-Bc15AVpN.js → StreamingChunkSequence-BWhC8bwx.js} +1 -1
  8. package/.output/public/assets/{_sessionId-Cz-HW2Wf.js → _sessionId-Cq-2OqUU.js} +1 -1
  9. package/.output/public/assets/{index-CEgwWY3E.js → index-BKi9IRtV.js} +71 -7
  10. package/.output/public/assets/index-BMYuDL5X.css +1 -0
  11. package/.output/public/assets/{index-CXYBU0j8.js → index-Cf8O0Oos.js} +1 -1
  12. package/.output/public/assets/{index-ClKXooQd.js → index-DdgTJ4QJ.js} +1 -1
  13. package/.output/public/assets/{json-viewer-QfU4qGdd.js → json-viewer-Bqu_4hmm.js} +1 -1
  14. package/.output/server/_libs/lucide-react.mjs +306 -246
  15. package/.output/server/{_sessionId-9FAmYZVf.mjs → _sessionId-Ba7H3Gjw.mjs} +2 -2
  16. package/.output/server/_ssr/{CompareDrawer-BLKjOzwD.mjs → CompareDrawer-aH59Ab6x.mjs} +3 -3
  17. package/.output/server/_ssr/{ProxyViewerContainer-Dcs2Mq8l.mjs → ProxyViewerContainer-By-I0tJv.mjs} +1791 -1259
  18. package/.output/server/_ssr/{ReplayDialog-oyiF4LIo.mjs → ReplayDialog-BO4Vy0et.mjs} +4 -4
  19. package/.output/server/_ssr/{RequestAnatomy-DkyQXSuj.mjs → RequestAnatomy-FtOr7vSS.mjs} +3 -3
  20. package/.output/server/_ssr/{ResponseView-3NTDEiDh.mjs → ResponseView-cvw6ajue.mjs} +3 -3
  21. package/.output/server/_ssr/{StreamingChunkSequence-DRq-5Bgn.mjs → StreamingChunkSequence-Oc8S2kD6.mjs} +3 -3
  22. package/.output/server/_ssr/{index-dtMiNUa1.mjs → index-BDRlQheR.mjs} +2 -2
  23. package/.output/server/_ssr/index.mjs +2 -2
  24. package/.output/server/_ssr/{json-viewer-BhUwicvp.mjs → json-viewer-Cd6trhQr.mjs} +3 -3
  25. package/.output/server/_ssr/{router-DO0OntUK.mjs → router-bofP-Ekl.mjs} +296 -91
  26. package/.output/server/{_tanstack-start-manifest_v-DO5v3BHY.mjs → _tanstack-start-manifest_v-BPuf86qB.mjs} +1 -1
  27. package/.output/server/index.mjs +76 -76
  28. package/package.json +3 -2
  29. package/src/components/ProxyViewer.tsx +138 -740
  30. package/src/components/ProxyViewerContainer.tsx +5 -47
  31. package/src/components/alerts/AlertsDialog.tsx +6 -2
  32. package/src/components/inspector-pet/InspectorPet.tsx +390 -0
  33. package/src/components/inspector-pet/inspectorPetLogic.ts +273 -0
  34. package/src/components/providers/ProviderCard.tsx +20 -579
  35. package/src/components/providers/ProviderTestResultsView.tsx +732 -0
  36. package/src/components/proxy-viewer/LogEntry.tsx +5 -0
  37. package/src/components/proxy-viewer/LogEntryHeader.tsx +7 -0
  38. package/src/components/proxy-viewer/ToolTraceEvents.tsx +33 -36
  39. package/src/components/proxy-viewer/TurnGroup.tsx +1 -5
  40. package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +46 -0
  41. package/src/components/proxy-viewer/proxyViewerLogic.ts +469 -0
  42. package/src/lib/themeMode.ts +45 -0
  43. package/src/lib/uiScale.ts +112 -0
  44. package/src/lib/useThemeMode.ts +40 -0
  45. package/src/lib/useUiScale.ts +52 -0
  46. package/src/routes/__root.tsx +78 -1
  47. package/styles/globals.css +403 -24
  48. package/.output/public/assets/ProxyViewerContainer-BlCrBvdm.js +0 -127
  49. package/.output/public/assets/index-B_SIQ47i.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-DO0OntUK.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-bofP-Ekl.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, H as Heart, e as Pause, f as Play, g as Plus, B as BrainCircuit, L as LoaderCircle, h as Send, A as ArrowLeft, i as ChevronUp, j as Copy, k as ChevronsLeft, l as ChevronLeft, m as ChevronRight, n as ChevronsRight, o as Clapperboard, p as Flag, q as Siren, r as Timer, G as Gauge, s as Sun, E as Eye, M as Moon, t as ALargeSmall, u as Layers, v as FlaskConical, w as Sparkles, x as Activity, y as Beaker, N as Network, z as Terminal, J as Settings, K as Monitor, O as Rocket, Q as PackageCheck, V as History, W as Scan, Y as CircleAlert, Z as ArrowUpRight, _ as ArrowDownRight, $ as Cpu, a0 as FolderOpen, a1 as Bell, a2 as Clock3, a3 as ShieldAlert, a4 as EyeOff, a5 as ExternalLink, a6 as Clock, a7 as RotateCw, a8 as Pencil, a9 as MessageSquare, aa as Zap, ab as Wrench, ac as Brain, ad as CircleCheckBig, ae as Minus, af as CircleX, ag as ShieldCheck, ah as Save, ai as FileSearch, aj as CloudUpload, ak as CircleQuestionMark, al as Server, am as Lock, an as Wifi, ao as WifiOff, ap as OctagonAlert, aq as Radio, ar as Globe, as as FileTerminal, at as ChevronsUp, au as ChevronsDown, av as FileDiff, aw as RotateCcw, ax as GitCompareArrows, ay as ArrowUp, az as ArrowDown, aA as Ellipsis, aB as CodeXml, aC as Blocks, aD as MousePointerClick, aE as FileText, aF as Rows3, aG 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-BLKjOzwD.mjs").then((m) => ({ default: m.CompareDrawer }))
2528
+ () => import("./CompareDrawer-aH59Ab6x.mjs").then((m) => ({ default: m.CompareDrawer }))
2476
2529
  );
2477
2530
  const LazyReplayDialog = reactExports.lazy(
2478
- () => import("./ReplayDialog-oyiF4LIo.mjs").then((m) => ({ default: m.ReplayDialog }))
2531
+ () => import("./ReplayDialog-BO4Vy0et.mjs").then((m) => ({ default: m.ReplayDialog }))
2479
2532
  );
2480
2533
  const LazyRequestAnatomy = reactExports.lazy(
2481
- () => import("./RequestAnatomy-DkyQXSuj.mjs").then((m) => ({ default: m.RequestAnatomy }))
2534
+ () => import("./RequestAnatomy-FtOr7vSS.mjs").then((m) => ({ default: m.RequestAnatomy }))
2482
2535
  );
2483
2536
  const LazyResponseView = reactExports.lazy(
2484
- () => import("./ResponseView-3NTDEiDh.mjs").then((m) => ({ default: m.ResponseView }))
2537
+ () => import("./ResponseView-cvw6ajue.mjs").then((m) => ({ default: m.ResponseView }))
2485
2538
  );
2486
2539
  const LazyStreamingChunkSequence = reactExports.lazy(
2487
- () => import("./StreamingChunkSequence-DRq-5Bgn.mjs").then((m) => ({
2540
+ () => import("./StreamingChunkSequence-Oc8S2kD6.mjs").then((m) => ({
2488
2541
  default: m.StreamingChunkSequence
2489
2542
  }))
2490
2543
  );
2491
2544
  const LazyJsonViewer = reactExports.lazy(
2492
- () => import("./json-viewer-BhUwicvp.mjs").then((m) => ({ default: m.JsonViewer }))
2545
+ () => import("./json-viewer-Cd6trhQr.mjs").then((m) => ({ default: m.JsonViewer }))
2493
2546
  );
2494
2547
  const LazyJsonViewerFromString = reactExports.lazy(
2495
- () => import("./json-viewer-BhUwicvp.mjs").then((m) => ({ default: m.JsonViewerFromString }))
2548
+ () => import("./json-viewer-Cd6trhQr.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,iVBORw0KGgoAAAANSUhEUgAAAPoAAAD6CAQAAAAi5ZK2AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfpCxsJDCnsge2KAAAM2ElEQVR42u2de3AW1RnGnyQQDARDwiUFpHIVMHIpYgFRB2nLxQrC0FIDiCKDWNqBUqBlai8wvciIpaRTWh3QDqCDIIjKDL0IaBC5SKlysWJArgqh3BMICYFs/wgpGUi+8579dr89e/Z5zp/59j2755fn3d2z5wJQFEVRFEVRoVQSm+AG1UFrtEdT1EcjNEA5SlCMIhxDAQoJ3S4lIwf9cD+6ozXq1vKbYuzHNmxEPo6xwcKtungIS3EajkYpwLPoyqYLp3KwACe1cFcvezATmWzEMOk+rMBV18CrSjHy0IqNGQb1wMa4cV8vZfgzGrNRTVYm8nDFQ+SV5QymIIWNa6ZGaz6y6ZR/I4cNbJrSkOcb8MpyCVPYzCapKz71GXllWY5b2dhm6EGcTwhyBw52oyUbPHgNx6WEIXfg4CDuYKMHq0kevI/rlhP4Ghs+OE1ERcKRO3BwEh3Z+NFC7sDBATQngMRrcoDIHTj4iE/yUXJ5VVlhYsMkWezy+a6urgRbsAP7cRincQGpqI+maI+O6IMuSHYRbwIW0X/mJvZTeAH9ah1CATTCaKxBmWbUi7iTOMxEXoCncIsodgvM1ezo2cFPMebdy89qfyHL0vxSN5FQzHL5ajRxVU8v7NO4cWQRjCnIy/DDOOq6FcvENS0gGjMS+wUMjvvdZ5awrsv4KvEE7/Iz+Lontc4Q1vc8AQXt8rO4x7Oa54hqLOLI2aBdfrenHVyviWqdQUx2uLxS6fhMUO8ugrLD5VXqLfpmfxdh2eHyKi0S1P5b4rLF5ZVqiiJl/Z8SmD0ur9TzgnNoQWi2uLxSt+Gy8ixGEpvZLs9AN3RGqsYRa5Xn8SeCM9XlDfFT7Ll2bDnWYYRwOMZjyjP5kOjMdPkDOHpTjPX4iuDILOWL2znCMxH5IyitMc4+ZAuO3q08nyYEaFpiHxbjYWyDIMm/qDyje4kwPMgdOBihjDBNeU7fJcQwIXewTnBzUJ3Vk8Rozr18mOAtuxT1FFH6KmNMJkhTkH+7lse3G0s7RZyuygjPEGVYEntV6aSIlKOM8CvCDEtirywVyFDE6q2M8WPiDJPLJcMgBiljPEWg4XG5AwfTlfEmKWPkEml4XO7gCBooI85TRvlm8I1ax3CX68w8PYeB2KGFfEWM6Yo3qgy5uKj8VU/lLz5HG2QjHRlIA1CCIhTjBI6ggg43LbGXYYggZj3lgkYVtbwaluITvIlf4gHhNEomdheJXfpeXoX8EVHUAR4sY1CKfEyN5gibMCIHXvJsBYur2IDxSCNy05HXx1mPFy/5L2ZFZb6rGR2uusiByb6sWlOMZ5FOl5vocqAuDvm2XNExjLV5t52wuhyY7vM6Ve+jA11uksuB5glYcLgIY+hyc1yeJBj87E35q6BXkC5PAHJgdgKXI9xlzzt8eF0OjE/wypQH7Li7h9nl4wJYjPQ4utPlwSF/LKD1Z8+gC10eLeQOHHyJ1nR54pGPCniVadmcGyK3xOVVZYvWnNpqb5hBIv+L1hCJAdiuhXyVcpT6dV3GSLylhXyxy7YrxHYU4CBO4yKABmiCNuiIe9DMVbT5mMp7eWJc/rgLl1dgC6bE2NelM6Zim4uoDzOxm5nYS/GicNOuznhJc0X5k2HprIkS8gos1cTSSrgYodFbhkQ5sR/F/a7aqD8Oa9TSny43x+UblDNialcmVovr2WP2qOYoIV8V57tREuaK6/o+E7sJid2bO+00Ya0HTPV6lFy+3LNWmymsMZfIg+1w9fZ5ep6ozp3mjaKjy90rGetF9d5H5Ha4vFLZOB62zYGIPH6NEk2NqEvk4U/s1V/f8gX1DyJye5ADQC/BGfyRyO1I7Nf1rvIcPiZye1xeqaGCua6ZNiMf7sOSAolD3h+v4otrH2RewYPCo1JxWnkmQ4jcROTZNcyGWSMcN7PQ5KUJw4zc33t5G+xHzfu3S7655wqmPRG5ccgPxxjkqN7DvbnyfDYRuVmJvY1iYMQEQYxCxRkVEnlYXF5ZdguibFLEKI/6S9pQg1x+Ow4IYrZSxlmqjJFGl4fF5dLVJBcoYzQl8jDcy3XestV7tbcl8vC43IGD3spo6gEVtxF5mJCXCu7H6u6ZRlFE/nAoE7sDB8sEEd9URqkTfuT2P7FXlSuiZQb2KKKU0OXhcbmDGYKYdZWrTO8l8nDcyx04mCeKqh5IsYaJPRyJ3cEfhHFnePTPQ+ShQQ68r4z1NJEHkdj9Q95KcN7didwmlwOzBC2dTOT2uByoj5PBPcbR5UEgly0tPo3I7XE5kI1zgoVOWhO5PS4HXhHE/IDI7XE5MFIU9QdEbo/LO6NIEPWS98Mn/F04pD+R16oWOCiK+4LXyMf4irwnin1ErrtWzGtGIW+MXcJvdB4v/H87LvqIvL1yWG90XZ6J7UHNpVvoI/KGKPDxS5q/Lm/ry5e06i7/SBi5HHd5izxVI/nqIpcM6aXL1WW+1z7v6SPyJ0I7RMJvl+sgP+H9uLihviFvp5FDmNhrL096/34+2Jf3cgBYw8TuAfI3/OiHu8MXl+tsMM97ee3lKBr7AT1JuVuwG+Sp2EvkcSMvR1+/PqbO9Bw5MIn38rgTu69rP6fFeJd2hzwFn/OJPU6XO5gNX5VTy6iNk66QA4+KLqoCY5nYay0L4bs6YedN1e5EJ5fRdogu60dM7DE+/SZk+lIqxmMzrl5br2wzxrvbCA5Ad9FlraTLay2vJ3YV2DS0Rds4VzuYI7isQ1q9TNG6l79uzsK/8ldAyffhYRoRwz0qJgLIgT6CC/uHMcjpck/0c8Gl9aHLbUIO/FN5aflEbhfyOoJBfrlEbhNyydf5C0jnvdwm5MAY5eWtInK7kAOzlRc4mYndLuTAq8pL7KGMMTzUXTGGdrj6qc2KS7yq7O27E1fYxx4ufaK4yCOK41M0ofg7dYHIRVIh2xr3gyBdbpxUW8+8ozh+PV0ePqkmSK1RJPdLhrj890Qu1ynFxa6PeXRTQ76X871cS6rPqh/GPDrLiJc0ulxTqgVtj8U8OglneS8Pn9RrHjaMefwbvJeHT4vj/JY+iMjDJ/UQip8oIqyNcewyJnYT9T3lpf9NEaEJPqvlyEV0uZnqKpjVkqWIkV3DTuKlmusfE3kClYwzygaYIIiSi03XxuA7OIF5aE7kJuttZRPsQpIoUga64W601l75mF0xCZdkYdvBPtZP5AGom6Ahtvu2brnfiZ3IXfbKye7rdHmo9AtBc5zyYftIujxAtRONcduIFLrcJm3wpdmDdfm/iDy2BgqbZnpoXE7kSiUJOy8qMNWD2nJwlC43QY+Km+i5OF/fHhD0ARJ5grpjt4qbaT1auM4oU1DGxG6Oev2/71xdCpEr7Jqtro5YpwWELk+AXtZqsHz01ojdDHM1PU7kCVEznNDEko8RuEXQ0bsAJZqRiTxhGqQ5FdGBg3NYgsfRpoZoDTEIv8N/tCNGDnlSwPU/hxkujyzCPhzBOZQiBY3QFB3i6Lidp7lhZSbe0VorcyVGoZwer1JdbHHlTC8LE3vC1US8CLg/xf+XtDqEfLPa4BiRR0/dBXsGhzOxE3kMdcGXCUc+h8iDT/IFCQR+BROJ3ARlaw5DcF8uYAiRm6J6yHPRXaNb9mhvUknkPmuocuGC+MoS1Cdy89QSK3wCvg8PaZ8NkSdM/YT7hsvLRcwSfKwh8kBVB08LN/JSlxIscNUzT+QBKBlDsC1O4OeR53LkDZEHqN7Ic9VRW4a3kav92EbkxigF38BcbEO5CMBhLME45Tz3yCJPChn8dPRBF7RHB3RARrVtvC6hGIewHwUowBYcirMW/e/lubhCZybunyALmR7/6zKxR05ETuRETuRETuRsNCKniJwicorIKSKniJwicorIKSKniJyqGfnHmvsyE3nIlRJzHxi63ErlEXnUNI6JPWrKUO7rTJdbp98QedSUjWIm9qjpGSKPnrYysUdNzYRLEBO5RRrLxF6p5AhB7yz4zUqMtn+CUpSgZyt/sZpz0mzTWuXU5sbRaIgoOT1T8fdCnCZ026RC2lJzQ25CD4EKFX9PwWLUI/RoQQe+hbdcrEBFGazRovf0tdFwe1SUJVyx5u90u03aKOx7J3aLNEP8lY1J3ho10hg3Q7dbo59pjJwhdkvUAMeJPXr6jtY2Ary3W6Jfaw2CptutUJLmJgLEboXSsUkLO5O8JQ9079LtxE7sxE7sxE7sFqkhH+mIXV1WhG43DMoD7OPZZNHD/gWnPUUR+4BwX2wyeQMAijEYH4h/fS+hRw97c0K3B/tAvCf6ZRmh26OLGCpy+wE2VfQe6XLYTFHD/h6bKGrYy9CDDWQr9vwakV/FE2wce5WGl29CfgrD2TC2qy+W4/w14Hsxy5aVKvjFSN1CLVAPp1DEpqAoiqIoyiz9D9lYMumhgrvLAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI1LTExLTI3VDA5OjEyOjQxKzAwOjAwvIZCxQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNS0xMS0yN1QwOToxMjo0MSswMDowMM3b+nkAAAAASUVORK5CYII=";
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);
8324
- }
8325
- if (providerHasEndpoint(provider.openaiBaseUrl)) {
8326
- results.push(testResults.openaiChat);
8327
- }
8328
- if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
8329
- results.push(testResults.openaiResponses);
8824
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-muted-foreground", children: "No testable endpoint configured." });
8330
8825
  }
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,176 +8965,63 @@ 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
  }
8396
- function compactModelLabel(provider) {
8397
- const models = provider.models ?? [];
8398
- if (models.length === 0) return "No models configured";
8399
- const visible = models.slice(0, 2).join(", ");
8400
- const hiddenCount = models.length - 2;
8401
- return hiddenCount > 0 ? `${visible} +${hiddenCount} more` : visible;
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 !== "";
8402
8978
  }
8403
- function ModelChip({ provider, model }) {
8404
- const metadata = findProviderModelMetadata(provider, model);
8405
- const sourceLink = getModelSourceLink(metadata?.sourceUrl);
8406
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: [
8407
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: model }),
8408
- metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children: formatContextWindowTokens(metadata.contextWindow) }),
8409
- sourceLink !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
8410
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
8411
- "a",
8412
- {
8413
- href: sourceLink.url,
8414
- target: "_blank",
8415
- rel: "noopener noreferrer",
8416
- className: "inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground",
8417
- "aria-label": sourceLink.title,
8418
- children: sourceLink.label
8419
- }
8420
- ) }),
8421
- /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: sourceLink.title })
8422
- ] }) })
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
8423
8983
  ] });
8424
8984
  }
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
- ] });
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." });
8535
8991
  }
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",
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
+ }
8998
+ function compactModelLabel(provider) {
8999
+ const models = provider.models ?? [];
9000
+ if (models.length === 0) return "No models configured";
9001
+ const visible = models.slice(0, 2).join(", ");
9002
+ const hiddenCount = models.length - 2;
9003
+ return hiddenCount > 0 ? `${visible} +${hiddenCount} more` : visible;
9004
+ }
9005
+ function ModelChip({ provider, model }) {
9006
+ const metadata = findProviderModelMetadata(provider, model);
9007
+ const sourceLink = getModelSourceLink(metadata?.sourceUrl);
9008
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground", children: [
9009
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: model }),
9010
+ metadata !== null && metadata.contextWindow !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-foreground/80", children: formatContextWindowTokens(metadata.contextWindow) }),
9011
+ sourceLink !== null && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
9012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
9013
+ "a",
8546
9014
  {
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
- ]
9015
+ href: sourceLink.url,
9016
+ target: "_blank",
9017
+ rel: "noopener noreferrer",
9018
+ className: "inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground",
9019
+ "aria-label": sourceLink.title,
9020
+ children: sourceLink.label
8556
9021
  }
8557
- )
8558
- ] }),
8559
- showDetails && hasDetails && /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderTestDetails, { result })
9022
+ ) }),
9023
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: sourceLink.title })
9024
+ ] }) })
8560
9025
  ] });
8561
9026
  }
8562
9027
  function formatTimeAgo(isoString) {
@@ -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(
@@ -11651,62 +12076,591 @@ function AgentLabDialog({ currentSessionId, logCount }) {
11651
12076
  response.isLoading && runnerPresets.length === 0 && [0, 1, 2, 3].map((index) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11652
12077
  "div",
11653
12078
  {
11654
- className: "h-32 animate-pulse rounded-lg bg-white/[0.025] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.055)]"
11655
- },
11656
- index
11657
- ))
11658
- ] })
11659
- ] }),
11660
- /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mt-3", children: [
11661
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
11662
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold", children: "TonyClaw packages" }),
11663
- response.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-[11px] text-muted-foreground", children: "checking npm..." })
12079
+ className: "h-32 animate-pulse rounded-lg bg-white/[0.025] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.055)]"
12080
+ },
12081
+ index
12082
+ ))
12083
+ ] })
12084
+ ] }),
12085
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mt-3", children: [
12086
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
12087
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold", children: "TonyClaw packages" }),
12088
+ response.isLoading && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-mono text-[11px] text-muted-foreground", children: "checking npm..." })
12089
+ ] }),
12090
+ response.error !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg bg-red-500/8 p-3 text-xs text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]", children: "Package status is temporarily unavailable." }),
12091
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [
12092
+ packages.map((pkg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
12093
+ PackageStatusCard,
12094
+ {
12095
+ pkg,
12096
+ activeTask,
12097
+ onStartTask: handleStartTask
12098
+ },
12099
+ pkg.id
12100
+ )),
12101
+ response.isLoading && packages.length === 0 && [0, 1, 2].map((index) => /* @__PURE__ */ jsxRuntimeExports.jsx(
12102
+ "div",
12103
+ {
12104
+ className: "h-36 animate-pulse rounded-lg bg-white/[0.025] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.055)]"
12105
+ },
12106
+ index
12107
+ ))
12108
+ ] })
12109
+ ] }),
12110
+ taskError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3 rounded-lg bg-red-500/8 p-3 text-xs text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]", children: taskError }),
12111
+ recentTasks.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mt-3", children: [
12112
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
12113
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold", children: "Recent Lab runs" }),
12114
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden font-mono text-[11px] text-muted-foreground sm:block", children: [
12115
+ "last ",
12116
+ String(recentTasks.length)
12117
+ ] })
12118
+ ] }),
12119
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid gap-2 md:grid-cols-2", children: recentTasks.map((task) => /* @__PURE__ */ jsxRuntimeExports.jsx(
12120
+ RecentTaskRow,
12121
+ {
12122
+ task,
12123
+ selected: activeTaskId === task.id,
12124
+ onSelect: (selectedTask) => {
12125
+ setActiveTaskId(selectedTask.id);
12126
+ void activeTaskResponse.mutate({ task: selectedTask }, { revalidate: true });
12127
+ }
12128
+ },
12129
+ task.id
12130
+ )) })
12131
+ ] }),
12132
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TaskConsole, { task: activeTask })
12133
+ ] })
12134
+ ] })
12135
+ ] });
12136
+ }
12137
+ const INSPECTOR_PET_SIZE = 56;
12138
+ const INSPECTOR_PET_MARGIN = 14;
12139
+ const INSPECTOR_PET_EDGE_PEEK = 18;
12140
+ const INSPECTOR_PET_MIN_STEP = 72;
12141
+ const INSPECTOR_PET_MAX_STEP = 156;
12142
+ const INSPECTOR_PET_SLEEP_AFTER_MS = 45e3;
12143
+ const INSPECTOR_PET_PANEL_WIDTH = 192;
12144
+ const INSPECTOR_PET_PANEL_HEIGHT = 68;
12145
+ const INSPECTOR_PET_PANEL_GAP = 10;
12146
+ const INSPECTOR_PET_VERTICAL_SAFE_MARGIN = 78;
12147
+ function viewportScale(viewport) {
12148
+ const scale = viewport.scale ?? 1;
12149
+ return Number.isFinite(scale) && scale > 0 ? scale : 1;
12150
+ }
12151
+ function scaledMetric(value, viewport) {
12152
+ return value * viewportScale(viewport);
12153
+ }
12154
+ function clampUnit(value) {
12155
+ return Math.min(Math.max(value, 0), 1);
12156
+ }
12157
+ function maximumPetX(viewport) {
12158
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12159
+ return Math.max(
12160
+ -edgePeek,
12161
+ viewport.width - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
12162
+ );
12163
+ }
12164
+ function maximumPetY(viewport) {
12165
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12166
+ return Math.max(
12167
+ -edgePeek,
12168
+ viewport.height - scaledMetric(INSPECTOR_PET_SIZE, viewport) + edgePeek
12169
+ );
12170
+ }
12171
+ function clampInspectorPetPosition(position, viewport) {
12172
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12173
+ return {
12174
+ x: Math.min(Math.max(position.x, -edgePeek), maximumPetX(viewport)),
12175
+ y: Math.min(Math.max(position.y, -edgePeek), maximumPetY(viewport))
12176
+ };
12177
+ }
12178
+ function nearestInspectorPetEdge(position, viewport) {
12179
+ const clamped = clampInspectorPetPosition(position, viewport);
12180
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12181
+ const distances = [
12182
+ { edge: "left", distance: clamped.x + edgePeek },
12183
+ { edge: "right", distance: maximumPetX(viewport) - clamped.x },
12184
+ { edge: "top", distance: clamped.y + INSPECTOR_PET_EDGE_PEEK },
12185
+ { edge: "bottom", distance: maximumPetY(viewport) - clamped.y }
12186
+ ];
12187
+ return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
12188
+ }
12189
+ function snapInspectorPetToEdge(position, viewport) {
12190
+ const clamped = clampInspectorPetPosition(position, viewport);
12191
+ const edge = nearestInspectorPetEdge(clamped, viewport);
12192
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12193
+ switch (edge) {
12194
+ case "left":
12195
+ return { x: -edgePeek, y: clamped.y };
12196
+ case "right":
12197
+ return { x: maximumPetX(viewport), y: clamped.y };
12198
+ case "top":
12199
+ return { x: clamped.x, y: -edgePeek };
12200
+ case "bottom":
12201
+ return { x: clamped.x, y: maximumPetY(viewport) };
12202
+ }
12203
+ }
12204
+ function randomInspectorPetEdgePosition(viewport, edgeSample, ySample) {
12205
+ const maxX = maximumPetX(viewport);
12206
+ const maxY = maximumPetY(viewport);
12207
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12208
+ const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
12209
+ const randomY = minimumSafeY + clampUnit(ySample) * (maxY - minimumSafeY);
12210
+ const edgeIndex = Math.min(1, Math.floor(clampUnit(edgeSample) * 2));
12211
+ switch (edgeIndex) {
12212
+ case 0:
12213
+ return { x: -edgePeek, y: randomY };
12214
+ case 1:
12215
+ return { x: maxX, y: randomY };
12216
+ default:
12217
+ return { x: maxX, y: maxY };
12218
+ }
12219
+ }
12220
+ function nextInspectorPetWalkingPosition(position, viewport, stepSample, directionSample) {
12221
+ const clamped = snapInspectorPetToEdge(position, viewport);
12222
+ const step = scaledMetric(INSPECTOR_PET_MIN_STEP, viewport) + clampUnit(stepSample) * scaledMetric(INSPECTOR_PET_MAX_STEP - INSPECTOR_PET_MIN_STEP, viewport);
12223
+ const direction = directionSample >= 0.5 ? 1 : -1;
12224
+ const edge = nearestInspectorPetEdge(clamped, viewport);
12225
+ const maxX = maximumPetX(viewport);
12226
+ const maxY = maximumPetY(viewport);
12227
+ const edgePeek = scaledMetric(INSPECTOR_PET_EDGE_PEEK, viewport);
12228
+ const minimumSafeY = Math.min(scaledMetric(INSPECTOR_PET_VERTICAL_SAFE_MARGIN, viewport), maxY);
12229
+ switch (edge) {
12230
+ case "left":
12231
+ return {
12232
+ x: -edgePeek,
12233
+ y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
12234
+ };
12235
+ case "right":
12236
+ return {
12237
+ x: maxX,
12238
+ y: Math.min(Math.max(clamped.y + step * direction, minimumSafeY), maxY)
12239
+ };
12240
+ case "top":
12241
+ return {
12242
+ x: Math.min(Math.max(clamped.x + step * direction, -edgePeek), maxX),
12243
+ y: -edgePeek
12244
+ };
12245
+ case "bottom":
12246
+ return {
12247
+ x: Math.min(Math.max(clamped.x + step * direction, -edgePeek), maxX),
12248
+ y: maxY
12249
+ };
12250
+ }
12251
+ }
12252
+ function inspectorPetPanelPosition(position, viewport) {
12253
+ const edge = nearestInspectorPetEdge(position, viewport);
12254
+ const petSize = scaledMetric(INSPECTOR_PET_SIZE, viewport);
12255
+ const panelWidth = scaledMetric(INSPECTOR_PET_PANEL_WIDTH, viewport);
12256
+ const panelHeight = scaledMetric(INSPECTOR_PET_PANEL_HEIGHT, viewport);
12257
+ const panelGap = scaledMetric(INSPECTOR_PET_PANEL_GAP, viewport);
12258
+ const margin = scaledMetric(INSPECTOR_PET_MARGIN, viewport);
12259
+ let x = position.x + petSize / 2 - panelWidth / 2;
12260
+ let y = position.y + petSize / 2 - panelHeight / 2;
12261
+ switch (edge) {
12262
+ case "left":
12263
+ x = position.x + petSize + panelGap;
12264
+ break;
12265
+ case "right":
12266
+ x = position.x - panelWidth - panelGap;
12267
+ break;
12268
+ case "top":
12269
+ y = position.y + petSize + panelGap;
12270
+ break;
12271
+ case "bottom":
12272
+ y = position.y - panelHeight - panelGap;
12273
+ break;
12274
+ }
12275
+ return {
12276
+ x: Math.min(Math.max(x, margin), Math.max(margin, viewport.width - panelWidth - margin)),
12277
+ y: Math.min(Math.max(y, margin), Math.max(margin, viewport.height - panelHeight - margin))
12278
+ };
12279
+ }
12280
+ function latestCapturedLog(logs) {
12281
+ let latest = null;
12282
+ for (const log of logs) {
12283
+ if (latest === null || log.id > latest.id) latest = log;
12284
+ }
12285
+ return latest;
12286
+ }
12287
+ function logNeedsAttention(log) {
12288
+ const hasHttpError = log.responseStatus !== null && log.responseStatus >= 400;
12289
+ const hasStreamError = log.error !== null && log.error !== void 0 && log.error !== "";
12290
+ return hasHttpError || hasStreamError;
12291
+ }
12292
+ function resolveInspectorPetStatus(logs, nowMs) {
12293
+ const pendingCount = logs.filter((log) => log.responseStatus === null).length;
12294
+ const capturedCount = logs.length;
12295
+ if (pendingCount > 0) {
12296
+ return {
12297
+ mood: "active",
12298
+ label: `${String(pendingCount)} request${pendingCount === 1 ? "" : "s"} in flight`,
12299
+ pendingCount,
12300
+ capturedCount
12301
+ };
12302
+ }
12303
+ const latest = latestCapturedLog(logs);
12304
+ if (latest !== null && logNeedsAttention(latest)) {
12305
+ return {
12306
+ mood: "attention",
12307
+ label: "Latest request needs attention",
12308
+ pendingCount,
12309
+ capturedCount
12310
+ };
12311
+ }
12312
+ if (latest === null) {
12313
+ return {
12314
+ mood: "idle",
12315
+ label: "Waiting for traffic",
12316
+ pendingCount,
12317
+ capturedCount
12318
+ };
12319
+ }
12320
+ const latestTimestamp = Date.parse(latest.timestamp);
12321
+ if (Number.isFinite(latestTimestamp) && nowMs - latestTimestamp >= INSPECTOR_PET_SLEEP_AFTER_MS) {
12322
+ return {
12323
+ mood: "sleeping",
12324
+ label: "All quiet",
12325
+ pendingCount,
12326
+ capturedCount
12327
+ };
12328
+ }
12329
+ return {
12330
+ mood: "idle",
12331
+ label: "Watching the session",
12332
+ pendingCount,
12333
+ capturedCount
12334
+ };
12335
+ }
12336
+ const INSPECTOR_PET_ROAMING_STORAGE_KEY = "agent-inspector.pet.roaming";
12337
+ const INSPECTOR_PET_MIN_DRIFT_MS = 5200;
12338
+ const INSPECTOR_PET_DRIFT_WINDOW_MS = 2400;
12339
+ const INSPECTOR_PET_PANEL_DISMISS_MS = 6e3;
12340
+ const INSPECTOR_PET_REACTION_MS = 760;
12341
+ const INSPECTOR_PET_DRAG_THRESHOLD = 4;
12342
+ function readViewport() {
12343
+ if (typeof window === "undefined") return { width: 1280, height: 720, scale: 1 };
12344
+ const rawScale = window.getComputedStyle(document.documentElement).getPropertyValue("--inspector-ui-scale");
12345
+ const parsedScale = Number.parseFloat(rawScale);
12346
+ const scale = Number.isFinite(parsedScale) && parsedScale > 0 ? parsedScale : 1;
12347
+ return {
12348
+ width: Math.max(window.innerWidth, INSPECTOR_PET_MARGIN * 2),
12349
+ height: Math.max(window.innerHeight, INSPECTOR_PET_MARGIN * 2),
12350
+ scale
12351
+ };
12352
+ }
12353
+ function readStoredRoaming() {
12354
+ if (typeof window === "undefined") return true;
12355
+ try {
12356
+ return window.localStorage.getItem(INSPECTOR_PET_ROAMING_STORAGE_KEY) !== "0";
12357
+ } catch {
12358
+ return true;
12359
+ }
12360
+ }
12361
+ function moodDotClass(mood) {
12362
+ switch (mood) {
12363
+ case "active":
12364
+ return "bg-cyan-400 shadow-[0_0_9px_rgba(34,211,238,0.65)]";
12365
+ case "attention":
12366
+ return "bg-red-500 shadow-[0_0_9px_rgba(239,68,68,0.55)]";
12367
+ case "idle":
12368
+ return "bg-amber-400 shadow-[0_0_8px_rgba(251,191,36,0.45)]";
12369
+ case "sleeping":
12370
+ return "bg-muted-foreground/45";
12371
+ }
12372
+ }
12373
+ function moodMotionClass(mood) {
12374
+ switch (mood) {
12375
+ case "active":
12376
+ return "inspector-pet-active";
12377
+ case "attention":
12378
+ return "inspector-pet-attention";
12379
+ case "idle":
12380
+ return "";
12381
+ case "sleeping":
12382
+ return "inspector-pet-sleeping";
12383
+ }
12384
+ }
12385
+ function InspectorPet({ logs }) {
12386
+ const [viewport, setViewport] = reactExports.useState(readViewport);
12387
+ const [position, setPosition] = reactExports.useState({
12388
+ x: INSPECTOR_PET_MARGIN,
12389
+ y: INSPECTOR_PET_MARGIN
12390
+ });
12391
+ const [clockMs, setClockMs] = reactExports.useState(() => Date.now());
12392
+ const [motionTick, setMotionTick] = reactExports.useState(0);
12393
+ const [panelTick, setPanelTick] = reactExports.useState(0);
12394
+ const [panelVisible, setPanelVisible] = reactExports.useState(false);
12395
+ const [reactionTick, setReactionTick] = reactExports.useState(0);
12396
+ const [reactionVisible, setReactionVisible] = reactExports.useState(false);
12397
+ const [dragging, setDragging] = reactExports.useState(false);
12398
+ const [roaming, setRoaming] = reactExports.useState(readStoredRoaming);
12399
+ const [reduceMotion, setReduceMotion] = reactExports.useState(false);
12400
+ const dragStateRef = reactExports.useRef(null);
12401
+ const dragOccurredRef = reactExports.useRef(false);
12402
+ const status = reactExports.useMemo(() => resolveInspectorPetStatus(logs, clockMs), [clockMs, logs]);
12403
+ const panelPosition = reactExports.useMemo(
12404
+ () => inspectorPetPanelPosition(position, viewport),
12405
+ [position, viewport]
12406
+ );
12407
+ reactExports.useEffect(() => {
12408
+ const nextViewport = readViewport();
12409
+ setViewport(nextViewport);
12410
+ setPosition(randomInspectorPetEdgePosition(nextViewport, Math.random(), Math.random()));
12411
+ }, []);
12412
+ reactExports.useEffect(() => {
12413
+ const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
12414
+ const handleChange = (event) => setReduceMotion(event.matches);
12415
+ setReduceMotion(mediaQuery.matches);
12416
+ mediaQuery.addEventListener("change", handleChange);
12417
+ return () => mediaQuery.removeEventListener("change", handleChange);
12418
+ }, []);
12419
+ reactExports.useEffect(() => {
12420
+ const intervalId = window.setInterval(() => setClockMs(Date.now()), 15e3);
12421
+ return () => window.clearInterval(intervalId);
12422
+ }, []);
12423
+ reactExports.useEffect(() => {
12424
+ try {
12425
+ window.localStorage.setItem(INSPECTOR_PET_ROAMING_STORAGE_KEY, roaming ? "1" : "0");
12426
+ } catch {
12427
+ }
12428
+ }, [roaming]);
12429
+ reactExports.useEffect(() => {
12430
+ if (!roaming || dragging || reduceMotion) return void 0;
12431
+ let timeoutId = null;
12432
+ const scheduleDrift = () => {
12433
+ const delay = INSPECTOR_PET_MIN_DRIFT_MS + Math.random() * INSPECTOR_PET_DRIFT_WINDOW_MS;
12434
+ timeoutId = window.setTimeout(() => {
12435
+ setMotionTick((current) => current + 1);
12436
+ setPosition(
12437
+ (current) => nextInspectorPetWalkingPosition(current, viewport, Math.random(), Math.random())
12438
+ );
12439
+ scheduleDrift();
12440
+ }, delay);
12441
+ };
12442
+ scheduleDrift();
12443
+ return () => {
12444
+ if (timeoutId !== null) window.clearTimeout(timeoutId);
12445
+ };
12446
+ }, [dragging, reduceMotion, roaming, viewport]);
12447
+ reactExports.useEffect(() => {
12448
+ const handleResize = () => {
12449
+ const nextViewport = readViewport();
12450
+ setViewport(nextViewport);
12451
+ setPosition((current) => snapInspectorPetToEdge(current, nextViewport));
12452
+ };
12453
+ window.addEventListener("resize", handleResize);
12454
+ return () => window.removeEventListener("resize", handleResize);
12455
+ }, []);
12456
+ reactExports.useEffect(() => {
12457
+ if (!panelVisible) return void 0;
12458
+ const timeoutId = window.setTimeout(
12459
+ () => setPanelVisible(false),
12460
+ INSPECTOR_PET_PANEL_DISMISS_MS
12461
+ );
12462
+ return () => window.clearTimeout(timeoutId);
12463
+ }, [panelTick, panelVisible]);
12464
+ reactExports.useEffect(() => {
12465
+ if (!reactionVisible) return void 0;
12466
+ const timeoutId = window.setTimeout(() => setReactionVisible(false), INSPECTOR_PET_REACTION_MS);
12467
+ return () => window.clearTimeout(timeoutId);
12468
+ }, [reactionTick, reactionVisible]);
12469
+ const handlePet = reactExports.useCallback(() => {
12470
+ if (dragOccurredRef.current) {
12471
+ dragOccurredRef.current = false;
12472
+ return;
12473
+ }
12474
+ setClockMs(Date.now());
12475
+ setPanelTick((current) => current + 1);
12476
+ setPanelVisible(true);
12477
+ setReactionTick((current) => current + 1);
12478
+ setReactionVisible(true);
12479
+ }, []);
12480
+ const handlePointerDown = reactExports.useCallback((event) => {
12481
+ const rect = event.currentTarget.getBoundingClientRect();
12482
+ dragOccurredRef.current = false;
12483
+ dragStateRef.current = {
12484
+ pointerId: event.pointerId,
12485
+ offsetX: event.clientX - rect.left,
12486
+ offsetY: event.clientY - rect.top,
12487
+ startX: event.clientX,
12488
+ startY: event.clientY,
12489
+ moved: false
12490
+ };
12491
+ event.currentTarget.setPointerCapture(event.pointerId);
12492
+ setPanelVisible(false);
12493
+ setDragging(true);
12494
+ }, []);
12495
+ const handlePointerMove = reactExports.useCallback(
12496
+ (event) => {
12497
+ const dragState = dragStateRef.current;
12498
+ if (dragState === null || dragState.pointerId !== event.pointerId) return;
12499
+ const moved = dragState.moved || Math.abs(event.clientX - dragState.startX) >= INSPECTOR_PET_DRAG_THRESHOLD || Math.abs(event.clientY - dragState.startY) >= INSPECTOR_PET_DRAG_THRESHOLD;
12500
+ dragState.moved = moved;
12501
+ dragOccurredRef.current = moved;
12502
+ setPosition(
12503
+ clampInspectorPetPosition(
12504
+ {
12505
+ x: event.clientX - dragState.offsetX,
12506
+ y: event.clientY - dragState.offsetY
12507
+ },
12508
+ viewport
12509
+ )
12510
+ );
12511
+ },
12512
+ [viewport]
12513
+ );
12514
+ const handlePointerUp = reactExports.useCallback(
12515
+ (event) => {
12516
+ const dragState = dragStateRef.current;
12517
+ if (dragState === null || dragState.pointerId !== event.pointerId) return;
12518
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
12519
+ event.currentTarget.releasePointerCapture(event.pointerId);
12520
+ }
12521
+ dragOccurredRef.current = dragState.moved;
12522
+ dragStateRef.current = null;
12523
+ setDragging(false);
12524
+ setPosition((current) => snapInspectorPetToEdge(current, viewport));
12525
+ },
12526
+ [viewport]
12527
+ );
12528
+ const handlePointerCancel = reactExports.useCallback((event) => {
12529
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
12530
+ event.currentTarget.releasePointerCapture(event.pointerId);
12531
+ }
12532
+ dragStateRef.current = null;
12533
+ dragOccurredRef.current = false;
12534
+ setDragging(false);
12535
+ }, []);
12536
+ const petStyle = reactExports.useMemo(
12537
+ () => ({
12538
+ transform: `translate3d(${String(position.x)}px, ${String(position.y)}px, 0) rotate(${motionTick % 2 === 0 ? "-4deg" : "5deg"}) scale(${dragging ? "1.08" : "1"})`,
12539
+ transition: dragging ? "none" : "transform 4800ms cubic-bezier(0.33, 1, 0.68, 1)",
12540
+ touchAction: "none"
12541
+ }),
12542
+ [dragging, motionTick, position.x, position.y]
12543
+ );
12544
+ const panelStyle = reactExports.useMemo(
12545
+ () => ({ left: panelPosition.x, top: panelPosition.y }),
12546
+ [panelPosition.x, panelPosition.y]
12547
+ );
12548
+ const roamingLabel = roaming ? "Pause pet roaming" : "Resume pet roaming";
12549
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
12550
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
12551
+ "button",
12552
+ {
12553
+ type: "button",
12554
+ className: cn(
12555
+ "fixed left-0 top-0 z-30 flex size-14 items-center justify-center bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_24px_rgba(245,158,11,0.36)] focus-visible:drop-shadow-[0_0_12px_rgba(251,191,36,0.72)]",
12556
+ dragging ? "cursor-grabbing" : "cursor-grab"
12557
+ ),
12558
+ style: petStyle,
12559
+ "data-pet-mood": status.mood,
12560
+ onClick: handlePet,
12561
+ onPointerDown: handlePointerDown,
12562
+ onPointerMove: handlePointerMove,
12563
+ onPointerUp: handlePointerUp,
12564
+ onPointerCancel: handlePointerCancel,
12565
+ "aria-label": `Inspector pet: ${status.label}`,
12566
+ title: status.label,
12567
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
12568
+ "span",
12569
+ {
12570
+ className: cn(
12571
+ "relative flex size-12 items-center justify-center",
12572
+ reactionVisible && "inspector-pet-patted"
12573
+ ),
12574
+ "aria-hidden": "true",
12575
+ children: [
12576
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
12577
+ "span",
12578
+ {
12579
+ className: cn(
12580
+ "relative flex size-12 items-center justify-center",
12581
+ moodMotionClass(status.mood)
12582
+ ),
12583
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
12584
+ "span",
12585
+ {
12586
+ className: cn(
12587
+ "relative flex size-12 items-center justify-center",
12588
+ !dragging && "floating-crab-body"
12589
+ ),
12590
+ children: [
12591
+ /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "relative z-10 size-11 text-amber-500 drop-shadow-[0_0_15px_rgba(245,158,11,0.30)]" }),
12592
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "pointer-events-none absolute inset-x-0 bottom-0 z-0 flex items-end justify-center gap-1 text-amber-400/80", children: [0, 1, 2, 3, 4, 5].map((leg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
12593
+ "span",
12594
+ {
12595
+ className: cn(
12596
+ "floating-crab-leg h-2 w-0.5 rounded-full bg-current shadow-[0_0_7px_rgba(245,158,11,0.24)]",
12597
+ leg < 3 ? "floating-crab-leg-left" : "floating-crab-leg-right",
12598
+ dragging && "animate-none"
12599
+ ),
12600
+ style: { animationDelay: `${String(leg % 2 === 0 ? 0 : 180)}ms` }
12601
+ },
12602
+ leg
12603
+ )) })
12604
+ ]
12605
+ }
12606
+ )
12607
+ }
12608
+ ),
12609
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
12610
+ "span",
12611
+ {
12612
+ className: cn(
12613
+ "absolute right-0 top-1 size-2 rounded-full border border-background/90",
12614
+ moodDotClass(status.mood),
12615
+ status.mood === "active" && !reduceMotion && "animate-pulse"
12616
+ )
12617
+ }
12618
+ ),
12619
+ reactionVisible && /* @__PURE__ */ jsxRuntimeExports.jsx(
12620
+ Heart,
12621
+ {
12622
+ className: "inspector-pet-heart absolute -right-1 -top-2 size-3.5 fill-amber-400 text-amber-400"
12623
+ },
12624
+ reactionTick
12625
+ )
12626
+ ]
12627
+ }
12628
+ )
12629
+ }
12630
+ ),
12631
+ panelVisible && /* @__PURE__ */ jsxRuntimeExports.jsxs(
12632
+ "div",
12633
+ {
12634
+ className: "fixed z-50 w-48 rounded-md border border-border/80 bg-popover/95 px-3 py-2 text-popover-foreground shadow-[0_14px_34px_rgba(0,0,0,0.28)] backdrop-blur",
12635
+ style: panelStyle,
12636
+ role: "status",
12637
+ "aria-live": "polite",
12638
+ children: [
12639
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
12640
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: cn("size-2 shrink-0 rounded-full", moodDotClass(status.mood)) }),
12641
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: status.label }),
12642
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
12643
+ "button",
12644
+ {
12645
+ type: "button",
12646
+ className: "inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:ring-1 focus-visible:ring-ring focus-visible:outline-none",
12647
+ onClick: () => setRoaming((current) => !current),
12648
+ "aria-label": roamingLabel,
12649
+ title: roamingLabel,
12650
+ children: roaming ? /* @__PURE__ */ jsxRuntimeExports.jsx(Pause, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Play, { className: "size-3.5" })
12651
+ }
12652
+ )
11664
12653
  ] }),
11665
- response.error !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "rounded-lg bg-red-500/8 p-3 text-xs text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]", children: "Package status is temporarily unavailable." }),
11666
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [
11667
- packages.map((pkg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11668
- PackageStatusCard,
11669
- {
11670
- pkg,
11671
- activeTask,
11672
- onStartTask: handleStartTask
11673
- },
11674
- pkg.id
11675
- )),
11676
- response.isLoading && packages.length === 0 && [0, 1, 2].map((index) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11677
- "div",
11678
- {
11679
- className: "h-36 animate-pulse rounded-lg bg-white/[0.025] shadow-[inset_0_0_0_1px_rgba(255,255,255,0.055)]"
11680
- },
11681
- index
11682
- ))
12654
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 flex items-center justify-between gap-3 font-mono text-[10px] text-muted-foreground", children: [
12655
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
12656
+ String(status.capturedCount),
12657
+ " captured"
12658
+ ] }),
12659
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: status.pendingCount > 0 ? `${String(status.pendingCount)} active` : roaming ? "Roaming" : "Resting" })
11683
12660
  ] })
11684
- ] }),
11685
- taskError !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-3 rounded-lg bg-red-500/8 p-3 text-xs text-red-100 shadow-[inset_0_0_0_1px_rgba(248,113,113,0.16)]", children: taskError }),
11686
- recentTasks.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "mt-3", children: [
11687
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center justify-between gap-3", children: [
11688
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-sm font-semibold", children: "Recent Lab runs" }),
11689
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden font-mono text-[11px] text-muted-foreground sm:block", children: [
11690
- "last ",
11691
- String(recentTasks.length)
11692
- ] })
11693
- ] }),
11694
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "grid gap-2 md:grid-cols-2", children: recentTasks.map((task) => /* @__PURE__ */ jsxRuntimeExports.jsx(
11695
- RecentTaskRow,
11696
- {
11697
- task,
11698
- selected: activeTaskId === task.id,
11699
- onSelect: (selectedTask) => {
11700
- setActiveTaskId(selectedTask.id);
11701
- void activeTaskResponse.mutate({ task: selectedTask }, { revalidate: true });
11702
- }
11703
- },
11704
- task.id
11705
- )) })
11706
- ] }),
11707
- /* @__PURE__ */ jsxRuntimeExports.jsx(TaskConsole, { task: activeTask })
11708
- ] })
11709
- ] })
12661
+ ]
12662
+ }
12663
+ )
11710
12664
  ] });
11711
12665
  }
11712
12666
  const DEFAULT_QUESTION = "Analyze the current Inspector session. What looks abnormal, slow, or likely to hang?";
@@ -13276,353 +14230,106 @@ function riskRank(risk) {
13276
14230
  return 2;
13277
14231
  case "ok":
13278
14232
  return 1;
13279
- }
13280
- }
13281
- function compareModelSummary(left, right) {
13282
- const riskDelta = riskRank(right.latest.riskLevel) - riskRank(left.latest.riskLevel);
13283
- if (riskDelta !== 0) return riskDelta;
13284
- const leftPeak = left.peak.usagePercent ?? -1;
13285
- const rightPeak = right.peak.usagePercent ?? -1;
13286
- const peakDelta = rightPeak - leftPeak;
13287
- if (peakDelta !== 0) return peakDelta;
13288
- return right.latest.logId - left.latest.logId;
13289
- }
13290
- function trendPercent(first, latest) {
13291
- if (first.usagePercent === null || latest.usagePercent === null) return null;
13292
- return (latest.usagePercent - first.usagePercent) * 100;
13293
- }
13294
- function logsInAscendingIdOrder(logs) {
13295
- for (let index = 1; index < logs.length; index++) {
13296
- const previous = logs[index - 1];
13297
- const current = logs[index];
13298
- if (previous === void 0 || current === void 0) continue;
13299
- if (previous.id > current.id) return [...logs].sort((left, right) => left.id - right.id);
13300
- }
13301
- return logs;
13302
- }
13303
- function buildSessionContextSummary(logs, providers = []) {
13304
- const sortedLogs = logsInAscendingIdOrder(logs);
13305
- const providersSignature = providerSignature(providers);
13306
- const byModel = /* @__PURE__ */ new Map();
13307
- for (const log of sortedLogs) {
13308
- const snapshot = cachedSnapshotFromLog(log, providers, providersSignature);
13309
- if (snapshot === null) continue;
13310
- const existing = byModel.get(snapshot.model);
13311
- if (existing === void 0) {
13312
- byModel.set(snapshot.model, {
13313
- model: snapshot.model,
13314
- requestCount: 1,
13315
- first: snapshot,
13316
- latest: snapshot,
13317
- peak: snapshot
13318
- });
13319
- continue;
13320
- }
13321
- byModel.set(snapshot.model, {
13322
- model: existing.model,
13323
- requestCount: existing.requestCount + 1,
13324
- first: existing.first,
13325
- latest: snapshot,
13326
- peak: isHigherPeak(snapshot, existing.peak) ? snapshot : existing.peak
13327
- });
13328
- }
13329
- const models = [...byModel.values()].map((entry) => ({
13330
- model: entry.model,
13331
- requestCount: entry.requestCount,
13332
- first: entry.first,
13333
- latest: entry.latest,
13334
- peak: entry.peak,
13335
- trendPercent: trendPercent(entry.first, entry.latest)
13336
- })).sort(compareModelSummary);
13337
- return { models };
13338
- }
13339
- function truncateSessionId(id) {
13340
- if (id.length <= 30) return id;
13341
- return `${id.slice(0, 12)}...${id.slice(-12)}`;
13342
- }
13343
- 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`;
14233
+ }
13428
14234
  }
13429
- function formatTokenPair(inputTokens, outputTokens) {
13430
- return `${formatTokens(inputTokens)} in / ${formatTokens(outputTokens)} out`;
14235
+ function compareModelSummary(left, right) {
14236
+ const riskDelta = riskRank(right.latest.riskLevel) - riskRank(left.latest.riskLevel);
14237
+ if (riskDelta !== 0) return riskDelta;
14238
+ const leftPeak = left.peak.usagePercent ?? -1;
14239
+ const rightPeak = right.peak.usagePercent ?? -1;
14240
+ const peakDelta = rightPeak - leftPeak;
14241
+ if (peakDelta !== 0) return peakDelta;
14242
+ return right.latest.logId - left.latest.logId;
13431
14243
  }
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;
14244
+ function trendPercent(first, latest) {
14245
+ if (first.usagePercent === null || latest.usagePercent === null) return null;
14246
+ return (latest.usagePercent - first.usagePercent) * 100;
13440
14247
  }
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
- };
14248
+ function logsInAscendingIdOrder(logs) {
14249
+ for (let index = 1; index < logs.length; index++) {
14250
+ const previous = logs[index - 1];
14251
+ const current = logs[index];
14252
+ if (previous === void 0 || current === void 0) continue;
14253
+ if (previous.id > current.id) return [...logs].sort((left, right) => left.id - right.id);
13459
14254
  }
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" };
14255
+ return logs;
14256
+ }
14257
+ function buildSessionContextSummary(logs, providers = []) {
14258
+ const sortedLogs = logsInAscendingIdOrder(logs);
14259
+ const providersSignature = providerSignature(providers);
14260
+ const byModel = /* @__PURE__ */ new Map();
14261
+ for (const log of sortedLogs) {
14262
+ const snapshot = cachedSnapshotFromLog(log, providers, providersSignature);
14263
+ if (snapshot === null) continue;
14264
+ const existing = byModel.get(snapshot.model);
14265
+ if (existing === void 0) {
14266
+ byModel.set(snapshot.model, {
14267
+ model: snapshot.model,
14268
+ requestCount: 1,
14269
+ first: snapshot,
14270
+ latest: snapshot,
14271
+ peak: snapshot
14272
+ });
14273
+ continue;
14274
+ }
14275
+ byModel.set(snapshot.model, {
14276
+ model: existing.model,
14277
+ requestCount: existing.requestCount + 1,
14278
+ first: existing.first,
14279
+ latest: snapshot,
14280
+ peak: isHigherPeak(snapshot, existing.peak) ? snapshot : existing.peak
14281
+ });
13471
14282
  }
14283
+ const models = [...byModel.values()].map((entry) => ({
14284
+ model: entry.model,
14285
+ requestCount: entry.requestCount,
14286
+ first: entry.first,
14287
+ latest: entry.latest,
14288
+ peak: entry.peak,
14289
+ trendPercent: trendPercent(entry.first, entry.latest)
14290
+ })).sort(compareModelSummary);
14291
+ return { models };
13472
14292
  }
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");
14293
+ function truncateSessionId(id) {
14294
+ if (id.length <= 30) return id;
14295
+ return `${id.slice(0, 12)}...${id.slice(-12)}`;
13477
14296
  }
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" };
14297
+ function buildDisplayNumberByLogId(groups) {
14298
+ const result = /* @__PURE__ */ new Map();
14299
+ for (const group of groups) {
14300
+ for (let index = 0; index < group.logs.length; index += 1) {
14301
+ const log = group.logs[index];
14302
+ if (log === void 0) continue;
14303
+ result.set(log.id, index + 1);
14304
+ }
13528
14305
  }
14306
+ return result;
13529
14307
  }
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;
14308
+ function displayNumberForLog(log, displayNumberByLogId) {
14309
+ return displayNumberByLogId.get(log.id) ?? log.id;
14310
+ }
14311
+ function computeTokenSummary(logs) {
13541
14312
  let totalIn = 0;
13542
14313
  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
- }
13573
- if (log.inputTokens !== null) totalIn += log.inputTokens;
13574
- 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;
13587
- }
13588
- }
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
- };
14314
+ for (const log of logs) {
14315
+ if (log.inputTokens !== null) totalIn += log.inputTokens;
14316
+ if (log.outputTokens !== null) totalOut += log.outputTokens;
14317
+ }
14318
+ return { totalIn, totalOut };
14319
+ }
14320
+ function getFirstUserAgent(logs) {
14321
+ for (const log of logs) {
14322
+ if (log.userAgent !== null && log.userAgent !== void 0 && log.userAgent !== "") {
14323
+ return log.userAgent;
14324
+ }
14325
+ }
14326
+ return null;
14327
+ }
14328
+ function formatTimeRange(logs, timeDisplayFormat) {
14329
+ const first = logs[0];
14330
+ const last = logs[logs.length - 1];
14331
+ if (first === void 0 || last === void 0) return null;
14332
+ return formatTimestampRange(first.timestamp, last.timestamp, timeDisplayFormat);
13626
14333
  }
13627
14334
  function slateMetricToneClass(tone) {
13628
14335
  switch (tone) {
@@ -13746,35 +14453,6 @@ function SessionOpeningSlate({ stats }) {
13746
14453
  ] })
13747
14454
  ] });
13748
14455
  }
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
14456
  function formatPercent(value) {
13779
14457
  if (value === null) return "Unknown";
13780
14458
  const percent = value * 100;
@@ -13971,23 +14649,6 @@ function SessionMembershipPanel({
13971
14649
  }) })
13972
14650
  ] });
13973
14651
  }
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
14652
  function LogPaginationButton({
13992
14653
  label,
13993
14654
  title,
@@ -14261,11 +14922,107 @@ function CrabFamily({
14261
14922
  }) })
14262
14923
  ] });
14263
14924
  }
14925
+ function ThemeModeToggle() {
14926
+ const { themeMode, setThemeMode } = useThemeMode();
14927
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14928
+ "div",
14929
+ {
14930
+ className: "border-border/70 bg-muted/70 inline-flex h-8 items-center rounded-md border p-0.5 shadow-sm",
14931
+ "aria-label": "Theme mode",
14932
+ role: "group",
14933
+ children: [
14934
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14935
+ "button",
14936
+ {
14937
+ type: "button",
14938
+ onClick: () => setThemeMode("light"),
14939
+ className: cn(
14940
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14941
+ themeMode === "light" && "bg-background text-foreground shadow-sm"
14942
+ ),
14943
+ "aria-label": "Use light mode",
14944
+ "aria-pressed": themeMode === "light",
14945
+ title: "Light mode",
14946
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Sun, { className: "size-3.5" })
14947
+ }
14948
+ ),
14949
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14950
+ "button",
14951
+ {
14952
+ type: "button",
14953
+ onClick: () => setThemeMode("eye-care"),
14954
+ className: cn(
14955
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14956
+ themeMode === "eye-care" && "bg-background text-foreground shadow-sm"
14957
+ ),
14958
+ "aria-label": "Use eye care mode",
14959
+ "aria-pressed": themeMode === "eye-care",
14960
+ title: "Eye care mode",
14961
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Eye, { className: "size-3.5" })
14962
+ }
14963
+ ),
14964
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14965
+ "button",
14966
+ {
14967
+ type: "button",
14968
+ onClick: () => setThemeMode("dark"),
14969
+ className: cn(
14970
+ "inline-flex size-7 items-center justify-center rounded-[6px] text-muted-foreground transition-colors hover:text-foreground",
14971
+ themeMode === "dark" && "bg-background text-foreground shadow-sm"
14972
+ ),
14973
+ "aria-label": "Use dark mode",
14974
+ "aria-pressed": themeMode === "dark",
14975
+ title: "Dark mode",
14976
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(Moon, { className: "size-3.5" })
14977
+ }
14978
+ )
14979
+ ]
14980
+ }
14981
+ );
14982
+ }
14983
+ function UiScaleControl({
14984
+ preference,
14985
+ resolvedScale,
14986
+ onChange
14987
+ }) {
14988
+ const resolvedLabel = formatUiScale(resolvedScale);
14989
+ const currentLabel = preference === "auto" ? `Auto (${resolvedLabel})` : `${preference}%`;
14990
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14991
+ Select,
14992
+ {
14993
+ value: preference,
14994
+ onValueChange: (value) => onChange(normalizeUiScalePreference(value)),
14995
+ children: [
14996
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
14997
+ SelectTrigger,
14998
+ {
14999
+ size: "sm",
15000
+ 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",
15001
+ "aria-label": `Interface size: ${currentLabel}`,
15002
+ children: [
15003
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ALargeSmall, { className: "size-3.5" }),
15004
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-[10px] font-semibold tabular-nums", children: resolvedLabel })
15005
+ ]
15006
+ }
15007
+ ),
15008
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(SelectContent, { align: "end", children: [
15009
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "auto", children: "Auto" }),
15010
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "100", children: "100%" }),
15011
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "125", children: "125%" }),
15012
+ /* @__PURE__ */ jsxRuntimeExports.jsx(SelectItem, { value: "150", children: "150%" })
15013
+ ] })
15014
+ ]
15015
+ }
15016
+ );
15017
+ }
14264
15018
  function BrandHeader({
14265
15019
  compact,
14266
15020
  crabEntrancePhase,
14267
15021
  currentSessionId,
14268
- logCount
15022
+ logCount,
15023
+ uiScalePreference,
15024
+ resolvedUiScale,
15025
+ onUiScaleChange
14269
15026
  }) {
14270
15027
  if (compact) {
14271
15028
  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 +15040,15 @@ function BrandHeader({
14283
15040
  ] })
14284
15041
  ] }),
14285
15042
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
15043
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeModeToggle, {}),
15044
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
15045
+ UiScaleControl,
15046
+ {
15047
+ preference: uiScalePreference,
15048
+ resolvedScale: resolvedUiScale,
15049
+ onChange: onUiScaleChange
15050
+ }
15051
+ ),
14286
15052
  /* @__PURE__ */ jsxRuntimeExports.jsx(AlertsDialog, {}),
14287
15053
  /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
14288
15054
  /* @__PURE__ */ jsxRuntimeExports.jsx(AgentLabDialog, { currentSessionId, logCount }),
@@ -14308,6 +15074,15 @@ function BrandHeader({
14308
15074
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-full text-sm font-semibold text-muted-foreground", children: "Local First. Evidence First." })
14309
15075
  ] }),
14310
15076
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 justify-end gap-1", children: [
15077
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ThemeModeToggle, {}),
15078
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
15079
+ UiScaleControl,
15080
+ {
15081
+ preference: uiScalePreference,
15082
+ resolvedScale: resolvedUiScale,
15083
+ onChange: onUiScaleChange
15084
+ }
15085
+ ),
14311
15086
  /* @__PURE__ */ jsxRuntimeExports.jsx(AlertsDialog, {}),
14312
15087
  /* @__PURE__ */ jsxRuntimeExports.jsx(GroupsDialog, {}),
14313
15088
  /* @__PURE__ */ jsxRuntimeExports.jsx(AgentLabDialog, { currentSessionId, logCount }),
@@ -14315,256 +15090,12 @@ function BrandHeader({
14315
15090
  ] })
14316
15091
  ] });
14317
15092
  }
14318
- const FLOATING_LOGO_SIZE = 56;
14319
- const FLOATING_LOGO_MARGIN = 14;
14320
- const FLOATING_LOGO_MIN_DRIFT_MS = 5200;
14321
- const FLOATING_LOGO_DRIFT_WINDOW_MS = 2400;
14322
- const FLOATING_LOGO_MIN_STEP = 72;
14323
- const FLOATING_LOGO_MAX_STEP = 156;
14324
15093
  const DETAILS_PANEL_COLLAPSED_STORAGE_KEY = "agent-inspector.detailsPanelCollapsed";
14325
15094
  function readStoredDetailsPanelCollapsed() {
14326
15095
  if (typeof window === "undefined") return true;
14327
15096
  const stored = window.localStorage.getItem(DETAILS_PANEL_COLLAPSED_STORAGE_KEY);
14328
15097
  return stored === null || stored === "1";
14329
15098
  }
14330
- function clampFloatingLogoPosition(position) {
14331
- const maxX = Math.max(
14332
- FLOATING_LOGO_MARGIN,
14333
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14334
- );
14335
- const maxY = Math.max(
14336
- FLOATING_LOGO_MARGIN,
14337
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14338
- );
14339
- return {
14340
- x: Math.min(Math.max(position.x, FLOATING_LOGO_MARGIN), maxX),
14341
- y: Math.min(Math.max(position.y, FLOATING_LOGO_MARGIN), maxY)
14342
- };
14343
- }
14344
- function snapFloatingLogoToEdge(position) {
14345
- const clamped = clampFloatingLogoPosition(position);
14346
- const maxX = Math.max(
14347
- FLOATING_LOGO_MARGIN,
14348
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14349
- );
14350
- const maxY = Math.max(
14351
- FLOATING_LOGO_MARGIN,
14352
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14353
- );
14354
- const leftDistance = clamped.x - FLOATING_LOGO_MARGIN;
14355
- const rightDistance = maxX - clamped.x;
14356
- const topDistance = clamped.y - FLOATING_LOGO_MARGIN;
14357
- const bottomDistance = maxY - clamped.y;
14358
- const nearestDistance = Math.min(leftDistance, rightDistance, topDistance, bottomDistance);
14359
- if (nearestDistance === leftDistance) return { x: FLOATING_LOGO_MARGIN, y: clamped.y };
14360
- if (nearestDistance === rightDistance) return { x: maxX, y: clamped.y };
14361
- if (nearestDistance === topDistance) return { x: clamped.x, y: FLOATING_LOGO_MARGIN };
14362
- return { x: clamped.x, y: maxY };
14363
- }
14364
- function randomFloatingLogoEdgePosition() {
14365
- const maxX = Math.max(
14366
- FLOATING_LOGO_MARGIN,
14367
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14368
- );
14369
- const maxY = Math.max(
14370
- FLOATING_LOGO_MARGIN,
14371
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14372
- );
14373
- const randomX = FLOATING_LOGO_MARGIN + Math.random() * (maxX - FLOATING_LOGO_MARGIN);
14374
- const randomY = FLOATING_LOGO_MARGIN + Math.random() * (maxY - FLOATING_LOGO_MARGIN);
14375
- const edgeIndex = Math.floor(Math.random() * 4);
14376
- switch (edgeIndex) {
14377
- case 0:
14378
- return { x: FLOATING_LOGO_MARGIN, y: randomY };
14379
- case 1:
14380
- return { x: maxX, y: randomY };
14381
- case 2:
14382
- return { x: randomX, y: FLOATING_LOGO_MARGIN };
14383
- case 3:
14384
- return { x: randomX, y: maxY };
14385
- default:
14386
- return { x: maxX, y: FLOATING_LOGO_MARGIN };
14387
- }
14388
- }
14389
- function nearestFloatingLogoEdge(position) {
14390
- const clamped = clampFloatingLogoPosition(position);
14391
- const maxX = Math.max(
14392
- FLOATING_LOGO_MARGIN,
14393
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14394
- );
14395
- const maxY = Math.max(
14396
- FLOATING_LOGO_MARGIN,
14397
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14398
- );
14399
- const distances = [
14400
- { edge: "left", distance: clamped.x - FLOATING_LOGO_MARGIN },
14401
- { edge: "right", distance: maxX - clamped.x },
14402
- { edge: "top", distance: clamped.y - FLOATING_LOGO_MARGIN },
14403
- { edge: "bottom", distance: maxY - clamped.y }
14404
- ];
14405
- return distances.reduce((best, current) => current.distance < best.distance ? current : best).edge;
14406
- }
14407
- function nextFloatingLogoWalkingPosition(position) {
14408
- const clamped = snapFloatingLogoToEdge(position);
14409
- const maxX = Math.max(
14410
- FLOATING_LOGO_MARGIN,
14411
- window.innerWidth - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14412
- );
14413
- const maxY = Math.max(
14414
- FLOATING_LOGO_MARGIN,
14415
- window.innerHeight - FLOATING_LOGO_SIZE - FLOATING_LOGO_MARGIN
14416
- );
14417
- const step = FLOATING_LOGO_MIN_STEP + Math.random() * (FLOATING_LOGO_MAX_STEP - FLOATING_LOGO_MIN_STEP);
14418
- const direction = Math.random() > 0.5 ? 1 : -1;
14419
- const edge = nearestFloatingLogoEdge(clamped);
14420
- switch (edge) {
14421
- case "left": {
14422
- const nextY = clamped.y + step * direction;
14423
- if (nextY <= FLOATING_LOGO_MARGIN)
14424
- return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
14425
- if (nextY >= maxY) return { x: FLOATING_LOGO_MARGIN, y: maxY };
14426
- return { x: FLOATING_LOGO_MARGIN, y: nextY };
14427
- }
14428
- case "right": {
14429
- const nextY = clamped.y + step * direction;
14430
- if (nextY <= FLOATING_LOGO_MARGIN) return { x: maxX, y: FLOATING_LOGO_MARGIN };
14431
- if (nextY >= maxY) return { x: maxX, y: maxY };
14432
- return { x: maxX, y: nextY };
14433
- }
14434
- case "top": {
14435
- const nextX = clamped.x + step * direction;
14436
- if (nextX <= FLOATING_LOGO_MARGIN)
14437
- return { x: FLOATING_LOGO_MARGIN, y: FLOATING_LOGO_MARGIN };
14438
- if (nextX >= maxX) return { x: maxX, y: FLOATING_LOGO_MARGIN };
14439
- return { x: nextX, y: FLOATING_LOGO_MARGIN };
14440
- }
14441
- case "bottom": {
14442
- const nextX = clamped.x + step * direction;
14443
- if (nextX <= FLOATING_LOGO_MARGIN) return { x: FLOATING_LOGO_MARGIN, y: maxY };
14444
- if (nextX >= maxX) return { x: maxX, y: maxY };
14445
- return { x: nextX, y: maxY };
14446
- }
14447
- }
14448
- }
14449
- function FloatingInspectorLogo() {
14450
- const [position, setPosition] = reactExports.useState({
14451
- x: FLOATING_LOGO_MARGIN,
14452
- y: FLOATING_LOGO_MARGIN
14453
- });
14454
- const [motionTick, setMotionTick] = reactExports.useState(0);
14455
- const [dragging, setDragging] = reactExports.useState(false);
14456
- const dragStateRef = reactExports.useRef(null);
14457
- reactExports.useEffect(() => {
14458
- setPosition(randomFloatingLogoEdgePosition());
14459
- }, []);
14460
- reactExports.useEffect(() => {
14461
- if (dragging) return void 0;
14462
- let timeoutId = null;
14463
- const scheduleDrift = () => {
14464
- const delay = FLOATING_LOGO_MIN_DRIFT_MS + Math.random() * FLOATING_LOGO_DRIFT_WINDOW_MS;
14465
- timeoutId = window.setTimeout(() => {
14466
- setMotionTick((current) => current + 1);
14467
- setPosition((current) => nextFloatingLogoWalkingPosition(current));
14468
- scheduleDrift();
14469
- }, delay);
14470
- };
14471
- scheduleDrift();
14472
- return () => {
14473
- if (timeoutId !== null) {
14474
- window.clearTimeout(timeoutId);
14475
- }
14476
- };
14477
- }, [dragging]);
14478
- reactExports.useEffect(() => {
14479
- const handleResize = () => {
14480
- setPosition((current) => snapFloatingLogoToEdge(current));
14481
- };
14482
- window.addEventListener("resize", handleResize);
14483
- return () => window.removeEventListener("resize", handleResize);
14484
- }, []);
14485
- const handlePointerDown = reactExports.useCallback((event) => {
14486
- const rect = event.currentTarget.getBoundingClientRect();
14487
- dragStateRef.current = {
14488
- pointerId: event.pointerId,
14489
- offsetX: event.clientX - rect.left,
14490
- offsetY: event.clientY - rect.top
14491
- };
14492
- event.currentTarget.setPointerCapture(event.pointerId);
14493
- setDragging(true);
14494
- }, []);
14495
- const handlePointerMove = reactExports.useCallback((event) => {
14496
- const dragState = dragStateRef.current;
14497
- if (dragState === null || dragState.pointerId !== event.pointerId) return;
14498
- setPosition(
14499
- clampFloatingLogoPosition({
14500
- x: event.clientX - dragState.offsetX,
14501
- y: event.clientY - dragState.offsetY
14502
- })
14503
- );
14504
- }, []);
14505
- const handlePointerUp = reactExports.useCallback((event) => {
14506
- const dragState = dragStateRef.current;
14507
- if (dragState === null || dragState.pointerId !== event.pointerId) return;
14508
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
14509
- event.currentTarget.releasePointerCapture(event.pointerId);
14510
- }
14511
- dragStateRef.current = null;
14512
- setDragging(false);
14513
- setPosition((current) => snapFloatingLogoToEdge(current));
14514
- }, []);
14515
- const style = reactExports.useMemo(
14516
- () => ({
14517
- transform: `translate3d(${position.x}px, ${position.y}px, 0) rotate(${motionTick % 2 === 0 ? "-4deg" : "5deg"}) scale(${dragging ? "1.08" : "1"})`,
14518
- transition: dragging ? "none" : "transform 4800ms cubic-bezier(0.33, 1, 0.68, 1)",
14519
- touchAction: "none"
14520
- }),
14521
- [dragging, motionTick, position.x, position.y]
14522
- );
14523
- return /* @__PURE__ */ jsxRuntimeExports.jsx(
14524
- "button",
14525
- {
14526
- type: "button",
14527
- className: cn(
14528
- "fixed left-0 top-0 z-40 flex size-14 items-center justify-center rounded-full bg-transparent text-foreground outline-none transition-[filter,opacity] duration-200 hover:opacity-95 hover:drop-shadow-[0_0_24px_rgba(245,158,11,0.36)] focus-visible:ring-1 focus-visible:ring-amber-300/55",
14529
- dragging ? "cursor-grabbing" : "cursor-grab"
14530
- ),
14531
- style,
14532
- onPointerDown: handlePointerDown,
14533
- onPointerMove: handlePointerMove,
14534
- onPointerUp: handlePointerUp,
14535
- onPointerCancel: handlePointerUp,
14536
- "aria-label": "Floating Inspector logo",
14537
- title: "Drag Inspector logo",
14538
- children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
14539
- "span",
14540
- {
14541
- className: cn(
14542
- "relative flex size-12 items-center justify-center",
14543
- !dragging && "floating-crab-body"
14544
- ),
14545
- "aria-hidden": "true",
14546
- children: [
14547
- /* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "relative z-10 size-11 text-amber-500 drop-shadow-[0_0_15px_rgba(245,158,11,0.30)]" }),
14548
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "pointer-events-none absolute inset-x-0 bottom-0 z-0 flex items-end justify-center gap-1 text-amber-400/80", children: [0, 1, 2, 3, 4, 5].map((leg) => /* @__PURE__ */ jsxRuntimeExports.jsx(
14549
- "span",
14550
- {
14551
- className: cn(
14552
- "floating-crab-leg h-2 w-0.5 rounded-full bg-current shadow-[0_0_7px_rgba(245,158,11,0.24)]",
14553
- leg < 3 ? "floating-crab-leg-left" : "floating-crab-leg-right",
14554
- dragging && "animate-none"
14555
- ),
14556
- style: {
14557
- animationDelay: `${leg % 2 === 0 ? 0 : 180}ms`
14558
- }
14559
- },
14560
- leg
14561
- )) })
14562
- ]
14563
- }
14564
- )
14565
- }
14566
- );
14567
- }
14568
15099
  function SessionContextLogButton({ label, logId }) {
14569
15100
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
14570
15101
  "button",
@@ -14881,6 +15412,7 @@ function ProxyViewer({
14881
15412
  }) {
14882
15413
  const { totalIn, totalOut } = reactExports.useMemo(() => computeTokenSummary(logs), [logs]);
14883
15414
  const { providers } = useProviders();
15415
+ const { uiScalePreference, resolvedUiScale, setUiScalePreference } = useUiScale();
14884
15416
  const [exporting, setExporting] = reactExports.useState(null);
14885
15417
  const [exportError, setExportError] = reactExports.useState(null);
14886
15418
  const [importing, setImporting] = reactExports.useState(false);
@@ -15122,7 +15654,7 @@ function ProxyViewer({
15122
15654
  onClick: toggleDetailsPanelCollapsed,
15123
15655
  className: cn(
15124
15656
  "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]"
15657
+ detailsPanelCollapsed ? "right-3" : "lg:right-[calc(21.25rem-0.875rem)] xl:right-[calc(22.5rem-0.875rem)]"
15126
15658
  ),
15127
15659
  "aria-label": detailsPanelCollapsed ? "Show details panel" : "Hide details panel",
15128
15660
  title: detailsPanelCollapsed ? "Show details panel" : "Hide details panel",
@@ -15132,13 +15664,13 @@ function ProxyViewer({
15132
15664
  ]
15133
15665
  }
15134
15666
  ),
15135
- detailsPanelCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(FloatingInspectorLogo, {}),
15667
+ detailsPanelCollapsed && /* @__PURE__ */ jsxRuntimeExports.jsx(InspectorPet, { logs }),
15136
15668
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
15137
15669
  "div",
15138
15670
  {
15139
15671
  className: cn(
15140
15672
  "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]"
15673
+ detailsPanelCollapsed ? "lg:grid-cols-[minmax(0,1fr)]" : "lg:grid-cols-[minmax(0,1fr)_21.25rem] xl:grid-cols-[minmax(0,1fr)_22.5rem]"
15142
15674
  ),
15143
15675
  children: [
15144
15676
  !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 +15680,10 @@ function ProxyViewer({
15148
15680
  compact: true,
15149
15681
  crabEntrancePhase,
15150
15682
  currentSessionId: labSessionId,
15151
- logCount: logs.length
15683
+ logCount: logs.length,
15684
+ uiScalePreference,
15685
+ resolvedUiScale,
15686
+ onUiScaleChange: setUiScalePreference
15152
15687
  }
15153
15688
  ),
15154
15689
  /* @__PURE__ */ jsxRuntimeExports.jsxs(SidebarPanel, { label: "Session", children: [
@@ -15489,6 +16024,32 @@ function ProxyViewer({
15489
16024
  ) })
15490
16025
  ] });
15491
16026
  }
16027
+ function filterLogs(logs, selectedSession, selectedModel) {
16028
+ if (selectedSession === "__all__" && selectedModel === "__all__") return logs;
16029
+ return logs.filter((l) => {
16030
+ if (selectedSession !== "__all__" && l.sessionId !== selectedSession) return false;
16031
+ if (selectedModel !== "__all__" && l.model !== selectedModel) return false;
16032
+ return true;
16033
+ });
16034
+ }
16035
+ function mergeLogsById(previousLogs, incomingLogs) {
16036
+ if (previousLogs.length === 0) return [...incomingLogs];
16037
+ if (incomingLogs.length === 0) return [...previousLogs];
16038
+ const byId = /* @__PURE__ */ new Map();
16039
+ for (const log of previousLogs) byId.set(log.id, log);
16040
+ for (const log of incomingLogs) byId.set(log.id, log);
16041
+ return [...byId.values()].toSorted((left, right) => left.id - right.id);
16042
+ }
16043
+ function mergeSessionIds(previousSessions, incomingSessions) {
16044
+ const merged = /* @__PURE__ */ new Set();
16045
+ for (const session of previousSessions) {
16046
+ if (session !== "") merged.add(session);
16047
+ }
16048
+ for (const session of incomingSessions) {
16049
+ if (session !== "") merged.add(session);
16050
+ }
16051
+ return [...merged].sort();
16052
+ }
15492
16053
  const SSEUpdateSchema = union([
15493
16054
  object({
15494
16055
  type: literal("init"),
@@ -15549,14 +16110,6 @@ function extractModels(logs) {
15549
16110
  }
15550
16111
  return [...set];
15551
16112
  }
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
16113
  const DEBOUNCE_MS = 100;
15561
16114
  const HASH_SCROLL_ATTEMPTS = 12;
15562
16115
  const HASH_HIGHLIGHT_MS = 1800;
@@ -15641,27 +16194,6 @@ async function deleteAllGroupsForFullHistoryDelete() {
15641
16194
  return { deleted: 0, ok: false };
15642
16195
  }
15643
16196
  }
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
16197
  function ProxyViewerContainer({
15666
16198
  initialSessionId
15667
16199
  } = {}) {