@invergent/agent-chat-react 1.5.3 → 1.5.5

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.
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
+ import { cn } from './chunk-QSC4UIVT.js';
1
2
  import { createContext, memo, useRef, useState, useEffect, useCallback, useMemo, lazy, useContext, Suspense, Children } from 'react';
2
3
  import { cva } from 'class-variance-authority';
3
4
  import { Collapsible as Collapsible$1, Slot, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Tooltip as Tooltip$1, Tabs as Tabs$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, HoverCard as HoverCard$1, Progress as Progress$1 } from 'radix-ui';
4
- import { clsx } from 'clsx';
5
- import { twMerge } from 'tailwind-merge';
6
5
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
6
  import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, GlobeIcon, SearchIcon, ListTodoIcon, XCircleIcon, CheckCircle2Icon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon, UsersIcon, MessageSquareIcon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, SquareIcon, ArrowDownIcon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, Maximize2Icon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, PlusIcon, CornerDownLeftIcon } from 'lucide-react';
8
7
  import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
@@ -21,7 +20,6 @@ import { nanoid } from 'nanoid';
21
20
  import 'pdfjs-dist/web/pdf_viewer.css';
22
21
  import { formatDistanceToNow } from 'date-fns';
23
22
 
24
- // src/agent-chat.tsx
25
23
  var AgentChatAdapterContext = createContext(null);
26
24
  var AgentChatAdapterProvider = AgentChatAdapterContext.Provider;
27
25
  function useAgentChatAdapterContext() {
@@ -33,9 +31,6 @@ function useAgentChatAdapterContext() {
33
31
  }
34
32
  return value;
35
33
  }
36
- function cn(...inputs) {
37
- return twMerge(clsx(inputs));
38
- }
39
34
  var buttonVariants = cva(
40
35
  "group/button inline-flex shrink-0 items-center justify-center rounded-none border border-transparent bg-clip-padding text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
41
36
  {
@@ -87,7 +82,7 @@ function Button({
87
82
  var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsx(
88
83
  StickToBottom,
89
84
  {
90
- className: cn("relative flex-1 overflow-y-auto", className),
85
+ className: cn("relative flex-1", className),
91
86
  initial: "smooth",
92
87
  resize: "smooth",
93
88
  role: "log",
@@ -570,6 +565,61 @@ function TimelineSeparator({
570
565
  }
571
566
  );
572
567
  }
568
+
569
+ // src/components/chat/tools/shared.ts
570
+ function statusColorClass(status) {
571
+ if (status === "running") return "bg-primary animate-pulse";
572
+ if (status === "error") return "bg-red-500";
573
+ if (status === "cancelled") return "bg-muted-foreground/40";
574
+ return "bg-emerald-500";
575
+ }
576
+ function effectiveStatus(tc) {
577
+ if (tc.cancelled) return "cancelled";
578
+ if (tc.status !== "complete" || !tc.result) return tc.status;
579
+ try {
580
+ const parsed = JSON.parse(tc.result);
581
+ if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
582
+ if (parsed?.error) return "error";
583
+ if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
584
+ if (parsed?.success === false) return "error";
585
+ } catch {
586
+ }
587
+ return "complete";
588
+ }
589
+ function formatArgs(args) {
590
+ try {
591
+ return JSON.stringify(JSON.parse(args), null, 2);
592
+ } catch {
593
+ return args;
594
+ }
595
+ }
596
+ function parseArgs(args) {
597
+ try {
598
+ return JSON.parse(args);
599
+ } catch {
600
+ return null;
601
+ }
602
+ }
603
+ function truncate(s, max) {
604
+ return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
605
+ }
606
+ function toolErrorSummary(result, max = 180) {
607
+ if (!result) return "";
608
+ try {
609
+ const parsed = JSON.parse(result);
610
+ const error = typeof parsed?.error === "string" ? parsed.error : "";
611
+ if (error === "sandbox_unavailable") {
612
+ return "Sandbox is unavailable. Workspace commands cannot run right now.";
613
+ }
614
+ const reason = typeof parsed?.reason === "string" ? parsed.reason : "";
615
+ const message = typeof parsed?.message === "string" ? parsed.message : "";
616
+ const detail = reason || message;
617
+ const summary = error && detail && detail !== error ? `${error}: ${detail}` : error || detail;
618
+ return summary ? summary.replace(/\s+/g, " ").slice(0, max) : "";
619
+ } catch {
620
+ return "";
621
+ }
622
+ }
573
623
  function CopyButton({ text }) {
574
624
  const [copied, setCopied] = useState(false);
575
625
  return /* @__PURE__ */ jsx(
@@ -605,7 +655,8 @@ function parseTerminalResult(result, args) {
605
655
  const hasOutput = typeof parsed?.output === "string";
606
656
  const hasStdout = typeof parsed?.stdout === "string";
607
657
  const hasExitCode = typeof parsed?.exit_code === "number";
608
- if (!hasOutput && !hasStdout && !hasExitCode) {
658
+ const errorSummary = toolErrorSummary(result, 400);
659
+ if (!hasOutput && !hasStdout && !hasExitCode && !errorSummary) {
609
660
  return null;
610
661
  }
611
662
  let command = "";
@@ -615,20 +666,20 @@ function parseTerminalResult(result, args) {
615
666
  } catch {
616
667
  }
617
668
  const output = parsed.output ?? parsed.stdout ?? "";
618
- const stderr = parsed.stderr ?? parsed.error ?? "";
669
+ const stderr = parsed.stderr ?? errorSummary;
619
670
  const combined = stderr ? `${output}
620
671
  ${stderr}`.trim() : output;
621
672
  return {
622
673
  output: combined,
623
- exit_code: parsed.exit_code ?? 0,
624
- error: parsed.error ?? null,
674
+ exit_code: parsed.exit_code ?? (errorSummary ? 1 : 0),
675
+ error: errorSummary || null,
625
676
  command
626
677
  };
627
678
  } catch {
628
679
  return null;
629
680
  }
630
681
  }
631
- function TerminalCollapsible({ command, output, isRunning }) {
682
+ function TerminalCollapsible({ command, output, isRunning, hasError }) {
632
683
  const [isOpen, setIsOpen] = useState(false);
633
684
  return /* @__PURE__ */ jsxs(
634
685
  Collapsible,
@@ -638,7 +689,7 @@ function TerminalCollapsible({ command, output, isRunning }) {
638
689
  className: "not-prose w-full",
639
690
  children: [
640
691
  /* @__PURE__ */ jsxs(CollapsibleTrigger, { className: "group/trigger flex w-fit items-center gap-2 text-sm transition-colors", children: [
641
- /* @__PURE__ */ jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
692
+ /* @__PURE__ */ jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : hasError ? /* @__PURE__ */ jsx("span", { className: "font-semibold text-destructive", children: "Command failed" }) : /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
642
693
  /* @__PURE__ */ jsx(
643
694
  ChevronDownIcon,
644
695
  {
@@ -695,7 +746,8 @@ function TerminalToolBlock({ tc }) {
695
746
  {
696
747
  command: result.command,
697
748
  output,
698
- isRunning
749
+ isRunning,
750
+ hasError: result.exit_code !== 0 || Boolean(result.error)
699
751
  }
700
752
  );
701
753
  }
@@ -2340,7 +2392,7 @@ var Terminal = ({
2340
2392
  }
2341
2393
  ) });
2342
2394
  };
2343
- function parseArgs(args) {
2395
+ function parseArgs2(args) {
2344
2396
  try {
2345
2397
  return JSON.parse(args);
2346
2398
  } catch {
@@ -2564,7 +2616,7 @@ function OutputPreview({ output }) {
2564
2616
  }
2565
2617
  function ProcessToolBlock({ tc }) {
2566
2618
  const isRunning = tc.status === "running";
2567
- const args = parseArgs(tc.args);
2619
+ const args = parseArgs2(tc.args);
2568
2620
  const result = parseResult(tc.result);
2569
2621
  const action = args.action || "unknown";
2570
2622
  const actionLabel = {
@@ -2647,44 +2699,6 @@ function Textarea({ className, ...props }) {
2647
2699
  }
2648
2700
  );
2649
2701
  }
2650
-
2651
- // src/components/chat/tools/shared.ts
2652
- function statusColorClass(status) {
2653
- if (status === "running") return "bg-primary animate-pulse";
2654
- if (status === "error") return "bg-red-500";
2655
- if (status === "cancelled") return "bg-muted-foreground/40";
2656
- return "bg-emerald-500";
2657
- }
2658
- function effectiveStatus(tc) {
2659
- if (tc.cancelled) return "cancelled";
2660
- if (tc.status !== "complete" || !tc.result) return tc.status;
2661
- try {
2662
- const parsed = JSON.parse(tc.result);
2663
- if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
2664
- if (parsed?.error) return "error";
2665
- if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
2666
- if (parsed?.success === false) return "error";
2667
- } catch {
2668
- }
2669
- return "complete";
2670
- }
2671
- function formatArgs(args) {
2672
- try {
2673
- return JSON.stringify(JSON.parse(args), null, 2);
2674
- } catch {
2675
- return args;
2676
- }
2677
- }
2678
- function parseArgs2(args) {
2679
- try {
2680
- return JSON.parse(args);
2681
- } catch {
2682
- return null;
2683
- }
2684
- }
2685
- function truncate(s, max) {
2686
- return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
2687
- }
2688
2702
  var MAX_REASON_LENGTH = 500;
2689
2703
  function ExpertToolBlock({ tc }) {
2690
2704
  const [expanded, setExpanded] = useState(false);
@@ -2720,7 +2734,7 @@ function ExpertToolBlock({ tc }) {
2720
2734
  }
2721
2735
  void submit("up");
2722
2736
  };
2723
- const args = parseArgs2(tc.args) ?? {};
2737
+ const args = parseArgs(tc.args) ?? {};
2724
2738
  const expertName = args.expert ?? null;
2725
2739
  const question = args.question ?? args.prompt ?? "";
2726
2740
  const result = parseExpertResult(tc.result);
@@ -2832,7 +2846,7 @@ function ExpertToolBlock({ tc }) {
2832
2846
  }
2833
2847
  function parseExpertResult(result) {
2834
2848
  if (!result) return null;
2835
- const parsed = parseArgs2(result);
2849
+ const parsed = parseArgs(result);
2836
2850
  if (parsed) {
2837
2851
  return {
2838
2852
  ...parsed,
@@ -2939,11 +2953,11 @@ function ReasonForm({
2939
2953
  ] });
2940
2954
  }
2941
2955
  function SkillsListBlock({ tc }) {
2942
- const args = parseArgs2(tc.args) ?? {};
2956
+ const args = parseArgs(tc.args) ?? {};
2943
2957
  const filter = args.category ? `category: ${args.category}` : "all";
2944
2958
  let summary = "";
2945
2959
  if (tc.result) {
2946
- const parsed = parseArgs2(tc.result);
2960
+ const parsed = parseArgs(tc.result);
2947
2961
  if (parsed?.count !== void 0) {
2948
2962
  summary = `${parsed.count} skill${parsed.count === 1 ? "" : "s"}`;
2949
2963
  }
@@ -2958,11 +2972,11 @@ function SkillsListBlock({ tc }) {
2958
2972
  ] });
2959
2973
  }
2960
2974
  function SkillViewBlock({ tc }) {
2961
- const args = parseArgs2(tc.args) ?? {};
2975
+ const args = parseArgs(tc.args) ?? {};
2962
2976
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
2963
2977
  let summary = "";
2964
2978
  if (tc.result) {
2965
- const parsed = parseArgs2(tc.result);
2979
+ const parsed = parseArgs(tc.result);
2966
2980
  if (parsed?.staged_at) {
2967
2981
  summary = `staged at ${parsed.staged_at}`;
2968
2982
  } else if (parsed?.token_estimate) {
@@ -3009,7 +3023,7 @@ function buildAnswer(q, sel) {
3009
3023
  }
3010
3024
  function ClarifyToolBlock({ tc }) {
3011
3025
  const { adapter, sessionId } = useAgentChatAdapterContext();
3012
- const args = useMemo(() => parseArgs2(tc.args), [tc.args]);
3026
+ const args = useMemo(() => parseArgs(tc.args), [tc.args]);
3013
3027
  const questions = args?.questions ?? [];
3014
3028
  const [active, setActive] = useState(0);
3015
3029
  const [selections, setSelections] = useState(
@@ -3291,9 +3305,9 @@ function ClarifyLocked({
3291
3305
  ] });
3292
3306
  }
3293
3307
  function ArtifactToolBlock({ tc }) {
3294
- const args = parseArgs2(tc.args) ?? {};
3308
+ const args = parseArgs(tc.args) ?? {};
3295
3309
  const status = effectiveStatus(tc);
3296
- const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3310
+ const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Creating artifact\u2026" : "Created";
3297
3311
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
3298
3312
  /* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: label }),
3299
3313
  args.name && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate", children: args.name })
@@ -3305,7 +3319,7 @@ function firstLine(s) {
3305
3319
  }
3306
3320
  function DelegateToolBlock({ tc }) {
3307
3321
  const [expanded, setExpanded] = useState(false);
3308
- const args = parseArgs2(tc.args);
3322
+ const args = parseArgs(tc.args);
3309
3323
  const goal = args?.goal ?? "";
3310
3324
  const context = args?.context ?? "";
3311
3325
  const agentType = args?.agent_type;
@@ -3415,8 +3429,8 @@ var TARGET_LABEL = {
3415
3429
  };
3416
3430
  function MemoryToolBlock({ tc }) {
3417
3431
  const [isOpen, setIsOpen] = useState(false);
3418
- const args = parseArgs2(tc.args) ?? {};
3419
- const result = tc.result ? parseArgs2(tc.result) : null;
3432
+ const args = parseArgs(tc.args) ?? {};
3433
+ const result = tc.result ? parseArgs(tc.result) : null;
3420
3434
  const action = args.action ?? "add";
3421
3435
  const target = args.target ?? "memory";
3422
3436
  const verb = ACTION_VERB[action] ?? action;
@@ -3494,8 +3508,8 @@ var ACTION_LABEL = {
3494
3508
  remove_file: "Remove skill file"
3495
3509
  };
3496
3510
  function SkillManageToolBlock({ tc }) {
3497
- const args = parseArgs2(tc.args) ?? {};
3498
- const result = tc.result ? parseArgs2(tc.result) : null;
3511
+ const args = parseArgs(tc.args) ?? {};
3512
+ const result = tc.result ? parseArgs(tc.result) : null;
3499
3513
  const action = args.action ?? "manage";
3500
3514
  const label = ACTION_LABEL[action] ?? "Manage skill";
3501
3515
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
@@ -3516,8 +3530,8 @@ function firstLine2(value) {
3516
3530
  return (index === -1 ? value : value.slice(0, index)).trim();
3517
3531
  }
3518
3532
  function CoordinatorToolBlock({ tc }) {
3519
- const args = parseArgs2(tc.args) ?? {};
3520
- const result = tc.result ? parseArgs2(tc.result) : null;
3533
+ const args = parseArgs(tc.args) ?? {};
3534
+ const result = tc.result ? parseArgs(tc.result) : null;
3521
3535
  const failed = Boolean(result?.error);
3522
3536
  let label = "Worker";
3523
3537
  let target = "";
@@ -5468,7 +5482,7 @@ function exportArtifact(payload) {
5468
5482
  };
5469
5483
  case "chart":
5470
5484
  return {
5471
- text: JSON.stringify(payload.spec.vega_lite, null, 2),
5485
+ text: JSON.stringify(payload.spec.chart_js, null, 2),
5472
5486
  mime: "application/json",
5473
5487
  extension: "json"
5474
5488
  };
@@ -5529,7 +5543,7 @@ async function copyText(text) {
5529
5543
  }
5530
5544
  }
5531
5545
  var ArtifactChart = lazy(
5532
- () => import('./artifact-chart-7J6GOR4M.js').then((m) => ({ default: m.ArtifactChart }))
5546
+ () => import('./artifact-chart-X53FKRDZ.js').then((m) => ({ default: m.ArtifactChart }))
5533
5547
  );
5534
5548
  var KIND_LABEL = {
5535
5549
  markdown: "Markdown document",
@@ -5654,7 +5668,7 @@ function ArtifactBody({
5654
5668
  Suspense,
5655
5669
  {
5656
5670
  fallback: /* @__PURE__ */ jsx(Shimmer, { duration: 5, className: "text-sm text-muted-foreground", children: "Loading chart\u2026" }),
5657
- children: /* @__PURE__ */ jsx(ArtifactChart, { spec: payload.spec })
5671
+ children: /* @__PURE__ */ jsx(ArtifactChart, { spec: payload.spec, fill })
5658
5672
  }
5659
5673
  );
5660
5674
  case "html":
@@ -5874,7 +5888,7 @@ function OrphanSystemMarker({
5874
5888
  );
5875
5889
  }
5876
5890
  if (message.systemKind === "error" && message.errorInfo) {
5877
- return /* @__PURE__ */ jsx("div", { className: "mx-auto my-2 w-full max-w-4xl px-4", children: /* @__PURE__ */ jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
5891
+ return /* @__PURE__ */ jsx("div", { className: "mx-auto my-2 w-full max-w-4xl", children: /* @__PURE__ */ jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
5878
5892
  }
5879
5893
  return null;
5880
5894
  }
@@ -5928,17 +5942,24 @@ function TimelineEntryItem({
5928
5942
  ] });
5929
5943
  }
5930
5944
  if (entry.kind === "tool") {
5945
+ const rawStatus = effectiveStatus(entry.tc);
5946
+ const hideArtifactFailure = rawStatus === "error" && entry.tc.toolName === "create_artifact";
5947
+ const indicatorStatus = hideArtifactFailure ? "running" : rawStatus;
5948
+ const failureSummary = rawStatus === "error" && !hideArtifactFailure ? toolErrorSummary(entry.tc.result) : "";
5931
5949
  return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
5932
5950
  /* @__PURE__ */ jsxs(TimelineHeader, { children: [
5933
5951
  /* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
5934
5952
  /* @__PURE__ */ jsx(
5935
5953
  TimelineIndicator,
5936
5954
  {
5937
- className: cn("size-2 border-none", statusColorClass(effectiveStatus(entry.tc)))
5955
+ className: cn("size-2 border-none", statusColorClass(indicatorStatus))
5938
5956
  }
5939
5957
  )
5940
5958
  ] }),
5941
- /* @__PURE__ */ jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }) })
5959
+ /* @__PURE__ */ jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
5960
+ /* @__PURE__ */ jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }),
5961
+ failureSummary ? /* @__PURE__ */ jsx("div", { className: "max-w-full truncate text-xs text-destructive", title: failureSummary, children: failureSummary }) : null
5962
+ ] }) })
5942
5963
  ] });
5943
5964
  }
5944
5965
  if (entry.kind === "text") {
@@ -5989,13 +6010,14 @@ function TimelineEntryItem({
5989
6010
  /* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
5990
6011
  /* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2 border-none bg-primary animate-pulse" })
5991
6012
  ] }),
5992
- /* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 5, className: "text-sm text-foreground", children: "Working on it..." }) })
6013
+ /* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
5993
6014
  ] });
5994
6015
  }
5995
6016
  function AssistantGroup({
5996
6017
  messages,
5997
6018
  lastGlobalIndex,
5998
6019
  totalMessages,
6020
+ isRunning,
5999
6021
  sessionId,
6000
6022
  onFileSelect,
6001
6023
  onRetry
@@ -6005,6 +6027,15 @@ function AssistantGroup({
6005
6027
  const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
6006
6028
  entries.push(...messageToEntries(messages[i], isLast));
6007
6029
  }
6030
+ const isTailGroup = lastGlobalIndex === totalMessages - 1;
6031
+ const lastEntry = entries[entries.length - 1];
6032
+ const hasRunningTool = entries.some(
6033
+ (e) => e.kind === "tool" && e.tc.status === "running"
6034
+ );
6035
+ const tailMsg = messages[messages.length - 1];
6036
+ if (isTailGroup && isRunning && !hasRunningTool && lastEntry?.kind !== "thinking") {
6037
+ entries.push({ kind: "thinking", key: `${tailMsg.id}-tail-thinking` });
6038
+ }
6008
6039
  const tail = messages[messages.length - 1];
6009
6040
  const showErrorInfo = tail && tail.role === "assistant" && tail.status === "error" && !!tail.errorInfo;
6010
6041
  return /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
@@ -6056,6 +6087,7 @@ function ChatThread({
6056
6087
  sessionId,
6057
6088
  messages,
6058
6089
  isRunning,
6090
+ isLoadingHistory = false,
6059
6091
  onSend,
6060
6092
  onStop,
6061
6093
  onFileSelect,
@@ -6078,7 +6110,14 @@ function ChatThread({
6078
6110
  }, [messages]);
6079
6111
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-sm", children: [
6080
6112
  /* @__PURE__ */ jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
6081
- /* @__PURE__ */ jsx(ConversationContent, { className: "mx-auto w-full max-w-3xl", children: messages.length === 0 && !disabled ? /* @__PURE__ */ jsx(
6113
+ /* @__PURE__ */ jsx(ConversationContent, { className: "mx-auto w-full max-w-4xl", children: messages.length === 0 && isLoadingHistory ? /* @__PURE__ */ jsx(
6114
+ ConversationEmptyState,
6115
+ {
6116
+ icon: /* @__PURE__ */ jsx(MessageSquareIcon, { className: "size-8 opacity-40" }),
6117
+ title: "Loading conversation",
6118
+ description: "Fetching the session history."
6119
+ }
6120
+ ) : messages.length === 0 && !disabled ? /* @__PURE__ */ jsx(
6082
6121
  ConversationEmptyState,
6083
6122
  {
6084
6123
  icon: /* @__PURE__ */ jsx(MessageSquareIcon, { className: "size-8 opacity-40" }),
@@ -6120,6 +6159,7 @@ function ChatThread({
6120
6159
  messages: group.messages,
6121
6160
  lastGlobalIndex: group.lastGlobalIndex,
6122
6161
  totalMessages: messages.length,
6162
+ isRunning,
6123
6163
  sessionId,
6124
6164
  onFileSelect,
6125
6165
  onRetry: groupRetry
@@ -6127,11 +6167,11 @@ function ChatThread({
6127
6167
  group.messages[0].id
6128
6168
  );
6129
6169
  }),
6130
- isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 5, className: "text-sm", children: "Working on it..." }) }) })
6170
+ isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsx(MessageContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) }) })
6131
6171
  ] }) }),
6132
6172
  /* @__PURE__ */ jsx(ConversationScrollButton, {})
6133
6173
  ] }),
6134
- /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 pb-5 pt-3", children: [
6174
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-4xl px-6 pb-5 pt-3", children: [
6135
6175
  retryIndicator && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx(RetryBanner, { indicator: retryIndicator }) }),
6136
6176
  /* @__PURE__ */ jsx(
6137
6177
  ChatComposer,
@@ -6732,7 +6772,7 @@ function formatPdfPreviewError(error) {
6732
6772
  return error instanceof Error ? error.message : "Failed to render PDF preview.";
6733
6773
  }
6734
6774
  var SKELETON_WIDTHS2 = [75, 60, 90, 65, 80, 70, 85, 55];
6735
- var DEFAULT_WIDTH = 500;
6775
+ var DEFAULT_WIDTH = 400;
6736
6776
  var MIN_WIDTH = 300;
6737
6777
  var MAX_WIDTH = 900;
6738
6778
  function collectExpandedPaths(entries, depth = 0) {
@@ -6814,6 +6854,9 @@ function WorkspacePanel({
6814
6854
  sessionId,
6815
6855
  selectedPath,
6816
6856
  onSelectedPathChange,
6857
+ collapsed = false,
6858
+ onCollapsedChange,
6859
+ refreshSignal = 0,
6817
6860
  disabled = false
6818
6861
  }) {
6819
6862
  const fileInputRef = useRef(null);
@@ -6893,6 +6936,13 @@ function WorkspacePanel({
6893
6936
  useEffect(() => {
6894
6937
  void fetchTree();
6895
6938
  }, [fetchTree]);
6939
+ useEffect(() => {
6940
+ if (!sessionId || refreshSignal === 0) return;
6941
+ const timer = setTimeout(() => {
6942
+ void fetchTree();
6943
+ }, 300);
6944
+ return () => clearTimeout(timer);
6945
+ }, [refreshSignal, sessionId, fetchTree]);
6896
6946
  useEffect(() => {
6897
6947
  if (!sessionId) {
6898
6948
  setFile(null);
@@ -7008,6 +7058,25 @@ function WorkspacePanel({
7008
7058
  window.removeEventListener("mouseup", onMouseUp);
7009
7059
  };
7010
7060
  }, []);
7061
+ if (collapsed) {
7062
+ return /* @__PURE__ */ jsx(
7063
+ "aside",
7064
+ {
7065
+ role: "button",
7066
+ tabIndex: 0,
7067
+ className: "relative z-10 flex min-h-0 w-10 shrink-0 cursor-pointer items-center justify-center border-l border-muted-foreground/20 bg-card text-muted-foreground transition-colors hover:bg-muted/50 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30",
7068
+ "aria-label": "Expand workspace",
7069
+ onClick: () => onCollapsedChange?.(false),
7070
+ onKeyDown: (event) => {
7071
+ if (event.key === "Enter" || event.key === " ") {
7072
+ event.preventDefault();
7073
+ onCollapsedChange?.(false);
7074
+ }
7075
+ },
7076
+ children: /* @__PURE__ */ jsx(FolderOpenIcon, { className: "size-4 text-amber-500" })
7077
+ }
7078
+ );
7079
+ }
7011
7080
  return /* @__PURE__ */ jsxs(
7012
7081
  "aside",
7013
7082
  {
@@ -7040,6 +7109,19 @@ function WorkspacePanel({
7040
7109
  /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
7041
7110
  /* @__PURE__ */ jsx("div", { className: "truncate text-xs text-faint", children: "Workspace" })
7042
7111
  ] }),
7112
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
7113
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
7114
+ Button,
7115
+ {
7116
+ variant: "ghost",
7117
+ size: "icon-sm",
7118
+ onClick: () => onCollapsedChange?.(true),
7119
+ "aria-label": "Collapse workspace",
7120
+ children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-4" })
7121
+ }
7122
+ ) }),
7123
+ /* @__PURE__ */ jsx(TooltipContent, { side: "bottom", children: "Collapse" })
7124
+ ] }),
7043
7125
  /* @__PURE__ */ jsxs(Tooltip, { children: [
7044
7126
  /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
7045
7127
  Button,
@@ -7201,6 +7283,12 @@ function ConfirmDialog({
7201
7283
  }
7202
7284
 
7203
7285
  // src/runtime/events.ts
7286
+ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
7287
+ "terminal",
7288
+ "write_file",
7289
+ "patch",
7290
+ "execute_code"
7291
+ ]);
7204
7292
  var AGENT_CHAT_LISTENED_EVENTS = [
7205
7293
  "user.message",
7206
7294
  "llm.request",
@@ -7239,21 +7327,24 @@ var EMPTY_TOKEN_USAGE = {
7239
7327
  contextWindow: 0,
7240
7328
  model: ""
7241
7329
  };
7242
- function createInitialAgentChatState() {
7330
+ function createInitialAgentChatState(options = {}) {
7243
7331
  return {
7244
7332
  messages: [],
7245
7333
  isRunning: false,
7334
+ isLoadingHistory: options.isLoadingHistory ?? false,
7246
7335
  tokenUsage: EMPTY_TOKEN_USAGE,
7247
7336
  retryIndicator: null,
7248
7337
  lastEventId: 0,
7249
7338
  sessionDone: false,
7250
7339
  hadDeltas: false,
7251
- terminal: false
7340
+ terminal: false,
7341
+ workspaceRefreshKey: 0
7252
7342
  };
7253
7343
  }
7254
7344
  function applyAgentChatEvent(state, event) {
7255
7345
  let nextState = {
7256
7346
  ...state,
7347
+ isLoadingHistory: false,
7257
7348
  lastEventId: Math.max(state.lastEventId, event.eventId),
7258
7349
  sessionDone: state.sessionDone || event.type === "session.done"
7259
7350
  };
@@ -7309,11 +7400,17 @@ function applyAgentChatEvent(state, event) {
7309
7400
  return applyLlmThinking(nextState, event);
7310
7401
  case "tool.call":
7311
7402
  return applyToolCall(nextState, event);
7312
- case "tool.result":
7313
- return withMessages(
7314
- nextState,
7315
- applyToolResult(nextState.messages, event.data)
7316
- );
7403
+ case "tool.result": {
7404
+ const toolCallId = stringValue(event.data.tool_call_id);
7405
+ const toolName = findToolNameById(nextState.messages, toolCallId);
7406
+ const messages = applyToolResult(nextState.messages, event.data);
7407
+ const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
7408
+ return {
7409
+ ...nextState,
7410
+ messages,
7411
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
7412
+ };
7413
+ }
7317
7414
  case "harness.wake":
7318
7415
  case "llm.request":
7319
7416
  return nextState.terminal ? nextState : { ...nextState, isRunning: true };
@@ -7753,6 +7850,14 @@ function hasUserAfterIndex(messages, idx) {
7753
7850
  }
7754
7851
  return false;
7755
7852
  }
7853
+ function findToolNameById(messages, toolCallId) {
7854
+ if (!toolCallId) return null;
7855
+ for (let i = messages.length - 1; i >= 0; i--) {
7856
+ const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
7857
+ if (tc) return tc.toolName;
7858
+ }
7859
+ return null;
7860
+ }
7756
7861
  function findLatestConsultExpertCall(messages) {
7757
7862
  for (let i = messages.length - 1; i >= 0; i--) {
7758
7863
  const msg = messages[i];
@@ -7787,11 +7892,12 @@ function useAgentChatRuntime({
7787
7892
  onSessionChange
7788
7893
  }) {
7789
7894
  const [state, setState] = useState(
7790
- () => createInitialAgentChatState()
7895
+ () => createInitialAgentChatState({ isLoadingHistory: Boolean(sessionId) })
7791
7896
  );
7792
7897
  const stateRef = useRef(state);
7793
7898
  const streamRef = useRef(null);
7794
7899
  const reconnectTimerRef = useRef(null);
7900
+ const previousSessionIdRef = useRef(sessionId);
7795
7901
  useEffect(() => {
7796
7902
  stateRef.current = state;
7797
7903
  }, [state]);
@@ -7807,6 +7913,8 @@ function useAgentChatRuntime({
7807
7913
  stream?.close();
7808
7914
  }, []);
7809
7915
  useEffect(() => {
7916
+ const previousSessionId = previousSessionIdRef.current;
7917
+ previousSessionIdRef.current = sessionId;
7810
7918
  clearReconnectTimer();
7811
7919
  closeStream();
7812
7920
  if (!sessionId) {
@@ -7814,11 +7922,11 @@ function useAgentChatRuntime({
7814
7922
  return;
7815
7923
  }
7816
7924
  let cancelled = false;
7817
- const connect = () => {
7925
+ const connect = (after) => {
7818
7926
  if (cancelled) return;
7819
7927
  const stream = adapter.openEventStream({
7820
7928
  sessionId,
7821
- after: stateRef.current.lastEventId
7929
+ after: after ?? stateRef.current.lastEventId
7822
7930
  });
7823
7931
  streamRef.current = stream;
7824
7932
  for (const eventType of AGENT_CHAT_LISTENED_EVENTS) {
@@ -7841,14 +7949,32 @@ function useAgentChatRuntime({
7841
7949
  streamRef.current = null;
7842
7950
  }
7843
7951
  if (!stateRef.current.sessionDone && !cancelled) {
7844
- reconnectTimerRef.current = setTimeout(connect, 3e3);
7952
+ reconnectTimerRef.current = setTimeout(() => connect(), 3e3);
7845
7953
  }
7846
7954
  };
7847
7955
  };
7848
- setState(createInitialAgentChatState());
7849
- connect();
7956
+ const currentState = stateRef.current;
7957
+ const preservePendingFirstMessage = previousSessionId === null && currentState.isRunning && currentState.messages.some(
7958
+ (message) => message.role === "user" && message.id.startsWith("local-")
7959
+ );
7960
+ const initialState = preservePendingFirstMessage ? {
7961
+ ...createInitialAgentChatState({ isLoadingHistory: false }),
7962
+ messages: currentState.messages,
7963
+ isRunning: true
7964
+ } : createInitialAgentChatState({
7965
+ isLoadingHistory: true
7966
+ });
7967
+ stateRef.current = initialState;
7968
+ setState(initialState);
7969
+ connect(0);
7850
7970
  adapter.getSession({ sessionId }).then((session) => {
7851
7971
  if (cancelled) return;
7972
+ if (session.messageCount === 0) {
7973
+ setState((prev) => ({
7974
+ ...prev,
7975
+ isLoadingHistory: false
7976
+ }));
7977
+ }
7852
7978
  if (isTerminalStatus(session.status)) {
7853
7979
  setState((prev) => ({
7854
7980
  ...prev,
@@ -7931,13 +8057,18 @@ function useAgentChatRuntime({
7931
8057
  }, []);
7932
8058
  const send = useCallback(
7933
8059
  async (content) => {
8060
+ markSending(content);
7934
8061
  if (!sessionId) {
7935
- const session = await adapter.createSession({ agentId });
7936
- await adapter.sendMessage({ sessionId: session.id, content });
7937
- onSessionChange?.(session.id);
8062
+ try {
8063
+ const session = await adapter.createSession({ agentId });
8064
+ onSessionChange?.(session.id);
8065
+ await adapter.sendMessage({ sessionId: session.id, content });
8066
+ } catch (error) {
8067
+ markSendError(error instanceof Error ? error.message : "send failed");
8068
+ throw error;
8069
+ }
7938
8070
  return;
7939
8071
  }
7940
- markSending(content);
7941
8072
  try {
7942
8073
  await adapter.sendMessage({ sessionId, content });
7943
8074
  } catch (error) {
@@ -7974,8 +8105,10 @@ function useAgentChatRuntime({
7974
8105
  return {
7975
8106
  messages: state.messages,
7976
8107
  isRunning: state.isRunning,
8108
+ isLoadingHistory: state.isLoadingHistory,
7977
8109
  tokenUsage: state.tokenUsage,
7978
8110
  retryIndicator: state.retryIndicator,
8111
+ workspaceRefreshKey: state.workspaceRefreshKey,
7979
8112
  send,
7980
8113
  stop,
7981
8114
  retry,
@@ -8010,6 +8143,7 @@ function AgentChat({
8010
8143
  disabled
8011
8144
  }) {
8012
8145
  const [workspacePath, setWorkspacePath] = useState(null);
8146
+ const [workspaceCollapsed, setWorkspaceCollapsed] = useState(false);
8013
8147
  const runtime = useAgentChatRuntime({
8014
8148
  adapter,
8015
8149
  agentId,
@@ -8041,6 +8175,7 @@ function AgentChat({
8041
8175
  sessionId,
8042
8176
  messages: runtime.messages,
8043
8177
  isRunning: runtime.isRunning,
8178
+ isLoadingHistory: runtime.isLoadingHistory,
8044
8179
  onSend: (content) => void runtime.send(content),
8045
8180
  onStop: () => void runtime.stop(),
8046
8181
  onRetry: runtime.retry,
@@ -8057,6 +8192,9 @@ function AgentChat({
8057
8192
  sessionId,
8058
8193
  selectedPath: workspacePath,
8059
8194
  onSelectedPathChange: setWorkspacePath,
8195
+ collapsed: workspaceCollapsed,
8196
+ onCollapsedChange: setWorkspaceCollapsed,
8197
+ refreshSignal: runtime.workspaceRefreshKey,
8060
8198
  disabled
8061
8199
  }
8062
8200
  )
@@ -8127,6 +8265,20 @@ function mergeTreeNodes(groups) {
8127
8265
  }
8128
8266
  return Array.from(byId.values());
8129
8267
  }
8268
+ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
8269
+ const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
8270
+ let changed = true;
8271
+ while (changed) {
8272
+ changed = false;
8273
+ for (const node of nodes) {
8274
+ if (node.parentId && deletedIds.has(node.parentId) && !deletedIds.has(node.id)) {
8275
+ deletedIds.add(node.id);
8276
+ changed = true;
8277
+ }
8278
+ }
8279
+ }
8280
+ return nodes.filter((node) => !deletedIds.has(node.id));
8281
+ }
8130
8282
  function formatSessionTime(value) {
8131
8283
  const date = new Date(value);
8132
8284
  if (Number.isNaN(date.getTime())) return "";
@@ -8147,6 +8299,7 @@ function TreeNodeRow({
8147
8299
  const hasChildren = entry.children.length > 0;
8148
8300
  const isActive = entry.id === activeSessionId;
8149
8301
  const isRunning = entry.status === "active";
8302
+ const isSubAgent = entry.parentId != null;
8150
8303
  const title = entry.title ?? "New session";
8151
8304
  const subtitle = [
8152
8305
  entry.model ?? "default",
@@ -8186,7 +8339,7 @@ function TreeNodeRow({
8186
8339
  /* @__PURE__ */ jsx("div", { className: "text-sm truncate", children: title }),
8187
8340
  /* @__PURE__ */ jsx("div", { className: "text-xs text-faint truncate", children: subtitle })
8188
8341
  ] }),
8189
- isRunning && canStop && /* @__PURE__ */ jsx(
8342
+ isRunning && canStop && isSubAgent && /* @__PURE__ */ jsx(
8190
8343
  "button",
8191
8344
  {
8192
8345
  type: "button",
@@ -8243,11 +8396,12 @@ function SessionTreePanel({
8243
8396
  title = "Running",
8244
8397
  sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
8245
8398
  hideRoot = false,
8399
+ hideHeader = false,
8400
+ loadList = false,
8246
8401
  onSessionSelect,
8247
8402
  onSessionDelete
8248
8403
  }) {
8249
8404
  const [nodes, setNodes] = useState([]);
8250
- const [loading, setLoading] = useState(false);
8251
8405
  const [error, setError] = useState(null);
8252
8406
  const [hasEverLoaded, setHasEverLoaded] = useState(false);
8253
8407
  const [deletingSessionId, setDeletingSessionId] = useState(
@@ -8259,18 +8413,16 @@ function SessionTreePanel({
8259
8413
  const resetContext = useRef(null);
8260
8414
  const refetch = useCallback(
8261
8415
  async (opts) => {
8262
- const canLoadSessionList = Boolean(agentId && adapter.listSessions);
8416
+ const canLoadSessionList = Boolean(loadList && adapter.listSessions);
8263
8417
  const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
8264
8418
  if (!canLoadSessionList && !canLoadSessionTree) {
8265
8419
  setNodes([]);
8266
8420
  setHasEverLoaded(true);
8267
- setLoading(false);
8268
8421
  return;
8269
8422
  }
8270
8423
  const currentRequestId = ++requestId.current;
8271
- if (!opts?.silent) setLoading(true);
8272
8424
  try {
8273
- const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
8425
+ const sessionListPromise = loadList && adapter.listSessions ? adapter.listSessions({
8274
8426
  agentId,
8275
8427
  limit: sessionListLimit
8276
8428
  }) : Promise.resolve(null);
@@ -8296,13 +8448,9 @@ function SessionTreePanel({
8296
8448
  if (!opts?.silent) {
8297
8449
  setError(e instanceof Error ? e.message : "Failed to load tree");
8298
8450
  }
8299
- } finally {
8300
- if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
8301
- setLoading(false);
8302
- }
8303
8451
  }
8304
8452
  },
8305
- [adapter, agentId, sessionId, sessionListLimit]
8453
+ [adapter, agentId, loadList, sessionId, sessionListLimit]
8306
8454
  );
8307
8455
  useEffect(() => {
8308
8456
  mounted.current = true;
@@ -8312,8 +8460,8 @@ function SessionTreePanel({
8312
8460
  }, []);
8313
8461
  useEffect(() => {
8314
8462
  const previous = resetContext.current;
8315
- const shouldReset = !previous || previous.adapter !== adapter || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8316
- resetContext.current = { adapter, agentId, sessionListLimit };
8463
+ const shouldReset = !previous || previous.loadList !== loadList || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8464
+ resetContext.current = { loadList, agentId, sessionListLimit };
8317
8465
  if (shouldReset) {
8318
8466
  setNodes([]);
8319
8467
  setHasEverLoaded(false);
@@ -8321,7 +8469,7 @@ function SessionTreePanel({
8321
8469
  }
8322
8470
  setError(null);
8323
8471
  void refetch();
8324
- }, [adapter, agentId, refetch, sessionListLimit]);
8472
+ }, [adapter, agentId, loadList, refetch, sessionListLimit]);
8325
8473
  const runningCount = useMemo(
8326
8474
  () => nodes.filter((n) => n.status === "active").length,
8327
8475
  [nodes]
@@ -8358,6 +8506,11 @@ function SessionTreePanel({
8358
8506
  setDeletingSessionId(id);
8359
8507
  try {
8360
8508
  await adapter.deleteSession({ sessionId: id });
8509
+ setNodes((current) => {
8510
+ const next = pruneDeletedSessionNodes(current, id);
8511
+ lastFingerprint.current = treeFingerprint(next);
8512
+ return next;
8513
+ });
8361
8514
  onSessionDelete?.(id);
8362
8515
  await refetch({ silent: true });
8363
8516
  } catch (e) {
@@ -8372,24 +8525,12 @@ function SessionTreePanel({
8372
8525
  if (nodes.length === 0) return null;
8373
8526
  const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
8374
8527
  if (topLevel.length === 0) return null;
8375
- return /* @__PURE__ */ jsxs("div", { className: "border-t border-line", children: [
8376
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide", children: [
8528
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
8529
+ !hideHeader && /* @__PURE__ */ jsxs("div", { className: "border-t border-line flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide", children: [
8377
8530
  /* @__PURE__ */ jsx(UsersIcon, { className: "w-3.5 h-3.5" }),
8378
8531
  /* @__PURE__ */ jsx("span", { children: title }),
8379
8532
  runningCount > 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
8380
8533
  ] }),
8381
- loading && /* @__PURE__ */ jsx(
8382
- "div",
8383
- {
8384
- className: "px-3 py-2",
8385
- role: "status",
8386
- "aria-label": "Loading sessions",
8387
- children: /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
8388
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3.5 w-28" }),
8389
- /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-20" })
8390
- ] })
8391
- }
8392
- ),
8393
8534
  error && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
8394
8535
  !error && /* @__PURE__ */ jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsx(
8395
8536
  TreeNodeRow,