@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.cjs
CHANGED
|
@@ -2817,16 +2817,20 @@ function ExpertToolBlock({ tc }) {
|
|
|
2817
2817
|
}
|
|
2818
2818
|
void submit("up");
|
|
2819
2819
|
};
|
|
2820
|
-
const
|
|
2821
|
-
|
|
2820
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
2821
|
+
const expertName = args.expert ?? null;
|
|
2822
|
+
const question = args.question ?? args.prompt ?? "";
|
|
2823
|
+
const result = parseExpertResult(tc.result);
|
|
2824
|
+
const failed = Boolean(result?.error);
|
|
2825
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
2822
2826
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2823
2827
|
"button",
|
|
2824
2828
|
{
|
|
2825
2829
|
type: "button",
|
|
2826
2830
|
onClick: () => setExpanded(!expanded),
|
|
2827
2831
|
className: cn(
|
|
2828
|
-
"flex w-full items-center gap-1.5 rounded-md px-
|
|
2829
|
-
"text-sm text-muted-foreground hover:
|
|
2832
|
+
"flex w-fit max-w-full items-center gap-1.5 rounded-md px-0 py-0.5",
|
|
2833
|
+
"text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
2830
2834
|
),
|
|
2831
2835
|
children: [
|
|
2832
2836
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2838,21 +2842,26 @@ function ExpertToolBlock({ tc }) {
|
|
|
2838
2842
|
)
|
|
2839
2843
|
}
|
|
2840
2844
|
),
|
|
2841
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-
|
|
2845
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: "Consulted expert" }),
|
|
2842
2846
|
expertName && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
|
|
2843
2847
|
"\xB7 ",
|
|
2844
2848
|
expertName
|
|
2845
|
-
] })
|
|
2849
|
+
] }),
|
|
2850
|
+
failed && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: "\xB7 failed" })
|
|
2846
2851
|
]
|
|
2847
2852
|
}
|
|
2848
2853
|
),
|
|
2849
|
-
expanded && /* @__PURE__ */ jsxRuntime.
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2854
|
+
result?.summary && !expanded && /* @__PURE__ */ jsxRuntime.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 }),
|
|
2855
|
+
expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-6 mt-0.5 space-y-1.5 text-sm", children: [
|
|
2856
|
+
question && /* @__PURE__ */ jsxRuntime.jsx(ExpertDetail, { label: "Question", content: question }),
|
|
2857
|
+
result?.summary && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2858
|
+
ExpertDetail,
|
|
2859
|
+
{
|
|
2860
|
+
label: failed ? "Error" : "Response",
|
|
2861
|
+
content: result.summary,
|
|
2862
|
+
tone: failed ? "error" : "default"
|
|
2863
|
+
}
|
|
2864
|
+
)
|
|
2856
2865
|
] }),
|
|
2857
2866
|
canRate && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-6 mt-1 space-y-1.5", children: [
|
|
2858
2867
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
@@ -2918,6 +2927,36 @@ function ExpertToolBlock({ tc }) {
|
|
|
2918
2927
|
] })
|
|
2919
2928
|
] });
|
|
2920
2929
|
}
|
|
2930
|
+
function parseExpertResult(result) {
|
|
2931
|
+
if (!result) return null;
|
|
2932
|
+
const parsed = parseArgs2(result);
|
|
2933
|
+
if (parsed) {
|
|
2934
|
+
return {
|
|
2935
|
+
...parsed,
|
|
2936
|
+
summary: parsed.summary ?? parsed.response ?? parsed.content
|
|
2937
|
+
};
|
|
2938
|
+
}
|
|
2939
|
+
return { summary: result };
|
|
2940
|
+
}
|
|
2941
|
+
function ExpertDetail({
|
|
2942
|
+
label,
|
|
2943
|
+
content,
|
|
2944
|
+
tone = "default"
|
|
2945
|
+
}) {
|
|
2946
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2947
|
+
"div",
|
|
2948
|
+
{
|
|
2949
|
+
className: cn(
|
|
2950
|
+
"rounded-md border-l-2 px-3 py-2",
|
|
2951
|
+
tone === "error" ? "border-red-500/50 bg-red-500/5" : "border-primary/50 bg-muted/30"
|
|
2952
|
+
),
|
|
2953
|
+
children: [
|
|
2954
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 text-[10px] uppercase tracking-wide text-muted-foreground/70", children: label }),
|
|
2955
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap wrap-break-word text-foreground/90", children: content })
|
|
2956
|
+
]
|
|
2957
|
+
}
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2921
2960
|
function ReasonForm({
|
|
2922
2961
|
initialValue,
|
|
2923
2962
|
busy,
|
|
@@ -3543,6 +3582,71 @@ function MemoryEntry({
|
|
|
3543
3582
|
}
|
|
3544
3583
|
);
|
|
3545
3584
|
}
|
|
3585
|
+
var ACTION_LABEL = {
|
|
3586
|
+
create: "Create skill",
|
|
3587
|
+
patch: "Patch skill",
|
|
3588
|
+
edit: "Edit skill",
|
|
3589
|
+
delete: "Delete skill",
|
|
3590
|
+
write_file: "Write skill file",
|
|
3591
|
+
remove_file: "Remove skill file"
|
|
3592
|
+
};
|
|
3593
|
+
function SkillManageToolBlock({ tc }) {
|
|
3594
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
3595
|
+
const result = tc.result ? parseArgs2(tc.result) : null;
|
|
3596
|
+
const action = args.action ?? "manage";
|
|
3597
|
+
const label = ACTION_LABEL[action] ?? "Manage skill";
|
|
3598
|
+
const target = args.file_path ? `${args.name ?? "?"}/${args.file_path}` : args.name ?? "?";
|
|
3599
|
+
const failed = result?.success === false || Boolean(result?.error);
|
|
3600
|
+
const summary = result?.error ?? result?.message ?? result?.path;
|
|
3601
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
3602
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: label }),
|
|
3603
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: target }),
|
|
3604
|
+
summary && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: failed ? "text-red-500 truncate" : "text-muted-foreground/70 truncate", children: [
|
|
3605
|
+
"\u2192 ",
|
|
3606
|
+
summary
|
|
3607
|
+
] })
|
|
3608
|
+
] });
|
|
3609
|
+
}
|
|
3610
|
+
function firstLine2(value) {
|
|
3611
|
+
if (!value) return "";
|
|
3612
|
+
const index = value.indexOf("\n");
|
|
3613
|
+
return (index === -1 ? value : value.slice(0, index)).trim();
|
|
3614
|
+
}
|
|
3615
|
+
function CoordinatorToolBlock({ tc }) {
|
|
3616
|
+
const args = parseArgs2(tc.args) ?? {};
|
|
3617
|
+
const result = tc.result ? parseArgs2(tc.result) : null;
|
|
3618
|
+
const failed = Boolean(result?.error);
|
|
3619
|
+
let label = "Worker";
|
|
3620
|
+
let target = "";
|
|
3621
|
+
let detail = "";
|
|
3622
|
+
if (tc.toolName === "spawn_worker") {
|
|
3623
|
+
label = tc.status === "running" ? "Spawning worker" : "Spawned worker";
|
|
3624
|
+
target = args.agent_type ?? args.model ?? "";
|
|
3625
|
+
detail = firstLine2(args.goal);
|
|
3626
|
+
} else if (tc.toolName === "send_worker_message") {
|
|
3627
|
+
label = "Message worker";
|
|
3628
|
+
target = args.worker_id ?? "";
|
|
3629
|
+
detail = firstLine2(args.message);
|
|
3630
|
+
} else if (tc.toolName === "stop_worker") {
|
|
3631
|
+
label = "Stop worker";
|
|
3632
|
+
target = args.worker_id ?? "";
|
|
3633
|
+
detail = firstLine2(args.reason);
|
|
3634
|
+
}
|
|
3635
|
+
const resultId = result?.worker_id ?? result?.session_id;
|
|
3636
|
+
const summary = result?.error ?? resultId ?? result?.status;
|
|
3637
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [
|
|
3638
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold text-foreground", children: label }),
|
|
3639
|
+
target && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: target }),
|
|
3640
|
+
detail && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 truncate", children: [
|
|
3641
|
+
"\xB7 ",
|
|
3642
|
+
detail
|
|
3643
|
+
] }),
|
|
3644
|
+
summary && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: failed ? "text-red-500 truncate" : "text-muted-foreground/70 truncate", children: [
|
|
3645
|
+
"\u2192 ",
|
|
3646
|
+
summary
|
|
3647
|
+
] })
|
|
3648
|
+
] });
|
|
3649
|
+
}
|
|
3546
3650
|
function DefaultToolBlock({ tc }) {
|
|
3547
3651
|
const [expanded, setExpanded] = react.useState(false);
|
|
3548
3652
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -3611,6 +3715,8 @@ function ToolCallBlock({ tc, onFileSelect }) {
|
|
|
3611
3715
|
return /* @__PURE__ */ jsxRuntime.jsx(SkillsListBlock, { tc });
|
|
3612
3716
|
case "skill_view":
|
|
3613
3717
|
return /* @__PURE__ */ jsxRuntime.jsx(SkillViewBlock, { tc });
|
|
3718
|
+
case "skill_manage":
|
|
3719
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SkillManageToolBlock, { tc });
|
|
3614
3720
|
case "clarify":
|
|
3615
3721
|
return /* @__PURE__ */ jsxRuntime.jsx(ClarifyToolBlock, { tc });
|
|
3616
3722
|
case "create_artifact":
|
|
@@ -3619,6 +3725,10 @@ function ToolCallBlock({ tc, onFileSelect }) {
|
|
|
3619
3725
|
return /* @__PURE__ */ jsxRuntime.jsx(DelegateToolBlock, { tc });
|
|
3620
3726
|
case "memory":
|
|
3621
3727
|
return /* @__PURE__ */ jsxRuntime.jsx(MemoryToolBlock, { tc });
|
|
3728
|
+
case "spawn_worker":
|
|
3729
|
+
case "send_worker_message":
|
|
3730
|
+
case "stop_worker":
|
|
3731
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CoordinatorToolBlock, { tc });
|
|
3622
3732
|
default:
|
|
3623
3733
|
return /* @__PURE__ */ jsxRuntime.jsx(DefaultToolBlock, { tc });
|
|
3624
3734
|
}
|
|
@@ -6509,7 +6619,8 @@ function WorkspacePanel({
|
|
|
6509
6619
|
adapter,
|
|
6510
6620
|
sessionId,
|
|
6511
6621
|
selectedPath,
|
|
6512
|
-
onSelectedPathChange
|
|
6622
|
+
onSelectedPathChange,
|
|
6623
|
+
disabled = false
|
|
6513
6624
|
}) {
|
|
6514
6625
|
const fileInputRef = react.useRef(null);
|
|
6515
6626
|
const [entries, setEntries] = react.useState([]);
|
|
@@ -6628,7 +6739,7 @@ function WorkspacePanel({
|
|
|
6628
6739
|
);
|
|
6629
6740
|
const handleUpload = react.useCallback(
|
|
6630
6741
|
async (files) => {
|
|
6631
|
-
if (!sessionId || files.length === 0) return;
|
|
6742
|
+
if (disabled || !sessionId || files.length === 0) return;
|
|
6632
6743
|
setUploading(true);
|
|
6633
6744
|
setNotice(null);
|
|
6634
6745
|
try {
|
|
@@ -6649,7 +6760,7 @@ function WorkspacePanel({
|
|
|
6649
6760
|
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
6650
6761
|
}
|
|
6651
6762
|
},
|
|
6652
|
-
[adapter, fetchTree, sessionId]
|
|
6763
|
+
[adapter, disabled, fetchTree, sessionId]
|
|
6653
6764
|
);
|
|
6654
6765
|
const handleDelete = react.useCallback(
|
|
6655
6766
|
async (path) => {
|
|
@@ -6723,6 +6834,7 @@ function WorkspacePanel({
|
|
|
6723
6834
|
type: "file",
|
|
6724
6835
|
multiple: true,
|
|
6725
6836
|
className: "hidden",
|
|
6837
|
+
disabled,
|
|
6726
6838
|
onChange: (event) => {
|
|
6727
6839
|
if (event.target.files) void handleUpload(event.target.files);
|
|
6728
6840
|
}
|
|
@@ -6741,7 +6853,7 @@ function WorkspacePanel({
|
|
|
6741
6853
|
variant: "ghost",
|
|
6742
6854
|
size: "icon-sm",
|
|
6743
6855
|
onClick: () => fileInputRef.current?.click(),
|
|
6744
|
-
disabled: !sessionId || uploading,
|
|
6856
|
+
disabled: disabled || !sessionId || uploading,
|
|
6745
6857
|
"aria-label": "Upload files",
|
|
6746
6858
|
children: uploading ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UploadIcon, { className: "size-4" })
|
|
6747
6859
|
}
|
|
@@ -6793,7 +6905,7 @@ function WorkspacePanel({
|
|
|
6793
6905
|
size: "sm",
|
|
6794
6906
|
className: "mt-3 gap-1.5",
|
|
6795
6907
|
onClick: () => fileInputRef.current?.click(),
|
|
6796
|
-
disabled: !sessionId,
|
|
6908
|
+
disabled: disabled || !sessionId,
|
|
6797
6909
|
children: [
|
|
6798
6910
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.UploadIcon, { className: "size-3.5" }),
|
|
6799
6911
|
"Upload files"
|
|
@@ -7744,16 +7856,328 @@ function AgentChat({
|
|
|
7744
7856
|
adapter,
|
|
7745
7857
|
sessionId,
|
|
7746
7858
|
selectedPath: workspacePath,
|
|
7747
|
-
onSelectedPathChange: setWorkspacePath
|
|
7859
|
+
onSelectedPathChange: setWorkspacePath,
|
|
7860
|
+
disabled
|
|
7748
7861
|
}
|
|
7749
7862
|
)
|
|
7750
7863
|
] }) })
|
|
7751
7864
|
}
|
|
7752
7865
|
);
|
|
7753
7866
|
}
|
|
7867
|
+
var POLL_INTERVAL_ACTIVE_MS = 4e3;
|
|
7868
|
+
var POLL_INTERVAL_IDLE_MS = 3e4;
|
|
7869
|
+
var DEFAULT_SESSION_LIST_LIMIT = 50;
|
|
7870
|
+
function buildTree(nodes) {
|
|
7871
|
+
const byId = /* @__PURE__ */ new Map();
|
|
7872
|
+
for (const n of nodes) byId.set(n.id, { ...n, children: [] });
|
|
7873
|
+
const roots = [];
|
|
7874
|
+
for (const n of byId.values()) {
|
|
7875
|
+
const parent = n.parentId ? byId.get(n.parentId) : void 0;
|
|
7876
|
+
if (parent) {
|
|
7877
|
+
parent.children.push(n);
|
|
7878
|
+
} else {
|
|
7879
|
+
roots.push(n);
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
const sortRec = (e) => {
|
|
7883
|
+
e.children.sort((a, b) => a.createdAt < b.createdAt ? -1 : 1);
|
|
7884
|
+
for (const c of e.children) sortRec(c);
|
|
7885
|
+
};
|
|
7886
|
+
for (const r of roots) sortRec(r);
|
|
7887
|
+
return roots;
|
|
7888
|
+
}
|
|
7889
|
+
function treeFingerprint(nodes) {
|
|
7890
|
+
return nodes.map(
|
|
7891
|
+
(n) => `${n.id}:${n.parentId ?? ""}:${n.status}:${n.agentType ?? ""}:${n.messageCount ?? 0}:${n.toolCallCount ?? 0}:${n.updatedAt}`
|
|
7892
|
+
).join("|");
|
|
7893
|
+
}
|
|
7894
|
+
function sessionToTreeNode(session) {
|
|
7895
|
+
const timestamp = session.updatedAt ?? session.createdAt ?? "";
|
|
7896
|
+
return {
|
|
7897
|
+
id: session.id,
|
|
7898
|
+
parentId: session.parentId ?? null,
|
|
7899
|
+
agentId: session.agentId,
|
|
7900
|
+
channel: session.channel,
|
|
7901
|
+
status: session.status,
|
|
7902
|
+
title: session.title,
|
|
7903
|
+
model: session.model,
|
|
7904
|
+
messageCount: session.messageCount,
|
|
7905
|
+
toolCallCount: session.toolCallCount,
|
|
7906
|
+
createdAt: session.createdAt ?? timestamp,
|
|
7907
|
+
updatedAt: session.updatedAt ?? timestamp
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7910
|
+
function mergeNodeFields(current, next) {
|
|
7911
|
+
return {
|
|
7912
|
+
...current,
|
|
7913
|
+
...Object.fromEntries(
|
|
7914
|
+
Object.entries(next).filter(([, value]) => value !== void 0)
|
|
7915
|
+
),
|
|
7916
|
+
messageCount: next.messageCount ?? current.messageCount,
|
|
7917
|
+
toolCallCount: next.toolCallCount ?? current.toolCallCount
|
|
7918
|
+
};
|
|
7919
|
+
}
|
|
7920
|
+
function mergeTreeNodes(groups) {
|
|
7921
|
+
const byId = /* @__PURE__ */ new Map();
|
|
7922
|
+
for (const group of groups) {
|
|
7923
|
+
for (const node of group) {
|
|
7924
|
+
const current = byId.get(node.id);
|
|
7925
|
+
byId.set(node.id, current ? mergeNodeFields(current, node) : node);
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
return Array.from(byId.values());
|
|
7929
|
+
}
|
|
7930
|
+
function statusColor(status) {
|
|
7931
|
+
switch (status) {
|
|
7932
|
+
case "active":
|
|
7933
|
+
return "default";
|
|
7934
|
+
case "completed":
|
|
7935
|
+
return "secondary";
|
|
7936
|
+
case "failed":
|
|
7937
|
+
return "destructive";
|
|
7938
|
+
default:
|
|
7939
|
+
return "outline";
|
|
7940
|
+
}
|
|
7941
|
+
}
|
|
7942
|
+
function TreeNodeRow({
|
|
7943
|
+
entry,
|
|
7944
|
+
depth,
|
|
7945
|
+
activeSessionId,
|
|
7946
|
+
canStop,
|
|
7947
|
+
onSelect,
|
|
7948
|
+
onStop
|
|
7949
|
+
}) {
|
|
7950
|
+
const [expanded, setExpanded] = react.useState(true);
|
|
7951
|
+
const hasChildren = entry.children.length > 0;
|
|
7952
|
+
const isActive = entry.id === activeSessionId;
|
|
7953
|
+
const isRunning = entry.status === "active";
|
|
7954
|
+
const messageCount = entry.messageCount ?? 0;
|
|
7955
|
+
const toolCallCount = entry.toolCallCount ?? 0;
|
|
7956
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7957
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7958
|
+
"div",
|
|
7959
|
+
{
|
|
7960
|
+
role: "button",
|
|
7961
|
+
tabIndex: 0,
|
|
7962
|
+
onClick: () => onSelect(entry.id),
|
|
7963
|
+
onKeyDown: (e) => {
|
|
7964
|
+
if (e.key === "Enter" || e.key === " ") onSelect(entry.id);
|
|
7965
|
+
},
|
|
7966
|
+
className: cn(
|
|
7967
|
+
"group flex items-center gap-1.5 py-1.5 pr-1 rounded cursor-pointer text-sm transition-colors",
|
|
7968
|
+
isActive ? "bg-line text-foreground" : "hover:bg-input text-subtle hover:text-foreground"
|
|
7969
|
+
),
|
|
7970
|
+
style: { paddingLeft: `${depth * 12 + 4}px` },
|
|
7971
|
+
children: [
|
|
7972
|
+
hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7973
|
+
"button",
|
|
7974
|
+
{
|
|
7975
|
+
type: "button",
|
|
7976
|
+
className: "p-0.5 rounded hover:bg-line",
|
|
7977
|
+
onClick: (e) => {
|
|
7978
|
+
e.stopPropagation();
|
|
7979
|
+
setExpanded(!expanded);
|
|
7980
|
+
},
|
|
7981
|
+
"aria-label": expanded ? "Collapse" : "Expand",
|
|
7982
|
+
"aria-expanded": expanded,
|
|
7983
|
+
children: expanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "w-3.5 h-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "w-3.5 h-3.5" })
|
|
7984
|
+
}
|
|
7985
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 shrink-0" }),
|
|
7986
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center gap-1.5", children: [
|
|
7987
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: entry.title ?? entry.channel ?? "session" }),
|
|
7988
|
+
entry.agentType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "outline", className: "h-4 px-1.5 text-[10px]", children: entry.agentType }),
|
|
7989
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7990
|
+
Badge,
|
|
7991
|
+
{
|
|
7992
|
+
variant: statusColor(entry.status),
|
|
7993
|
+
className: "h-4 px-1.5 text-[10px]",
|
|
7994
|
+
children: entry.status
|
|
7995
|
+
}
|
|
7996
|
+
)
|
|
7997
|
+
] }),
|
|
7998
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7999
|
+
"span",
|
|
8000
|
+
{
|
|
8001
|
+
className: "text-xs text-faint shrink-0 tabular-nums",
|
|
8002
|
+
"aria-label": `${messageCount} messages, ${toolCallCount} tool calls`,
|
|
8003
|
+
title: `${messageCount} messages, ${toolCallCount} tool calls`,
|
|
8004
|
+
children: [
|
|
8005
|
+
messageCount,
|
|
8006
|
+
"m/",
|
|
8007
|
+
toolCallCount,
|
|
8008
|
+
"t"
|
|
8009
|
+
]
|
|
8010
|
+
}
|
|
8011
|
+
),
|
|
8012
|
+
isRunning && canStop && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8013
|
+
"button",
|
|
8014
|
+
{
|
|
8015
|
+
type: "button",
|
|
8016
|
+
className: "p-1 rounded opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all",
|
|
8017
|
+
onClick: (e) => {
|
|
8018
|
+
e.stopPropagation();
|
|
8019
|
+
onStop(entry.id);
|
|
8020
|
+
},
|
|
8021
|
+
"aria-label": "Stop sub-agent",
|
|
8022
|
+
title: "Stop sub-agent",
|
|
8023
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SquareIcon, { className: "w-3 h-3", fill: "currentColor" })
|
|
8024
|
+
}
|
|
8025
|
+
)
|
|
8026
|
+
]
|
|
8027
|
+
}
|
|
8028
|
+
),
|
|
8029
|
+
hasChildren && expanded && entry.children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8030
|
+
TreeNodeRow,
|
|
8031
|
+
{
|
|
8032
|
+
entry: child,
|
|
8033
|
+
depth: depth + 1,
|
|
8034
|
+
activeSessionId,
|
|
8035
|
+
canStop,
|
|
8036
|
+
onSelect,
|
|
8037
|
+
onStop
|
|
8038
|
+
},
|
|
8039
|
+
child.id
|
|
8040
|
+
))
|
|
8041
|
+
] });
|
|
8042
|
+
}
|
|
8043
|
+
function SessionTreePanel({
|
|
8044
|
+
adapter,
|
|
8045
|
+
sessionId = null,
|
|
8046
|
+
activeSessionId = sessionId ?? void 0,
|
|
8047
|
+
agentId,
|
|
8048
|
+
title = "Running",
|
|
8049
|
+
sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
|
|
8050
|
+
hideRoot = false,
|
|
8051
|
+
onSessionSelect
|
|
8052
|
+
}) {
|
|
8053
|
+
const [nodes, setNodes] = react.useState([]);
|
|
8054
|
+
const [loading, setLoading] = react.useState(false);
|
|
8055
|
+
const [error, setError] = react.useState(null);
|
|
8056
|
+
const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
|
|
8057
|
+
const mounted = react.useRef(true);
|
|
8058
|
+
const requestId = react.useRef(0);
|
|
8059
|
+
const lastFingerprint = react.useRef("");
|
|
8060
|
+
const refetch = react.useCallback(
|
|
8061
|
+
async (opts) => {
|
|
8062
|
+
const canLoadSessionList = Boolean(agentId && adapter.listSessions);
|
|
8063
|
+
const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
|
|
8064
|
+
if (!canLoadSessionList && !canLoadSessionTree) {
|
|
8065
|
+
setNodes([]);
|
|
8066
|
+
setHasEverLoaded(true);
|
|
8067
|
+
setLoading(false);
|
|
8068
|
+
return;
|
|
8069
|
+
}
|
|
8070
|
+
const currentRequestId = ++requestId.current;
|
|
8071
|
+
if (!opts?.silent) setLoading(true);
|
|
8072
|
+
try {
|
|
8073
|
+
const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
|
|
8074
|
+
agentId,
|
|
8075
|
+
limit: sessionListLimit
|
|
8076
|
+
}) : Promise.resolve(null);
|
|
8077
|
+
const sessionTreePromise = sessionId && adapter.getSessionTree ? adapter.getSessionTree({ sessionId }) : Promise.resolve(null);
|
|
8078
|
+
const [sessionList, sessionTree] = await Promise.all([
|
|
8079
|
+
sessionListPromise,
|
|
8080
|
+
sessionTreePromise
|
|
8081
|
+
]);
|
|
8082
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
8083
|
+
const nextNodes = mergeTreeNodes([
|
|
8084
|
+
sessionList?.sessions.map(sessionToTreeNode) ?? [],
|
|
8085
|
+
sessionTree?.nodes ?? []
|
|
8086
|
+
]);
|
|
8087
|
+
const fp = treeFingerprint(nextNodes);
|
|
8088
|
+
if (fp !== lastFingerprint.current) {
|
|
8089
|
+
lastFingerprint.current = fp;
|
|
8090
|
+
setNodes(nextNodes);
|
|
8091
|
+
}
|
|
8092
|
+
setError(null);
|
|
8093
|
+
setHasEverLoaded(true);
|
|
8094
|
+
} catch (e) {
|
|
8095
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
8096
|
+
if (!opts?.silent) {
|
|
8097
|
+
setError(e instanceof Error ? e.message : "Failed to load tree");
|
|
8098
|
+
}
|
|
8099
|
+
} finally {
|
|
8100
|
+
if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
|
|
8101
|
+
setLoading(false);
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
8104
|
+
},
|
|
8105
|
+
[adapter, agentId, sessionId, sessionListLimit]
|
|
8106
|
+
);
|
|
8107
|
+
react.useEffect(() => {
|
|
8108
|
+
mounted.current = true;
|
|
8109
|
+
return () => {
|
|
8110
|
+
mounted.current = false;
|
|
8111
|
+
};
|
|
8112
|
+
}, []);
|
|
8113
|
+
react.useEffect(() => {
|
|
8114
|
+
setNodes([]);
|
|
8115
|
+
setError(null);
|
|
8116
|
+
setHasEverLoaded(false);
|
|
8117
|
+
lastFingerprint.current = "";
|
|
8118
|
+
void refetch();
|
|
8119
|
+
}, [refetch, sessionId]);
|
|
8120
|
+
const runningCount = react.useMemo(
|
|
8121
|
+
() => nodes.filter((n) => n.status === "active").length,
|
|
8122
|
+
[nodes]
|
|
8123
|
+
);
|
|
8124
|
+
react.useEffect(() => {
|
|
8125
|
+
const interval = runningCount > 0 ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
|
|
8126
|
+
const id = setInterval(() => {
|
|
8127
|
+
void refetch({ silent: true });
|
|
8128
|
+
}, interval);
|
|
8129
|
+
return () => clearInterval(id);
|
|
8130
|
+
}, [refetch, runningCount]);
|
|
8131
|
+
const roots = react.useMemo(() => buildTree(nodes), [nodes]);
|
|
8132
|
+
const handleSelect = react.useCallback(
|
|
8133
|
+
(id) => {
|
|
8134
|
+
onSessionSelect?.(id);
|
|
8135
|
+
},
|
|
8136
|
+
[onSessionSelect]
|
|
8137
|
+
);
|
|
8138
|
+
const handleStop = react.useCallback(
|
|
8139
|
+
async (id) => {
|
|
8140
|
+
if (!adapter.stopSession) return;
|
|
8141
|
+
try {
|
|
8142
|
+
await adapter.stopSession({ sessionId: id });
|
|
8143
|
+
await refetch({ silent: true });
|
|
8144
|
+
} catch (e) {
|
|
8145
|
+
setError(e instanceof Error ? e.message : "Failed to stop sub-agent");
|
|
8146
|
+
}
|
|
8147
|
+
},
|
|
8148
|
+
[adapter, refetch]
|
|
8149
|
+
);
|
|
8150
|
+
if (!hasEverLoaded) return null;
|
|
8151
|
+
if (nodes.length === 0) return null;
|
|
8152
|
+
const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
|
|
8153
|
+
if (topLevel.length === 0) return null;
|
|
8154
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-line", children: [
|
|
8155
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-3 py-2 text-xs font-semibold uppercase tracking-wide text-faint", children: [
|
|
8156
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.UsersIcon, { className: "w-3.5 h-3.5" }),
|
|
8157
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
|
|
8158
|
+
runningCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
|
|
8159
|
+
] }),
|
|
8160
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "Loading..." }),
|
|
8161
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
|
|
8162
|
+
!error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8163
|
+
TreeNodeRow,
|
|
8164
|
+
{
|
|
8165
|
+
entry,
|
|
8166
|
+
depth: 0,
|
|
8167
|
+
activeSessionId: activeSessionId ?? "",
|
|
8168
|
+
canStop: Boolean(adapter.stopSession),
|
|
8169
|
+
onSelect: handleSelect,
|
|
8170
|
+
onStop: handleStop
|
|
8171
|
+
},
|
|
8172
|
+
entry.id
|
|
8173
|
+
)) })
|
|
8174
|
+
] });
|
|
8175
|
+
}
|
|
7754
8176
|
|
|
7755
8177
|
exports.AgentChat = AgentChat;
|
|
7756
8178
|
exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
|
|
8179
|
+
exports.MessageResponse = MessageResponse;
|
|
8180
|
+
exports.SessionTreePanel = SessionTreePanel;
|
|
7757
8181
|
exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
|
|
7758
8182
|
exports.useAgentChatRuntime = useAgentChatRuntime;
|
|
7759
8183
|
//# sourceMappingURL=index.cjs.map
|