@invergent/agent-chat-react 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
+ var clsx = require('clsx');
4
+ var tailwindMerge = require('tailwind-merge');
3
5
  var react = require('react');
4
- var reactVega = require('react-vega');
6
+ var auto = require('chart.js/auto');
5
7
  var nextThemes = require('next-themes');
6
8
  var jsxRuntime = require('react/jsx-runtime');
7
9
  var classVarianceAuthority = require('class-variance-authority');
8
10
  var radixUi = require('radix-ui');
9
- var clsx = require('clsx');
10
- var tailwindMerge = require('tailwind-merge');
11
11
  var lucideReact = require('lucide-react');
12
12
  var useStickToBottom = require('use-stick-to-bottom');
13
13
  var cjk = require('@streamdown/cjk');
@@ -39,56 +39,63 @@ var __export = (target, all) => {
39
39
  for (var name in all)
40
40
  __defProp(target, name, { get: all[name], enumerable: true });
41
41
  };
42
+ function cn(...inputs) {
43
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
44
+ }
45
+ var init_utils = __esm({
46
+ "src/lib/utils.ts"() {
47
+ }
48
+ });
42
49
 
43
50
  // src/components/chat/artifacts/artifact-chart.tsx
44
51
  var artifact_chart_exports = {};
45
52
  __export(artifact_chart_exports, {
46
53
  ArtifactChart: () => ArtifactChart
47
54
  });
48
- function ArtifactChart({ spec }) {
55
+ function ArtifactChart({
56
+ spec,
57
+ fill = false
58
+ }) {
49
59
  const { resolvedTheme } = nextThemes.useTheme();
50
60
  const isDark = resolvedTheme === "dark";
51
- const containerRef = react.useRef(null);
52
- const [width, setWidth] = react.useState(null);
53
- react.useEffect(() => {
54
- const el = containerRef.current;
55
- if (!el) return;
56
- const observer = new ResizeObserver((entries) => {
57
- const w = entries[0]?.contentRect.width ?? el.offsetWidth;
58
- if (w >= MIN_CHART_WIDTH) setWidth(Math.floor(w));
59
- });
60
- observer.observe(el);
61
- if (el.offsetWidth >= MIN_CHART_WIDTH) setWidth(el.offsetWidth);
62
- return () => observer.disconnect();
63
- }, []);
61
+ const canvasRef = react.useRef(null);
62
+ const chartRef = react.useRef(null);
64
63
  const [error, setError] = react.useState(null);
65
- const merged = react.useMemo(() => {
66
- if (width == null) return null;
67
- const base = spec.vega_lite ?? {};
68
- const themeConfig = isDark ? DARK_CONFIG : LIGHT_CONFIG;
64
+ const config = react.useMemo(() => {
65
+ const base = cloneChartConfig(spec.chart_js ?? {});
69
66
  return {
70
- $schema: VEGA_LITE_SCHEMA,
71
- width,
72
- height: DEFAULT_CHART_HEIGHT,
73
67
  ...base,
74
- config: {
75
- ...themeConfig,
76
- ...base.config ?? {}
77
- }
68
+ options: mergeThemeOptions(base.options, base.type, isDark)
78
69
  };
79
- }, [spec.vega_lite, isDark, width]);
70
+ }, [spec.chart_js, isDark]);
80
71
  react.useEffect(() => {
72
+ const canvas = canvasRef.current;
73
+ if (!canvas) return;
74
+ chartRef.current?.destroy();
75
+ chartRef.current = null;
81
76
  setError(null);
82
- }, [spec.vega_lite]);
77
+ try {
78
+ chartRef.current = new auto.Chart(canvas, config);
79
+ } catch (e) {
80
+ setError(e instanceof Error ? e.message : String(e));
81
+ }
82
+ return () => {
83
+ chartRef.current?.destroy();
84
+ chartRef.current = null;
85
+ };
86
+ }, [config]);
83
87
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
84
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, className: "w-full", children: merged && /* @__PURE__ */ jsxRuntime.jsx(
85
- reactVega.VegaEmbed,
88
+ /* @__PURE__ */ jsxRuntime.jsx(
89
+ "div",
86
90
  {
87
- spec: merged,
88
- options: { actions: false, renderer: "svg" },
89
- onError: (e) => setError(e instanceof Error ? e.message : String(e))
91
+ className: cn("relative w-full", fill && "h-full min-h-80"),
92
+ style: fill ? void 0 : {
93
+ height: DEFAULT_CHART_HEIGHT,
94
+ maxHeight: MAX_INLINE_CHART_HEIGHT
95
+ },
96
+ children: /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef })
90
97
  }
91
- ) }),
98
+ ),
92
99
  error && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-destructive", children: [
93
100
  "Chart error: ",
94
101
  error
@@ -96,35 +103,101 @@ function ArtifactChart({ spec }) {
96
103
  spec.caption && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: spec.caption })
97
104
  ] });
98
105
  }
99
- var VEGA_LITE_SCHEMA, DEFAULT_CHART_HEIGHT, MIN_CHART_WIDTH, LIGHT_CONFIG, DARK_CONFIG;
106
+ function cloneChartConfig(value) {
107
+ return JSON.parse(JSON.stringify(value));
108
+ }
109
+ function mergeThemeOptions(options, chartType, isDark) {
110
+ const theme = isDark ? DARK_THEME : LIGHT_THEME;
111
+ const base = isRecord(options) ? options : {};
112
+ const plugins = isRecord(base.plugins) ? base.plugins : {};
113
+ const legend = isRecord(plugins.legend) ? plugins.legend : {};
114
+ const legendLabels = isRecord(legend.labels) ? legend.labels : {};
115
+ const title = isRecord(plugins.title) ? plugins.title : {};
116
+ const scales = isRecord(base.scales) ? base.scales : {};
117
+ return {
118
+ responsive: true,
119
+ maintainAspectRatio: false,
120
+ color: theme.text,
121
+ ...base,
122
+ plugins: {
123
+ ...plugins,
124
+ legend: {
125
+ ...legend,
126
+ labels: {
127
+ color: theme.text,
128
+ ...legendLabels
129
+ }
130
+ },
131
+ title: {
132
+ color: theme.title,
133
+ ...title
134
+ }
135
+ },
136
+ scales: mergeScales(scales, chartType, theme)
137
+ };
138
+ }
139
+ function mergeScales(scales, chartType, theme) {
140
+ const names = /* @__PURE__ */ new Set([...defaultScaleNames(chartType), ...Object.keys(scales)]);
141
+ const merged = {};
142
+ for (const name of names) {
143
+ const scale = isRecord(scales[name]) ? scales[name] : {};
144
+ const ticks = isRecord(scale.ticks) ? scale.ticks : {};
145
+ const grid = isRecord(scale.grid) ? scale.grid : {};
146
+ const title = isRecord(scale.title) ? scale.title : {};
147
+ merged[name] = {
148
+ ...scale,
149
+ ticks: {
150
+ color: theme.text,
151
+ ...ticks
152
+ },
153
+ grid: {
154
+ color: theme.grid,
155
+ ...grid
156
+ },
157
+ border: {
158
+ color: theme.border,
159
+ ...isRecord(scale.border) ? scale.border : {}
160
+ },
161
+ title: {
162
+ color: theme.title,
163
+ ...title
164
+ }
165
+ };
166
+ }
167
+ return merged;
168
+ }
169
+ function defaultScaleNames(chartType) {
170
+ if (chartType === "bar" || chartType === "line" || chartType === "scatter") {
171
+ return ["x", "y"];
172
+ }
173
+ if (chartType === "bubble") {
174
+ return ["x", "y"];
175
+ }
176
+ if (chartType === "radar" || chartType === "polarArea") {
177
+ return ["r"];
178
+ }
179
+ return [];
180
+ }
181
+ function isRecord(value) {
182
+ return typeof value === "object" && value !== null && !Array.isArray(value);
183
+ }
184
+ var DEFAULT_CHART_HEIGHT, MAX_INLINE_CHART_HEIGHT, LIGHT_THEME, DARK_THEME;
100
185
  var init_artifact_chart = __esm({
101
186
  "src/components/chat/artifacts/artifact-chart.tsx"() {
102
- VEGA_LITE_SCHEMA = "https://vega.github.io/schema/vega-lite/v5.json";
187
+ init_utils();
103
188
  DEFAULT_CHART_HEIGHT = 320;
104
- MIN_CHART_WIDTH = 120;
105
- LIGHT_CONFIG = {
106
- background: "transparent",
107
- axis: {
108
- labelColor: "#444",
109
- titleColor: "#111",
110
- gridColor: "#e5e7eb",
111
- domainColor: "#d1d5db",
112
- tickColor: "#d1d5db"
113
- },
114
- view: { stroke: "transparent" },
115
- legend: { labelColor: "#444", titleColor: "#111" }
189
+ MAX_INLINE_CHART_HEIGHT = 800;
190
+ LIGHT_THEME = {
191
+ text: "#444",
192
+ title: "#111",
193
+ grid: "#e5e7eb",
194
+ border: "#d1d5db"
116
195
  };
117
- DARK_CONFIG = {
118
- background: "transparent",
119
- axis: {
120
- labelColor: "#cbd5e1",
121
- titleColor: "#f1f5f9",
122
- gridColor: "#374151",
123
- domainColor: "#4b5563",
124
- tickColor: "#4b5563"
125
- },
126
- view: { stroke: "transparent" },
127
- legend: { labelColor: "#cbd5e1", titleColor: "#f1f5f9" }
196
+ DARK_THEME = {
197
+ text: "#cbd5e1",
198
+ title: "#f1f5f9",
199
+ grid: "#374151",
200
+ border: "#4b5563"
128
201
  };
129
202
  }
130
203
  });
@@ -139,9 +212,9 @@ function useAgentChatAdapterContext() {
139
212
  }
140
213
  return value;
141
214
  }
142
- function cn(...inputs) {
143
- return tailwindMerge.twMerge(clsx.clsx(inputs));
144
- }
215
+
216
+ // src/components/ui/button.tsx
217
+ init_utils();
145
218
  var buttonVariants = classVarianceAuthority.cva(
146
219
  "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",
147
220
  {
@@ -190,10 +263,13 @@ function Button({
190
263
  }
191
264
  );
192
265
  }
266
+
267
+ // src/components/ai-elements/conversation.tsx
268
+ init_utils();
193
269
  var Conversation = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
194
270
  useStickToBottom.StickToBottom,
195
271
  {
196
- className: cn("relative flex-1 overflow-y-auto", className),
272
+ className: cn("relative flex-1", className),
197
273
  initial: "smooth",
198
274
  resize: "smooth",
199
275
  role: "log",
@@ -258,6 +334,9 @@ var ConversationScrollButton = ({
258
334
  }
259
335
  );
260
336
  };
337
+
338
+ // src/components/ui/tooltip.tsx
339
+ init_utils();
261
340
  function TooltipProvider({
262
341
  delayDuration = 0,
263
342
  ...props
@@ -304,6 +383,9 @@ function TooltipContent({
304
383
  }
305
384
  ) });
306
385
  }
386
+
387
+ // src/components/ai-elements/message.tsx
388
+ init_utils();
307
389
  var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
308
390
  "div",
309
391
  {
@@ -378,6 +460,12 @@ function CollapsibleContent({
378
460
  }
379
461
  );
380
462
  }
463
+
464
+ // src/components/ai-elements/reasoning.tsx
465
+ init_utils();
466
+
467
+ // src/components/ai-elements/shimmer.tsx
468
+ init_utils();
381
469
  var ShimmerComponent = ({
382
470
  children,
383
471
  as: Component = "p",
@@ -554,6 +642,9 @@ var ReasoningContent = react.memo(
554
642
  Reasoning.displayName = "Reasoning";
555
643
  ReasoningTrigger.displayName = "ReasoningTrigger";
556
644
  ReasoningContent.displayName = "ReasoningContent";
645
+
646
+ // src/components/reui/timeline.tsx
647
+ init_utils();
557
648
  var TimelineContext = react.createContext(
558
649
  void 0
559
650
  );
@@ -676,6 +767,62 @@ function TimelineSeparator({
676
767
  }
677
768
  );
678
769
  }
770
+ init_utils();
771
+
772
+ // src/components/chat/tools/shared.ts
773
+ function statusColorClass(status) {
774
+ if (status === "running") return "bg-primary animate-pulse";
775
+ if (status === "error") return "bg-red-500";
776
+ if (status === "cancelled") return "bg-muted-foreground/40";
777
+ return "bg-emerald-500";
778
+ }
779
+ function effectiveStatus(tc) {
780
+ if (tc.cancelled) return "cancelled";
781
+ if (tc.status !== "complete" || !tc.result) return tc.status;
782
+ try {
783
+ const parsed = JSON.parse(tc.result);
784
+ if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
785
+ if (parsed?.error) return "error";
786
+ if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
787
+ if (parsed?.success === false) return "error";
788
+ } catch {
789
+ }
790
+ return "complete";
791
+ }
792
+ function formatArgs(args) {
793
+ try {
794
+ return JSON.stringify(JSON.parse(args), null, 2);
795
+ } catch {
796
+ return args;
797
+ }
798
+ }
799
+ function parseArgs(args) {
800
+ try {
801
+ return JSON.parse(args);
802
+ } catch {
803
+ return null;
804
+ }
805
+ }
806
+ function truncate(s, max) {
807
+ return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
808
+ }
809
+ function toolErrorSummary(result, max = 180) {
810
+ if (!result) return "";
811
+ try {
812
+ const parsed = JSON.parse(result);
813
+ const error = typeof parsed?.error === "string" ? parsed.error : "";
814
+ if (error === "sandbox_unavailable") {
815
+ return "Sandbox is unavailable. Workspace commands cannot run right now.";
816
+ }
817
+ const reason = typeof parsed?.reason === "string" ? parsed.reason : "";
818
+ const message = typeof parsed?.message === "string" ? parsed.message : "";
819
+ const detail = reason || message;
820
+ const summary = error && detail && detail !== error ? `${error}: ${detail}` : error || detail;
821
+ return summary ? summary.replace(/\s+/g, " ").slice(0, max) : "";
822
+ } catch {
823
+ return "";
824
+ }
825
+ }
679
826
  function CopyButton({ text }) {
680
827
  const [copied, setCopied] = react.useState(false);
681
828
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -711,7 +858,8 @@ function parseTerminalResult(result, args) {
711
858
  const hasOutput = typeof parsed?.output === "string";
712
859
  const hasStdout = typeof parsed?.stdout === "string";
713
860
  const hasExitCode = typeof parsed?.exit_code === "number";
714
- if (!hasOutput && !hasStdout && !hasExitCode) {
861
+ const errorSummary = toolErrorSummary(result, 400);
862
+ if (!hasOutput && !hasStdout && !hasExitCode && !errorSummary) {
715
863
  return null;
716
864
  }
717
865
  let command = "";
@@ -721,20 +869,20 @@ function parseTerminalResult(result, args) {
721
869
  } catch {
722
870
  }
723
871
  const output = parsed.output ?? parsed.stdout ?? "";
724
- const stderr = parsed.stderr ?? parsed.error ?? "";
872
+ const stderr = parsed.stderr ?? errorSummary;
725
873
  const combined = stderr ? `${output}
726
874
  ${stderr}`.trim() : output;
727
875
  return {
728
876
  output: combined,
729
- exit_code: parsed.exit_code ?? 0,
730
- error: parsed.error ?? null,
877
+ exit_code: parsed.exit_code ?? (errorSummary ? 1 : 0),
878
+ error: errorSummary || null,
731
879
  command
732
880
  };
733
881
  } catch {
734
882
  return null;
735
883
  }
736
884
  }
737
- function TerminalCollapsible({ command, output, isRunning }) {
885
+ function TerminalCollapsible({ command, output, isRunning, hasError }) {
738
886
  const [isOpen, setIsOpen] = react.useState(false);
739
887
  return /* @__PURE__ */ jsxRuntime.jsxs(
740
888
  Collapsible,
@@ -744,7 +892,7 @@ function TerminalCollapsible({ command, output, isRunning }) {
744
892
  className: "not-prose w-full",
745
893
  children: [
746
894
  /* @__PURE__ */ jsxRuntime.jsxs(CollapsibleTrigger, { className: "group/trigger flex w-fit items-center gap-2 text-sm transition-colors", children: [
747
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
895
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-left", children: isRunning ? /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { as: "span", duration: 1, children: "Running command..." }) : hasError ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-destructive", children: "Command failed" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Command result" }) }),
748
896
  /* @__PURE__ */ jsxRuntime.jsx(
749
897
  lucideReact.ChevronDownIcon,
750
898
  {
@@ -801,7 +949,8 @@ function TerminalToolBlock({ tc }) {
801
949
  {
802
950
  command: result.command,
803
951
  output,
804
- isRunning
952
+ isRunning,
953
+ hasError: result.exit_code !== 0 || Boolean(result.error)
805
954
  }
806
955
  );
807
956
  }
@@ -816,6 +965,9 @@ function TerminalToolBlock({ tc }) {
816
965
  }
817
966
  return null;
818
967
  }
968
+
969
+ // src/components/ui/scroll-area.tsx
970
+ init_utils();
819
971
  function ScrollArea({
820
972
  className,
821
973
  children,
@@ -868,6 +1020,9 @@ function ScrollBar({
868
1020
  }
869
1021
  );
870
1022
  }
1023
+
1024
+ // src/components/ai-elements/queue.tsx
1025
+ init_utils();
871
1026
  var QueueItem = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
872
1027
  "li",
873
1028
  {
@@ -1020,6 +1175,9 @@ function TodoToolBlock({ tc }) {
1020
1175
  }) }) })
1021
1176
  ] }) });
1022
1177
  }
1178
+
1179
+ // src/components/ai-elements/code-block.tsx
1180
+ init_utils();
1023
1181
  var isItalic = (fontStyle) => fontStyle && fontStyle & 1;
1024
1182
  var isBold = (fontStyle) => fontStyle && fontStyle & 2;
1025
1183
  var isUnderline = (fontStyle) => (
@@ -1309,6 +1467,9 @@ var CodeBlockCopyButton = ({
1309
1467
  }
1310
1468
  );
1311
1469
  };
1470
+
1471
+ // src/components/ui/tabs.tsx
1472
+ init_utils();
1312
1473
  function Tabs({
1313
1474
  className,
1314
1475
  orientation = "horizontal",
@@ -1388,6 +1549,12 @@ function TabsContent({
1388
1549
  }
1389
1550
  );
1390
1551
  }
1552
+
1553
+ // src/components/ai-elements/sandbox.tsx
1554
+ init_utils();
1555
+
1556
+ // src/components/ui/badge.tsx
1557
+ init_utils();
1391
1558
  var badgeVariants = classVarianceAuthority.cva(
1392
1559
  "group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1.5 overflow-hidden rounded-none border-0 bg-transparent px-0 py-0 text-[0.625rem] font-semibold tracking-widest whitespace-nowrap uppercase transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-0 has-data-[icon=inline-start]:pl-0 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
1393
1560
  {
@@ -1645,6 +1812,9 @@ function WebToolBlock({ tc }) {
1645
1812
  displayText && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: displayText })
1646
1813
  ] });
1647
1814
  }
1815
+
1816
+ // src/components/ui/dialog.tsx
1817
+ init_utils();
1648
1818
  function Dialog({
1649
1819
  ...props
1650
1820
  }) {
@@ -1774,6 +1944,12 @@ function DialogDescription({
1774
1944
  }
1775
1945
  );
1776
1946
  }
1947
+
1948
+ // src/components/chat/tools/file-tools.tsx
1949
+ init_utils();
1950
+
1951
+ // src/components/chat/diff-viewer.tsx
1952
+ init_utils();
1777
1953
  function splitChangeLines(change) {
1778
1954
  const v = change.value;
1779
1955
  if (v === "") return [];
@@ -2252,6 +2428,9 @@ function ListFilesBlock({ tc }) {
2252
2428
  ] })
2253
2429
  ] });
2254
2430
  }
2431
+
2432
+ // src/components/ai-elements/terminal.tsx
2433
+ init_utils();
2255
2434
  var TerminalContext = react.createContext({
2256
2435
  autoScroll: true,
2257
2436
  isStreaming: false,
@@ -2446,7 +2625,10 @@ var Terminal = ({
2446
2625
  }
2447
2626
  ) });
2448
2627
  };
2449
- function parseArgs(args) {
2628
+
2629
+ // src/components/chat/tools/process-tool.tsx
2630
+ init_utils();
2631
+ function parseArgs2(args) {
2450
2632
  try {
2451
2633
  return JSON.parse(args);
2452
2634
  } catch {
@@ -2670,7 +2852,7 @@ function OutputPreview({ output }) {
2670
2852
  }
2671
2853
  function ProcessToolBlock({ tc }) {
2672
2854
  const isRunning = tc.status === "running";
2673
- const args = parseArgs(tc.args);
2855
+ const args = parseArgs2(tc.args);
2674
2856
  const result = parseResult(tc.result);
2675
2857
  const action = args.action || "unknown";
2676
2858
  const actionLabel = {
@@ -2740,6 +2922,12 @@ function ProcessToolBlock({ tc }) {
2740
2922
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-background pb-1", children: content })
2741
2923
  ] });
2742
2924
  }
2925
+
2926
+ // src/components/chat/tools/expert-tool.tsx
2927
+ init_utils();
2928
+
2929
+ // src/components/ui/textarea.tsx
2930
+ init_utils();
2743
2931
  function Textarea({ className, ...props }) {
2744
2932
  return /* @__PURE__ */ jsxRuntime.jsx(
2745
2933
  "textarea",
@@ -2753,44 +2941,6 @@ function Textarea({ className, ...props }) {
2753
2941
  }
2754
2942
  );
2755
2943
  }
2756
-
2757
- // src/components/chat/tools/shared.ts
2758
- function statusColorClass(status) {
2759
- if (status === "running") return "bg-primary animate-pulse";
2760
- if (status === "error") return "bg-red-500";
2761
- if (status === "cancelled") return "bg-muted-foreground/40";
2762
- return "bg-emerald-500";
2763
- }
2764
- function effectiveStatus(tc) {
2765
- if (tc.cancelled) return "cancelled";
2766
- if (tc.status !== "complete" || !tc.result) return tc.status;
2767
- try {
2768
- const parsed = JSON.parse(tc.result);
2769
- if (parsed?.exit_code !== void 0 && parsed.exit_code !== 0) return "error";
2770
- if (parsed?.error) return "error";
2771
- if (parsed?.status === "blocked" || parsed?.status === "error") return "error";
2772
- if (parsed?.success === false) return "error";
2773
- } catch {
2774
- }
2775
- return "complete";
2776
- }
2777
- function formatArgs(args) {
2778
- try {
2779
- return JSON.stringify(JSON.parse(args), null, 2);
2780
- } catch {
2781
- return args;
2782
- }
2783
- }
2784
- function parseArgs2(args) {
2785
- try {
2786
- return JSON.parse(args);
2787
- } catch {
2788
- return null;
2789
- }
2790
- }
2791
- function truncate(s, max) {
2792
- return s.length > max ? s.slice(0, max) + "\n... (truncated)" : s;
2793
- }
2794
2944
  var MAX_REASON_LENGTH = 500;
2795
2945
  function ExpertToolBlock({ tc }) {
2796
2946
  const [expanded, setExpanded] = react.useState(false);
@@ -2826,7 +2976,7 @@ function ExpertToolBlock({ tc }) {
2826
2976
  }
2827
2977
  void submit("up");
2828
2978
  };
2829
- const args = parseArgs2(tc.args) ?? {};
2979
+ const args = parseArgs(tc.args) ?? {};
2830
2980
  const expertName = args.expert ?? null;
2831
2981
  const question = args.question ?? args.prompt ?? "";
2832
2982
  const result = parseExpertResult(tc.result);
@@ -2938,7 +3088,7 @@ function ExpertToolBlock({ tc }) {
2938
3088
  }
2939
3089
  function parseExpertResult(result) {
2940
3090
  if (!result) return null;
2941
- const parsed = parseArgs2(result);
3091
+ const parsed = parseArgs(result);
2942
3092
  if (parsed) {
2943
3093
  return {
2944
3094
  ...parsed,
@@ -3045,11 +3195,11 @@ function ReasonForm({
3045
3195
  ] });
3046
3196
  }
3047
3197
  function SkillsListBlock({ tc }) {
3048
- const args = parseArgs2(tc.args) ?? {};
3198
+ const args = parseArgs(tc.args) ?? {};
3049
3199
  const filter = args.category ? `category: ${args.category}` : "all";
3050
3200
  let summary = "";
3051
3201
  if (tc.result) {
3052
- const parsed = parseArgs2(tc.result);
3202
+ const parsed = parseArgs(tc.result);
3053
3203
  if (parsed?.count !== void 0) {
3054
3204
  summary = `${parsed.count} skill${parsed.count === 1 ? "" : "s"}`;
3055
3205
  }
@@ -3064,11 +3214,11 @@ function SkillsListBlock({ tc }) {
3064
3214
  ] });
3065
3215
  }
3066
3216
  function SkillViewBlock({ tc }) {
3067
- const args = parseArgs2(tc.args) ?? {};
3217
+ const args = parseArgs(tc.args) ?? {};
3068
3218
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
3069
3219
  let summary = "";
3070
3220
  if (tc.result) {
3071
- const parsed = parseArgs2(tc.result);
3221
+ const parsed = parseArgs(tc.result);
3072
3222
  if (parsed?.staged_at) {
3073
3223
  summary = `staged at ${parsed.staged_at}`;
3074
3224
  } else if (parsed?.token_estimate) {
@@ -3084,6 +3234,12 @@ function SkillViewBlock({ tc }) {
3084
3234
  ] })
3085
3235
  ] });
3086
3236
  }
3237
+
3238
+ // src/components/chat/tools/clarify-tool.tsx
3239
+ init_utils();
3240
+
3241
+ // src/components/ui/input.tsx
3242
+ init_utils();
3087
3243
  function Input({ className, type, ...props }) {
3088
3244
  return /* @__PURE__ */ jsxRuntime.jsx(
3089
3245
  "input",
@@ -3115,7 +3271,7 @@ function buildAnswer(q, sel) {
3115
3271
  }
3116
3272
  function ClarifyToolBlock({ tc }) {
3117
3273
  const { adapter, sessionId } = useAgentChatAdapterContext();
3118
- const args = react.useMemo(() => parseArgs2(tc.args), [tc.args]);
3274
+ const args = react.useMemo(() => parseArgs(tc.args), [tc.args]);
3119
3275
  const questions = args?.questions ?? [];
3120
3276
  const [active, setActive] = react.useState(0);
3121
3277
  const [selections, setSelections] = react.useState(
@@ -3397,7 +3553,7 @@ function ClarifyLocked({
3397
3553
  ] });
3398
3554
  }
3399
3555
  function ArtifactToolBlock({ tc }) {
3400
- const args = parseArgs2(tc.args) ?? {};
3556
+ const args = parseArgs(tc.args) ?? {};
3401
3557
  const status = effectiveStatus(tc);
3402
3558
  const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3403
3559
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
@@ -3405,13 +3561,16 @@ function ArtifactToolBlock({ tc }) {
3405
3561
  args.name && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: args.name })
3406
3562
  ] });
3407
3563
  }
3564
+
3565
+ // src/components/chat/tools/delegate-tool.tsx
3566
+ init_utils();
3408
3567
  function firstLine(s) {
3409
3568
  const idx = s.indexOf("\n");
3410
3569
  return idx === -1 ? s : s.slice(0, idx);
3411
3570
  }
3412
3571
  function DelegateToolBlock({ tc }) {
3413
3572
  const [expanded, setExpanded] = react.useState(false);
3414
- const args = parseArgs2(tc.args);
3573
+ const args = parseArgs(tc.args);
3415
3574
  const goal = args?.goal ?? "";
3416
3575
  const context = args?.context ?? "";
3417
3576
  const agentType = args?.agent_type;
@@ -3505,6 +3664,7 @@ function DelegateToolBlock({ tc }) {
3505
3664
  ] })
3506
3665
  ] });
3507
3666
  }
3667
+ init_utils();
3508
3668
  var ACTION_VERB = {
3509
3669
  add: "Saved",
3510
3670
  replace: "Updated",
@@ -3521,8 +3681,8 @@ var TARGET_LABEL = {
3521
3681
  };
3522
3682
  function MemoryToolBlock({ tc }) {
3523
3683
  const [isOpen, setIsOpen] = react.useState(false);
3524
- const args = parseArgs2(tc.args) ?? {};
3525
- const result = tc.result ? parseArgs2(tc.result) : null;
3684
+ const args = parseArgs(tc.args) ?? {};
3685
+ const result = tc.result ? parseArgs(tc.result) : null;
3526
3686
  const action = args.action ?? "add";
3527
3687
  const target = args.target ?? "memory";
3528
3688
  const verb = ACTION_VERB[action] ?? action;
@@ -3600,8 +3760,8 @@ var ACTION_LABEL = {
3600
3760
  remove_file: "Remove skill file"
3601
3761
  };
3602
3762
  function SkillManageToolBlock({ tc }) {
3603
- const args = parseArgs2(tc.args) ?? {};
3604
- const result = tc.result ? parseArgs2(tc.result) : null;
3763
+ const args = parseArgs(tc.args) ?? {};
3764
+ const result = tc.result ? parseArgs(tc.result) : null;
3605
3765
  const action = args.action ?? "manage";
3606
3766
  const label = ACTION_LABEL[action] ?? "Manage skill";
3607
3767
  const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
@@ -3622,8 +3782,8 @@ function firstLine2(value) {
3622
3782
  return (index === -1 ? value : value.slice(0, index)).trim();
3623
3783
  }
3624
3784
  function CoordinatorToolBlock({ tc }) {
3625
- const args = parseArgs2(tc.args) ?? {};
3626
- const result = tc.result ? parseArgs2(tc.result) : null;
3785
+ const args = parseArgs(tc.args) ?? {};
3786
+ const result = tc.result ? parseArgs(tc.result) : null;
3627
3787
  const failed = Boolean(result?.error);
3628
3788
  let label = "Worker";
3629
3789
  let target = "";
@@ -3656,6 +3816,9 @@ function CoordinatorToolBlock({ tc }) {
3656
3816
  ] })
3657
3817
  ] });
3658
3818
  }
3819
+
3820
+ // src/components/chat/tools/default-tool.tsx
3821
+ init_utils();
3659
3822
  var TOOL_LABELS = {
3660
3823
  kb_list_pages: "Knowledge Base",
3661
3824
  kb_read_page: "Read KB Page",
@@ -3810,6 +3973,9 @@ function AssistantMessage({
3810
3973
  ] })
3811
3974
  ] }) });
3812
3975
  }
3976
+
3977
+ // src/components/ui/hover-card.tsx
3978
+ init_utils();
3813
3979
  function HoverCard({
3814
3980
  ...props
3815
3981
  }) {
@@ -3840,6 +4006,9 @@ function HoverCardContent({
3840
4006
  }
3841
4007
  ) });
3842
4008
  }
4009
+
4010
+ // src/components/ui/progress.tsx
4011
+ init_utils();
3843
4012
  function Progress({
3844
4013
  className,
3845
4014
  value,
@@ -3865,6 +4034,9 @@ function Progress({
3865
4034
  }
3866
4035
  );
3867
4036
  }
4037
+
4038
+ // src/components/ai-elements/context.tsx
4039
+ init_utils();
3868
4040
  var PERCENT_MAX = 100;
3869
4041
  var ICON_RADIUS = 10;
3870
4042
  var ICON_VIEWBOX = 24;
@@ -4129,6 +4301,12 @@ var ContextCacheUsage = ({
4129
4301
  }
4130
4302
  );
4131
4303
  };
4304
+
4305
+ // src/components/ui/command.tsx
4306
+ init_utils();
4307
+
4308
+ // src/components/ui/input-group.tsx
4309
+ init_utils();
4132
4310
  function InputGroup({ className, ...props }) {
4133
4311
  return /* @__PURE__ */ jsxRuntime.jsx(
4134
4312
  "div",
@@ -4313,6 +4491,9 @@ function CommandItem({
4313
4491
  }
4314
4492
  );
4315
4493
  }
4494
+
4495
+ // src/components/ui/dropdown-menu.tsx
4496
+ init_utils();
4316
4497
  function DropdownMenu({
4317
4498
  ...props
4318
4499
  }) {
@@ -4366,9 +4547,15 @@ function DropdownMenuItem({
4366
4547
  }
4367
4548
  );
4368
4549
  }
4550
+
4551
+ // src/components/ui/spinner.tsx
4552
+ init_utils();
4369
4553
  function Spinner({ className, ...props }) {
4370
4554
  return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
4371
4555
  }
4556
+
4557
+ // src/components/ai-elements/prompt-input.tsx
4558
+ init_utils();
4372
4559
  var convertBlobUrlToDataUrl = async (url) => {
4373
4560
  try {
4374
4561
  const response = await fetch(url);
@@ -5066,6 +5253,9 @@ var PromptInputSubmit = ({
5066
5253
  }
5067
5254
  );
5068
5255
  };
5256
+
5257
+ // src/components/ui/popover.tsx
5258
+ init_utils();
5069
5259
  function Popover({
5070
5260
  ...props
5071
5261
  }) {
@@ -5313,6 +5503,9 @@ function ChatComposerInner({
5313
5503
  )
5314
5504
  ] });
5315
5505
  }
5506
+
5507
+ // src/components/ai-elements/artifact.tsx
5508
+ init_utils();
5316
5509
  var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
5317
5510
  "div",
5318
5511
  {
@@ -5433,6 +5626,9 @@ function ArtifactTable({ spec }) {
5433
5626
  )) })
5434
5627
  ] }) });
5435
5628
  }
5629
+
5630
+ // src/components/chat/artifacts/artifact-html.tsx
5631
+ init_utils();
5436
5632
  var DEFAULT_HEIGHT_PX = 480;
5437
5633
  var EXPANDED_HEIGHT_PX = 800;
5438
5634
  var MIN_ZOOM = 0.5;
@@ -5574,7 +5770,7 @@ function exportArtifact(payload) {
5574
5770
  };
5575
5771
  case "chart":
5576
5772
  return {
5577
- text: JSON.stringify(payload.spec.vega_lite, null, 2),
5773
+ text: JSON.stringify(payload.spec.chart_js, null, 2),
5578
5774
  mime: "application/json",
5579
5775
  extension: "json"
5580
5776
  };
@@ -5760,7 +5956,7 @@ function ArtifactBody({
5760
5956
  react.Suspense,
5761
5957
  {
5762
5958
  fallback: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm text-muted-foreground", children: "Loading chart\u2026" }),
5763
- children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactChart2, { spec: payload.spec })
5959
+ children: /* @__PURE__ */ jsxRuntime.jsx(ArtifactChart2, { spec: payload.spec, fill })
5764
5960
  }
5765
5961
  );
5766
5962
  case "html":
@@ -5769,6 +5965,7 @@ function ArtifactBody({
5769
5965
  return /* @__PURE__ */ jsxRuntime.jsx(ArtifactSvg, { spec: payload.spec });
5770
5966
  }
5771
5967
  }
5968
+ init_utils();
5772
5969
  var ErrorMessage = react.memo(function ErrorMessage2({
5773
5970
  errorInfo,
5774
5971
  onRetry,
@@ -5849,6 +6046,9 @@ var ErrorMessage = react.memo(function ErrorMessage2({
5849
6046
  }
5850
6047
  );
5851
6048
  });
6049
+
6050
+ // src/components/chat/chat-thread.tsx
6051
+ init_utils();
5852
6052
  function messageToEntries(msg, isLast) {
5853
6053
  if (msg.role === "system") {
5854
6054
  if (msg.systemKind === "skill_invoked") {
@@ -5980,7 +6180,7 @@ function OrphanSystemMarker({
5980
6180
  );
5981
6181
  }
5982
6182
  if (message.systemKind === "error" && message.errorInfo) {
5983
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto my-2 w-full max-w-4xl px-4", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
6183
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mx-auto my-2 w-full max-w-4xl", children: /* @__PURE__ */ jsxRuntime.jsx(ErrorMessage, { errorInfo: message.errorInfo, onRetry }) });
5984
6184
  }
5985
6185
  return null;
5986
6186
  }
@@ -6034,6 +6234,7 @@ function TimelineEntryItem({
6034
6234
  ] });
6035
6235
  }
6036
6236
  if (entry.kind === "tool") {
6237
+ const failureSummary = effectiveStatus(entry.tc) === "error" ? toolErrorSummary(entry.tc.result) : "";
6037
6238
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6038
6239
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6039
6240
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
@@ -6044,7 +6245,10 @@ function TimelineEntryItem({
6044
6245
  }
6045
6246
  )
6046
6247
  ] }),
6047
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }) })
6248
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
6249
+ /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }),
6250
+ failureSummary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-full truncate text-xs text-destructive", title: failureSummary, children: failureSummary }) : null
6251
+ ] }) })
6048
6252
  ] });
6049
6253
  }
6050
6254
  if (entry.kind === "text") {
@@ -6162,6 +6366,7 @@ function ChatThread({
6162
6366
  sessionId,
6163
6367
  messages,
6164
6368
  isRunning,
6369
+ isLoadingHistory = false,
6165
6370
  onSend,
6166
6371
  onStop,
6167
6372
  onFileSelect,
@@ -6184,7 +6389,14 @@ function ChatThread({
6184
6389
  }, [messages]);
6185
6390
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-sm", children: [
6186
6391
  /* @__PURE__ */ jsxRuntime.jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
6187
- /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-3xl", children: messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
6392
+ /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-4xl", children: messages.length === 0 && isLoadingHistory ? /* @__PURE__ */ jsxRuntime.jsx(
6393
+ ConversationEmptyState,
6394
+ {
6395
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
6396
+ title: "Loading conversation",
6397
+ description: "Fetching the session history."
6398
+ }
6399
+ ) : messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
6188
6400
  ConversationEmptyState,
6189
6401
  {
6190
6402
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
@@ -6237,7 +6449,7 @@ function ChatThread({
6237
6449
  ] }) }),
6238
6450
  /* @__PURE__ */ jsxRuntime.jsx(ConversationScrollButton, {})
6239
6451
  ] }),
6240
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 pb-5 pt-3", children: [
6452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-6 pb-5 pt-3", children: [
6241
6453
  retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
6242
6454
  /* @__PURE__ */ jsxRuntime.jsx(
6243
6455
  ChatComposer,
@@ -6252,6 +6464,9 @@ function ChatThread({
6252
6464
  ] })
6253
6465
  ] });
6254
6466
  }
6467
+
6468
+ // src/components/ai-elements/file-tree.tsx
6469
+ init_utils();
6255
6470
  var noop = () => {
6256
6471
  };
6257
6472
  var FileTreeContext = react.createContext({
@@ -6418,6 +6633,9 @@ function FileTreeFile({
6418
6633
  }
6419
6634
  );
6420
6635
  }
6636
+
6637
+ // src/components/ui/skeleton.tsx
6638
+ init_utils();
6421
6639
  function Skeleton({ className, ...props }) {
6422
6640
  return /* @__PURE__ */ jsxRuntime.jsx(
6423
6641
  "div",
@@ -6428,6 +6646,9 @@ function Skeleton({ className, ...props }) {
6428
6646
  );
6429
6647
  }
6430
6648
 
6649
+ // src/components/workspace/workspace-panel.tsx
6650
+ init_utils();
6651
+
6431
6652
  // src/lib/format.ts
6432
6653
  function formatFileSize(bytes) {
6433
6654
  if (bytes < 1024) return `${bytes} B`;
@@ -6838,7 +7059,7 @@ function formatPdfPreviewError(error) {
6838
7059
  return error instanceof Error ? error.message : "Failed to render PDF preview.";
6839
7060
  }
6840
7061
  var SKELETON_WIDTHS2 = [75, 60, 90, 65, 80, 70, 85, 55];
6841
- var DEFAULT_WIDTH = 500;
7062
+ var DEFAULT_WIDTH = 400;
6842
7063
  var MIN_WIDTH = 300;
6843
7064
  var MAX_WIDTH = 900;
6844
7065
  function collectExpandedPaths(entries, depth = 0) {
@@ -6920,6 +7141,8 @@ function WorkspacePanel({
6920
7141
  sessionId,
6921
7142
  selectedPath,
6922
7143
  onSelectedPathChange,
7144
+ collapsed = false,
7145
+ onCollapsedChange,
6923
7146
  disabled = false
6924
7147
  }) {
6925
7148
  const fileInputRef = react.useRef(null);
@@ -7114,6 +7337,25 @@ function WorkspacePanel({
7114
7337
  window.removeEventListener("mouseup", onMouseUp);
7115
7338
  };
7116
7339
  }, []);
7340
+ if (collapsed) {
7341
+ return /* @__PURE__ */ jsxRuntime.jsx(
7342
+ "aside",
7343
+ {
7344
+ role: "button",
7345
+ tabIndex: 0,
7346
+ 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",
7347
+ "aria-label": "Expand workspace",
7348
+ onClick: () => onCollapsedChange?.(false),
7349
+ onKeyDown: (event) => {
7350
+ if (event.key === "Enter" || event.key === " ") {
7351
+ event.preventDefault();
7352
+ onCollapsedChange?.(false);
7353
+ }
7354
+ },
7355
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpenIcon, { className: "size-4 text-amber-500" })
7356
+ }
7357
+ );
7358
+ }
7117
7359
  return /* @__PURE__ */ jsxRuntime.jsxs(
7118
7360
  "aside",
7119
7361
  {
@@ -7146,6 +7388,19 @@ function WorkspacePanel({
7146
7388
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
7147
7389
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-faint", children: "Workspace" })
7148
7390
  ] }),
7391
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7392
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7393
+ Button,
7394
+ {
7395
+ variant: "ghost",
7396
+ size: "icon-sm",
7397
+ onClick: () => onCollapsedChange?.(true),
7398
+ "aria-label": "Collapse workspace",
7399
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-4" })
7400
+ }
7401
+ ) }),
7402
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Collapse" })
7403
+ ] }),
7149
7404
  /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7150
7405
  /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7151
7406
  Button,
@@ -7345,10 +7600,11 @@ var EMPTY_TOKEN_USAGE = {
7345
7600
  contextWindow: 0,
7346
7601
  model: ""
7347
7602
  };
7348
- function createInitialAgentChatState() {
7603
+ function createInitialAgentChatState(options = {}) {
7349
7604
  return {
7350
7605
  messages: [],
7351
7606
  isRunning: false,
7607
+ isLoadingHistory: options.isLoadingHistory ?? false,
7352
7608
  tokenUsage: EMPTY_TOKEN_USAGE,
7353
7609
  retryIndicator: null,
7354
7610
  lastEventId: 0,
@@ -7360,6 +7616,7 @@ function createInitialAgentChatState() {
7360
7616
  function applyAgentChatEvent(state, event) {
7361
7617
  let nextState = {
7362
7618
  ...state,
7619
+ isLoadingHistory: false,
7363
7620
  lastEventId: Math.max(state.lastEventId, event.eventId),
7364
7621
  sessionDone: state.sessionDone || event.type === "session.done"
7365
7622
  };
@@ -7893,11 +8150,12 @@ function useAgentChatRuntime({
7893
8150
  onSessionChange
7894
8151
  }) {
7895
8152
  const [state, setState] = react.useState(
7896
- () => createInitialAgentChatState()
8153
+ () => createInitialAgentChatState({ isLoadingHistory: Boolean(sessionId) })
7897
8154
  );
7898
8155
  const stateRef = react.useRef(state);
7899
8156
  const streamRef = react.useRef(null);
7900
8157
  const reconnectTimerRef = react.useRef(null);
8158
+ const previousSessionIdRef = react.useRef(sessionId);
7901
8159
  react.useEffect(() => {
7902
8160
  stateRef.current = state;
7903
8161
  }, [state]);
@@ -7913,6 +8171,8 @@ function useAgentChatRuntime({
7913
8171
  stream?.close();
7914
8172
  }, []);
7915
8173
  react.useEffect(() => {
8174
+ const previousSessionId = previousSessionIdRef.current;
8175
+ previousSessionIdRef.current = sessionId;
7916
8176
  clearReconnectTimer();
7917
8177
  closeStream();
7918
8178
  if (!sessionId) {
@@ -7920,11 +8180,11 @@ function useAgentChatRuntime({
7920
8180
  return;
7921
8181
  }
7922
8182
  let cancelled = false;
7923
- const connect = () => {
8183
+ const connect = (after) => {
7924
8184
  if (cancelled) return;
7925
8185
  const stream = adapter.openEventStream({
7926
8186
  sessionId,
7927
- after: stateRef.current.lastEventId
8187
+ after: after ?? stateRef.current.lastEventId
7928
8188
  });
7929
8189
  streamRef.current = stream;
7930
8190
  for (const eventType of AGENT_CHAT_LISTENED_EVENTS) {
@@ -7947,14 +8207,32 @@ function useAgentChatRuntime({
7947
8207
  streamRef.current = null;
7948
8208
  }
7949
8209
  if (!stateRef.current.sessionDone && !cancelled) {
7950
- reconnectTimerRef.current = setTimeout(connect, 3e3);
8210
+ reconnectTimerRef.current = setTimeout(() => connect(), 3e3);
7951
8211
  }
7952
8212
  };
7953
8213
  };
7954
- setState(createInitialAgentChatState());
7955
- connect();
8214
+ const currentState = stateRef.current;
8215
+ const preservePendingFirstMessage = previousSessionId === null && currentState.isRunning && currentState.messages.some(
8216
+ (message) => message.role === "user" && message.id.startsWith("local-")
8217
+ );
8218
+ const initialState = preservePendingFirstMessage ? {
8219
+ ...createInitialAgentChatState({ isLoadingHistory: false }),
8220
+ messages: currentState.messages,
8221
+ isRunning: true
8222
+ } : createInitialAgentChatState({
8223
+ isLoadingHistory: true
8224
+ });
8225
+ stateRef.current = initialState;
8226
+ setState(initialState);
8227
+ connect(0);
7956
8228
  adapter.getSession({ sessionId }).then((session) => {
7957
8229
  if (cancelled) return;
8230
+ if (session.messageCount === 0) {
8231
+ setState((prev) => ({
8232
+ ...prev,
8233
+ isLoadingHistory: false
8234
+ }));
8235
+ }
7958
8236
  if (isTerminalStatus(session.status)) {
7959
8237
  setState((prev) => ({
7960
8238
  ...prev,
@@ -8037,13 +8315,18 @@ function useAgentChatRuntime({
8037
8315
  }, []);
8038
8316
  const send = react.useCallback(
8039
8317
  async (content) => {
8318
+ markSending(content);
8040
8319
  if (!sessionId) {
8041
- const session = await adapter.createSession({ agentId });
8042
- await adapter.sendMessage({ sessionId: session.id, content });
8043
- onSessionChange?.(session.id);
8320
+ try {
8321
+ const session = await adapter.createSession({ agentId });
8322
+ onSessionChange?.(session.id);
8323
+ await adapter.sendMessage({ sessionId: session.id, content });
8324
+ } catch (error) {
8325
+ markSendError(error instanceof Error ? error.message : "send failed");
8326
+ throw error;
8327
+ }
8044
8328
  return;
8045
8329
  }
8046
- markSending(content);
8047
8330
  try {
8048
8331
  await adapter.sendMessage({ sessionId, content });
8049
8332
  } catch (error) {
@@ -8080,6 +8363,7 @@ function useAgentChatRuntime({
8080
8363
  return {
8081
8364
  messages: state.messages,
8082
8365
  isRunning: state.isRunning,
8366
+ isLoadingHistory: state.isLoadingHistory,
8083
8367
  tokenUsage: state.tokenUsage,
8084
8368
  retryIndicator: state.retryIndicator,
8085
8369
  send,
@@ -8116,6 +8400,7 @@ function AgentChat({
8116
8400
  disabled
8117
8401
  }) {
8118
8402
  const [workspacePath, setWorkspacePath] = react.useState(null);
8403
+ const [workspaceCollapsed, setWorkspaceCollapsed] = react.useState(false);
8119
8404
  const runtime = useAgentChatRuntime({
8120
8405
  adapter,
8121
8406
  agentId,
@@ -8147,6 +8432,7 @@ function AgentChat({
8147
8432
  sessionId,
8148
8433
  messages: runtime.messages,
8149
8434
  isRunning: runtime.isRunning,
8435
+ isLoadingHistory: runtime.isLoadingHistory,
8150
8436
  onSend: (content) => void runtime.send(content),
8151
8437
  onStop: () => void runtime.stop(),
8152
8438
  onRetry: runtime.retry,
@@ -8163,6 +8449,8 @@ function AgentChat({
8163
8449
  sessionId,
8164
8450
  selectedPath: workspacePath,
8165
8451
  onSelectedPathChange: setWorkspacePath,
8452
+ collapsed: workspaceCollapsed,
8453
+ onCollapsedChange: setWorkspaceCollapsed,
8166
8454
  disabled
8167
8455
  }
8168
8456
  )
@@ -8170,6 +8458,7 @@ function AgentChat({
8170
8458
  }
8171
8459
  );
8172
8460
  }
8461
+ init_utils();
8173
8462
  var POLL_INTERVAL_ACTIVE_MS = 4e3;
8174
8463
  var POLL_INTERVAL_IDLE_MS = 3e4;
8175
8464
  var DEFAULT_SESSION_LIST_LIMIT = 50;
@@ -8233,6 +8522,20 @@ function mergeTreeNodes(groups) {
8233
8522
  }
8234
8523
  return Array.from(byId.values());
8235
8524
  }
8525
+ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
8526
+ const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
8527
+ let changed = true;
8528
+ while (changed) {
8529
+ changed = false;
8530
+ for (const node of nodes) {
8531
+ if (node.parentId && deletedIds.has(node.parentId) && !deletedIds.has(node.id)) {
8532
+ deletedIds.add(node.id);
8533
+ changed = true;
8534
+ }
8535
+ }
8536
+ }
8537
+ return nodes.filter((node) => !deletedIds.has(node.id));
8538
+ }
8236
8539
  function formatSessionTime(value) {
8237
8540
  const date = new Date(value);
8238
8541
  if (Number.isNaN(date.getTime())) return "";
@@ -8349,11 +8652,12 @@ function SessionTreePanel({
8349
8652
  title = "Running",
8350
8653
  sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
8351
8654
  hideRoot = false,
8655
+ hideHeader = false,
8656
+ loadList = false,
8352
8657
  onSessionSelect,
8353
8658
  onSessionDelete
8354
8659
  }) {
8355
8660
  const [nodes, setNodes] = react.useState([]);
8356
- const [loading, setLoading] = react.useState(false);
8357
8661
  const [error, setError] = react.useState(null);
8358
8662
  const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
8359
8663
  const [deletingSessionId, setDeletingSessionId] = react.useState(
@@ -8365,18 +8669,16 @@ function SessionTreePanel({
8365
8669
  const resetContext = react.useRef(null);
8366
8670
  const refetch = react.useCallback(
8367
8671
  async (opts) => {
8368
- const canLoadSessionList = Boolean(agentId && adapter.listSessions);
8672
+ const canLoadSessionList = Boolean(loadList && adapter.listSessions);
8369
8673
  const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
8370
8674
  if (!canLoadSessionList && !canLoadSessionTree) {
8371
8675
  setNodes([]);
8372
8676
  setHasEverLoaded(true);
8373
- setLoading(false);
8374
8677
  return;
8375
8678
  }
8376
8679
  const currentRequestId = ++requestId.current;
8377
- if (!opts?.silent) setLoading(true);
8378
8680
  try {
8379
- const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
8681
+ const sessionListPromise = loadList && adapter.listSessions ? adapter.listSessions({
8380
8682
  agentId,
8381
8683
  limit: sessionListLimit
8382
8684
  }) : Promise.resolve(null);
@@ -8402,13 +8704,9 @@ function SessionTreePanel({
8402
8704
  if (!opts?.silent) {
8403
8705
  setError(e instanceof Error ? e.message : "Failed to load tree");
8404
8706
  }
8405
- } finally {
8406
- if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
8407
- setLoading(false);
8408
- }
8409
8707
  }
8410
8708
  },
8411
- [adapter, agentId, sessionId, sessionListLimit]
8709
+ [adapter, agentId, loadList, sessionId, sessionListLimit]
8412
8710
  );
8413
8711
  react.useEffect(() => {
8414
8712
  mounted.current = true;
@@ -8418,8 +8716,8 @@ function SessionTreePanel({
8418
8716
  }, []);
8419
8717
  react.useEffect(() => {
8420
8718
  const previous = resetContext.current;
8421
- const shouldReset = !previous || previous.adapter !== adapter || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8422
- resetContext.current = { adapter, agentId, sessionListLimit };
8719
+ const shouldReset = !previous || previous.loadList !== loadList || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8720
+ resetContext.current = { loadList, agentId, sessionListLimit };
8423
8721
  if (shouldReset) {
8424
8722
  setNodes([]);
8425
8723
  setHasEverLoaded(false);
@@ -8427,7 +8725,7 @@ function SessionTreePanel({
8427
8725
  }
8428
8726
  setError(null);
8429
8727
  void refetch();
8430
- }, [adapter, agentId, refetch, sessionListLimit]);
8728
+ }, [adapter, agentId, loadList, refetch, sessionListLimit]);
8431
8729
  const runningCount = react.useMemo(
8432
8730
  () => nodes.filter((n) => n.status === "active").length,
8433
8731
  [nodes]
@@ -8464,6 +8762,11 @@ function SessionTreePanel({
8464
8762
  setDeletingSessionId(id);
8465
8763
  try {
8466
8764
  await adapter.deleteSession({ sessionId: id });
8765
+ setNodes((current) => {
8766
+ const next = pruneDeletedSessionNodes(current, id);
8767
+ lastFingerprint.current = treeFingerprint(next);
8768
+ return next;
8769
+ });
8467
8770
  onSessionDelete?.(id);
8468
8771
  await refetch({ silent: true });
8469
8772
  } catch (e) {
@@ -8478,24 +8781,12 @@ function SessionTreePanel({
8478
8781
  if (nodes.length === 0) return null;
8479
8782
  const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
8480
8783
  if (topLevel.length === 0) return null;
8481
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-line", children: [
8482
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide", children: [
8784
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8785
+ !hideHeader && /* @__PURE__ */ jsxRuntime.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: [
8483
8786
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UsersIcon, { className: "w-3.5 h-3.5" }),
8484
8787
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
8485
8788
  runningCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
8486
8789
  ] }),
8487
- loading && /* @__PURE__ */ jsxRuntime.jsx(
8488
- "div",
8489
- {
8490
- className: "px-3 py-2",
8491
- role: "status",
8492
- "aria-label": "Loading sessions",
8493
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
8494
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3.5 w-28" }),
8495
- /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-3 w-20" })
8496
- ] })
8497
- }
8498
- ),
8499
8790
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
8500
8791
  !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
8501
8792
  TreeNodeRow,