@invergent/agent-chat-react 1.4.5 → 1.4.9
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 +443 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -3
- package/dist/index.d.ts +49 -3
- package/dist/index.js +443 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Collapsible as Collapsible$1, Slot, Dialog as Dialog$1, ScrollArea as S
|
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, ListTodoIcon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircle2Icon, XCircleIcon, CheckCircleIcon, CircleIcon, MessageSquareIcon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ArrowDownIcon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, Maximize2Icon, FolderIcon, FileIcon, PlusIcon, CornerDownLeftIcon
|
|
7
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, ChevronRightIcon, GitBranchIcon, XIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, Loader2Icon, ListTodoIcon, Code, CheckIcon, CopyIcon, TerminalIcon, Trash2Icon, CircleDotIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircle2Icon, XCircleIcon, CheckCircleIcon, CircleIcon, UsersIcon, MessageSquareIcon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, SquareIcon, ArrowDownIcon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, Maximize2Icon, FolderIcon, FileIcon, PlusIcon, CornerDownLeftIcon } from 'lucide-react';
|
|
8
8
|
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
|
|
9
9
|
import { cjk } from '@streamdown/cjk';
|
|
10
10
|
import { code } from '@streamdown/code';
|
|
@@ -2712,16 +2712,20 @@ function ExpertToolBlock({ tc }) {
|
|
|
2712
2712
|
}
|
|
2713
2713
|
void submit("up");
|
|
2714
2714
|
};
|
|
2715
|
-
const
|
|
2716
|
-
|
|
2715
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
2716
|
+
const expertName = args.expert ?? null;
|
|
2717
|
+
const question = args.question ?? args.prompt ?? "";
|
|
2718
|
+
const result = parseExpertResult(tc.result);
|
|
2719
|
+
const failed = Boolean(result?.error);
|
|
2720
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2717
2721
|
/* @__PURE__ */ jsxs(
|
|
2718
2722
|
"button",
|
|
2719
2723
|
{
|
|
2720
2724
|
type: "button",
|
|
2721
2725
|
onClick: () => setExpanded(!expanded),
|
|
2722
2726
|
className: cn(
|
|
2723
|
-
"flex w-full items-center gap-1.5 rounded-md px-
|
|
2724
|
-
"text-sm text-muted-foreground hover:
|
|
2727
|
+
"flex w-fit max-w-full items-center gap-1.5 rounded-md px-0 py-0.5",
|
|
2728
|
+
"text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
2725
2729
|
),
|
|
2726
2730
|
children: [
|
|
2727
2731
|
/* @__PURE__ */ jsx(
|
|
@@ -2733,21 +2737,26 @@ function ExpertToolBlock({ tc }) {
|
|
|
2733
2737
|
)
|
|
2734
2738
|
}
|
|
2735
2739
|
),
|
|
2736
|
-
/* @__PURE__ */ jsx("span", { className: "font-
|
|
2740
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: "Consulted expert" }),
|
|
2737
2741
|
expertName && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
2738
2742
|
"\xB7 ",
|
|
2739
2743
|
expertName
|
|
2740
|
-
] })
|
|
2744
|
+
] }),
|
|
2745
|
+
failed && /* @__PURE__ */ jsx("span", { className: "text-red-500", children: "\xB7 failed" })
|
|
2741
2746
|
]
|
|
2742
2747
|
}
|
|
2743
2748
|
),
|
|
2744
|
-
expanded && /* @__PURE__ */
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2749
|
+
result?.summary && !expanded && /* @__PURE__ */ jsx("div", { className: "ml-6 rounded-md border-l-2 border-primary/50 bg-muted/30 px-3 py-2 text-sm text-foreground/90", children: result.summary }),
|
|
2750
|
+
expanded && /* @__PURE__ */ jsxs("div", { className: "ml-6 mt-0.5 space-y-1.5 text-sm", children: [
|
|
2751
|
+
question && /* @__PURE__ */ jsx(ExpertDetail, { label: "Question", content: question }),
|
|
2752
|
+
result?.summary && /* @__PURE__ */ jsx(
|
|
2753
|
+
ExpertDetail,
|
|
2754
|
+
{
|
|
2755
|
+
label: failed ? "Error" : "Response",
|
|
2756
|
+
content: result.summary,
|
|
2757
|
+
tone: failed ? "error" : "default"
|
|
2758
|
+
}
|
|
2759
|
+
)
|
|
2751
2760
|
] }),
|
|
2752
2761
|
canRate && /* @__PURE__ */ jsxs("div", { className: "ml-6 mt-1 space-y-1.5", children: [
|
|
2753
2762
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
@@ -2813,6 +2822,36 @@ function ExpertToolBlock({ tc }) {
|
|
|
2813
2822
|
] })
|
|
2814
2823
|
] });
|
|
2815
2824
|
}
|
|
2825
|
+
function parseExpertResult(result) {
|
|
2826
|
+
if (!result) return null;
|
|
2827
|
+
const parsed = parseArgs2(result);
|
|
2828
|
+
if (parsed) {
|
|
2829
|
+
return {
|
|
2830
|
+
...parsed,
|
|
2831
|
+
summary: parsed.summary ?? parsed.response ?? parsed.content
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
return { summary: result };
|
|
2835
|
+
}
|
|
2836
|
+
function ExpertDetail({
|
|
2837
|
+
label,
|
|
2838
|
+
content,
|
|
2839
|
+
tone = "default"
|
|
2840
|
+
}) {
|
|
2841
|
+
return /* @__PURE__ */ jsxs(
|
|
2842
|
+
"div",
|
|
2843
|
+
{
|
|
2844
|
+
className: cn(
|
|
2845
|
+
"rounded-md border-l-2 px-3 py-2",
|
|
2846
|
+
tone === "error" ? "border-red-500/50 bg-red-500/5" : "border-primary/50 bg-muted/30"
|
|
2847
|
+
),
|
|
2848
|
+
children: [
|
|
2849
|
+
/* @__PURE__ */ jsx("div", { className: "mb-1 text-[10px] uppercase tracking-wide text-muted-foreground/70", children: label }),
|
|
2850
|
+
/* @__PURE__ */ jsx("div", { className: "whitespace-pre-wrap wrap-break-word text-foreground/90", children: content })
|
|
2851
|
+
]
|
|
2852
|
+
}
|
|
2853
|
+
);
|
|
2854
|
+
}
|
|
2816
2855
|
function ReasonForm({
|
|
2817
2856
|
initialValue,
|
|
2818
2857
|
busy,
|
|
@@ -3438,6 +3477,71 @@ function MemoryEntry({
|
|
|
3438
3477
|
}
|
|
3439
3478
|
);
|
|
3440
3479
|
}
|
|
3480
|
+
var ACTION_LABEL = {
|
|
3481
|
+
create: "Create skill",
|
|
3482
|
+
patch: "Patch skill",
|
|
3483
|
+
edit: "Edit skill",
|
|
3484
|
+
delete: "Delete skill",
|
|
3485
|
+
write_file: "Write skill file",
|
|
3486
|
+
remove_file: "Remove skill file"
|
|
3487
|
+
};
|
|
3488
|
+
function SkillManageToolBlock({ tc }) {
|
|
3489
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
3490
|
+
const result = tc.result ? parseArgs2(tc.result) : null;
|
|
3491
|
+
const action = args.action ?? "manage";
|
|
3492
|
+
const label = ACTION_LABEL[action] ?? "Manage skill";
|
|
3493
|
+
const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
|
|
3494
|
+
const failed = result?.success === false || Boolean(result?.error);
|
|
3495
|
+
const summary = result?.error ?? result?.message ?? result?.path;
|
|
3496
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
3497
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: label }),
|
|
3498
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate", children: target }),
|
|
3499
|
+
summary && /* @__PURE__ */ jsxs("span", { className: failed ? "text-red-500 truncate" : "text-muted-foreground/70 truncate", children: [
|
|
3500
|
+
"\u2192 ",
|
|
3501
|
+
summary
|
|
3502
|
+
] })
|
|
3503
|
+
] });
|
|
3504
|
+
}
|
|
3505
|
+
function firstLine2(value) {
|
|
3506
|
+
if (!value) return "";
|
|
3507
|
+
const index = value.indexOf("\n");
|
|
3508
|
+
return (index === -1 ? value : value.slice(0, index)).trim();
|
|
3509
|
+
}
|
|
3510
|
+
function CoordinatorToolBlock({ tc }) {
|
|
3511
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
3512
|
+
const result = tc.result ? parseArgs2(tc.result) : null;
|
|
3513
|
+
const failed = Boolean(result?.error);
|
|
3514
|
+
let label = "Worker";
|
|
3515
|
+
let target = "";
|
|
3516
|
+
let detail = "";
|
|
3517
|
+
if (tc.toolName === "spawn_worker") {
|
|
3518
|
+
label = tc.status === "running" ? "Spawning worker" : "Spawned worker";
|
|
3519
|
+
target = args.agent_type ?? args.model ?? "";
|
|
3520
|
+
detail = firstLine2(args.goal);
|
|
3521
|
+
} else if (tc.toolName === "send_worker_message") {
|
|
3522
|
+
label = "Message worker";
|
|
3523
|
+
target = args.worker_id ?? "";
|
|
3524
|
+
detail = firstLine2(args.message);
|
|
3525
|
+
} else if (tc.toolName === "stop_worker") {
|
|
3526
|
+
label = "Stop worker";
|
|
3527
|
+
target = args.worker_id ?? "";
|
|
3528
|
+
detail = firstLine2(args.reason);
|
|
3529
|
+
}
|
|
3530
|
+
const resultId = result?.worker_id ?? result?.session_id;
|
|
3531
|
+
const summary = result?.error ?? resultId ?? result?.status;
|
|
3532
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
3533
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-foreground", children: label }),
|
|
3534
|
+
target && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate", children: target }),
|
|
3535
|
+
detail && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/70 truncate", children: [
|
|
3536
|
+
"\xB7 ",
|
|
3537
|
+
detail
|
|
3538
|
+
] }),
|
|
3539
|
+
summary && /* @__PURE__ */ jsxs("span", { className: failed ? "text-red-500 truncate" : "text-muted-foreground/70 truncate", children: [
|
|
3540
|
+
"\u2192 ",
|
|
3541
|
+
summary
|
|
3542
|
+
] })
|
|
3543
|
+
] });
|
|
3544
|
+
}
|
|
3441
3545
|
function DefaultToolBlock({ tc }) {
|
|
3442
3546
|
const [expanded, setExpanded] = useState(false);
|
|
3443
3547
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -3506,6 +3610,8 @@ function ToolCallBlock({ tc, onFileSelect }) {
|
|
|
3506
3610
|
return /* @__PURE__ */ jsx(SkillsListBlock, { tc });
|
|
3507
3611
|
case "skill_view":
|
|
3508
3612
|
return /* @__PURE__ */ jsx(SkillViewBlock, { tc });
|
|
3613
|
+
case "skill_manage":
|
|
3614
|
+
return /* @__PURE__ */ jsx(SkillManageToolBlock, { tc });
|
|
3509
3615
|
case "clarify":
|
|
3510
3616
|
return /* @__PURE__ */ jsx(ClarifyToolBlock, { tc });
|
|
3511
3617
|
case "create_artifact":
|
|
@@ -3514,6 +3620,10 @@ function ToolCallBlock({ tc, onFileSelect }) {
|
|
|
3514
3620
|
return /* @__PURE__ */ jsx(DelegateToolBlock, { tc });
|
|
3515
3621
|
case "memory":
|
|
3516
3622
|
return /* @__PURE__ */ jsx(MemoryToolBlock, { tc });
|
|
3623
|
+
case "spawn_worker":
|
|
3624
|
+
case "send_worker_message":
|
|
3625
|
+
case "stop_worker":
|
|
3626
|
+
return /* @__PURE__ */ jsx(CoordinatorToolBlock, { tc });
|
|
3517
3627
|
default:
|
|
3518
3628
|
return /* @__PURE__ */ jsx(DefaultToolBlock, { tc });
|
|
3519
3629
|
}
|
|
@@ -6404,7 +6514,8 @@ function WorkspacePanel({
|
|
|
6404
6514
|
adapter,
|
|
6405
6515
|
sessionId,
|
|
6406
6516
|
selectedPath,
|
|
6407
|
-
onSelectedPathChange
|
|
6517
|
+
onSelectedPathChange,
|
|
6518
|
+
disabled = false
|
|
6408
6519
|
}) {
|
|
6409
6520
|
const fileInputRef = useRef(null);
|
|
6410
6521
|
const [entries, setEntries] = useState([]);
|
|
@@ -6523,7 +6634,7 @@ function WorkspacePanel({
|
|
|
6523
6634
|
);
|
|
6524
6635
|
const handleUpload = useCallback(
|
|
6525
6636
|
async (files) => {
|
|
6526
|
-
if (!sessionId || files.length === 0) return;
|
|
6637
|
+
if (disabled || !sessionId || files.length === 0) return;
|
|
6527
6638
|
setUploading(true);
|
|
6528
6639
|
setNotice(null);
|
|
6529
6640
|
try {
|
|
@@ -6544,7 +6655,7 @@ function WorkspacePanel({
|
|
|
6544
6655
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
6545
6656
|
}
|
|
6546
6657
|
},
|
|
6547
|
-
[adapter, fetchTree, sessionId]
|
|
6658
|
+
[adapter, disabled, fetchTree, sessionId]
|
|
6548
6659
|
);
|
|
6549
6660
|
const handleDelete = useCallback(
|
|
6550
6661
|
async (path) => {
|
|
@@ -6618,6 +6729,7 @@ function WorkspacePanel({
|
|
|
6618
6729
|
type: "file",
|
|
6619
6730
|
multiple: true,
|
|
6620
6731
|
className: "hidden",
|
|
6732
|
+
disabled,
|
|
6621
6733
|
onChange: (event) => {
|
|
6622
6734
|
if (event.target.files) void handleUpload(event.target.files);
|
|
6623
6735
|
}
|
|
@@ -6636,7 +6748,7 @@ function WorkspacePanel({
|
|
|
6636
6748
|
variant: "ghost",
|
|
6637
6749
|
size: "icon-sm",
|
|
6638
6750
|
onClick: () => fileInputRef.current?.click(),
|
|
6639
|
-
disabled: !sessionId || uploading,
|
|
6751
|
+
disabled: disabled || !sessionId || uploading,
|
|
6640
6752
|
"aria-label": "Upload files",
|
|
6641
6753
|
children: uploading ? /* @__PURE__ */ jsx(Loader2Icon, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(UploadIcon, { className: "size-4" })
|
|
6642
6754
|
}
|
|
@@ -6688,7 +6800,7 @@ function WorkspacePanel({
|
|
|
6688
6800
|
size: "sm",
|
|
6689
6801
|
className: "mt-3 gap-1.5",
|
|
6690
6802
|
onClick: () => fileInputRef.current?.click(),
|
|
6691
|
-
disabled: !sessionId,
|
|
6803
|
+
disabled: disabled || !sessionId,
|
|
6692
6804
|
children: [
|
|
6693
6805
|
/* @__PURE__ */ jsx(UploadIcon, { className: "size-3.5" }),
|
|
6694
6806
|
"Upload files"
|
|
@@ -7639,14 +7751,324 @@ function AgentChat({
|
|
|
7639
7751
|
adapter,
|
|
7640
7752
|
sessionId,
|
|
7641
7753
|
selectedPath: workspacePath,
|
|
7642
|
-
onSelectedPathChange: setWorkspacePath
|
|
7754
|
+
onSelectedPathChange: setWorkspacePath,
|
|
7755
|
+
disabled
|
|
7643
7756
|
}
|
|
7644
7757
|
)
|
|
7645
7758
|
] }) })
|
|
7646
7759
|
}
|
|
7647
7760
|
);
|
|
7648
7761
|
}
|
|
7762
|
+
var POLL_INTERVAL_ACTIVE_MS = 4e3;
|
|
7763
|
+
var POLL_INTERVAL_IDLE_MS = 3e4;
|
|
7764
|
+
var DEFAULT_SESSION_LIST_LIMIT = 50;
|
|
7765
|
+
function buildTree(nodes) {
|
|
7766
|
+
const byId = /* @__PURE__ */ new Map();
|
|
7767
|
+
for (const n of nodes) byId.set(n.id, { ...n, children: [] });
|
|
7768
|
+
const roots = [];
|
|
7769
|
+
for (const n of byId.values()) {
|
|
7770
|
+
const parent = n.parentId ? byId.get(n.parentId) : void 0;
|
|
7771
|
+
if (parent) {
|
|
7772
|
+
parent.children.push(n);
|
|
7773
|
+
} else {
|
|
7774
|
+
roots.push(n);
|
|
7775
|
+
}
|
|
7776
|
+
}
|
|
7777
|
+
const sortRec = (e) => {
|
|
7778
|
+
e.children.sort((a, b) => a.createdAt < b.createdAt ? -1 : 1);
|
|
7779
|
+
for (const c of e.children) sortRec(c);
|
|
7780
|
+
};
|
|
7781
|
+
for (const r of roots) sortRec(r);
|
|
7782
|
+
return roots;
|
|
7783
|
+
}
|
|
7784
|
+
function treeFingerprint(nodes) {
|
|
7785
|
+
return nodes.map(
|
|
7786
|
+
(n) => `${n.id}:${n.parentId ?? ""}:${n.status}:${n.agentType ?? ""}:${n.messageCount ?? 0}:${n.toolCallCount ?? 0}:${n.updatedAt}`
|
|
7787
|
+
).join("|");
|
|
7788
|
+
}
|
|
7789
|
+
function sessionToTreeNode(session) {
|
|
7790
|
+
const timestamp = session.updatedAt ?? session.createdAt ?? "";
|
|
7791
|
+
return {
|
|
7792
|
+
id: session.id,
|
|
7793
|
+
parentId: session.parentId ?? null,
|
|
7794
|
+
agentId: session.agentId,
|
|
7795
|
+
channel: session.channel,
|
|
7796
|
+
status: session.status,
|
|
7797
|
+
title: session.title,
|
|
7798
|
+
model: session.model,
|
|
7799
|
+
messageCount: session.messageCount,
|
|
7800
|
+
toolCallCount: session.toolCallCount,
|
|
7801
|
+
createdAt: session.createdAt ?? timestamp,
|
|
7802
|
+
updatedAt: session.updatedAt ?? timestamp
|
|
7803
|
+
};
|
|
7804
|
+
}
|
|
7805
|
+
function mergeNodeFields(current, next) {
|
|
7806
|
+
return {
|
|
7807
|
+
...current,
|
|
7808
|
+
...Object.fromEntries(
|
|
7809
|
+
Object.entries(next).filter(([, value]) => value !== void 0)
|
|
7810
|
+
),
|
|
7811
|
+
messageCount: next.messageCount ?? current.messageCount,
|
|
7812
|
+
toolCallCount: next.toolCallCount ?? current.toolCallCount
|
|
7813
|
+
};
|
|
7814
|
+
}
|
|
7815
|
+
function mergeTreeNodes(groups) {
|
|
7816
|
+
const byId = /* @__PURE__ */ new Map();
|
|
7817
|
+
for (const group of groups) {
|
|
7818
|
+
for (const node of group) {
|
|
7819
|
+
const current = byId.get(node.id);
|
|
7820
|
+
byId.set(node.id, current ? mergeNodeFields(current, node) : node);
|
|
7821
|
+
}
|
|
7822
|
+
}
|
|
7823
|
+
return Array.from(byId.values());
|
|
7824
|
+
}
|
|
7825
|
+
function statusColor(status) {
|
|
7826
|
+
switch (status) {
|
|
7827
|
+
case "active":
|
|
7828
|
+
return "default";
|
|
7829
|
+
case "completed":
|
|
7830
|
+
return "secondary";
|
|
7831
|
+
case "failed":
|
|
7832
|
+
return "destructive";
|
|
7833
|
+
default:
|
|
7834
|
+
return "outline";
|
|
7835
|
+
}
|
|
7836
|
+
}
|
|
7837
|
+
function TreeNodeRow({
|
|
7838
|
+
entry,
|
|
7839
|
+
depth,
|
|
7840
|
+
activeSessionId,
|
|
7841
|
+
canStop,
|
|
7842
|
+
onSelect,
|
|
7843
|
+
onStop
|
|
7844
|
+
}) {
|
|
7845
|
+
const [expanded, setExpanded] = useState(true);
|
|
7846
|
+
const hasChildren = entry.children.length > 0;
|
|
7847
|
+
const isActive = entry.id === activeSessionId;
|
|
7848
|
+
const isRunning = entry.status === "active";
|
|
7849
|
+
const messageCount = entry.messageCount ?? 0;
|
|
7850
|
+
const toolCallCount = entry.toolCallCount ?? 0;
|
|
7851
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7852
|
+
/* @__PURE__ */ jsxs(
|
|
7853
|
+
"div",
|
|
7854
|
+
{
|
|
7855
|
+
role: "button",
|
|
7856
|
+
tabIndex: 0,
|
|
7857
|
+
onClick: () => onSelect(entry.id),
|
|
7858
|
+
onKeyDown: (e) => {
|
|
7859
|
+
if (e.key === "Enter" || e.key === " ") onSelect(entry.id);
|
|
7860
|
+
},
|
|
7861
|
+
className: cn(
|
|
7862
|
+
"group flex items-center gap-1.5 py-1.5 pr-1 rounded cursor-pointer text-sm transition-colors",
|
|
7863
|
+
isActive ? "bg-line text-foreground" : "hover:bg-input text-subtle hover:text-foreground"
|
|
7864
|
+
),
|
|
7865
|
+
style: { paddingLeft: `${depth * 12 + 4}px` },
|
|
7866
|
+
children: [
|
|
7867
|
+
hasChildren ? /* @__PURE__ */ jsx(
|
|
7868
|
+
"button",
|
|
7869
|
+
{
|
|
7870
|
+
type: "button",
|
|
7871
|
+
className: "p-0.5 rounded hover:bg-line",
|
|
7872
|
+
onClick: (e) => {
|
|
7873
|
+
e.stopPropagation();
|
|
7874
|
+
setExpanded(!expanded);
|
|
7875
|
+
},
|
|
7876
|
+
"aria-label": expanded ? "Collapse" : "Expand",
|
|
7877
|
+
"aria-expanded": expanded,
|
|
7878
|
+
children: expanded ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3.5 h-3.5" })
|
|
7879
|
+
}
|
|
7880
|
+
) : /* @__PURE__ */ jsx("span", { className: "w-4 h-4 shrink-0" }),
|
|
7881
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center gap-1.5", children: [
|
|
7882
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: entry.title ?? entry.channel ?? "session" }),
|
|
7883
|
+
entry.agentType && /* @__PURE__ */ jsx(Badge, { variant: "outline", className: "h-4 px-1.5 text-[10px]", children: entry.agentType }),
|
|
7884
|
+
/* @__PURE__ */ jsx(
|
|
7885
|
+
Badge,
|
|
7886
|
+
{
|
|
7887
|
+
variant: statusColor(entry.status),
|
|
7888
|
+
className: "h-4 px-1.5 text-[10px]",
|
|
7889
|
+
children: entry.status
|
|
7890
|
+
}
|
|
7891
|
+
)
|
|
7892
|
+
] }),
|
|
7893
|
+
/* @__PURE__ */ jsxs(
|
|
7894
|
+
"span",
|
|
7895
|
+
{
|
|
7896
|
+
className: "text-xs text-faint shrink-0 tabular-nums",
|
|
7897
|
+
"aria-label": `${messageCount} messages, ${toolCallCount} tool calls`,
|
|
7898
|
+
title: `${messageCount} messages, ${toolCallCount} tool calls`,
|
|
7899
|
+
children: [
|
|
7900
|
+
messageCount,
|
|
7901
|
+
"m/",
|
|
7902
|
+
toolCallCount,
|
|
7903
|
+
"t"
|
|
7904
|
+
]
|
|
7905
|
+
}
|
|
7906
|
+
),
|
|
7907
|
+
isRunning && canStop && /* @__PURE__ */ jsx(
|
|
7908
|
+
"button",
|
|
7909
|
+
{
|
|
7910
|
+
type: "button",
|
|
7911
|
+
className: "p-1 rounded opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all",
|
|
7912
|
+
onClick: (e) => {
|
|
7913
|
+
e.stopPropagation();
|
|
7914
|
+
onStop(entry.id);
|
|
7915
|
+
},
|
|
7916
|
+
"aria-label": "Stop sub-agent",
|
|
7917
|
+
title: "Stop sub-agent",
|
|
7918
|
+
children: /* @__PURE__ */ jsx(SquareIcon, { className: "w-3 h-3", fill: "currentColor" })
|
|
7919
|
+
}
|
|
7920
|
+
)
|
|
7921
|
+
]
|
|
7922
|
+
}
|
|
7923
|
+
),
|
|
7924
|
+
hasChildren && expanded && entry.children.map((child) => /* @__PURE__ */ jsx(
|
|
7925
|
+
TreeNodeRow,
|
|
7926
|
+
{
|
|
7927
|
+
entry: child,
|
|
7928
|
+
depth: depth + 1,
|
|
7929
|
+
activeSessionId,
|
|
7930
|
+
canStop,
|
|
7931
|
+
onSelect,
|
|
7932
|
+
onStop
|
|
7933
|
+
},
|
|
7934
|
+
child.id
|
|
7935
|
+
))
|
|
7936
|
+
] });
|
|
7937
|
+
}
|
|
7938
|
+
function SessionTreePanel({
|
|
7939
|
+
adapter,
|
|
7940
|
+
sessionId = null,
|
|
7941
|
+
activeSessionId = sessionId ?? void 0,
|
|
7942
|
+
agentId,
|
|
7943
|
+
title = "Running",
|
|
7944
|
+
sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
|
|
7945
|
+
hideRoot = false,
|
|
7946
|
+
onSessionSelect
|
|
7947
|
+
}) {
|
|
7948
|
+
const [nodes, setNodes] = useState([]);
|
|
7949
|
+
const [loading, setLoading] = useState(false);
|
|
7950
|
+
const [error, setError] = useState(null);
|
|
7951
|
+
const [hasEverLoaded, setHasEverLoaded] = useState(false);
|
|
7952
|
+
const mounted = useRef(true);
|
|
7953
|
+
const requestId = useRef(0);
|
|
7954
|
+
const lastFingerprint = useRef("");
|
|
7955
|
+
const refetch = useCallback(
|
|
7956
|
+
async (opts) => {
|
|
7957
|
+
const canLoadSessionList = Boolean(agentId && adapter.listSessions);
|
|
7958
|
+
const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
|
|
7959
|
+
if (!canLoadSessionList && !canLoadSessionTree) {
|
|
7960
|
+
setNodes([]);
|
|
7961
|
+
setHasEverLoaded(true);
|
|
7962
|
+
setLoading(false);
|
|
7963
|
+
return;
|
|
7964
|
+
}
|
|
7965
|
+
const currentRequestId = ++requestId.current;
|
|
7966
|
+
if (!opts?.silent) setLoading(true);
|
|
7967
|
+
try {
|
|
7968
|
+
const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
|
|
7969
|
+
agentId,
|
|
7970
|
+
limit: sessionListLimit
|
|
7971
|
+
}) : Promise.resolve(null);
|
|
7972
|
+
const sessionTreePromise = sessionId && adapter.getSessionTree ? adapter.getSessionTree({ sessionId }) : Promise.resolve(null);
|
|
7973
|
+
const [sessionList, sessionTree] = await Promise.all([
|
|
7974
|
+
sessionListPromise,
|
|
7975
|
+
sessionTreePromise
|
|
7976
|
+
]);
|
|
7977
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
7978
|
+
const nextNodes = mergeTreeNodes([
|
|
7979
|
+
sessionList?.sessions.map(sessionToTreeNode) ?? [],
|
|
7980
|
+
sessionTree?.nodes ?? []
|
|
7981
|
+
]);
|
|
7982
|
+
const fp = treeFingerprint(nextNodes);
|
|
7983
|
+
if (fp !== lastFingerprint.current) {
|
|
7984
|
+
lastFingerprint.current = fp;
|
|
7985
|
+
setNodes(nextNodes);
|
|
7986
|
+
}
|
|
7987
|
+
setError(null);
|
|
7988
|
+
setHasEverLoaded(true);
|
|
7989
|
+
} catch (e) {
|
|
7990
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
7991
|
+
if (!opts?.silent) {
|
|
7992
|
+
setError(e instanceof Error ? e.message : "Failed to load tree");
|
|
7993
|
+
}
|
|
7994
|
+
} finally {
|
|
7995
|
+
if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
|
|
7996
|
+
setLoading(false);
|
|
7997
|
+
}
|
|
7998
|
+
}
|
|
7999
|
+
},
|
|
8000
|
+
[adapter, agentId, sessionId, sessionListLimit]
|
|
8001
|
+
);
|
|
8002
|
+
useEffect(() => {
|
|
8003
|
+
mounted.current = true;
|
|
8004
|
+
return () => {
|
|
8005
|
+
mounted.current = false;
|
|
8006
|
+
};
|
|
8007
|
+
}, []);
|
|
8008
|
+
useEffect(() => {
|
|
8009
|
+
setNodes([]);
|
|
8010
|
+
setError(null);
|
|
8011
|
+
setHasEverLoaded(false);
|
|
8012
|
+
lastFingerprint.current = "";
|
|
8013
|
+
void refetch();
|
|
8014
|
+
}, [refetch, sessionId]);
|
|
8015
|
+
const runningCount = useMemo(
|
|
8016
|
+
() => nodes.filter((n) => n.status === "active").length,
|
|
8017
|
+
[nodes]
|
|
8018
|
+
);
|
|
8019
|
+
useEffect(() => {
|
|
8020
|
+
const interval = runningCount > 0 ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
|
|
8021
|
+
const id = setInterval(() => {
|
|
8022
|
+
void refetch({ silent: true });
|
|
8023
|
+
}, interval);
|
|
8024
|
+
return () => clearInterval(id);
|
|
8025
|
+
}, [refetch, runningCount]);
|
|
8026
|
+
const roots = useMemo(() => buildTree(nodes), [nodes]);
|
|
8027
|
+
const handleSelect = useCallback(
|
|
8028
|
+
(id) => {
|
|
8029
|
+
onSessionSelect?.(id);
|
|
8030
|
+
},
|
|
8031
|
+
[onSessionSelect]
|
|
8032
|
+
);
|
|
8033
|
+
const handleStop = useCallback(
|
|
8034
|
+
async (id) => {
|
|
8035
|
+
if (!adapter.stopSession) return;
|
|
8036
|
+
try {
|
|
8037
|
+
await adapter.stopSession({ sessionId: id });
|
|
8038
|
+
await refetch({ silent: true });
|
|
8039
|
+
} catch (e) {
|
|
8040
|
+
setError(e instanceof Error ? e.message : "Failed to stop sub-agent");
|
|
8041
|
+
}
|
|
8042
|
+
},
|
|
8043
|
+
[adapter, refetch]
|
|
8044
|
+
);
|
|
8045
|
+
if (!hasEverLoaded) return null;
|
|
8046
|
+
if (nodes.length === 0) return null;
|
|
8047
|
+
const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
|
|
8048
|
+
if (topLevel.length === 0) return null;
|
|
8049
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-t border-line", children: [
|
|
8050
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-faint", children: [
|
|
8051
|
+
/* @__PURE__ */ jsx(UsersIcon, { className: "w-3.5 h-3.5" }),
|
|
8052
|
+
/* @__PURE__ */ jsx("span", { children: title }),
|
|
8053
|
+
runningCount > 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
|
|
8054
|
+
] }),
|
|
8055
|
+
loading && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "Loading..." }),
|
|
8056
|
+
error && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
|
|
8057
|
+
!error && /* @__PURE__ */ jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsx(
|
|
8058
|
+
TreeNodeRow,
|
|
8059
|
+
{
|
|
8060
|
+
entry,
|
|
8061
|
+
depth: 0,
|
|
8062
|
+
activeSessionId: activeSessionId ?? "",
|
|
8063
|
+
canStop: Boolean(adapter.stopSession),
|
|
8064
|
+
onSelect: handleSelect,
|
|
8065
|
+
onStop: handleStop
|
|
8066
|
+
},
|
|
8067
|
+
entry.id
|
|
8068
|
+
)) })
|
|
8069
|
+
] });
|
|
8070
|
+
}
|
|
7649
8071
|
|
|
7650
|
-
export { AgentChat, AgentChatAdapterProvider, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
8072
|
+
export { AgentChat, AgentChatAdapterProvider, MessageResponse, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime };
|
|
7651
8073
|
//# sourceMappingURL=index.js.map
|
|
7652
8074
|
//# sourceMappingURL=index.js.map
|