@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.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,21 +3553,24 @@ 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
- const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Tried to create" : "Created";
3558
+ const label = status === "running" ? "Creating artifact\u2026" : status === "error" ? "Creating artifact\u2026" : "Created";
3403
3559
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm ", children: [
3404
3560
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: label }),
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,17 +6234,24 @@ function TimelineEntryItem({
6034
6234
  ] });
6035
6235
  }
6036
6236
  if (entry.kind === "tool") {
6237
+ const rawStatus = effectiveStatus(entry.tc);
6238
+ const hideArtifactFailure = rawStatus === "error" && entry.tc.toolName === "create_artifact";
6239
+ const indicatorStatus = hideArtifactFailure ? "running" : rawStatus;
6240
+ const failureSummary = rawStatus === "error" && !hideArtifactFailure ? toolErrorSummary(entry.tc.result) : "";
6037
6241
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
6038
6242
  /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
6039
6243
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6040
6244
  /* @__PURE__ */ jsxRuntime.jsx(
6041
6245
  TimelineIndicator,
6042
6246
  {
6043
- className: cn("size-2 border-none", statusColorClass(effectiveStatus(entry.tc)))
6247
+ className: cn("size-2 border-none", statusColorClass(indicatorStatus))
6044
6248
  }
6045
6249
  )
6046
6250
  ] }),
6047
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }) })
6251
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: entry.tc.cancelled ? /* @__PURE__ */ jsxRuntime.jsx(CancelledToolRow, { tc: entry.tc }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
6252
+ /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc: entry.tc, onFileSelect }),
6253
+ failureSummary ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-full truncate text-xs text-destructive", title: failureSummary, children: failureSummary }) : null
6254
+ ] }) })
6048
6255
  ] });
6049
6256
  }
6050
6257
  if (entry.kind === "text") {
@@ -6095,13 +6302,14 @@ function TimelineEntryItem({
6095
6302
  /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
6096
6303
  /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2 border-none bg-primary animate-pulse" })
6097
6304
  ] }),
6098
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm text-foreground", children: "Working on it..." }) })
6305
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
6099
6306
  ] });
6100
6307
  }
6101
6308
  function AssistantGroup({
6102
6309
  messages,
6103
6310
  lastGlobalIndex,
6104
6311
  totalMessages,
6312
+ isRunning,
6105
6313
  sessionId,
6106
6314
  onFileSelect,
6107
6315
  onRetry
@@ -6111,6 +6319,15 @@ function AssistantGroup({
6111
6319
  const isLast = i === messages.length - 1 && lastGlobalIndex === totalMessages - 1;
6112
6320
  entries.push(...messageToEntries(messages[i], isLast));
6113
6321
  }
6322
+ const isTailGroup = lastGlobalIndex === totalMessages - 1;
6323
+ const lastEntry = entries[entries.length - 1];
6324
+ const hasRunningTool = entries.some(
6325
+ (e) => e.kind === "tool" && e.tc.status === "running"
6326
+ );
6327
+ const tailMsg = messages[messages.length - 1];
6328
+ if (isTailGroup && isRunning && !hasRunningTool && lastEntry?.kind !== "thinking") {
6329
+ entries.push({ kind: "thinking", key: `${tailMsg.id}-tail-thinking` });
6330
+ }
6114
6331
  const tail = messages[messages.length - 1];
6115
6332
  const showErrorInfo = tail && tail.role === "assistant" && tail.status === "error" && !!tail.errorInfo;
6116
6333
  return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
@@ -6162,6 +6379,7 @@ function ChatThread({
6162
6379
  sessionId,
6163
6380
  messages,
6164
6381
  isRunning,
6382
+ isLoadingHistory = false,
6165
6383
  onSend,
6166
6384
  onStop,
6167
6385
  onFileSelect,
@@ -6184,7 +6402,14 @@ function ChatThread({
6184
6402
  }, [messages]);
6185
6403
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-sm", children: [
6186
6404
  /* @__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(
6405
+ /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-4xl", children: messages.length === 0 && isLoadingHistory ? /* @__PURE__ */ jsxRuntime.jsx(
6406
+ ConversationEmptyState,
6407
+ {
6408
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
6409
+ title: "Loading conversation",
6410
+ description: "Fetching the session history."
6411
+ }
6412
+ ) : messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
6188
6413
  ConversationEmptyState,
6189
6414
  {
6190
6415
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
@@ -6226,6 +6451,7 @@ function ChatThread({
6226
6451
  messages: group.messages,
6227
6452
  lastGlobalIndex: group.lastGlobalIndex,
6228
6453
  totalMessages: messages.length,
6454
+ isRunning,
6229
6455
  sessionId,
6230
6456
  onFileSelect,
6231
6457
  onRetry: groupRetry
@@ -6233,11 +6459,11 @@ function ChatThread({
6233
6459
  group.messages[0].id
6234
6460
  );
6235
6461
  }),
6236
- isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 5, className: "text-sm", children: "Working on it..." }) }) })
6462
+ isRunning && messages.length > 0 && messages[messages.length - 1].role === "user" && /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsx(MessageContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) }) })
6237
6463
  ] }) }),
6238
6464
  /* @__PURE__ */ jsxRuntime.jsx(ConversationScrollButton, {})
6239
6465
  ] }),
6240
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-3xl px-6 pb-5 pt-3", children: [
6466
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-6 pb-5 pt-3", children: [
6241
6467
  retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
6242
6468
  /* @__PURE__ */ jsxRuntime.jsx(
6243
6469
  ChatComposer,
@@ -6252,6 +6478,9 @@ function ChatThread({
6252
6478
  ] })
6253
6479
  ] });
6254
6480
  }
6481
+
6482
+ // src/components/ai-elements/file-tree.tsx
6483
+ init_utils();
6255
6484
  var noop = () => {
6256
6485
  };
6257
6486
  var FileTreeContext = react.createContext({
@@ -6418,6 +6647,9 @@ function FileTreeFile({
6418
6647
  }
6419
6648
  );
6420
6649
  }
6650
+
6651
+ // src/components/ui/skeleton.tsx
6652
+ init_utils();
6421
6653
  function Skeleton({ className, ...props }) {
6422
6654
  return /* @__PURE__ */ jsxRuntime.jsx(
6423
6655
  "div",
@@ -6428,6 +6660,9 @@ function Skeleton({ className, ...props }) {
6428
6660
  );
6429
6661
  }
6430
6662
 
6663
+ // src/components/workspace/workspace-panel.tsx
6664
+ init_utils();
6665
+
6431
6666
  // src/lib/format.ts
6432
6667
  function formatFileSize(bytes) {
6433
6668
  if (bytes < 1024) return `${bytes} B`;
@@ -6838,7 +7073,7 @@ function formatPdfPreviewError(error) {
6838
7073
  return error instanceof Error ? error.message : "Failed to render PDF preview.";
6839
7074
  }
6840
7075
  var SKELETON_WIDTHS2 = [75, 60, 90, 65, 80, 70, 85, 55];
6841
- var DEFAULT_WIDTH = 500;
7076
+ var DEFAULT_WIDTH = 400;
6842
7077
  var MIN_WIDTH = 300;
6843
7078
  var MAX_WIDTH = 900;
6844
7079
  function collectExpandedPaths(entries, depth = 0) {
@@ -6920,6 +7155,9 @@ function WorkspacePanel({
6920
7155
  sessionId,
6921
7156
  selectedPath,
6922
7157
  onSelectedPathChange,
7158
+ collapsed = false,
7159
+ onCollapsedChange,
7160
+ refreshSignal = 0,
6923
7161
  disabled = false
6924
7162
  }) {
6925
7163
  const fileInputRef = react.useRef(null);
@@ -6999,6 +7237,13 @@ function WorkspacePanel({
6999
7237
  react.useEffect(() => {
7000
7238
  void fetchTree();
7001
7239
  }, [fetchTree]);
7240
+ react.useEffect(() => {
7241
+ if (!sessionId || refreshSignal === 0) return;
7242
+ const timer = setTimeout(() => {
7243
+ void fetchTree();
7244
+ }, 300);
7245
+ return () => clearTimeout(timer);
7246
+ }, [refreshSignal, sessionId, fetchTree]);
7002
7247
  react.useEffect(() => {
7003
7248
  if (!sessionId) {
7004
7249
  setFile(null);
@@ -7114,6 +7359,25 @@ function WorkspacePanel({
7114
7359
  window.removeEventListener("mouseup", onMouseUp);
7115
7360
  };
7116
7361
  }, []);
7362
+ if (collapsed) {
7363
+ return /* @__PURE__ */ jsxRuntime.jsx(
7364
+ "aside",
7365
+ {
7366
+ role: "button",
7367
+ tabIndex: 0,
7368
+ 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",
7369
+ "aria-label": "Expand workspace",
7370
+ onClick: () => onCollapsedChange?.(false),
7371
+ onKeyDown: (event) => {
7372
+ if (event.key === "Enter" || event.key === " ") {
7373
+ event.preventDefault();
7374
+ onCollapsedChange?.(false);
7375
+ }
7376
+ },
7377
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpenIcon, { className: "size-4 text-amber-500" })
7378
+ }
7379
+ );
7380
+ }
7117
7381
  return /* @__PURE__ */ jsxRuntime.jsxs(
7118
7382
  "aside",
7119
7383
  {
@@ -7146,6 +7410,19 @@ function WorkspacePanel({
7146
7410
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-sm font-medium text-foreground", children: rootName }),
7147
7411
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-xs text-faint", children: "Workspace" })
7148
7412
  ] }),
7413
+ /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7414
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7415
+ Button,
7416
+ {
7417
+ variant: "ghost",
7418
+ size: "icon-sm",
7419
+ onClick: () => onCollapsedChange?.(true),
7420
+ "aria-label": "Collapse workspace",
7421
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-4" })
7422
+ }
7423
+ ) }),
7424
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "bottom", children: "Collapse" })
7425
+ ] }),
7149
7426
  /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
7150
7427
  /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7151
7428
  Button,
@@ -7307,6 +7584,12 @@ function ConfirmDialog({
7307
7584
  }
7308
7585
 
7309
7586
  // src/runtime/events.ts
7587
+ var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
7588
+ "terminal",
7589
+ "write_file",
7590
+ "patch",
7591
+ "execute_code"
7592
+ ]);
7310
7593
  var AGENT_CHAT_LISTENED_EVENTS = [
7311
7594
  "user.message",
7312
7595
  "llm.request",
@@ -7345,21 +7628,24 @@ var EMPTY_TOKEN_USAGE = {
7345
7628
  contextWindow: 0,
7346
7629
  model: ""
7347
7630
  };
7348
- function createInitialAgentChatState() {
7631
+ function createInitialAgentChatState(options = {}) {
7349
7632
  return {
7350
7633
  messages: [],
7351
7634
  isRunning: false,
7635
+ isLoadingHistory: options.isLoadingHistory ?? false,
7352
7636
  tokenUsage: EMPTY_TOKEN_USAGE,
7353
7637
  retryIndicator: null,
7354
7638
  lastEventId: 0,
7355
7639
  sessionDone: false,
7356
7640
  hadDeltas: false,
7357
- terminal: false
7641
+ terminal: false,
7642
+ workspaceRefreshKey: 0
7358
7643
  };
7359
7644
  }
7360
7645
  function applyAgentChatEvent(state, event) {
7361
7646
  let nextState = {
7362
7647
  ...state,
7648
+ isLoadingHistory: false,
7363
7649
  lastEventId: Math.max(state.lastEventId, event.eventId),
7364
7650
  sessionDone: state.sessionDone || event.type === "session.done"
7365
7651
  };
@@ -7415,11 +7701,17 @@ function applyAgentChatEvent(state, event) {
7415
7701
  return applyLlmThinking(nextState, event);
7416
7702
  case "tool.call":
7417
7703
  return applyToolCall(nextState, event);
7418
- case "tool.result":
7419
- return withMessages(
7420
- nextState,
7421
- applyToolResult(nextState.messages, event.data)
7422
- );
7704
+ case "tool.result": {
7705
+ const toolCallId = stringValue(event.data.tool_call_id);
7706
+ const toolName = findToolNameById(nextState.messages, toolCallId);
7707
+ const messages = applyToolResult(nextState.messages, event.data);
7708
+ const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
7709
+ return {
7710
+ ...nextState,
7711
+ messages,
7712
+ workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
7713
+ };
7714
+ }
7423
7715
  case "harness.wake":
7424
7716
  case "llm.request":
7425
7717
  return nextState.terminal ? nextState : { ...nextState, isRunning: true };
@@ -7859,6 +8151,14 @@ function hasUserAfterIndex(messages, idx) {
7859
8151
  }
7860
8152
  return false;
7861
8153
  }
8154
+ function findToolNameById(messages, toolCallId) {
8155
+ if (!toolCallId) return null;
8156
+ for (let i = messages.length - 1; i >= 0; i--) {
8157
+ const tc = messages[i]?.toolCalls?.find((c) => c.id === toolCallId);
8158
+ if (tc) return tc.toolName;
8159
+ }
8160
+ return null;
8161
+ }
7862
8162
  function findLatestConsultExpertCall(messages) {
7863
8163
  for (let i = messages.length - 1; i >= 0; i--) {
7864
8164
  const msg = messages[i];
@@ -7893,11 +8193,12 @@ function useAgentChatRuntime({
7893
8193
  onSessionChange
7894
8194
  }) {
7895
8195
  const [state, setState] = react.useState(
7896
- () => createInitialAgentChatState()
8196
+ () => createInitialAgentChatState({ isLoadingHistory: Boolean(sessionId) })
7897
8197
  );
7898
8198
  const stateRef = react.useRef(state);
7899
8199
  const streamRef = react.useRef(null);
7900
8200
  const reconnectTimerRef = react.useRef(null);
8201
+ const previousSessionIdRef = react.useRef(sessionId);
7901
8202
  react.useEffect(() => {
7902
8203
  stateRef.current = state;
7903
8204
  }, [state]);
@@ -7913,6 +8214,8 @@ function useAgentChatRuntime({
7913
8214
  stream?.close();
7914
8215
  }, []);
7915
8216
  react.useEffect(() => {
8217
+ const previousSessionId = previousSessionIdRef.current;
8218
+ previousSessionIdRef.current = sessionId;
7916
8219
  clearReconnectTimer();
7917
8220
  closeStream();
7918
8221
  if (!sessionId) {
@@ -7920,11 +8223,11 @@ function useAgentChatRuntime({
7920
8223
  return;
7921
8224
  }
7922
8225
  let cancelled = false;
7923
- const connect = () => {
8226
+ const connect = (after) => {
7924
8227
  if (cancelled) return;
7925
8228
  const stream = adapter.openEventStream({
7926
8229
  sessionId,
7927
- after: stateRef.current.lastEventId
8230
+ after: after ?? stateRef.current.lastEventId
7928
8231
  });
7929
8232
  streamRef.current = stream;
7930
8233
  for (const eventType of AGENT_CHAT_LISTENED_EVENTS) {
@@ -7947,14 +8250,32 @@ function useAgentChatRuntime({
7947
8250
  streamRef.current = null;
7948
8251
  }
7949
8252
  if (!stateRef.current.sessionDone && !cancelled) {
7950
- reconnectTimerRef.current = setTimeout(connect, 3e3);
8253
+ reconnectTimerRef.current = setTimeout(() => connect(), 3e3);
7951
8254
  }
7952
8255
  };
7953
8256
  };
7954
- setState(createInitialAgentChatState());
7955
- connect();
8257
+ const currentState = stateRef.current;
8258
+ const preservePendingFirstMessage = previousSessionId === null && currentState.isRunning && currentState.messages.some(
8259
+ (message) => message.role === "user" && message.id.startsWith("local-")
8260
+ );
8261
+ const initialState = preservePendingFirstMessage ? {
8262
+ ...createInitialAgentChatState({ isLoadingHistory: false }),
8263
+ messages: currentState.messages,
8264
+ isRunning: true
8265
+ } : createInitialAgentChatState({
8266
+ isLoadingHistory: true
8267
+ });
8268
+ stateRef.current = initialState;
8269
+ setState(initialState);
8270
+ connect(0);
7956
8271
  adapter.getSession({ sessionId }).then((session) => {
7957
8272
  if (cancelled) return;
8273
+ if (session.messageCount === 0) {
8274
+ setState((prev) => ({
8275
+ ...prev,
8276
+ isLoadingHistory: false
8277
+ }));
8278
+ }
7958
8279
  if (isTerminalStatus(session.status)) {
7959
8280
  setState((prev) => ({
7960
8281
  ...prev,
@@ -8037,13 +8358,18 @@ function useAgentChatRuntime({
8037
8358
  }, []);
8038
8359
  const send = react.useCallback(
8039
8360
  async (content) => {
8361
+ markSending(content);
8040
8362
  if (!sessionId) {
8041
- const session = await adapter.createSession({ agentId });
8042
- await adapter.sendMessage({ sessionId: session.id, content });
8043
- onSessionChange?.(session.id);
8363
+ try {
8364
+ const session = await adapter.createSession({ agentId });
8365
+ onSessionChange?.(session.id);
8366
+ await adapter.sendMessage({ sessionId: session.id, content });
8367
+ } catch (error) {
8368
+ markSendError(error instanceof Error ? error.message : "send failed");
8369
+ throw error;
8370
+ }
8044
8371
  return;
8045
8372
  }
8046
- markSending(content);
8047
8373
  try {
8048
8374
  await adapter.sendMessage({ sessionId, content });
8049
8375
  } catch (error) {
@@ -8080,8 +8406,10 @@ function useAgentChatRuntime({
8080
8406
  return {
8081
8407
  messages: state.messages,
8082
8408
  isRunning: state.isRunning,
8409
+ isLoadingHistory: state.isLoadingHistory,
8083
8410
  tokenUsage: state.tokenUsage,
8084
8411
  retryIndicator: state.retryIndicator,
8412
+ workspaceRefreshKey: state.workspaceRefreshKey,
8085
8413
  send,
8086
8414
  stop,
8087
8415
  retry,
@@ -8116,6 +8444,7 @@ function AgentChat({
8116
8444
  disabled
8117
8445
  }) {
8118
8446
  const [workspacePath, setWorkspacePath] = react.useState(null);
8447
+ const [workspaceCollapsed, setWorkspaceCollapsed] = react.useState(false);
8119
8448
  const runtime = useAgentChatRuntime({
8120
8449
  adapter,
8121
8450
  agentId,
@@ -8147,6 +8476,7 @@ function AgentChat({
8147
8476
  sessionId,
8148
8477
  messages: runtime.messages,
8149
8478
  isRunning: runtime.isRunning,
8479
+ isLoadingHistory: runtime.isLoadingHistory,
8150
8480
  onSend: (content) => void runtime.send(content),
8151
8481
  onStop: () => void runtime.stop(),
8152
8482
  onRetry: runtime.retry,
@@ -8163,6 +8493,9 @@ function AgentChat({
8163
8493
  sessionId,
8164
8494
  selectedPath: workspacePath,
8165
8495
  onSelectedPathChange: setWorkspacePath,
8496
+ collapsed: workspaceCollapsed,
8497
+ onCollapsedChange: setWorkspaceCollapsed,
8498
+ refreshSignal: runtime.workspaceRefreshKey,
8166
8499
  disabled
8167
8500
  }
8168
8501
  )
@@ -8170,6 +8503,7 @@ function AgentChat({
8170
8503
  }
8171
8504
  );
8172
8505
  }
8506
+ init_utils();
8173
8507
  var POLL_INTERVAL_ACTIVE_MS = 4e3;
8174
8508
  var POLL_INTERVAL_IDLE_MS = 3e4;
8175
8509
  var DEFAULT_SESSION_LIST_LIMIT = 50;
@@ -8233,6 +8567,20 @@ function mergeTreeNodes(groups) {
8233
8567
  }
8234
8568
  return Array.from(byId.values());
8235
8569
  }
8570
+ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
8571
+ const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
8572
+ let changed = true;
8573
+ while (changed) {
8574
+ changed = false;
8575
+ for (const node of nodes) {
8576
+ if (node.parentId && deletedIds.has(node.parentId) && !deletedIds.has(node.id)) {
8577
+ deletedIds.add(node.id);
8578
+ changed = true;
8579
+ }
8580
+ }
8581
+ }
8582
+ return nodes.filter((node) => !deletedIds.has(node.id));
8583
+ }
8236
8584
  function formatSessionTime(value) {
8237
8585
  const date = new Date(value);
8238
8586
  if (Number.isNaN(date.getTime())) return "";
@@ -8253,6 +8601,7 @@ function TreeNodeRow({
8253
8601
  const hasChildren = entry.children.length > 0;
8254
8602
  const isActive = entry.id === activeSessionId;
8255
8603
  const isRunning = entry.status === "active";
8604
+ const isSubAgent = entry.parentId != null;
8256
8605
  const title = entry.title ?? "New session";
8257
8606
  const subtitle = [
8258
8607
  entry.model ?? "default",
@@ -8292,7 +8641,7 @@ function TreeNodeRow({
8292
8641
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm truncate", children: title }),
8293
8642
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-faint truncate", children: subtitle })
8294
8643
  ] }),
8295
- isRunning && canStop && /* @__PURE__ */ jsxRuntime.jsx(
8644
+ isRunning && canStop && isSubAgent && /* @__PURE__ */ jsxRuntime.jsx(
8296
8645
  "button",
8297
8646
  {
8298
8647
  type: "button",
@@ -8349,11 +8698,12 @@ function SessionTreePanel({
8349
8698
  title = "Running",
8350
8699
  sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
8351
8700
  hideRoot = false,
8701
+ hideHeader = false,
8702
+ loadList = false,
8352
8703
  onSessionSelect,
8353
8704
  onSessionDelete
8354
8705
  }) {
8355
8706
  const [nodes, setNodes] = react.useState([]);
8356
- const [loading, setLoading] = react.useState(false);
8357
8707
  const [error, setError] = react.useState(null);
8358
8708
  const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
8359
8709
  const [deletingSessionId, setDeletingSessionId] = react.useState(
@@ -8365,18 +8715,16 @@ function SessionTreePanel({
8365
8715
  const resetContext = react.useRef(null);
8366
8716
  const refetch = react.useCallback(
8367
8717
  async (opts) => {
8368
- const canLoadSessionList = Boolean(agentId && adapter.listSessions);
8718
+ const canLoadSessionList = Boolean(loadList && adapter.listSessions);
8369
8719
  const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
8370
8720
  if (!canLoadSessionList && !canLoadSessionTree) {
8371
8721
  setNodes([]);
8372
8722
  setHasEverLoaded(true);
8373
- setLoading(false);
8374
8723
  return;
8375
8724
  }
8376
8725
  const currentRequestId = ++requestId.current;
8377
- if (!opts?.silent) setLoading(true);
8378
8726
  try {
8379
- const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
8727
+ const sessionListPromise = loadList && adapter.listSessions ? adapter.listSessions({
8380
8728
  agentId,
8381
8729
  limit: sessionListLimit
8382
8730
  }) : Promise.resolve(null);
@@ -8402,13 +8750,9 @@ function SessionTreePanel({
8402
8750
  if (!opts?.silent) {
8403
8751
  setError(e instanceof Error ? e.message : "Failed to load tree");
8404
8752
  }
8405
- } finally {
8406
- if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
8407
- setLoading(false);
8408
- }
8409
8753
  }
8410
8754
  },
8411
- [adapter, agentId, sessionId, sessionListLimit]
8755
+ [adapter, agentId, loadList, sessionId, sessionListLimit]
8412
8756
  );
8413
8757
  react.useEffect(() => {
8414
8758
  mounted.current = true;
@@ -8418,8 +8762,8 @@ function SessionTreePanel({
8418
8762
  }, []);
8419
8763
  react.useEffect(() => {
8420
8764
  const previous = resetContext.current;
8421
- const shouldReset = !previous || previous.adapter !== adapter || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8422
- resetContext.current = { adapter, agentId, sessionListLimit };
8765
+ const shouldReset = !previous || previous.loadList !== loadList || previous.agentId !== agentId || previous.sessionListLimit !== sessionListLimit;
8766
+ resetContext.current = { loadList, agentId, sessionListLimit };
8423
8767
  if (shouldReset) {
8424
8768
  setNodes([]);
8425
8769
  setHasEverLoaded(false);
@@ -8427,7 +8771,7 @@ function SessionTreePanel({
8427
8771
  }
8428
8772
  setError(null);
8429
8773
  void refetch();
8430
- }, [adapter, agentId, refetch, sessionListLimit]);
8774
+ }, [adapter, agentId, loadList, refetch, sessionListLimit]);
8431
8775
  const runningCount = react.useMemo(
8432
8776
  () => nodes.filter((n) => n.status === "active").length,
8433
8777
  [nodes]
@@ -8464,6 +8808,11 @@ function SessionTreePanel({
8464
8808
  setDeletingSessionId(id);
8465
8809
  try {
8466
8810
  await adapter.deleteSession({ sessionId: id });
8811
+ setNodes((current) => {
8812
+ const next = pruneDeletedSessionNodes(current, id);
8813
+ lastFingerprint.current = treeFingerprint(next);
8814
+ return next;
8815
+ });
8467
8816
  onSessionDelete?.(id);
8468
8817
  await refetch({ silent: true });
8469
8818
  } catch (e) {
@@ -8478,24 +8827,12 @@ function SessionTreePanel({
8478
8827
  if (nodes.length === 0) return null;
8479
8828
  const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
8480
8829
  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: [
8830
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
8831
+ !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
8832
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UsersIcon, { className: "w-3.5 h-3.5" }),
8484
8833
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
8485
8834
  runningCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
8486
8835
  ] }),
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
8836
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
8500
8837
  !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
8501
8838
  TreeNodeRow,