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