@invergent/agent-chat-react 1.4.6 → 1.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2817,16 +2817,20 @@ function ExpertToolBlock({ tc }) {
2817
2817
  }
2818
2818
  void submit("up");
2819
2819
  };
2820
- const expertName = parseArgs2(tc.args)?.expert ?? null;
2821
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
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-2 py-1",
2829
- "text-sm text-muted-foreground hover:bg-muted/50 transition-colors"
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-medium text-foreground/80", children: "consult_expert" }),
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.jsxs("div", { className: "ml-6 mt-0.5 space-y-1 text-sm ", children: [
2850
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto rounded bg-muted/40 px-2 py-1 text-muted-foreground whitespace-pre-wrap break-all", children: formatArgs(tc.args) }),
2851
- tc.result && /* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "overflow-x-auto rounded bg-muted/40 px-2 py-1 text-muted-foreground whitespace-pre-wrap break-all max-h-64 overflow-y-auto", children: [
2852
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-emerald-600 dark:text-emerald-400", children: "Result:" }),
2853
- "\n",
2854
- truncate(tc.result, 2e3)
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,356 @@ 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
+ canDelete,
7948
+ deletingSessionId,
7949
+ onSelect,
7950
+ onStop,
7951
+ onDelete
7952
+ }) {
7953
+ const [expanded, setExpanded] = react.useState(true);
7954
+ const hasChildren = entry.children.length > 0;
7955
+ const isActive = entry.id === activeSessionId;
7956
+ const isRunning = entry.status === "active";
7957
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
7958
+ /* @__PURE__ */ jsxRuntime.jsxs(
7959
+ "div",
7960
+ {
7961
+ role: "button",
7962
+ tabIndex: 0,
7963
+ onClick: () => onSelect(entry.id),
7964
+ onKeyDown: (e) => {
7965
+ if (e.key === "Enter" || e.key === " ") onSelect(entry.id);
7966
+ },
7967
+ className: cn(
7968
+ "group flex items-center gap-1.5 py-1.5 pr-1 rounded cursor-pointer text-sm transition-colors",
7969
+ isActive ? "bg-line text-foreground" : "hover:bg-input text-subtle hover:text-foreground"
7970
+ ),
7971
+ style: { paddingLeft: `${depth * 12 + 4}px` },
7972
+ children: [
7973
+ hasChildren ? /* @__PURE__ */ jsxRuntime.jsx(
7974
+ "button",
7975
+ {
7976
+ type: "button",
7977
+ className: "p-0.5 rounded hover:bg-line",
7978
+ onClick: (e) => {
7979
+ e.stopPropagation();
7980
+ setExpanded(!expanded);
7981
+ },
7982
+ "aria-label": expanded ? "Collapse" : "Expand",
7983
+ "aria-expanded": expanded,
7984
+ 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" })
7985
+ }
7986
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-4 h-4 shrink-0" }),
7987
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0 flex items-center gap-1.5", children: [
7988
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: entry.title ?? "session" }),
7989
+ entry.agentType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "outline", className: "h-4 px-1.5 text-[10px]", children: entry.agentType }),
7990
+ /* @__PURE__ */ jsxRuntime.jsx(
7991
+ Badge,
7992
+ {
7993
+ variant: statusColor(entry.status),
7994
+ className: "h-4 px-1.5 text-[10px]",
7995
+ children: entry.status
7996
+ }
7997
+ )
7998
+ ] }),
7999
+ isRunning && canStop && /* @__PURE__ */ jsxRuntime.jsx(
8000
+ "button",
8001
+ {
8002
+ type: "button",
8003
+ className: "p-1 rounded opacity-0 group-hover:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all",
8004
+ onClick: (e) => {
8005
+ e.stopPropagation();
8006
+ onStop(entry.id);
8007
+ },
8008
+ "aria-label": "Stop sub-agent",
8009
+ title: "Stop sub-agent",
8010
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SquareIcon, { className: "w-3 h-3", fill: "currentColor" })
8011
+ }
8012
+ ),
8013
+ canDelete && /* @__PURE__ */ jsxRuntime.jsx(
8014
+ "button",
8015
+ {
8016
+ type: "button",
8017
+ className: "p-1 rounded opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none disabled:opacity-50 transition-all",
8018
+ onClick: (e) => {
8019
+ e.stopPropagation();
8020
+ onDelete(entry.id);
8021
+ },
8022
+ "aria-label": "Delete session",
8023
+ title: "Delete session",
8024
+ disabled: deletingSessionId === entry.id,
8025
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "w-3 h-3" })
8026
+ }
8027
+ )
8028
+ ]
8029
+ }
8030
+ ),
8031
+ hasChildren && expanded && entry.children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(
8032
+ TreeNodeRow,
8033
+ {
8034
+ entry: child,
8035
+ depth: depth + 1,
8036
+ activeSessionId,
8037
+ canStop,
8038
+ canDelete,
8039
+ deletingSessionId,
8040
+ onSelect,
8041
+ onStop,
8042
+ onDelete
8043
+ },
8044
+ child.id
8045
+ ))
8046
+ ] });
8047
+ }
8048
+ function SessionTreePanel({
8049
+ adapter,
8050
+ sessionId = null,
8051
+ activeSessionId = sessionId ?? void 0,
8052
+ agentId,
8053
+ title = "Running",
8054
+ sessionListLimit = DEFAULT_SESSION_LIST_LIMIT,
8055
+ hideRoot = false,
8056
+ onSessionSelect,
8057
+ onSessionDelete
8058
+ }) {
8059
+ const [nodes, setNodes] = react.useState([]);
8060
+ const [loading, setLoading] = react.useState(false);
8061
+ const [error, setError] = react.useState(null);
8062
+ const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
8063
+ const [deletingSessionId, setDeletingSessionId] = react.useState(
8064
+ null
8065
+ );
8066
+ const mounted = react.useRef(true);
8067
+ const requestId = react.useRef(0);
8068
+ const lastFingerprint = react.useRef("");
8069
+ const refetch = react.useCallback(
8070
+ async (opts) => {
8071
+ const canLoadSessionList = Boolean(agentId && adapter.listSessions);
8072
+ const canLoadSessionTree = Boolean(sessionId && adapter.getSessionTree);
8073
+ if (!canLoadSessionList && !canLoadSessionTree) {
8074
+ setNodes([]);
8075
+ setHasEverLoaded(true);
8076
+ setLoading(false);
8077
+ return;
8078
+ }
8079
+ const currentRequestId = ++requestId.current;
8080
+ if (!opts?.silent) setLoading(true);
8081
+ try {
8082
+ const sessionListPromise = agentId && adapter.listSessions ? adapter.listSessions({
8083
+ agentId,
8084
+ limit: sessionListLimit
8085
+ }) : Promise.resolve(null);
8086
+ const sessionTreePromise = sessionId && adapter.getSessionTree ? adapter.getSessionTree({ sessionId }) : Promise.resolve(null);
8087
+ const [sessionList, sessionTree] = await Promise.all([
8088
+ sessionListPromise,
8089
+ sessionTreePromise
8090
+ ]);
8091
+ if (!mounted.current || currentRequestId !== requestId.current) return;
8092
+ const nextNodes = mergeTreeNodes([
8093
+ sessionList?.sessions.map(sessionToTreeNode) ?? [],
8094
+ sessionTree?.nodes ?? []
8095
+ ]);
8096
+ const fp = treeFingerprint(nextNodes);
8097
+ if (fp !== lastFingerprint.current) {
8098
+ lastFingerprint.current = fp;
8099
+ setNodes(nextNodes);
8100
+ }
8101
+ setError(null);
8102
+ setHasEverLoaded(true);
8103
+ } catch (e) {
8104
+ if (!mounted.current || currentRequestId !== requestId.current) return;
8105
+ if (!opts?.silent) {
8106
+ setError(e instanceof Error ? e.message : "Failed to load tree");
8107
+ }
8108
+ } finally {
8109
+ if (mounted.current && currentRequestId === requestId.current && !opts?.silent) {
8110
+ setLoading(false);
8111
+ }
8112
+ }
8113
+ },
8114
+ [adapter, agentId, sessionId, sessionListLimit]
8115
+ );
8116
+ react.useEffect(() => {
8117
+ mounted.current = true;
8118
+ return () => {
8119
+ mounted.current = false;
8120
+ };
8121
+ }, []);
8122
+ react.useEffect(() => {
8123
+ setNodes([]);
8124
+ setError(null);
8125
+ setHasEverLoaded(false);
8126
+ lastFingerprint.current = "";
8127
+ void refetch();
8128
+ }, [refetch, sessionId]);
8129
+ const runningCount = react.useMemo(
8130
+ () => nodes.filter((n) => n.status === "active").length,
8131
+ [nodes]
8132
+ );
8133
+ react.useEffect(() => {
8134
+ const interval = runningCount > 0 ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
8135
+ const id = setInterval(() => {
8136
+ void refetch({ silent: true });
8137
+ }, interval);
8138
+ return () => clearInterval(id);
8139
+ }, [refetch, runningCount]);
8140
+ const roots = react.useMemo(() => buildTree(nodes), [nodes]);
8141
+ const handleSelect = react.useCallback(
8142
+ (id) => {
8143
+ onSessionSelect?.(id);
8144
+ },
8145
+ [onSessionSelect]
8146
+ );
8147
+ const handleStop = react.useCallback(
8148
+ async (id) => {
8149
+ if (!adapter.stopSession) return;
8150
+ try {
8151
+ await adapter.stopSession({ sessionId: id });
8152
+ await refetch({ silent: true });
8153
+ } catch (e) {
8154
+ setError(e instanceof Error ? e.message : "Failed to stop sub-agent");
8155
+ }
8156
+ },
8157
+ [adapter, refetch]
8158
+ );
8159
+ const handleDelete = react.useCallback(
8160
+ async (id) => {
8161
+ if (!adapter.deleteSession || deletingSessionId) return;
8162
+ setDeletingSessionId(id);
8163
+ try {
8164
+ await adapter.deleteSession({ sessionId: id });
8165
+ onSessionDelete?.(id);
8166
+ await refetch({ silent: true });
8167
+ } catch (e) {
8168
+ setError(e instanceof Error ? e.message : "Failed to delete session");
8169
+ } finally {
8170
+ if (mounted.current) setDeletingSessionId(null);
8171
+ }
8172
+ },
8173
+ [adapter, deletingSessionId, onSessionDelete, refetch]
8174
+ );
8175
+ if (!hasEverLoaded) return null;
8176
+ if (nodes.length === 0) return null;
8177
+ const topLevel = hideRoot ? roots.flatMap((r) => r.children) : roots;
8178
+ if (topLevel.length === 0) return null;
8179
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-line", children: [
8180
+ /* @__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: [
8181
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.UsersIcon, { className: "w-3.5 h-3.5" }),
8182
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
8183
+ runningCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "h-4 px-1.5 text-[10px] ml-auto", children: runningCount })
8184
+ ] }),
8185
+ loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "Loading..." }),
8186
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
8187
+ !error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 pb-2", children: topLevel.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
8188
+ TreeNodeRow,
8189
+ {
8190
+ entry,
8191
+ depth: 0,
8192
+ activeSessionId: activeSessionId ?? "",
8193
+ canStop: Boolean(adapter.stopSession),
8194
+ canDelete: Boolean(adapter.deleteSession),
8195
+ deletingSessionId,
8196
+ onSelect: handleSelect,
8197
+ onStop: handleStop,
8198
+ onDelete: handleDelete
8199
+ },
8200
+ entry.id
8201
+ )) })
8202
+ ] });
8203
+ }
7754
8204
 
7755
8205
  exports.AgentChat = AgentChat;
7756
8206
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
8207
+ exports.MessageResponse = MessageResponse;
8208
+ exports.SessionTreePanel = SessionTreePanel;
7757
8209
  exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
7758
8210
  exports.useAgentChatRuntime = useAgentChatRuntime;
7759
8211
  //# sourceMappingURL=index.cjs.map