@invergent/agent-chat-react 2.4.13 → 2.5.1

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.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { cn } from './chunk-QSC4UIVT.js';
2
- import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, lazy, useContext, Fragment as Fragment$1, Suspense, Children } from 'react';
3
- import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, ChevronRightIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, Loader2Icon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, CheckIcon, CopyIcon, TerminalIcon, SparklesIcon, ClockIcon, PlusIcon, CloudIcon, HardDriveIcon, PaperclipIcon, GlobeIcon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, SearchIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, GitBranchIcon, ActivityIcon, EyeIcon, ListTodoIcon, ArrowUpIcon, ListIcon, PenLineIcon, ArrowRight, BookOpenIcon, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
2
+ import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, useLayoutEffect, lazy, useContext, Fragment as Fragment$1, Suspense, Children } from 'react';
3
+ import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, ChevronRightIcon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, Loader2Icon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, CheckIcon, CopyIcon, TerminalIcon, SparklesIcon, ClockIcon, PlusIcon, CloudIcon, HardDriveIcon, PaperclipIcon, GlobeIcon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, SearchIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, BookOpenIcon, GitBranchIcon, ActivityIcon, EyeIcon, ListTodoIcon, ArrowUpIcon, ListIcon, PenLineIcon, ArrowRight, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
4
4
  import { cva } from 'class-variance-authority';
5
5
  import { Collapsible as Collapsible$1, Slot, Tooltip as Tooltip$1, Progress as Progress$1, Tabs as Tabs$1, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Popover as Popover$1 } from 'radix-ui';
6
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -2201,6 +2201,37 @@ function MCPToolBlock({ tc }) {
2201
2201
  summary && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/60 truncate text-xs", children: summary })
2202
2202
  ] });
2203
2203
  }
2204
+ function ResearchMemoryBlock({ tc }) {
2205
+ const args = parseArgs(tc.args) ?? {};
2206
+ const result = tc.result ? parseArgs(tc.result) ?? {} : {};
2207
+ let detail = "";
2208
+ if (args.action === "add") {
2209
+ const host = args.url ? hostname(args.url) : "";
2210
+ const sid = result.source_id ?? "";
2211
+ detail = sid ? `recorded ${sid}${host ? ` \xB7 ${host}` : ""}` : host ? `recorded \xB7 ${host}` : "recorded";
2212
+ } else if (args.action === "retrieve") {
2213
+ const n = result.sources?.length ?? 0;
2214
+ detail = args.query ? `${n} source${n === 1 ? "" : "s"} for "${truncate2(args.query, 40)}"` : `${n} source${n === 1 ? "" : "s"}`;
2215
+ } else if (args.action === "list") {
2216
+ const n = result.sources?.length ?? 0;
2217
+ detail = `${n} source${n === 1 ? "" : "s"}`;
2218
+ }
2219
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2220
+ /* @__PURE__ */ jsx(BookOpenIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2221
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Research" }),
2222
+ detail && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: detail })
2223
+ ] });
2224
+ }
2225
+ function hostname(url) {
2226
+ try {
2227
+ return new URL(url).hostname.replace(/^www\./, "");
2228
+ } catch {
2229
+ return url;
2230
+ }
2231
+ }
2232
+ function truncate2(s, n) {
2233
+ return s.length > n ? `${s.slice(0, n)}\u2026` : s;
2234
+ }
2204
2235
  function VisionAnalyzeBlock({ tc }) {
2205
2236
  let image = "";
2206
2237
  try {
@@ -3898,13 +3929,9 @@ function firstLine(s) {
3898
3929
  return idx === -1 ? s : s.slice(0, idx);
3899
3930
  }
3900
3931
  function DelegateToolBlock({ tc }) {
3901
- const [expanded, setExpanded] = useState(false);
3902
3932
  const args = parseArgs(tc.args);
3903
- const goal = args?.goal ?? "";
3904
- const context = args?.context ?? "";
3933
+ const goal = firstLine(args?.goal ?? "").trim();
3905
3934
  const agentType = args?.agent_type;
3906
- const model = args?.model;
3907
- const summary = firstLine(goal).trim();
3908
3935
  let resultError = null;
3909
3936
  if (tc.result) {
3910
3937
  try {
@@ -3913,84 +3940,29 @@ function DelegateToolBlock({ tc }) {
3913
3940
  } catch {
3914
3941
  }
3915
3942
  }
3916
- return /* @__PURE__ */ jsxs("div", { children: [
3917
- /* @__PURE__ */ jsxs(
3918
- "button",
3943
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
3944
+ /* @__PURE__ */ jsx(GitBranchIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
3945
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: "Delegate" }),
3946
+ agentType && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
3947
+ "\xB7 ",
3948
+ agentType
3949
+ ] }),
3950
+ goal && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground/70 truncate text-xs italic", children: [
3951
+ "\xB7 \u201C",
3952
+ goal,
3953
+ "\u201D"
3954
+ ] }),
3955
+ resultError && /* @__PURE__ */ jsx(
3956
+ "span",
3919
3957
  {
3920
- type: "button",
3921
- onClick: () => setExpanded(!expanded),
3922
3958
  className: cn(
3923
- "flex w-full items-center gap-1.5 rounded-md px-2 py-1",
3924
- "text-sm text-muted-foreground hover:bg-muted/50 transition-colors"
3959
+ "shrink-0 text-xs",
3960
+ "text-destructive"
3925
3961
  ),
3926
- children: [
3927
- /* @__PURE__ */ jsx(
3928
- ChevronRightIcon,
3929
- {
3930
- className: cn(
3931
- "size-3 shrink-0 transition-transform duration-150",
3932
- expanded && "rotate-90"
3933
- )
3934
- }
3935
- ),
3936
- /* @__PURE__ */ jsx(GitBranchIcon, { className: "size-3.5 shrink-0 text-muted-foreground" }),
3937
- /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground/80", children: "delegate_task" }),
3938
- agentType && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
3939
- "\xB7 ",
3940
- agentType
3941
- ] }),
3942
- summary && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground truncate min-w-0", children: [
3943
- "\xB7 ",
3944
- summary
3945
- ] })
3946
- ]
3962
+ title: resultError,
3963
+ children: "\xB7 failed"
3947
3964
  }
3948
- ),
3949
- expanded && /* @__PURE__ */ jsxs("div", { className: "ml-6 mt-0.5 space-y-1.5 text-sm font-mono", children: [
3950
- (agentType || model) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-3 gap-y-0.5 text-xs text-muted-foreground", children: [
3951
- agentType && /* @__PURE__ */ jsxs("span", { children: [
3952
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: "agent:" }),
3953
- " ",
3954
- agentType
3955
- ] }),
3956
- model && /* @__PURE__ */ jsxs("span", { children: [
3957
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: "model:" }),
3958
- " ",
3959
- model
3960
- ] })
3961
- ] }),
3962
- goal && /* @__PURE__ */ jsxs("div", { className: "rounded bg-muted/40 px-2 py-1", children: [
3963
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground/70", children: "Goal" }),
3964
- /* @__PURE__ */ jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-foreground/90", children: goal })
3965
- ] }),
3966
- context && /* @__PURE__ */ jsxs("div", { className: "rounded bg-muted/40 px-2 py-1", children: [
3967
- /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground/70", children: "Context" }),
3968
- /* @__PURE__ */ jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-muted-foreground", children: truncate(context, 2e3) })
3969
- ] }),
3970
- tc.status === "running" && !tc.result && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground italic", children: "Sub-agent running\u2026" }),
3971
- tc.result && /* @__PURE__ */ jsxs(
3972
- "div",
3973
- {
3974
- className: cn(
3975
- "rounded px-2 py-1 max-h-64 overflow-y-auto",
3976
- resultError ? "bg-destructive/10" : "bg-muted/40"
3977
- ),
3978
- children: [
3979
- /* @__PURE__ */ jsx(
3980
- "div",
3981
- {
3982
- className: cn(
3983
- "text-[10px] uppercase tracking-wide",
3984
- resultError ? "text-destructive/80" : "text-emerald-600 dark:text-emerald-400"
3985
- ),
3986
- children: resultError ? "Error" : "Sub-agent response"
3987
- }
3988
- ),
3989
- /* @__PURE__ */ jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-foreground/90", children: truncate(resultError ?? tc.result, 4e3) })
3990
- ]
3991
- }
3992
- )
3993
- ] })
3965
+ )
3994
3966
  ] });
3995
3967
  }
3996
3968
  var ACTION_VERB = {
@@ -4216,6 +4188,19 @@ function DefaultToolBlock({ tc }) {
4216
4188
  ] })
4217
4189
  ] });
4218
4190
  }
4191
+ function ResearchOutlineBlock({ tc }) {
4192
+ const args = parseArgs(tc.args) ?? {};
4193
+ const result = tc.result ? parseArgs(tc.result) ?? {} : {};
4194
+ const outline = args.action === "set" ? args.outline ?? "" : result.outline ?? "";
4195
+ const sections = result.sections ?? [];
4196
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-sm border border-border bg-muted/40 p-2 text-xs", children: [
4197
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 font-semibold uppercase tracking-widest text-muted-foreground", children: [
4198
+ "Research outline",
4199
+ sections.length ? ` \xB7 ${sections.length} sections` : ""
4200
+ ] }),
4201
+ outline ? /* @__PURE__ */ jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-snug", children: outline }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "updated" })
4202
+ ] });
4203
+ }
4219
4204
  function ToolCallBlock({
4220
4205
  tc,
4221
4206
  resolvedArtifactName,
@@ -4268,6 +4253,10 @@ function ToolCallBlock({
4268
4253
  return /* @__PURE__ */ jsx(DelegateToolBlock, { tc });
4269
4254
  case "memory":
4270
4255
  return /* @__PURE__ */ jsx(MemoryToolBlock, { tc });
4256
+ case "research_outline":
4257
+ return /* @__PURE__ */ jsx(ResearchOutlineBlock, { tc });
4258
+ case "research_memory":
4259
+ return /* @__PURE__ */ jsx(ResearchMemoryBlock, { tc });
4271
4260
  case "spawn_worker":
4272
4261
  case "send_worker_message":
4273
4262
  case "stop_worker":
@@ -4321,12 +4310,13 @@ function WebSearchGroupBlock({ tcs }) {
4321
4310
  /* @__PURE__ */ jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: tcs.map((tc) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(WebSearchRow, { tc }) }, tc.id)) })
4322
4311
  ] });
4323
4312
  }
4313
+ var COLLAPSED_MAX_HEIGHT_PX = 160;
4324
4314
  var ChatMessage = memo(function ChatMessage2({
4325
4315
  message,
4326
4316
  onFileSelect
4327
4317
  }) {
4328
4318
  return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
4329
- message.content,
4319
+ /* @__PURE__ */ jsx(CollapsibleMessageBody, { content: message.content }),
4330
4320
  message.images && message.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsx(
4331
4321
  "img",
4332
4322
  {
@@ -4346,6 +4336,48 @@ var ChatMessage = memo(function ChatMessage2({
4346
4336
  )) })
4347
4337
  ] }) });
4348
4338
  });
4339
+ function CollapsibleMessageBody({ content }) {
4340
+ const ref = useRef(null);
4341
+ const [overflowing, setOverflowing] = useState(false);
4342
+ const [expanded, setExpanded] = useState(false);
4343
+ useLayoutEffect(() => {
4344
+ const el = ref.current;
4345
+ if (!el) return;
4346
+ const measure = () => {
4347
+ setOverflowing(el.scrollHeight > el.clientHeight + 1);
4348
+ };
4349
+ measure();
4350
+ const observer = new ResizeObserver(measure);
4351
+ observer.observe(el);
4352
+ return () => observer.disconnect();
4353
+ }, [content]);
4354
+ return /* @__PURE__ */ jsxs("div", { children: [
4355
+ /* @__PURE__ */ jsx(
4356
+ "div",
4357
+ {
4358
+ ref,
4359
+ className: cn(
4360
+ "whitespace-pre-wrap wrap-break-word",
4361
+ !expanded && "overflow-hidden"
4362
+ ),
4363
+ style: !expanded ? { maxHeight: COLLAPSED_MAX_HEIGHT_PX } : void 0,
4364
+ children: content
4365
+ }
4366
+ ),
4367
+ (overflowing || expanded) && /* @__PURE__ */ jsx(
4368
+ "button",
4369
+ {
4370
+ type: "button",
4371
+ onClick: () => setExpanded((prev) => !prev),
4372
+ className: cn(
4373
+ "mt-2 text-xs font-bold text-primary",
4374
+ "hover:underline focus:outline-none focus-visible:underline"
4375
+ ),
4376
+ children: expanded ? "Show less..." : "Show more..."
4377
+ }
4378
+ )
4379
+ ] });
4380
+ }
4349
4381
  function iconForMime(mime) {
4350
4382
  if (!mime) return File$1;
4351
4383
  if (mime.startsWith("image/")) return FileImage;
@@ -5792,6 +5824,7 @@ function ChatComposerInner({
5792
5824
  onToggleWorkspace,
5793
5825
  canShowBrowser = false,
5794
5826
  canShowWorkspace = false,
5827
+ deepResearchEnabled = false,
5795
5828
  viewMode = "simple",
5796
5829
  onViewModeChange
5797
5830
  }) {
@@ -5810,10 +5843,10 @@ function ChatComposerInner({
5810
5843
  }
5811
5844
  }, [buttonMenuOpen, menuDismissed, textInput.value]);
5812
5845
  useEffect(() => {
5813
- if (textInput.value.startsWith("/")) {
5846
+ if (!buttonMenuOpen && textInput.value.startsWith("/")) {
5814
5847
  setMenuMode("all");
5815
5848
  }
5816
- }, [textInput.value]);
5849
+ }, [buttonMenuOpen, textInput.value]);
5817
5850
  useEffect(() => {
5818
5851
  if (showSlashMenu && (menuMode === "skills" || menuMode === "all")) {
5819
5852
  adapter.listSlashCommands?.().then(setAdapterCommands).catch(() => {
@@ -5821,24 +5854,34 @@ function ChatComposerInner({
5821
5854
  }
5822
5855
  }, [adapter, showSlashMenu, menuMode]);
5823
5856
  const builtinCommands = useMemo(
5824
- () => [
5825
- { value: "/clear", label: "/clear", description: "Clear conversation" },
5826
- { value: "/compress", label: "/compress", description: "Compress context" },
5827
- { value: "/goal", label: "/goal", description: "Define an outcome goal" },
5828
- { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
5829
- { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
5830
- { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
5831
- { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
5832
- { value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
5833
- { value: "/mission status", label: "/mission status", description: "Show current mission status" },
5834
- { value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
5835
- { value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
5836
- { value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
5837
- { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
5838
- { value: "/loop list", label: "/loop list", description: "List active loops" },
5839
- { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
5840
- ],
5841
- []
5857
+ () => {
5858
+ const base = [
5859
+ { value: "/clear", label: "/clear", description: "Clear conversation" },
5860
+ { value: "/compress", label: "/compress", description: "Compress context" },
5861
+ { value: "/goal", label: "/goal", description: "Define an outcome goal" },
5862
+ { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
5863
+ { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
5864
+ { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
5865
+ { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
5866
+ { value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
5867
+ { value: "/mission status", label: "/mission status", description: "Show current mission status" },
5868
+ { value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
5869
+ { value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
5870
+ { value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
5871
+ { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
5872
+ { value: "/loop list", label: "/loop list", description: "List active loops" },
5873
+ { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
5874
+ ];
5875
+ if (deepResearchEnabled) {
5876
+ base.push({
5877
+ value: "/deep-research ",
5878
+ label: "/deep-research",
5879
+ description: "Delegate a topic to the deep-research workflow"
5880
+ });
5881
+ }
5882
+ return base;
5883
+ },
5884
+ [deepResearchEnabled]
5842
5885
  );
5843
5886
  const scheduledExamples = useMemo(
5844
5887
  () => [
@@ -5975,7 +6018,7 @@ function ChatComposerInner({
5975
6018
  },
5976
6019
  children: [
5977
6020
  /* @__PURE__ */ jsx(AttachmentPreviewStrip, {}),
5978
- viewMode === "expert" && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 px-1 pb-2", children: [
6021
+ viewMode === "expert" && !disabled && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 px-1 pb-2", children: [
5979
6022
  /* @__PURE__ */ jsxs(
5980
6023
  Button,
5981
6024
  {
@@ -6050,7 +6093,7 @@ function ChatComposerInner({
6050
6093
  ) }),
6051
6094
  /* @__PURE__ */ jsxs(PromptInputFooter, { children: [
6052
6095
  /* @__PURE__ */ jsxs(PromptInputTools, { children: [
6053
- /* @__PURE__ */ jsxs(Popover, { open: addMenuOpen, onOpenChange: setAddMenuOpen, children: [
6096
+ !disabled && /* @__PURE__ */ jsxs(Popover, { open: addMenuOpen, onOpenChange: setAddMenuOpen, children: [
6054
6097
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PromptInputButton, { "aria-label": "Add", children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }) }),
6055
6098
  /* @__PURE__ */ jsx(
6056
6099
  PopoverContent,
@@ -6122,7 +6165,7 @@ function ChatComposerInner({
6122
6165
  children: /* @__PURE__ */ jsx(FolderIcon, { className: "size-4" })
6123
6166
  }
6124
6167
  ),
6125
- tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxs(
6168
+ !disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxs(
6126
6169
  Context,
6127
6170
  {
6128
6171
  usedTokens: tokenUsage.totalTokens,
@@ -6210,7 +6253,7 @@ function ChatComposerInner({
6210
6253
  ]
6211
6254
  }
6212
6255
  ),
6213
- /* @__PURE__ */ jsx(
6256
+ !disabled && /* @__PURE__ */ jsx(
6214
6257
  PromptInputSubmit,
6215
6258
  {
6216
6259
  status,
@@ -6347,6 +6390,80 @@ function ChatComposerInner({
6347
6390
  }
6348
6391
  );
6349
6392
  }
6393
+ function ResearchSourcesPanel({
6394
+ sources
6395
+ }) {
6396
+ const [expanded, setExpanded] = useState(false);
6397
+ const rootRef = useRef(null);
6398
+ useEffect(() => {
6399
+ const root = rootRef.current;
6400
+ if (!root) return;
6401
+ const handler = (event) => {
6402
+ const target = event.target;
6403
+ if (target?.closest('[id^="source-"]')) {
6404
+ setExpanded(true);
6405
+ }
6406
+ };
6407
+ document.addEventListener("scroll-to-source", handler, true);
6408
+ return () => document.removeEventListener("scroll-to-source", handler, true);
6409
+ }, []);
6410
+ if (sources.length === 0) return null;
6411
+ return /* @__PURE__ */ jsxs(
6412
+ "div",
6413
+ {
6414
+ ref: rootRef,
6415
+ className: "rounded-md border border-line bg-card",
6416
+ children: [
6417
+ /* @__PURE__ */ jsxs(
6418
+ "button",
6419
+ {
6420
+ type: "button",
6421
+ onClick: () => setExpanded((prev) => !prev),
6422
+ "aria-expanded": expanded,
6423
+ className: "flex w-full items-center gap-1.5 px-2 py-1.5 text-left hover:bg-muted/40",
6424
+ children: [
6425
+ /* @__PURE__ */ jsx(
6426
+ ChevronRightIcon,
6427
+ {
6428
+ className: cn(
6429
+ "size-3 shrink-0 text-muted-foreground transition-transform duration-150",
6430
+ expanded && "rotate-90"
6431
+ )
6432
+ }
6433
+ ),
6434
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-muted-foreground", children: [
6435
+ "Sources \xB7 ",
6436
+ sources.length
6437
+ ] })
6438
+ ]
6439
+ }
6440
+ ),
6441
+ expanded && /* @__PURE__ */ jsx("ul", { className: "max-h-35 overflow-y-auto border-t border-line py-1", children: sources.map((s) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6442
+ "a",
6443
+ {
6444
+ id: `source-${s.sourceId}`,
6445
+ href: s.url,
6446
+ target: "_blank",
6447
+ rel: "noreferrer",
6448
+ className: "group flex items-baseline gap-2 px-2 py-1 hover:bg-muted/60",
6449
+ children: [
6450
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[10px] font-semibold text-primary tabular-nums", children: s.sourceId }),
6451
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-foreground group-hover:underline", children: s.title || s.url }),
6452
+ /* @__PURE__ */ jsx("span", { className: "ml-auto shrink-0 truncate text-[10px] text-muted-foreground max-w-[40%]", children: hostname2(s.url) })
6453
+ ]
6454
+ }
6455
+ ) }, s.sourceId)) })
6456
+ ]
6457
+ }
6458
+ );
6459
+ }
6460
+ function hostname2(url) {
6461
+ try {
6462
+ return new URL(url).hostname.replace(/^www\./, "");
6463
+ } catch {
6464
+ return url;
6465
+ }
6466
+ }
6350
6467
  var MAX_REASON_LENGTH2 = 500;
6351
6468
  function TurnFeedback({ msg }) {
6352
6469
  const { adapter, sessionId } = useAgentChatAdapterContext();
@@ -6719,8 +6836,20 @@ var ArtifactContent = ({
6719
6836
  className,
6720
6837
  ...props
6721
6838
  }) => /* @__PURE__ */ jsx("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
6722
- function ArtifactMarkdown({ spec }) {
6723
- return /* @__PURE__ */ jsx(MessageResponse, { children: spec.content ?? "" });
6839
+ function ArtifactMarkdown({
6840
+ spec,
6841
+ fill = false
6842
+ }) {
6843
+ return /* @__PURE__ */ jsx(
6844
+ "div",
6845
+ {
6846
+ className: cn(
6847
+ "px-4 py-3",
6848
+ !fill && "max-h-112 overflow-y-auto"
6849
+ ),
6850
+ children: /* @__PURE__ */ jsx(MessageResponse, { children: spec.content ?? "" })
6851
+ }
6852
+ );
6724
6853
  }
6725
6854
  function formatCell(value) {
6726
6855
  if (value == null) return "";
@@ -7090,7 +7219,7 @@ function ArtifactBody({
7090
7219
  }
7091
7220
  switch (payload.kind) {
7092
7221
  case "markdown":
7093
- return /* @__PURE__ */ jsx(ArtifactMarkdown, { spec: payload.spec });
7222
+ return /* @__PURE__ */ jsx(ArtifactMarkdown, { spec: payload.spec, fill });
7094
7223
  case "table":
7095
7224
  return /* @__PURE__ */ jsx(ArtifactTable, { spec: payload.spec });
7096
7225
  case "chart":
@@ -7983,17 +8112,15 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
7983
8112
  }];
7984
8113
  }
7985
8114
  if (msg.systemKind === "artifact") {
7986
- const { artifactId, name, kind, version } = unpackArtifactMeta(
7987
- msg.systemMeta,
7988
- msg.content
7989
- );
8115
+ const { artifactId, name, kind, version, originatingSessionId } = unpackArtifactMeta(msg.systemMeta, msg.content);
7990
8116
  return [{
7991
8117
  kind: "artifact",
7992
8118
  key: msg.id,
7993
8119
  artifactId,
7994
8120
  name,
7995
8121
  artifactKind: kind,
7996
- version
8122
+ version,
8123
+ originatingSessionId
7997
8124
  }];
7998
8125
  }
7999
8126
  if (msg.systemKind === "browser_marker" || msg.systemKind === "browser_marker_warning") {
@@ -8165,7 +8292,8 @@ function unpackArtifactMeta(systemMeta, fallbackName) {
8165
8292
  artifactId: meta.artifact_id ?? "",
8166
8293
  name: meta.name ?? fallbackName,
8167
8294
  kind: meta.kind ?? "markdown",
8168
- version: meta.version ?? 1
8295
+ version: meta.version ?? 1,
8296
+ originatingSessionId: meta.originating_session_id ?? null
8169
8297
  };
8170
8298
  }
8171
8299
  function OrphanSystemMarker({
@@ -8191,7 +8319,7 @@ function OrphanSystemMarker({
8191
8319
  return /* @__PURE__ */ jsx(
8192
8320
  ArtifactBlock,
8193
8321
  {
8194
- sessionId,
8322
+ sessionId: unpacked.originatingSessionId ?? sessionId,
8195
8323
  artifactId: unpacked.artifactId,
8196
8324
  name: unpacked.name,
8197
8325
  kind: unpacked.kind,
@@ -8224,25 +8352,32 @@ function OrphanSystemMarker({
8224
8352
  function cancelledToolLabel(toolName) {
8225
8353
  const map = {
8226
8354
  terminal: "Command",
8227
- execute_code: "Execute Code",
8355
+ execute_code: "Execute code",
8228
8356
  read_file: "Read",
8229
8357
  write_file: "Write",
8230
8358
  patch: "Patch",
8231
- search_files: "Search Files",
8232
- list_files: "List Files",
8233
- web_search: "Web Search",
8234
- web_extract: "Web Fetch",
8235
- web_crawl: "Web Crawl",
8236
- session_search: "Session Search",
8359
+ search_files: "Search files",
8360
+ list_files: "List files",
8361
+ web_search: "Web search",
8362
+ web_extract: "Web fetch",
8363
+ web_crawl: "Web crawl",
8364
+ session_search: "Session search",
8237
8365
  memory: "Memory",
8238
8366
  todo: "Todo",
8239
8367
  skills_list: "Skills",
8240
8368
  skill_view: "Skill",
8241
8369
  consult_expert: "Expert",
8242
- delegate_task: "Delegate",
8370
+ delegate_task: "Delegate task",
8243
8371
  ask_user_question: "Ask User Question",
8244
8372
  process: "Process",
8245
- create_artifact: "Artifact"
8373
+ create_artifact: "Create artifact",
8374
+ // ``Research memory`` reads as a noun ("memory of research") and
8375
+ // makes the shimmer awkward ("Running Research memory…"). The
8376
+ // one-liner renderer shows just ``Research`` as the label, so
8377
+ // mirror that here so the live shimmer matches the row that
8378
+ // replaces it on completion.
8379
+ research_memory: "Research",
8380
+ research_outline: "Research"
8246
8381
  };
8247
8382
  return map[toolName] ?? toolName;
8248
8383
  }
@@ -8371,7 +8506,7 @@ function TimelineEntryItem({
8371
8506
  /* @__PURE__ */ jsx(TimelineContent, { children: sessionId ? /* @__PURE__ */ jsx(
8372
8507
  ArtifactBlock,
8373
8508
  {
8374
- sessionId,
8509
+ sessionId: entry.originatingSessionId ?? sessionId,
8375
8510
  artifactId: entry.artifactId,
8376
8511
  name: entry.name,
8377
8512
  kind: entry.artifactKind,
@@ -8503,17 +8638,24 @@ function extractToolDetail(tc) {
8503
8638
  }
8504
8639
  case "terminal": {
8505
8640
  const cmd = stringArg("command");
8506
- return cmd ? truncate2(cmd, 40) : null;
8641
+ return cmd ? truncate3(cmd, 40) : null;
8507
8642
  }
8508
8643
  case "execute_code": {
8509
8644
  const code3 = stringArg("code");
8510
- return code3 ? truncate2(code3.split("\n")[0] ?? "", 40) : null;
8645
+ return code3 ? truncate3(code3.split("\n")[0] ?? "", 40) : null;
8511
8646
  }
8512
8647
  case "web_search":
8513
8648
  case "web_crawl":
8514
8649
  return stringArg("query");
8515
- case "web_extract":
8516
- return stringArg("url");
8650
+ case "web_extract": {
8651
+ const url = stringArg("url");
8652
+ if (!url) return null;
8653
+ try {
8654
+ return new URL(url).hostname.replace(/^www\./, "");
8655
+ } catch {
8656
+ return truncate3(url, 40);
8657
+ }
8658
+ }
8517
8659
  case "skill_view":
8518
8660
  case "skill_manage":
8519
8661
  return stringArg("name") ?? stringArg("skill");
@@ -8523,6 +8665,40 @@ function extractToolDetail(tc) {
8523
8665
  return stringArg("name") ?? stringArg("task") ?? stringArg("title");
8524
8666
  case "memory":
8525
8667
  return stringArg("action") ?? stringArg("key");
8668
+ case "research_memory": {
8669
+ const action = stringArg("action");
8670
+ if (action === "add") {
8671
+ const title = stringArg("title");
8672
+ if (title) return truncate3(title, 60);
8673
+ const url = stringArg("url");
8674
+ if (url) {
8675
+ try {
8676
+ return new URL(url).hostname.replace(/^www\./, "");
8677
+ } catch {
8678
+ return truncate3(url, 40);
8679
+ }
8680
+ }
8681
+ return null;
8682
+ }
8683
+ if (action === "retrieve") {
8684
+ return stringArg("query");
8685
+ }
8686
+ return action;
8687
+ }
8688
+ case "research_outline": {
8689
+ const action = stringArg("action");
8690
+ if (action === "set") {
8691
+ const outline = stringArg("outline");
8692
+ if (outline) {
8693
+ const sections = outline.split(/\r?\n/).filter((line) => /^#{2,6}\s+\S/.test(line)).length;
8694
+ if (sections > 0) {
8695
+ return `${sections} ${sections === 1 ? "section" : "sections"}`;
8696
+ }
8697
+ }
8698
+ return "outline";
8699
+ }
8700
+ return action;
8701
+ }
8526
8702
  default:
8527
8703
  return null;
8528
8704
  }
@@ -8532,7 +8708,7 @@ function lastPathSegment(path) {
8532
8708
  const idx = cleaned.lastIndexOf("/");
8533
8709
  return idx >= 0 ? cleaned.slice(idx + 1) : cleaned;
8534
8710
  }
8535
- function truncate2(s, max) {
8711
+ function truncate3(s, max) {
8536
8712
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
8537
8713
  }
8538
8714
  var _SIMPLE_MODE_HIDDEN_TOOLS = /* @__PURE__ */ new Set([
@@ -8608,7 +8784,9 @@ var _TOOL_ROW_ICON = {
8608
8784
  delegate_task: ArrowRight,
8609
8785
  create_artifact: FileTextIcon,
8610
8786
  memory: PenLineIcon,
8611
- todo: ListIcon
8787
+ todo: ListIcon,
8788
+ research_memory: BookOpenIcon,
8789
+ research_outline: ListIcon
8612
8790
  };
8613
8791
  function _toolRowIcon(toolName) {
8614
8792
  return _TOOL_ROW_ICON[toolName] ?? WrenchIcon;
@@ -8654,6 +8832,31 @@ function _toolRowLabel(tc) {
8654
8832
  return detail ? `Memory ${detail}` : "Updated memory";
8655
8833
  case "todo":
8656
8834
  return detail ? `Todo ${detail}` : "Updated todo list";
8835
+ case "research_memory": {
8836
+ const args = parseArgs(tc.args);
8837
+ const action = args?.action;
8838
+ if (action === "add") {
8839
+ return detail ? `Stored source "${detail}"` : "Stored a source";
8840
+ }
8841
+ if (action === "retrieve") {
8842
+ return detail ? `Retrieved sources for "${detail}"` : "Retrieved sources";
8843
+ }
8844
+ if (action === "list") {
8845
+ return "Listed sources";
8846
+ }
8847
+ return "Updated research memory";
8848
+ }
8849
+ case "research_outline": {
8850
+ const args = parseArgs(tc.args);
8851
+ const action = args?.action;
8852
+ if (action === "set") {
8853
+ return detail ? `Updated outline (${detail})` : "Updated outline";
8854
+ }
8855
+ if (action === "get") {
8856
+ return "Read outline";
8857
+ }
8858
+ return "Touched outline";
8859
+ }
8657
8860
  default:
8658
8861
  return cancelledToolLabel(tc.toolName);
8659
8862
  }
@@ -8827,7 +9030,8 @@ function SimpleAssistantGroup({
8827
9030
  sessionId,
8828
9031
  artifactFallbacks,
8829
9032
  onFileSelect,
8830
- onRetry
9033
+ onRetry,
9034
+ hideTurnSummary = false
8831
9035
  }) {
8832
9036
  const assistantMessages = messages.filter((m) => m.role === "assistant");
8833
9037
  const tail = assistantMessages[assistantMessages.length - 1];
@@ -8895,7 +9099,7 @@ function SimpleAssistantGroup({
8895
9099
  tail
8896
9100
  }
8897
9101
  ),
8898
- effectiveTurnSummary && /* @__PURE__ */ jsx(
9102
+ effectiveTurnSummary && !hideTurnSummary && /* @__PURE__ */ jsx(
8899
9103
  TurnSummaryCard,
8900
9104
  {
8901
9105
  summary: effectiveTurnSummary,
@@ -8916,7 +9120,8 @@ function AssistantGroup({
8916
9120
  artifactFallbacks,
8917
9121
  onFileSelect,
8918
9122
  onRetry,
8919
- viewMode = "simple"
9123
+ viewMode = "simple",
9124
+ hideTurnSummary = false
8920
9125
  }) {
8921
9126
  if (viewMode === "simple") {
8922
9127
  return /* @__PURE__ */ jsx(
@@ -8927,7 +9132,8 @@ function AssistantGroup({
8927
9132
  sessionId,
8928
9133
  artifactFallbacks,
8929
9134
  onFileSelect,
8930
- onRetry
9135
+ onRetry,
9136
+ hideTurnSummary
8931
9137
  }
8932
9138
  );
8933
9139
  }
@@ -9033,7 +9239,10 @@ function ChatThread({
9033
9239
  canShowBrowser = false,
9034
9240
  canShowWorkspace = false,
9035
9241
  viewMode = "simple",
9036
- onViewModeChange
9242
+ onViewModeChange,
9243
+ deepResearchEnabled = false,
9244
+ researchSources = [],
9245
+ hideTurnSummary = false
9037
9246
  }) {
9038
9247
  const groups = useMemo(() => groupMessages(messages), [messages]);
9039
9248
  const awaitingInput = useMemo(() => isAwaitingUserInput(messages), [messages]);
@@ -9131,7 +9340,8 @@ function ChatThread({
9131
9340
  artifactFallbacks,
9132
9341
  onFileSelect,
9133
9342
  onRetry: groupRetry,
9134
- viewMode
9343
+ viewMode,
9344
+ hideTurnSummary
9135
9345
  },
9136
9346
  group.messages[0].id
9137
9347
  );
@@ -9142,14 +9352,8 @@ function ChatThread({
9142
9352
  ] }),
9143
9353
  /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-4xl px-3 sm:px-6 pb-3 sm:pb-5 pt-3", children: [
9144
9354
  retryIndicator && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx(RetryBanner, { indicator: retryIndicator }) }),
9145
- composerDisabled && composerDisabledReason ? /* @__PURE__ */ jsx(
9146
- "div",
9147
- {
9148
- className: "rounded border border-line bg-muted/40 px-3 py-2 text-sm text-foreground/60",
9149
- role: "status",
9150
- children: composerDisabledReason
9151
- }
9152
- ) : /* @__PURE__ */ jsx(
9355
+ researchSources.length > 0 && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx(ResearchSourcesPanel, { sources: researchSources }) }),
9356
+ /* @__PURE__ */ jsx(
9153
9357
  ChatComposer,
9154
9358
  {
9155
9359
  onSend,
@@ -9166,7 +9370,8 @@ function ChatThread({
9166
9370
  canShowBrowser,
9167
9371
  canShowWorkspace,
9168
9372
  viewMode,
9169
- onViewModeChange
9373
+ onViewModeChange,
9374
+ deepResearchEnabled
9170
9375
  }
9171
9376
  )
9172
9377
  ] })
@@ -9840,6 +10045,29 @@ function isScheduledRunSession(session) {
9840
10045
  session?.channel === "scheduled" || session?.config?.scheduled_session_id
9841
10046
  );
9842
10047
  }
10048
+ var _SUB_AGENT_CHANNELS = /* @__PURE__ */ new Set([
10049
+ "delegation",
10050
+ "worker",
10051
+ "task"
10052
+ ]);
10053
+ function isSubAgentSession(session) {
10054
+ if (!session) return false;
10055
+ if (session.parentId) return true;
10056
+ if (session.channel && _SUB_AGENT_CHANNELS.has(session.channel)) return true;
10057
+ return false;
10058
+ }
10059
+ function readOnlyReasonForSession(session) {
10060
+ if (isScheduledRunSession(session)) {
10061
+ return { readOnly: true, reason: "Scheduled run is read-only" };
10062
+ }
10063
+ if (isSubAgentSession(session)) {
10064
+ return {
10065
+ readOnly: true,
10066
+ reason: "Sub-task started by parent session \u2014 read-only"
10067
+ };
10068
+ }
10069
+ return { readOnly: false };
10070
+ }
9843
10071
 
9844
10072
  // src/runtime/events.ts
9845
10073
  var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
@@ -9909,7 +10137,8 @@ function createInitialAgentChatState(options = {}) {
9909
10137
  terminal: false,
9910
10138
  workspaceRefreshKey: 0,
9911
10139
  browser: null,
9912
- viewMode: options.viewMode ?? "simple"
10140
+ viewMode: options.viewMode ?? "simple",
10141
+ researchSources: []
9913
10142
  };
9914
10143
  }
9915
10144
  function applyAgentChatEvent(state, event) {
@@ -9961,7 +10190,13 @@ function applyAgentChatEvent(state, event) {
9961
10190
  name: event.data.name,
9962
10191
  kind: event.data.kind,
9963
10192
  version: event.data.version,
9964
- size: event.data.size
10193
+ size: event.data.size,
10194
+ // Propagated artifacts (a delegated child's report
10195
+ // surfacing on the parent's chat thread) carry this
10196
+ // field; without it the ArtifactBlock fetches via the
10197
+ // chat's own session id and 404s because the spec lives
10198
+ // under the writer's S3 prefix, not the parent's.
10199
+ originating_session_id: event.data.originating_session_id
9965
10200
  }
9966
10201
  }
9967
10202
  ]);
@@ -9995,11 +10230,12 @@ function applyAgentChatEvent(state, event) {
9995
10230
  const toolName = findToolNameById(nextState.messages, toolCallId);
9996
10231
  const messages = applyToolResult(nextState.messages, event.data);
9997
10232
  const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
9998
- return {
10233
+ const withResult = {
9999
10234
  ...nextState,
10000
10235
  messages,
10001
10236
  workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
10002
10237
  };
10238
+ return collectResearchSource(withResult, toolName, event.data);
10003
10239
  }
10004
10240
  case "harness.wake":
10005
10241
  case "llm.request":
@@ -10347,7 +10583,7 @@ function applyLlmThinking(state, event) {
10347
10583
  } else {
10348
10584
  messages[idx] = {
10349
10585
  ...prev,
10350
- reasoning: (prev.reasoning ?? "") + reasoningText,
10586
+ reasoning: appendText(prev.reasoning, reasoningText),
10351
10587
  turnId: turnId ?? prev.turnId,
10352
10588
  iterationIndex: iterationIndex ?? prev.iterationIndex
10353
10589
  };
@@ -10731,6 +10967,32 @@ function findToolNameById(messages, toolCallId) {
10731
10967
  }
10732
10968
  return null;
10733
10969
  }
10970
+ function collectResearchSource(state, toolName, data) {
10971
+ if (toolName !== "research_memory") return state;
10972
+ const rawResult = data.content ?? data.result;
10973
+ const resultText = typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult ?? {});
10974
+ let parsed;
10975
+ try {
10976
+ parsed = JSON.parse(resultText);
10977
+ } catch {
10978
+ return state;
10979
+ }
10980
+ if (!parsed.success || !parsed.source_id || !parsed.url) return state;
10981
+ if (state.researchSources.some((s) => s.sourceId === parsed.source_id)) {
10982
+ return state;
10983
+ }
10984
+ return {
10985
+ ...state,
10986
+ researchSources: [
10987
+ ...state.researchSources,
10988
+ {
10989
+ sourceId: parsed.source_id,
10990
+ url: parsed.url,
10991
+ title: parsed.title ?? ""
10992
+ }
10993
+ ]
10994
+ };
10995
+ }
10734
10996
  function findLatestConsultExpertCall(messages) {
10735
10997
  for (let i = messages.length - 1; i >= 0; i--) {
10736
10998
  const msg = messages[i];
@@ -11180,7 +11442,8 @@ function useAgentChatRuntime({
11180
11442
  markSending,
11181
11443
  markSendError,
11182
11444
  viewMode: state.viewMode,
11183
- setViewMode
11445
+ setViewMode,
11446
+ researchSources: state.researchSources
11184
11447
  };
11185
11448
  }
11186
11449
  function parseEventData(data) {
@@ -11239,7 +11502,8 @@ function AgentChat({
11239
11502
  onFileSelect,
11240
11503
  onMessagesChange,
11241
11504
  disabled,
11242
- onComposerError
11505
+ onComposerError,
11506
+ deepResearchEnabled = false
11243
11507
  }) {
11244
11508
  const [workspacePath, setWorkspacePath] = useState(null);
11245
11509
  const [mobileView, setMobileView] = useState("chat");
@@ -11254,9 +11518,18 @@ function AgentChat({
11254
11518
  useEffect(() => {
11255
11519
  setShowWorkspace(runtime.viewMode === "expert");
11256
11520
  }, [runtime.viewMode]);
11257
- const isReadOnlySession = isScheduledRunSession(runtime.session);
11258
- const effectiveDisabled = disabled || isReadOnlySession;
11259
- const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
11521
+ const readOnly = readOnlyReasonForSession(runtime.session);
11522
+ const effectiveDisabled = disabled || readOnly.readOnly;
11523
+ const disabledReason = readOnly.reason;
11524
+ const orchestratesDeepResearch = useMemo(
11525
+ () => runtime.messages.some(
11526
+ (m) => m.toolCalls?.some(
11527
+ (tc) => tc.toolName === "delegate_task" && delegateTaskTargets(tc.args).includes("deep-research")
11528
+ )
11529
+ ),
11530
+ [runtime.messages]
11531
+ );
11532
+ const hideTurnSummary = isSubAgentSession(runtime.session) || orchestratesDeepResearch;
11260
11533
  const browserState = runtime.state.browser;
11261
11534
  const browserAvailable = browserState !== null && browserState.status !== "closed" && !!sessionId;
11262
11535
  const browserVisible = browserAvailable && showBrowser;
@@ -11377,7 +11650,10 @@ function AgentChat({
11377
11650
  canShowBrowser: browserAvailable,
11378
11651
  canShowWorkspace: workspaceAvailable,
11379
11652
  viewMode: runtime.viewMode,
11380
- onViewModeChange: runtime.setViewMode
11653
+ onViewModeChange: runtime.setViewMode,
11654
+ deepResearchEnabled,
11655
+ researchSources: runtime.researchSources,
11656
+ hideTurnSummary
11381
11657
  }
11382
11658
  )
11383
11659
  }
@@ -11447,6 +11723,27 @@ function AgentChat({
11447
11723
  }
11448
11724
  );
11449
11725
  }
11726
+ function delegateTaskTargets(rawArgs) {
11727
+ if (!rawArgs) return [];
11728
+ let parsed;
11729
+ try {
11730
+ parsed = JSON.parse(rawArgs);
11731
+ } catch {
11732
+ return [];
11733
+ }
11734
+ if (!parsed || typeof parsed !== "object") return [];
11735
+ const out = [];
11736
+ const a = parsed;
11737
+ if (typeof a.agent_type === "string") out.push(a.agent_type);
11738
+ if (Array.isArray(a.goals)) {
11739
+ for (const g of a.goals) {
11740
+ if (g && typeof g === "object" && typeof g.agent_type === "string") {
11741
+ out.push(g.agent_type);
11742
+ }
11743
+ }
11744
+ }
11745
+ return out;
11746
+ }
11450
11747
  var DEFAULT_LIMIT = 50;
11451
11748
  function requireInboxAdapter(adapter) {
11452
11749
  if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
@@ -12182,7 +12479,7 @@ function groupMissionTasksByBucket(tasks) {
12182
12479
  function deriveMissionWorkerActivityLabel(worker) {
12183
12480
  const kind = worker.latestEventKind;
12184
12481
  if (kind === "tool.call" && worker.latestEventSummary) {
12185
- return `tool: ${truncate3(worker.latestEventSummary, 80)}`;
12482
+ return `tool: ${truncate4(worker.latestEventSummary, 80)}`;
12186
12483
  }
12187
12484
  if (kind === "llm.response") {
12188
12485
  return "thinking";
@@ -12191,11 +12488,11 @@ function deriveMissionWorkerActivityLabel(worker) {
12191
12488
  return "received tool result";
12192
12489
  }
12193
12490
  if (kind && worker.latestEventSummary) {
12194
- return `${kind}: ${truncate3(worker.latestEventSummary, 60)}`;
12491
+ return `${kind}: ${truncate4(worker.latestEventSummary, 60)}`;
12195
12492
  }
12196
12493
  return worker.sessionStatus || "active";
12197
12494
  }
12198
- function truncate3(s, max) {
12495
+ function truncate4(s, max) {
12199
12496
  if (s.length <= max) return s;
12200
12497
  return `${s.slice(0, max - 1)}\u2026`;
12201
12498
  }
@@ -13182,6 +13479,50 @@ function MissionsPanel({
13182
13479
  )) })
13183
13480
  ] });
13184
13481
  }
13482
+ var CITATION_RE = /\[(S\d+(?:\s*,\s*S\d+)*)\]/g;
13483
+ function splitCitations(text) {
13484
+ if (!text) return [];
13485
+ const segments = [];
13486
+ let lastIndex = 0;
13487
+ for (const match of text.matchAll(CITATION_RE)) {
13488
+ const start = match.index ?? 0;
13489
+ if (start > lastIndex) {
13490
+ segments.push({ kind: "text", value: text.slice(lastIndex, start) });
13491
+ }
13492
+ for (const id of match[1].split(",")) {
13493
+ segments.push({ kind: "cite", value: id.trim() });
13494
+ }
13495
+ lastIndex = start + match[0].length;
13496
+ }
13497
+ if (lastIndex < text.length) {
13498
+ segments.push({ kind: "text", value: text.slice(lastIndex) });
13499
+ }
13500
+ return segments;
13501
+ }
13502
+ function CitationText({
13503
+ text,
13504
+ sources,
13505
+ onCitationClick
13506
+ }) {
13507
+ const byId = new Map(sources.map((s) => [s.sourceId, s]));
13508
+ return /* @__PURE__ */ jsx(Fragment, { children: splitCitations(text).map((seg, i) => {
13509
+ if (seg.kind === "text") {
13510
+ return /* @__PURE__ */ jsx("span", { children: seg.value }, i);
13511
+ }
13512
+ const src = byId.get(seg.value);
13513
+ return /* @__PURE__ */ jsx(
13514
+ "button",
13515
+ {
13516
+ type: "button",
13517
+ title: src ? `${src.title} \u2014 ${src.url}` : seg.value,
13518
+ onClick: () => onCitationClick?.(seg.value),
13519
+ className: "mx-0.5 inline-flex items-center rounded-sm bg-muted px-1 text-[10px] font-semibold text-primary hover:bg-primary/10",
13520
+ children: seg.value
13521
+ },
13522
+ i
13523
+ );
13524
+ }) });
13525
+ }
13185
13526
  var DEFAULT_LIMIT2 = 50;
13186
13527
  var DEFAULT_POLL_INTERVAL_MS3 = 3e4;
13187
13528
  function fingerprint2(items) {
@@ -13548,7 +13889,7 @@ function mergeTreeNodes(groups) {
13548
13889
  }
13549
13890
  return Array.from(byId.values());
13550
13891
  }
13551
- function pruneDeletedSessionNodes(nodes, deletedSessionId) {
13892
+ function collectDeletedSessionIds(nodes, deletedSessionId) {
13552
13893
  const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
13553
13894
  let changed = true;
13554
13895
  while (changed) {
@@ -13560,7 +13901,7 @@ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
13560
13901
  }
13561
13902
  }
13562
13903
  }
13563
- return nodes.filter((node) => !deletedIds.has(node.id));
13904
+ return deletedIds;
13564
13905
  }
13565
13906
  function formatSessionTime(value) {
13566
13907
  const normalized = /[zZ]|[+-]\d{2}:?\d{2}$/.test(value) ? value : `${value}Z`;
@@ -13591,7 +13932,6 @@ function TreeNodeRow({
13591
13932
  activeSessionId,
13592
13933
  canStop,
13593
13934
  canDelete,
13594
- deletingSessionId,
13595
13935
  onSelect,
13596
13936
  onStop,
13597
13937
  onDelete
@@ -13659,18 +13999,14 @@ function TreeNodeRow({
13659
13999
  "button",
13660
14000
  {
13661
14001
  type: "button",
13662
- className: cn(
13663
- "p-2 md:p-1 rounded hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none transition-all",
13664
- deletingSessionId === entry.id ? "opacity-100" : "opacity-60 md:opacity-50 md:group-hover:opacity-100 md:focus-visible:opacity-100"
13665
- ),
14002
+ className: "p-2 md:p-1 rounded opacity-60 md:opacity-50 md:group-hover:opacity-100 md:focus-visible:opacity-100 hover:bg-destructive/10 hover:text-destructive transition-all",
13666
14003
  onClick: (e) => {
13667
14004
  e.stopPropagation();
13668
14005
  onDelete(entry.id);
13669
14006
  },
13670
14007
  "aria-label": "Delete session",
13671
14008
  title: "Delete session",
13672
- disabled: deletingSessionId === entry.id,
13673
- children: deletingSessionId === entry.id ? /* @__PURE__ */ jsx(Loader2Icon, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsx(Trash2Icon, { className: "w-3 h-3" })
14009
+ children: /* @__PURE__ */ jsx(Trash2Icon, { className: "w-3 h-3" })
13674
14010
  }
13675
14011
  )
13676
14012
  ]
@@ -13684,7 +14020,6 @@ function TreeNodeRow({
13684
14020
  activeSessionId,
13685
14021
  canStop,
13686
14022
  canDelete,
13687
- deletingSessionId,
13688
14023
  onSelect,
13689
14024
  onStop,
13690
14025
  onDelete
@@ -13709,10 +14044,8 @@ function SessionTreePanel({
13709
14044
  const [nodes, setNodes] = useState([]);
13710
14045
  const [error, setError] = useState(null);
13711
14046
  const [hasEverLoaded, setHasEverLoaded] = useState(false);
13712
- const [deletingSessionId, setDeletingSessionId] = useState(
13713
- null
13714
- );
13715
14047
  const mounted = useRef(true);
14048
+ const pendingDeleteIds = useRef(/* @__PURE__ */ new Set());
13716
14049
  const requestId = useRef(0);
13717
14050
  const lastFingerprint = useRef("");
13718
14051
  const resetContext = useRef(null);
@@ -13737,10 +14070,23 @@ function SessionTreePanel({
13737
14070
  sessionTreePromise
13738
14071
  ]);
13739
14072
  if (!mounted.current || currentRequestId !== requestId.current) return;
13740
- const nextNodes = mergeTreeNodes([
14073
+ let nextNodes = mergeTreeNodes([
13741
14074
  sessionList?.sessions.map(sessionToTreeNode) ?? [],
13742
14075
  sessionTree?.nodes ?? []
13743
14076
  ]);
14077
+ if (pendingDeleteIds.current.size > 0) {
14078
+ const stillReturned = /* @__PURE__ */ new Set();
14079
+ nextNodes = nextNodes.filter((node) => {
14080
+ if (!pendingDeleteIds.current.has(node.id)) return true;
14081
+ stillReturned.add(node.id);
14082
+ return false;
14083
+ });
14084
+ for (const pendingId of pendingDeleteIds.current) {
14085
+ if (!stillReturned.has(pendingId)) {
14086
+ pendingDeleteIds.current.delete(pendingId);
14087
+ }
14088
+ }
14089
+ }
13744
14090
  const fp = treeFingerprint(nextNodes);
13745
14091
  if (fp !== lastFingerprint.current) {
13746
14092
  lastFingerprint.current = fp;
@@ -13771,6 +14117,7 @@ function SessionTreePanel({
13771
14117
  setNodes([]);
13772
14118
  setHasEverLoaded(false);
13773
14119
  lastFingerprint.current = "";
14120
+ pendingDeleteIds.current.clear();
13774
14121
  }
13775
14122
  setError(null);
13776
14123
  void refetch();
@@ -13807,24 +14154,31 @@ function SessionTreePanel({
13807
14154
  );
13808
14155
  const handleDelete = useCallback(
13809
14156
  async (id) => {
13810
- if (!adapter.deleteSession || deletingSessionId) return;
13811
- setDeletingSessionId(id);
14157
+ if (!adapter.deleteSession) return;
14158
+ const deletedIds = collectDeletedSessionIds(nodes, id);
14159
+ for (const deletedId of deletedIds) {
14160
+ pendingDeleteIds.current.add(deletedId);
14161
+ }
14162
+ setNodes((current) => {
14163
+ const next = current.filter((node) => !deletedIds.has(node.id));
14164
+ lastFingerprint.current = treeFingerprint(next);
14165
+ return next;
14166
+ });
14167
+ onSessionDelete?.(id);
13812
14168
  try {
13813
14169
  await adapter.deleteSession({ sessionId: id });
13814
- setNodes((current) => {
13815
- const next = pruneDeletedSessionNodes(current, id);
13816
- lastFingerprint.current = treeFingerprint(next);
13817
- return next;
13818
- });
13819
- onSessionDelete?.(id);
13820
14170
  await refetch({ silent: true });
13821
14171
  } catch (e) {
13822
- setError(e instanceof Error ? e.message : "Failed to delete session");
13823
- } finally {
13824
- if (mounted.current) setDeletingSessionId(null);
14172
+ for (const deletedId of deletedIds) {
14173
+ pendingDeleteIds.current.delete(deletedId);
14174
+ }
14175
+ if (mounted.current) {
14176
+ setError(e instanceof Error ? e.message : "Failed to delete session");
14177
+ }
14178
+ await refetch({ silent: true });
13825
14179
  }
13826
14180
  },
13827
- [adapter, deletingSessionId, onSessionDelete, refetch]
14181
+ [adapter, nodes, onSessionDelete, refetch]
13828
14182
  );
13829
14183
  if (!hasEverLoaded) return null;
13830
14184
  if (nodes.length === 0) return null;
@@ -13845,7 +14199,6 @@ function SessionTreePanel({
13845
14199
  activeSessionId: activeSessionId ?? "",
13846
14200
  canStop: Boolean(adapter.stopSession),
13847
14201
  canDelete: Boolean(adapter.deleteSession),
13848
- deletingSessionId,
13849
14202
  onSelect: handleSelect,
13850
14203
  onStop: handleStop,
13851
14204
  onDelete: handleDelete
@@ -13855,6 +14208,6 @@ function SessionTreePanel({
13855
14208
  ] });
13856
14209
  }
13857
14210
 
13858
- export { ACTIVE_MISSION_STATUSES, AgentChat, AgentChatAdapterProvider, InboxPanel, MessageResponse, MissionDashboard, MissionsPanel, ScheduledWorkPanel, SessionTreePanel, deriveMissionWorkerActivityLabel, groupMissionTasksByBucket, isTerminalMissionStatus, missionTaskBucket, useAgentChatAdapterContext, useAgentChatRuntime, useChatViewMode, useInboxUnreadCount };
14211
+ export { ACTIVE_MISSION_STATUSES, AgentChat, AgentChatAdapterProvider, CitationText, InboxPanel, MessageResponse, MissionDashboard, MissionsPanel, ResearchSourcesPanel, ScheduledWorkPanel, SessionTreePanel, deriveMissionWorkerActivityLabel, groupMissionTasksByBucket, isTerminalMissionStatus, missionTaskBucket, splitCitations, useAgentChatAdapterContext, useAgentChatRuntime, useChatViewMode, useInboxUnreadCount };
13859
14212
  //# sourceMappingURL=index.js.map
13860
14213
  //# sourceMappingURL=index.js.map