@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.cjs CHANGED
@@ -2428,6 +2428,37 @@ function MCPToolBlock({ tc }) {
2428
2428
  summary && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 truncate text-xs", children: summary })
2429
2429
  ] });
2430
2430
  }
2431
+ function ResearchMemoryBlock({ tc }) {
2432
+ const args = parseArgs(tc.args) ?? {};
2433
+ const result = tc.result ? parseArgs(tc.result) ?? {} : {};
2434
+ let detail = "";
2435
+ if (args.action === "add") {
2436
+ const host = args.url ? hostname(args.url) : "";
2437
+ const sid = result.source_id ?? "";
2438
+ detail = sid ? `recorded ${sid}${host ? ` \xB7 ${host}` : ""}` : host ? `recorded \xB7 ${host}` : "recorded";
2439
+ } else if (args.action === "retrieve") {
2440
+ const n = result.sources?.length ?? 0;
2441
+ detail = args.query ? `${n} source${n === 1 ? "" : "s"} for "${truncate2(args.query, 40)}"` : `${n} source${n === 1 ? "" : "s"}`;
2442
+ } else if (args.action === "list") {
2443
+ const n = result.sources?.length ?? 0;
2444
+ detail = `${n} source${n === 1 ? "" : "s"}`;
2445
+ }
2446
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
2447
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BookOpenIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
2448
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Research" }),
2449
+ detail && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70 truncate text-xs", children: detail })
2450
+ ] });
2451
+ }
2452
+ function hostname(url) {
2453
+ try {
2454
+ return new URL(url).hostname.replace(/^www\./, "");
2455
+ } catch {
2456
+ return url;
2457
+ }
2458
+ }
2459
+ function truncate2(s, n) {
2460
+ return s.length > n ? `${s.slice(0, n)}\u2026` : s;
2461
+ }
2431
2462
  function VisionAnalyzeBlock({ tc }) {
2432
2463
  let image = "";
2433
2464
  try {
@@ -4143,21 +4174,15 @@ function ArtifactToolBlock({
4143
4174
  name && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground truncate", children: name })
4144
4175
  ] });
4145
4176
  }
4146
-
4147
- // src/components/chat/tools/delegate-tool.tsx
4148
4177
  init_utils();
4149
4178
  function firstLine(s) {
4150
4179
  const idx = s.indexOf("\n");
4151
4180
  return idx === -1 ? s : s.slice(0, idx);
4152
4181
  }
4153
4182
  function DelegateToolBlock({ tc }) {
4154
- const [expanded, setExpanded] = react.useState(false);
4155
4183
  const args = parseArgs(tc.args);
4156
- const goal = args?.goal ?? "";
4157
- const context = args?.context ?? "";
4184
+ const goal = firstLine(args?.goal ?? "").trim();
4158
4185
  const agentType = args?.agent_type;
4159
- const model = args?.model;
4160
- const summary = firstLine(goal).trim();
4161
4186
  let resultError = null;
4162
4187
  if (tc.result) {
4163
4188
  try {
@@ -4166,84 +4191,29 @@ function DelegateToolBlock({ tc }) {
4166
4191
  } catch {
4167
4192
  }
4168
4193
  }
4169
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4170
- /* @__PURE__ */ jsxRuntime.jsxs(
4171
- "button",
4194
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm py-0.5", children: [
4195
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranchIcon, { className: "size-3.5 text-muted-foreground/60 shrink-0" }),
4196
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: "Delegate" }),
4197
+ agentType && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 text-xs", children: [
4198
+ "\xB7 ",
4199
+ agentType
4200
+ ] }),
4201
+ goal && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 truncate text-xs italic", children: [
4202
+ "\xB7 \u201C",
4203
+ goal,
4204
+ "\u201D"
4205
+ ] }),
4206
+ resultError && /* @__PURE__ */ jsxRuntime.jsx(
4207
+ "span",
4172
4208
  {
4173
- type: "button",
4174
- onClick: () => setExpanded(!expanded),
4175
4209
  className: cn(
4176
- "flex w-full items-center gap-1.5 rounded-md px-2 py-1",
4177
- "text-sm text-muted-foreground hover:bg-muted/50 transition-colors"
4210
+ "shrink-0 text-xs",
4211
+ "text-destructive"
4178
4212
  ),
4179
- children: [
4180
- /* @__PURE__ */ jsxRuntime.jsx(
4181
- lucideReact.ChevronRightIcon,
4182
- {
4183
- className: cn(
4184
- "size-3 shrink-0 transition-transform duration-150",
4185
- expanded && "rotate-90"
4186
- )
4187
- }
4188
- ),
4189
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GitBranchIcon, { className: "size-3.5 shrink-0 text-muted-foreground" }),
4190
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground/80", children: "delegate_task" }),
4191
- agentType && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
4192
- "\xB7 ",
4193
- agentType
4194
- ] }),
4195
- summary && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground truncate min-w-0", children: [
4196
- "\xB7 ",
4197
- summary
4198
- ] })
4199
- ]
4213
+ title: resultError,
4214
+ children: "\xB7 failed"
4200
4215
  }
4201
- ),
4202
- expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-6 mt-0.5 space-y-1.5 text-sm font-mono", children: [
4203
- (agentType || model) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-x-3 gap-y-0.5 text-xs text-muted-foreground", children: [
4204
- agentType && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4205
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70", children: "agent:" }),
4206
- " ",
4207
- agentType
4208
- ] }),
4209
- model && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
4210
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70", children: "model:" }),
4211
- " ",
4212
- model
4213
- ] })
4214
- ] }),
4215
- goal && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded bg-muted/40 px-2 py-1", children: [
4216
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground/70", children: "Goal" }),
4217
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-foreground/90", children: goal })
4218
- ] }),
4219
- context && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded bg-muted/40 px-2 py-1", children: [
4220
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground/70", children: "Context" }),
4221
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-muted-foreground", children: truncate(context, 2e3) })
4222
- ] }),
4223
- tc.status === "running" && !tc.result && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground italic", children: "Sub-agent running\u2026" }),
4224
- tc.result && /* @__PURE__ */ jsxRuntime.jsxs(
4225
- "div",
4226
- {
4227
- className: cn(
4228
- "rounded px-2 py-1 max-h-64 overflow-y-auto",
4229
- resultError ? "bg-destructive/10" : "bg-muted/40"
4230
- ),
4231
- children: [
4232
- /* @__PURE__ */ jsxRuntime.jsx(
4233
- "div",
4234
- {
4235
- className: cn(
4236
- "text-[10px] uppercase tracking-wide",
4237
- resultError ? "text-destructive/80" : "text-emerald-600 dark:text-emerald-400"
4238
- ),
4239
- children: resultError ? "Error" : "Sub-agent response"
4240
- }
4241
- ),
4242
- /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-0.5 whitespace-pre-wrap break-words text-foreground/90", children: truncate(resultError ?? tc.result, 4e3) })
4243
- ]
4244
- }
4245
- )
4246
- ] })
4216
+ )
4247
4217
  ] });
4248
4218
  }
4249
4219
  init_utils();
@@ -4473,6 +4443,19 @@ function DefaultToolBlock({ tc }) {
4473
4443
  ] })
4474
4444
  ] });
4475
4445
  }
4446
+ function ResearchOutlineBlock({ tc }) {
4447
+ const args = parseArgs(tc.args) ?? {};
4448
+ const result = tc.result ? parseArgs(tc.result) ?? {} : {};
4449
+ const outline = args.action === "set" ? args.outline ?? "" : result.outline ?? "";
4450
+ const sections = result.sections ?? [];
4451
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-sm border border-border bg-muted/40 p-2 text-xs", children: [
4452
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 font-semibold uppercase tracking-widest text-muted-foreground", children: [
4453
+ "Research outline",
4454
+ sections.length ? ` \xB7 ${sections.length} sections` : ""
4455
+ ] }),
4456
+ outline ? /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[11px] leading-snug", children: outline }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "updated" })
4457
+ ] });
4458
+ }
4476
4459
  function ToolCallBlock({
4477
4460
  tc,
4478
4461
  resolvedArtifactName,
@@ -4525,6 +4508,10 @@ function ToolCallBlock({
4525
4508
  return /* @__PURE__ */ jsxRuntime.jsx(DelegateToolBlock, { tc });
4526
4509
  case "memory":
4527
4510
  return /* @__PURE__ */ jsxRuntime.jsx(MemoryToolBlock, { tc });
4511
+ case "research_outline":
4512
+ return /* @__PURE__ */ jsxRuntime.jsx(ResearchOutlineBlock, { tc });
4513
+ case "research_memory":
4514
+ return /* @__PURE__ */ jsxRuntime.jsx(ResearchMemoryBlock, { tc });
4528
4515
  case "spawn_worker":
4529
4516
  case "send_worker_message":
4530
4517
  case "stop_worker":
@@ -4578,12 +4565,14 @@ function WebSearchGroupBlock({ tcs }) {
4578
4565
  /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-1 ml-5 space-y-0.5", children: tcs.map((tc) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(WebSearchRow, { tc }) }, tc.id)) })
4579
4566
  ] });
4580
4567
  }
4568
+ init_utils();
4569
+ var COLLAPSED_MAX_HEIGHT_PX = 160;
4581
4570
  var ChatMessage = react.memo(function ChatMessage2({
4582
4571
  message,
4583
4572
  onFileSelect
4584
4573
  }) {
4585
4574
  return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "user", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
4586
- message.content,
4575
+ /* @__PURE__ */ jsxRuntime.jsx(CollapsibleMessageBody, { content: message.content }),
4587
4576
  message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx(
4588
4577
  "img",
4589
4578
  {
@@ -4603,6 +4592,48 @@ var ChatMessage = react.memo(function ChatMessage2({
4603
4592
  )) })
4604
4593
  ] }) });
4605
4594
  });
4595
+ function CollapsibleMessageBody({ content }) {
4596
+ const ref = react.useRef(null);
4597
+ const [overflowing, setOverflowing] = react.useState(false);
4598
+ const [expanded, setExpanded] = react.useState(false);
4599
+ react.useLayoutEffect(() => {
4600
+ const el = ref.current;
4601
+ if (!el) return;
4602
+ const measure = () => {
4603
+ setOverflowing(el.scrollHeight > el.clientHeight + 1);
4604
+ };
4605
+ measure();
4606
+ const observer = new ResizeObserver(measure);
4607
+ observer.observe(el);
4608
+ return () => observer.disconnect();
4609
+ }, [content]);
4610
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
4611
+ /* @__PURE__ */ jsxRuntime.jsx(
4612
+ "div",
4613
+ {
4614
+ ref,
4615
+ className: cn(
4616
+ "whitespace-pre-wrap wrap-break-word",
4617
+ !expanded && "overflow-hidden"
4618
+ ),
4619
+ style: !expanded ? { maxHeight: COLLAPSED_MAX_HEIGHT_PX } : void 0,
4620
+ children: content
4621
+ }
4622
+ ),
4623
+ (overflowing || expanded) && /* @__PURE__ */ jsxRuntime.jsx(
4624
+ "button",
4625
+ {
4626
+ type: "button",
4627
+ onClick: () => setExpanded((prev) => !prev),
4628
+ className: cn(
4629
+ "mt-2 text-xs font-bold text-primary",
4630
+ "hover:underline focus:outline-none focus-visible:underline"
4631
+ ),
4632
+ children: expanded ? "Show less..." : "Show more..."
4633
+ }
4634
+ )
4635
+ ] });
4636
+ }
4606
4637
  function iconForMime(mime) {
4607
4638
  if (!mime) return lucideReact.File;
4608
4639
  if (mime.startsWith("image/")) return lucideReact.FileImage;
@@ -6073,6 +6104,7 @@ function ChatComposerInner({
6073
6104
  onToggleWorkspace,
6074
6105
  canShowBrowser = false,
6075
6106
  canShowWorkspace = false,
6107
+ deepResearchEnabled = false,
6076
6108
  viewMode = "simple",
6077
6109
  onViewModeChange
6078
6110
  }) {
@@ -6091,10 +6123,10 @@ function ChatComposerInner({
6091
6123
  }
6092
6124
  }, [buttonMenuOpen, menuDismissed, textInput.value]);
6093
6125
  react.useEffect(() => {
6094
- if (textInput.value.startsWith("/")) {
6126
+ if (!buttonMenuOpen && textInput.value.startsWith("/")) {
6095
6127
  setMenuMode("all");
6096
6128
  }
6097
- }, [textInput.value]);
6129
+ }, [buttonMenuOpen, textInput.value]);
6098
6130
  react.useEffect(() => {
6099
6131
  if (showSlashMenu && (menuMode === "skills" || menuMode === "all")) {
6100
6132
  adapter.listSlashCommands?.().then(setAdapterCommands).catch(() => {
@@ -6102,24 +6134,34 @@ function ChatComposerInner({
6102
6134
  }
6103
6135
  }, [adapter, showSlashMenu, menuMode]);
6104
6136
  const builtinCommands = react.useMemo(
6105
- () => [
6106
- { value: "/clear", label: "/clear", description: "Clear conversation" },
6107
- { value: "/compress", label: "/compress", description: "Compress context" },
6108
- { value: "/goal", label: "/goal", description: "Define an outcome goal" },
6109
- { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
6110
- { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
6111
- { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
6112
- { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
6113
- { value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
6114
- { value: "/mission status", label: "/mission status", description: "Show current mission status" },
6115
- { value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
6116
- { value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
6117
- { value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
6118
- { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
6119
- { value: "/loop list", label: "/loop list", description: "List active loops" },
6120
- { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
6121
- ],
6122
- []
6137
+ () => {
6138
+ const base = [
6139
+ { value: "/clear", label: "/clear", description: "Clear conversation" },
6140
+ { value: "/compress", label: "/compress", description: "Compress context" },
6141
+ { value: "/goal", label: "/goal", description: "Define an outcome goal" },
6142
+ { value: "/goal status", label: "/goal status", description: "Show outcome goal status" },
6143
+ { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
6144
+ { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
6145
+ { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
6146
+ { value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
6147
+ { value: "/mission status", label: "/mission status", description: "Show current mission status" },
6148
+ { value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
6149
+ { value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
6150
+ { value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
6151
+ { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
6152
+ { value: "/loop list", label: "/loop list", description: "List active loops" },
6153
+ { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
6154
+ ];
6155
+ if (deepResearchEnabled) {
6156
+ base.push({
6157
+ value: "/deep-research ",
6158
+ label: "/deep-research",
6159
+ description: "Delegate a topic to the deep-research workflow"
6160
+ });
6161
+ }
6162
+ return base;
6163
+ },
6164
+ [deepResearchEnabled]
6123
6165
  );
6124
6166
  const scheduledExamples = react.useMemo(
6125
6167
  () => [
@@ -6256,7 +6298,7 @@ function ChatComposerInner({
6256
6298
  },
6257
6299
  children: [
6258
6300
  /* @__PURE__ */ jsxRuntime.jsx(AttachmentPreviewStrip, {}),
6259
- viewMode === "expert" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 px-1 pb-2", children: [
6301
+ viewMode === "expert" && !disabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 px-1 pb-2", children: [
6260
6302
  /* @__PURE__ */ jsxRuntime.jsxs(
6261
6303
  Button,
6262
6304
  {
@@ -6331,7 +6373,7 @@ function ChatComposerInner({
6331
6373
  ) }),
6332
6374
  /* @__PURE__ */ jsxRuntime.jsxs(PromptInputFooter, { children: [
6333
6375
  /* @__PURE__ */ jsxRuntime.jsxs(PromptInputTools, { children: [
6334
- /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: addMenuOpen, onOpenChange: setAddMenuOpen, children: [
6376
+ !disabled && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open: addMenuOpen, onOpenChange: setAddMenuOpen, children: [
6335
6377
  /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(PromptInputButton, { "aria-label": "Add", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "size-4" }) }) }),
6336
6378
  /* @__PURE__ */ jsxRuntime.jsx(
6337
6379
  PopoverContent,
@@ -6403,7 +6445,7 @@ function ChatComposerInner({
6403
6445
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderIcon, { className: "size-4" })
6404
6446
  }
6405
6447
  ),
6406
- tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
6448
+ !disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
6407
6449
  Context,
6408
6450
  {
6409
6451
  usedTokens: tokenUsage.totalTokens,
@@ -6491,7 +6533,7 @@ function ChatComposerInner({
6491
6533
  ]
6492
6534
  }
6493
6535
  ),
6494
- /* @__PURE__ */ jsxRuntime.jsx(
6536
+ !disabled && /* @__PURE__ */ jsxRuntime.jsx(
6495
6537
  PromptInputSubmit,
6496
6538
  {
6497
6539
  status,
@@ -6629,6 +6671,83 @@ function ChatComposerInner({
6629
6671
  );
6630
6672
  }
6631
6673
 
6674
+ // src/components/research/research-sources-panel.tsx
6675
+ init_utils();
6676
+ function ResearchSourcesPanel({
6677
+ sources
6678
+ }) {
6679
+ const [expanded, setExpanded] = react.useState(false);
6680
+ const rootRef = react.useRef(null);
6681
+ react.useEffect(() => {
6682
+ const root = rootRef.current;
6683
+ if (!root) return;
6684
+ const handler = (event) => {
6685
+ const target = event.target;
6686
+ if (target?.closest('[id^="source-"]')) {
6687
+ setExpanded(true);
6688
+ }
6689
+ };
6690
+ document.addEventListener("scroll-to-source", handler, true);
6691
+ return () => document.removeEventListener("scroll-to-source", handler, true);
6692
+ }, []);
6693
+ if (sources.length === 0) return null;
6694
+ return /* @__PURE__ */ jsxRuntime.jsxs(
6695
+ "div",
6696
+ {
6697
+ ref: rootRef,
6698
+ className: "rounded-md border border-line bg-card",
6699
+ children: [
6700
+ /* @__PURE__ */ jsxRuntime.jsxs(
6701
+ "button",
6702
+ {
6703
+ type: "button",
6704
+ onClick: () => setExpanded((prev) => !prev),
6705
+ "aria-expanded": expanded,
6706
+ className: "flex w-full items-center gap-1.5 px-2 py-1.5 text-left hover:bg-muted/40",
6707
+ children: [
6708
+ /* @__PURE__ */ jsxRuntime.jsx(
6709
+ lucideReact.ChevronRightIcon,
6710
+ {
6711
+ className: cn(
6712
+ "size-3 shrink-0 text-muted-foreground transition-transform duration-150",
6713
+ expanded && "rotate-90"
6714
+ )
6715
+ }
6716
+ ),
6717
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-muted-foreground", children: [
6718
+ "Sources \xB7 ",
6719
+ sources.length
6720
+ ] })
6721
+ ]
6722
+ }
6723
+ ),
6724
+ expanded && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-35 overflow-y-auto border-t border-line py-1", children: sources.map((s) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
6725
+ "a",
6726
+ {
6727
+ id: `source-${s.sourceId}`,
6728
+ href: s.url,
6729
+ target: "_blank",
6730
+ rel: "noreferrer",
6731
+ className: "group flex items-baseline gap-2 px-2 py-1 hover:bg-muted/60",
6732
+ children: [
6733
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-[10px] font-semibold text-primary tabular-nums", children: s.sourceId }),
6734
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-foreground group-hover:underline", children: s.title || s.url }),
6735
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 truncate text-[10px] text-muted-foreground max-w-[40%]", children: hostname2(s.url) })
6736
+ ]
6737
+ }
6738
+ ) }, s.sourceId)) })
6739
+ ]
6740
+ }
6741
+ );
6742
+ }
6743
+ function hostname2(url) {
6744
+ try {
6745
+ return new URL(url).hostname.replace(/^www\./, "");
6746
+ } catch {
6747
+ return url;
6748
+ }
6749
+ }
6750
+
6632
6751
  // src/components/chat/turn-feedback.tsx
6633
6752
  init_utils();
6634
6753
  var MAX_REASON_LENGTH2 = 500;
@@ -7006,8 +7125,23 @@ var ArtifactContent = ({
7006
7125
  className,
7007
7126
  ...props
7008
7127
  }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex-1 overflow-auto p-4", className), ...props });
7009
- function ArtifactMarkdown({ spec }) {
7010
- return /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: spec.content ?? "" });
7128
+
7129
+ // src/components/chat/artifacts/artifact-markdown.tsx
7130
+ init_utils();
7131
+ function ArtifactMarkdown({
7132
+ spec,
7133
+ fill = false
7134
+ }) {
7135
+ return /* @__PURE__ */ jsxRuntime.jsx(
7136
+ "div",
7137
+ {
7138
+ className: cn(
7139
+ "px-4 py-3",
7140
+ !fill && "max-h-112 overflow-y-auto"
7141
+ ),
7142
+ children: /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: spec.content ?? "" })
7143
+ }
7144
+ );
7011
7145
  }
7012
7146
  function formatCell(value) {
7013
7147
  if (value == null) return "";
@@ -7380,7 +7514,7 @@ function ArtifactBody({
7380
7514
  }
7381
7515
  switch (payload.kind) {
7382
7516
  case "markdown":
7383
- return /* @__PURE__ */ jsxRuntime.jsx(ArtifactMarkdown, { spec: payload.spec });
7517
+ return /* @__PURE__ */ jsxRuntime.jsx(ArtifactMarkdown, { spec: payload.spec, fill });
7384
7518
  case "table":
7385
7519
  return /* @__PURE__ */ jsxRuntime.jsx(ArtifactTable, { spec: payload.spec });
7386
7520
  case "chart":
@@ -8281,17 +8415,15 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
8281
8415
  }];
8282
8416
  }
8283
8417
  if (msg.systemKind === "artifact") {
8284
- const { artifactId, name, kind, version } = unpackArtifactMeta(
8285
- msg.systemMeta,
8286
- msg.content
8287
- );
8418
+ const { artifactId, name, kind, version, originatingSessionId } = unpackArtifactMeta(msg.systemMeta, msg.content);
8288
8419
  return [{
8289
8420
  kind: "artifact",
8290
8421
  key: msg.id,
8291
8422
  artifactId,
8292
8423
  name,
8293
8424
  artifactKind: kind,
8294
- version
8425
+ version,
8426
+ originatingSessionId
8295
8427
  }];
8296
8428
  }
8297
8429
  if (msg.systemKind === "browser_marker" || msg.systemKind === "browser_marker_warning") {
@@ -8463,7 +8595,8 @@ function unpackArtifactMeta(systemMeta, fallbackName) {
8463
8595
  artifactId: meta.artifact_id ?? "",
8464
8596
  name: meta.name ?? fallbackName,
8465
8597
  kind: meta.kind ?? "markdown",
8466
- version: meta.version ?? 1
8598
+ version: meta.version ?? 1,
8599
+ originatingSessionId: meta.originating_session_id ?? null
8467
8600
  };
8468
8601
  }
8469
8602
  function OrphanSystemMarker({
@@ -8489,7 +8622,7 @@ function OrphanSystemMarker({
8489
8622
  return /* @__PURE__ */ jsxRuntime.jsx(
8490
8623
  ArtifactBlock,
8491
8624
  {
8492
- sessionId,
8625
+ sessionId: unpacked.originatingSessionId ?? sessionId,
8493
8626
  artifactId: unpacked.artifactId,
8494
8627
  name: unpacked.name,
8495
8628
  kind: unpacked.kind,
@@ -8522,25 +8655,32 @@ function OrphanSystemMarker({
8522
8655
  function cancelledToolLabel(toolName) {
8523
8656
  const map = {
8524
8657
  terminal: "Command",
8525
- execute_code: "Execute Code",
8658
+ execute_code: "Execute code",
8526
8659
  read_file: "Read",
8527
8660
  write_file: "Write",
8528
8661
  patch: "Patch",
8529
- search_files: "Search Files",
8530
- list_files: "List Files",
8531
- web_search: "Web Search",
8532
- web_extract: "Web Fetch",
8533
- web_crawl: "Web Crawl",
8534
- session_search: "Session Search",
8662
+ search_files: "Search files",
8663
+ list_files: "List files",
8664
+ web_search: "Web search",
8665
+ web_extract: "Web fetch",
8666
+ web_crawl: "Web crawl",
8667
+ session_search: "Session search",
8535
8668
  memory: "Memory",
8536
8669
  todo: "Todo",
8537
8670
  skills_list: "Skills",
8538
8671
  skill_view: "Skill",
8539
8672
  consult_expert: "Expert",
8540
- delegate_task: "Delegate",
8673
+ delegate_task: "Delegate task",
8541
8674
  ask_user_question: "Ask User Question",
8542
8675
  process: "Process",
8543
- create_artifact: "Artifact"
8676
+ create_artifact: "Create artifact",
8677
+ // ``Research memory`` reads as a noun ("memory of research") and
8678
+ // makes the shimmer awkward ("Running Research memory…"). The
8679
+ // one-liner renderer shows just ``Research`` as the label, so
8680
+ // mirror that here so the live shimmer matches the row that
8681
+ // replaces it on completion.
8682
+ research_memory: "Research",
8683
+ research_outline: "Research"
8544
8684
  };
8545
8685
  return map[toolName] ?? toolName;
8546
8686
  }
@@ -8669,7 +8809,7 @@ function TimelineEntryItem({
8669
8809
  /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: sessionId ? /* @__PURE__ */ jsxRuntime.jsx(
8670
8810
  ArtifactBlock,
8671
8811
  {
8672
- sessionId,
8812
+ sessionId: entry.originatingSessionId ?? sessionId,
8673
8813
  artifactId: entry.artifactId,
8674
8814
  name: entry.name,
8675
8815
  kind: entry.artifactKind,
@@ -8801,17 +8941,24 @@ function extractToolDetail(tc) {
8801
8941
  }
8802
8942
  case "terminal": {
8803
8943
  const cmd = stringArg("command");
8804
- return cmd ? truncate2(cmd, 40) : null;
8944
+ return cmd ? truncate3(cmd, 40) : null;
8805
8945
  }
8806
8946
  case "execute_code": {
8807
8947
  const code3 = stringArg("code");
8808
- return code3 ? truncate2(code3.split("\n")[0] ?? "", 40) : null;
8948
+ return code3 ? truncate3(code3.split("\n")[0] ?? "", 40) : null;
8809
8949
  }
8810
8950
  case "web_search":
8811
8951
  case "web_crawl":
8812
8952
  return stringArg("query");
8813
- case "web_extract":
8814
- return stringArg("url");
8953
+ case "web_extract": {
8954
+ const url = stringArg("url");
8955
+ if (!url) return null;
8956
+ try {
8957
+ return new URL(url).hostname.replace(/^www\./, "");
8958
+ } catch {
8959
+ return truncate3(url, 40);
8960
+ }
8961
+ }
8815
8962
  case "skill_view":
8816
8963
  case "skill_manage":
8817
8964
  return stringArg("name") ?? stringArg("skill");
@@ -8821,6 +8968,40 @@ function extractToolDetail(tc) {
8821
8968
  return stringArg("name") ?? stringArg("task") ?? stringArg("title");
8822
8969
  case "memory":
8823
8970
  return stringArg("action") ?? stringArg("key");
8971
+ case "research_memory": {
8972
+ const action = stringArg("action");
8973
+ if (action === "add") {
8974
+ const title = stringArg("title");
8975
+ if (title) return truncate3(title, 60);
8976
+ const url = stringArg("url");
8977
+ if (url) {
8978
+ try {
8979
+ return new URL(url).hostname.replace(/^www\./, "");
8980
+ } catch {
8981
+ return truncate3(url, 40);
8982
+ }
8983
+ }
8984
+ return null;
8985
+ }
8986
+ if (action === "retrieve") {
8987
+ return stringArg("query");
8988
+ }
8989
+ return action;
8990
+ }
8991
+ case "research_outline": {
8992
+ const action = stringArg("action");
8993
+ if (action === "set") {
8994
+ const outline = stringArg("outline");
8995
+ if (outline) {
8996
+ const sections = outline.split(/\r?\n/).filter((line) => /^#{2,6}\s+\S/.test(line)).length;
8997
+ if (sections > 0) {
8998
+ return `${sections} ${sections === 1 ? "section" : "sections"}`;
8999
+ }
9000
+ }
9001
+ return "outline";
9002
+ }
9003
+ return action;
9004
+ }
8824
9005
  default:
8825
9006
  return null;
8826
9007
  }
@@ -8830,7 +9011,7 @@ function lastPathSegment(path) {
8830
9011
  const idx = cleaned.lastIndexOf("/");
8831
9012
  return idx >= 0 ? cleaned.slice(idx + 1) : cleaned;
8832
9013
  }
8833
- function truncate2(s, max) {
9014
+ function truncate3(s, max) {
8834
9015
  return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
8835
9016
  }
8836
9017
  var _SIMPLE_MODE_HIDDEN_TOOLS = /* @__PURE__ */ new Set([
@@ -8906,7 +9087,9 @@ var _TOOL_ROW_ICON = {
8906
9087
  delegate_task: lucideReact.ArrowRight,
8907
9088
  create_artifact: lucideReact.FileTextIcon,
8908
9089
  memory: lucideReact.PenLineIcon,
8909
- todo: lucideReact.ListIcon
9090
+ todo: lucideReact.ListIcon,
9091
+ research_memory: lucideReact.BookOpenIcon,
9092
+ research_outline: lucideReact.ListIcon
8910
9093
  };
8911
9094
  function _toolRowIcon(toolName) {
8912
9095
  return _TOOL_ROW_ICON[toolName] ?? lucideReact.WrenchIcon;
@@ -8952,6 +9135,31 @@ function _toolRowLabel(tc) {
8952
9135
  return detail ? `Memory ${detail}` : "Updated memory";
8953
9136
  case "todo":
8954
9137
  return detail ? `Todo ${detail}` : "Updated todo list";
9138
+ case "research_memory": {
9139
+ const args = parseArgs(tc.args);
9140
+ const action = args?.action;
9141
+ if (action === "add") {
9142
+ return detail ? `Stored source "${detail}"` : "Stored a source";
9143
+ }
9144
+ if (action === "retrieve") {
9145
+ return detail ? `Retrieved sources for "${detail}"` : "Retrieved sources";
9146
+ }
9147
+ if (action === "list") {
9148
+ return "Listed sources";
9149
+ }
9150
+ return "Updated research memory";
9151
+ }
9152
+ case "research_outline": {
9153
+ const args = parseArgs(tc.args);
9154
+ const action = args?.action;
9155
+ if (action === "set") {
9156
+ return detail ? `Updated outline (${detail})` : "Updated outline";
9157
+ }
9158
+ if (action === "get") {
9159
+ return "Read outline";
9160
+ }
9161
+ return "Touched outline";
9162
+ }
8955
9163
  default:
8956
9164
  return cancelledToolLabel(tc.toolName);
8957
9165
  }
@@ -9125,7 +9333,8 @@ function SimpleAssistantGroup({
9125
9333
  sessionId,
9126
9334
  artifactFallbacks,
9127
9335
  onFileSelect,
9128
- onRetry
9336
+ onRetry,
9337
+ hideTurnSummary = false
9129
9338
  }) {
9130
9339
  const assistantMessages = messages.filter((m) => m.role === "assistant");
9131
9340
  const tail = assistantMessages[assistantMessages.length - 1];
@@ -9193,7 +9402,7 @@ function SimpleAssistantGroup({
9193
9402
  tail
9194
9403
  }
9195
9404
  ),
9196
- effectiveTurnSummary && /* @__PURE__ */ jsxRuntime.jsx(
9405
+ effectiveTurnSummary && !hideTurnSummary && /* @__PURE__ */ jsxRuntime.jsx(
9197
9406
  TurnSummaryCard,
9198
9407
  {
9199
9408
  summary: effectiveTurnSummary,
@@ -9214,7 +9423,8 @@ function AssistantGroup({
9214
9423
  artifactFallbacks,
9215
9424
  onFileSelect,
9216
9425
  onRetry,
9217
- viewMode = "simple"
9426
+ viewMode = "simple",
9427
+ hideTurnSummary = false
9218
9428
  }) {
9219
9429
  if (viewMode === "simple") {
9220
9430
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -9225,7 +9435,8 @@ function AssistantGroup({
9225
9435
  sessionId,
9226
9436
  artifactFallbacks,
9227
9437
  onFileSelect,
9228
- onRetry
9438
+ onRetry,
9439
+ hideTurnSummary
9229
9440
  }
9230
9441
  );
9231
9442
  }
@@ -9331,7 +9542,10 @@ function ChatThread({
9331
9542
  canShowBrowser = false,
9332
9543
  canShowWorkspace = false,
9333
9544
  viewMode = "simple",
9334
- onViewModeChange
9545
+ onViewModeChange,
9546
+ deepResearchEnabled = false,
9547
+ researchSources = [],
9548
+ hideTurnSummary = false
9335
9549
  }) {
9336
9550
  const groups = react.useMemo(() => groupMessages(messages), [messages]);
9337
9551
  const awaitingInput = react.useMemo(() => isAwaitingUserInput(messages), [messages]);
@@ -9429,7 +9643,8 @@ function ChatThread({
9429
9643
  artifactFallbacks,
9430
9644
  onFileSelect,
9431
9645
  onRetry: groupRetry,
9432
- viewMode
9646
+ viewMode,
9647
+ hideTurnSummary
9433
9648
  },
9434
9649
  group.messages[0].id
9435
9650
  );
@@ -9440,14 +9655,8 @@ function ChatThread({
9440
9655
  ] }),
9441
9656
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-3 sm:px-6 pb-3 sm:pb-5 pt-3", children: [
9442
9657
  retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
9443
- composerDisabled && composerDisabledReason ? /* @__PURE__ */ jsxRuntime.jsx(
9444
- "div",
9445
- {
9446
- className: "rounded border border-line bg-muted/40 px-3 py-2 text-sm text-foreground/60",
9447
- role: "status",
9448
- children: composerDisabledReason
9449
- }
9450
- ) : /* @__PURE__ */ jsxRuntime.jsx(
9658
+ researchSources.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(ResearchSourcesPanel, { sources: researchSources }) }),
9659
+ /* @__PURE__ */ jsxRuntime.jsx(
9451
9660
  ChatComposer,
9452
9661
  {
9453
9662
  onSend,
@@ -9464,7 +9673,8 @@ function ChatThread({
9464
9673
  canShowBrowser,
9465
9674
  canShowWorkspace,
9466
9675
  viewMode,
9467
- onViewModeChange
9676
+ onViewModeChange,
9677
+ deepResearchEnabled
9468
9678
  }
9469
9679
  )
9470
9680
  ] })
@@ -10147,6 +10357,29 @@ function isScheduledRunSession(session) {
10147
10357
  session?.channel === "scheduled" || session?.config?.scheduled_session_id
10148
10358
  );
10149
10359
  }
10360
+ var _SUB_AGENT_CHANNELS = /* @__PURE__ */ new Set([
10361
+ "delegation",
10362
+ "worker",
10363
+ "task"
10364
+ ]);
10365
+ function isSubAgentSession(session) {
10366
+ if (!session) return false;
10367
+ if (session.parentId) return true;
10368
+ if (session.channel && _SUB_AGENT_CHANNELS.has(session.channel)) return true;
10369
+ return false;
10370
+ }
10371
+ function readOnlyReasonForSession(session) {
10372
+ if (isScheduledRunSession(session)) {
10373
+ return { readOnly: true, reason: "Scheduled run is read-only" };
10374
+ }
10375
+ if (isSubAgentSession(session)) {
10376
+ return {
10377
+ readOnly: true,
10378
+ reason: "Sub-task started by parent session \u2014 read-only"
10379
+ };
10380
+ }
10381
+ return { readOnly: false };
10382
+ }
10150
10383
 
10151
10384
  // src/runtime/events.ts
10152
10385
  var WORKSPACE_MUTATING_TOOLS = /* @__PURE__ */ new Set([
@@ -10216,7 +10449,8 @@ function createInitialAgentChatState(options = {}) {
10216
10449
  terminal: false,
10217
10450
  workspaceRefreshKey: 0,
10218
10451
  browser: null,
10219
- viewMode: options.viewMode ?? "simple"
10452
+ viewMode: options.viewMode ?? "simple",
10453
+ researchSources: []
10220
10454
  };
10221
10455
  }
10222
10456
  function applyAgentChatEvent(state, event) {
@@ -10268,7 +10502,13 @@ function applyAgentChatEvent(state, event) {
10268
10502
  name: event.data.name,
10269
10503
  kind: event.data.kind,
10270
10504
  version: event.data.version,
10271
- size: event.data.size
10505
+ size: event.data.size,
10506
+ // Propagated artifacts (a delegated child's report
10507
+ // surfacing on the parent's chat thread) carry this
10508
+ // field; without it the ArtifactBlock fetches via the
10509
+ // chat's own session id and 404s because the spec lives
10510
+ // under the writer's S3 prefix, not the parent's.
10511
+ originating_session_id: event.data.originating_session_id
10272
10512
  }
10273
10513
  }
10274
10514
  ]);
@@ -10302,11 +10542,12 @@ function applyAgentChatEvent(state, event) {
10302
10542
  const toolName = findToolNameById(nextState.messages, toolCallId);
10303
10543
  const messages = applyToolResult(nextState.messages, event.data);
10304
10544
  const mutatesWorkspace = toolName !== null && WORKSPACE_MUTATING_TOOLS.has(toolName);
10305
- return {
10545
+ const withResult = {
10306
10546
  ...nextState,
10307
10547
  messages,
10308
10548
  workspaceRefreshKey: mutatesWorkspace ? nextState.workspaceRefreshKey + 1 : nextState.workspaceRefreshKey
10309
10549
  };
10550
+ return collectResearchSource(withResult, toolName, event.data);
10310
10551
  }
10311
10552
  case "harness.wake":
10312
10553
  case "llm.request":
@@ -10654,7 +10895,7 @@ function applyLlmThinking(state, event) {
10654
10895
  } else {
10655
10896
  messages[idx] = {
10656
10897
  ...prev,
10657
- reasoning: (prev.reasoning ?? "") + reasoningText,
10898
+ reasoning: appendText(prev.reasoning, reasoningText),
10658
10899
  turnId: turnId ?? prev.turnId,
10659
10900
  iterationIndex: iterationIndex ?? prev.iterationIndex
10660
10901
  };
@@ -11038,6 +11279,32 @@ function findToolNameById(messages, toolCallId) {
11038
11279
  }
11039
11280
  return null;
11040
11281
  }
11282
+ function collectResearchSource(state, toolName, data) {
11283
+ if (toolName !== "research_memory") return state;
11284
+ const rawResult = data.content ?? data.result;
11285
+ const resultText = typeof rawResult === "string" ? rawResult : JSON.stringify(rawResult ?? {});
11286
+ let parsed;
11287
+ try {
11288
+ parsed = JSON.parse(resultText);
11289
+ } catch {
11290
+ return state;
11291
+ }
11292
+ if (!parsed.success || !parsed.source_id || !parsed.url) return state;
11293
+ if (state.researchSources.some((s) => s.sourceId === parsed.source_id)) {
11294
+ return state;
11295
+ }
11296
+ return {
11297
+ ...state,
11298
+ researchSources: [
11299
+ ...state.researchSources,
11300
+ {
11301
+ sourceId: parsed.source_id,
11302
+ url: parsed.url,
11303
+ title: parsed.title ?? ""
11304
+ }
11305
+ ]
11306
+ };
11307
+ }
11041
11308
  function findLatestConsultExpertCall(messages) {
11042
11309
  for (let i = messages.length - 1; i >= 0; i--) {
11043
11310
  const msg = messages[i];
@@ -11487,7 +11754,8 @@ function useAgentChatRuntime({
11487
11754
  markSending,
11488
11755
  markSendError,
11489
11756
  viewMode: state.viewMode,
11490
- setViewMode
11757
+ setViewMode,
11758
+ researchSources: state.researchSources
11491
11759
  };
11492
11760
  }
11493
11761
  function parseEventData(data) {
@@ -11546,7 +11814,8 @@ function AgentChat({
11546
11814
  onFileSelect,
11547
11815
  onMessagesChange,
11548
11816
  disabled,
11549
- onComposerError
11817
+ onComposerError,
11818
+ deepResearchEnabled = false
11550
11819
  }) {
11551
11820
  const [workspacePath, setWorkspacePath] = react.useState(null);
11552
11821
  const [mobileView, setMobileView] = react.useState("chat");
@@ -11561,9 +11830,18 @@ function AgentChat({
11561
11830
  react.useEffect(() => {
11562
11831
  setShowWorkspace(runtime.viewMode === "expert");
11563
11832
  }, [runtime.viewMode]);
11564
- const isReadOnlySession = isScheduledRunSession(runtime.session);
11565
- const effectiveDisabled = disabled || isReadOnlySession;
11566
- const disabledReason = isReadOnlySession ? "Scheduled run is read-only" : void 0;
11833
+ const readOnly = readOnlyReasonForSession(runtime.session);
11834
+ const effectiveDisabled = disabled || readOnly.readOnly;
11835
+ const disabledReason = readOnly.reason;
11836
+ const orchestratesDeepResearch = react.useMemo(
11837
+ () => runtime.messages.some(
11838
+ (m) => m.toolCalls?.some(
11839
+ (tc) => tc.toolName === "delegate_task" && delegateTaskTargets(tc.args).includes("deep-research")
11840
+ )
11841
+ ),
11842
+ [runtime.messages]
11843
+ );
11844
+ const hideTurnSummary = isSubAgentSession(runtime.session) || orchestratesDeepResearch;
11567
11845
  const browserState = runtime.state.browser;
11568
11846
  const browserAvailable = browserState !== null && browserState.status !== "closed" && !!sessionId;
11569
11847
  const browserVisible = browserAvailable && showBrowser;
@@ -11684,7 +11962,10 @@ function AgentChat({
11684
11962
  canShowBrowser: browserAvailable,
11685
11963
  canShowWorkspace: workspaceAvailable,
11686
11964
  viewMode: runtime.viewMode,
11687
- onViewModeChange: runtime.setViewMode
11965
+ onViewModeChange: runtime.setViewMode,
11966
+ deepResearchEnabled,
11967
+ researchSources: runtime.researchSources,
11968
+ hideTurnSummary
11688
11969
  }
11689
11970
  )
11690
11971
  }
@@ -11754,6 +12035,27 @@ function AgentChat({
11754
12035
  }
11755
12036
  );
11756
12037
  }
12038
+ function delegateTaskTargets(rawArgs) {
12039
+ if (!rawArgs) return [];
12040
+ let parsed;
12041
+ try {
12042
+ parsed = JSON.parse(rawArgs);
12043
+ } catch {
12044
+ return [];
12045
+ }
12046
+ if (!parsed || typeof parsed !== "object") return [];
12047
+ const out = [];
12048
+ const a = parsed;
12049
+ if (typeof a.agent_type === "string") out.push(a.agent_type);
12050
+ if (Array.isArray(a.goals)) {
12051
+ for (const g of a.goals) {
12052
+ if (g && typeof g === "object" && typeof g.agent_type === "string") {
12053
+ out.push(g.agent_type);
12054
+ }
12055
+ }
12056
+ }
12057
+ return out;
12058
+ }
11757
12059
  init_utils();
11758
12060
  var DEFAULT_LIMIT = 50;
11759
12061
  function requireInboxAdapter(adapter) {
@@ -12493,7 +12795,7 @@ function groupMissionTasksByBucket(tasks) {
12493
12795
  function deriveMissionWorkerActivityLabel(worker) {
12494
12796
  const kind = worker.latestEventKind;
12495
12797
  if (kind === "tool.call" && worker.latestEventSummary) {
12496
- return `tool: ${truncate3(worker.latestEventSummary, 80)}`;
12798
+ return `tool: ${truncate4(worker.latestEventSummary, 80)}`;
12497
12799
  }
12498
12800
  if (kind === "llm.response") {
12499
12801
  return "thinking";
@@ -12502,11 +12804,11 @@ function deriveMissionWorkerActivityLabel(worker) {
12502
12804
  return "received tool result";
12503
12805
  }
12504
12806
  if (kind && worker.latestEventSummary) {
12505
- return `${kind}: ${truncate3(worker.latestEventSummary, 60)}`;
12807
+ return `${kind}: ${truncate4(worker.latestEventSummary, 60)}`;
12506
12808
  }
12507
12809
  return worker.sessionStatus || "active";
12508
12810
  }
12509
- function truncate3(s, max) {
12811
+ function truncate4(s, max) {
12510
12812
  if (s.length <= max) return s;
12511
12813
  return `${s.slice(0, max - 1)}\u2026`;
12512
12814
  }
@@ -13494,6 +13796,50 @@ function MissionsPanel({
13494
13796
  )) })
13495
13797
  ] });
13496
13798
  }
13799
+ var CITATION_RE = /\[(S\d+(?:\s*,\s*S\d+)*)\]/g;
13800
+ function splitCitations(text) {
13801
+ if (!text) return [];
13802
+ const segments = [];
13803
+ let lastIndex = 0;
13804
+ for (const match of text.matchAll(CITATION_RE)) {
13805
+ const start = match.index ?? 0;
13806
+ if (start > lastIndex) {
13807
+ segments.push({ kind: "text", value: text.slice(lastIndex, start) });
13808
+ }
13809
+ for (const id of match[1].split(",")) {
13810
+ segments.push({ kind: "cite", value: id.trim() });
13811
+ }
13812
+ lastIndex = start + match[0].length;
13813
+ }
13814
+ if (lastIndex < text.length) {
13815
+ segments.push({ kind: "text", value: text.slice(lastIndex) });
13816
+ }
13817
+ return segments;
13818
+ }
13819
+ function CitationText({
13820
+ text,
13821
+ sources,
13822
+ onCitationClick
13823
+ }) {
13824
+ const byId = new Map(sources.map((s) => [s.sourceId, s]));
13825
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: splitCitations(text).map((seg, i) => {
13826
+ if (seg.kind === "text") {
13827
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: seg.value }, i);
13828
+ }
13829
+ const src = byId.get(seg.value);
13830
+ return /* @__PURE__ */ jsxRuntime.jsx(
13831
+ "button",
13832
+ {
13833
+ type: "button",
13834
+ title: src ? `${src.title} \u2014 ${src.url}` : seg.value,
13835
+ onClick: () => onCitationClick?.(seg.value),
13836
+ className: "mx-0.5 inline-flex items-center rounded-sm bg-muted px-1 text-[10px] font-semibold text-primary hover:bg-primary/10",
13837
+ children: seg.value
13838
+ },
13839
+ i
13840
+ );
13841
+ }) });
13842
+ }
13497
13843
  init_utils();
13498
13844
  var DEFAULT_LIMIT2 = 50;
13499
13845
  var DEFAULT_POLL_INTERVAL_MS3 = 3e4;
@@ -13862,7 +14208,7 @@ function mergeTreeNodes(groups) {
13862
14208
  }
13863
14209
  return Array.from(byId.values());
13864
14210
  }
13865
- function pruneDeletedSessionNodes(nodes, deletedSessionId) {
14211
+ function collectDeletedSessionIds(nodes, deletedSessionId) {
13866
14212
  const deletedIds = /* @__PURE__ */ new Set([deletedSessionId]);
13867
14213
  let changed = true;
13868
14214
  while (changed) {
@@ -13874,7 +14220,7 @@ function pruneDeletedSessionNodes(nodes, deletedSessionId) {
13874
14220
  }
13875
14221
  }
13876
14222
  }
13877
- return nodes.filter((node) => !deletedIds.has(node.id));
14223
+ return deletedIds;
13878
14224
  }
13879
14225
  function formatSessionTime(value) {
13880
14226
  const normalized = /[zZ]|[+-]\d{2}:?\d{2}$/.test(value) ? value : `${value}Z`;
@@ -13905,7 +14251,6 @@ function TreeNodeRow({
13905
14251
  activeSessionId,
13906
14252
  canStop,
13907
14253
  canDelete,
13908
- deletingSessionId,
13909
14254
  onSelect,
13910
14255
  onStop,
13911
14256
  onDelete
@@ -13973,18 +14318,14 @@ function TreeNodeRow({
13973
14318
  "button",
13974
14319
  {
13975
14320
  type: "button",
13976
- className: cn(
13977
- "p-2 md:p-1 rounded hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none transition-all",
13978
- deletingSessionId === entry.id ? "opacity-100" : "opacity-60 md:opacity-50 md:group-hover:opacity-100 md:focus-visible:opacity-100"
13979
- ),
14321
+ 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",
13980
14322
  onClick: (e) => {
13981
14323
  e.stopPropagation();
13982
14324
  onDelete(entry.id);
13983
14325
  },
13984
14326
  "aria-label": "Delete session",
13985
14327
  title: "Delete session",
13986
- disabled: deletingSessionId === entry.id,
13987
- children: deletingSessionId === entry.id ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "w-3 h-3 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "w-3 h-3" })
14328
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "w-3 h-3" })
13988
14329
  }
13989
14330
  )
13990
14331
  ]
@@ -13998,7 +14339,6 @@ function TreeNodeRow({
13998
14339
  activeSessionId,
13999
14340
  canStop,
14000
14341
  canDelete,
14001
- deletingSessionId,
14002
14342
  onSelect,
14003
14343
  onStop,
14004
14344
  onDelete
@@ -14023,10 +14363,8 @@ function SessionTreePanel({
14023
14363
  const [nodes, setNodes] = react.useState([]);
14024
14364
  const [error, setError] = react.useState(null);
14025
14365
  const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
14026
- const [deletingSessionId, setDeletingSessionId] = react.useState(
14027
- null
14028
- );
14029
14366
  const mounted = react.useRef(true);
14367
+ const pendingDeleteIds = react.useRef(/* @__PURE__ */ new Set());
14030
14368
  const requestId = react.useRef(0);
14031
14369
  const lastFingerprint = react.useRef("");
14032
14370
  const resetContext = react.useRef(null);
@@ -14051,10 +14389,23 @@ function SessionTreePanel({
14051
14389
  sessionTreePromise
14052
14390
  ]);
14053
14391
  if (!mounted.current || currentRequestId !== requestId.current) return;
14054
- const nextNodes = mergeTreeNodes([
14392
+ let nextNodes = mergeTreeNodes([
14055
14393
  sessionList?.sessions.map(sessionToTreeNode) ?? [],
14056
14394
  sessionTree?.nodes ?? []
14057
14395
  ]);
14396
+ if (pendingDeleteIds.current.size > 0) {
14397
+ const stillReturned = /* @__PURE__ */ new Set();
14398
+ nextNodes = nextNodes.filter((node) => {
14399
+ if (!pendingDeleteIds.current.has(node.id)) return true;
14400
+ stillReturned.add(node.id);
14401
+ return false;
14402
+ });
14403
+ for (const pendingId of pendingDeleteIds.current) {
14404
+ if (!stillReturned.has(pendingId)) {
14405
+ pendingDeleteIds.current.delete(pendingId);
14406
+ }
14407
+ }
14408
+ }
14058
14409
  const fp = treeFingerprint(nextNodes);
14059
14410
  if (fp !== lastFingerprint.current) {
14060
14411
  lastFingerprint.current = fp;
@@ -14085,6 +14436,7 @@ function SessionTreePanel({
14085
14436
  setNodes([]);
14086
14437
  setHasEverLoaded(false);
14087
14438
  lastFingerprint.current = "";
14439
+ pendingDeleteIds.current.clear();
14088
14440
  }
14089
14441
  setError(null);
14090
14442
  void refetch();
@@ -14121,24 +14473,31 @@ function SessionTreePanel({
14121
14473
  );
14122
14474
  const handleDelete = react.useCallback(
14123
14475
  async (id) => {
14124
- if (!adapter.deleteSession || deletingSessionId) return;
14125
- setDeletingSessionId(id);
14476
+ if (!adapter.deleteSession) return;
14477
+ const deletedIds = collectDeletedSessionIds(nodes, id);
14478
+ for (const deletedId of deletedIds) {
14479
+ pendingDeleteIds.current.add(deletedId);
14480
+ }
14481
+ setNodes((current) => {
14482
+ const next = current.filter((node) => !deletedIds.has(node.id));
14483
+ lastFingerprint.current = treeFingerprint(next);
14484
+ return next;
14485
+ });
14486
+ onSessionDelete?.(id);
14126
14487
  try {
14127
14488
  await adapter.deleteSession({ sessionId: id });
14128
- setNodes((current) => {
14129
- const next = pruneDeletedSessionNodes(current, id);
14130
- lastFingerprint.current = treeFingerprint(next);
14131
- return next;
14132
- });
14133
- onSessionDelete?.(id);
14134
14489
  await refetch({ silent: true });
14135
14490
  } catch (e) {
14136
- setError(e instanceof Error ? e.message : "Failed to delete session");
14137
- } finally {
14138
- if (mounted.current) setDeletingSessionId(null);
14491
+ for (const deletedId of deletedIds) {
14492
+ pendingDeleteIds.current.delete(deletedId);
14493
+ }
14494
+ if (mounted.current) {
14495
+ setError(e instanceof Error ? e.message : "Failed to delete session");
14496
+ }
14497
+ await refetch({ silent: true });
14139
14498
  }
14140
14499
  },
14141
- [adapter, deletingSessionId, onSessionDelete, refetch]
14500
+ [adapter, nodes, onSessionDelete, refetch]
14142
14501
  );
14143
14502
  if (!hasEverLoaded) return null;
14144
14503
  if (nodes.length === 0) return null;
@@ -14159,7 +14518,6 @@ function SessionTreePanel({
14159
14518
  activeSessionId: activeSessionId ?? "",
14160
14519
  canStop: Boolean(adapter.stopSession),
14161
14520
  canDelete: Boolean(adapter.deleteSession),
14162
- deletingSessionId,
14163
14521
  onSelect: handleSelect,
14164
14522
  onStop: handleStop,
14165
14523
  onDelete: handleDelete
@@ -14172,16 +14530,19 @@ function SessionTreePanel({
14172
14530
  exports.ACTIVE_MISSION_STATUSES = ACTIVE_MISSION_STATUSES;
14173
14531
  exports.AgentChat = AgentChat;
14174
14532
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
14533
+ exports.CitationText = CitationText;
14175
14534
  exports.InboxPanel = InboxPanel;
14176
14535
  exports.MessageResponse = MessageResponse;
14177
14536
  exports.MissionDashboard = MissionDashboard;
14178
14537
  exports.MissionsPanel = MissionsPanel;
14538
+ exports.ResearchSourcesPanel = ResearchSourcesPanel;
14179
14539
  exports.ScheduledWorkPanel = ScheduledWorkPanel;
14180
14540
  exports.SessionTreePanel = SessionTreePanel;
14181
14541
  exports.deriveMissionWorkerActivityLabel = deriveMissionWorkerActivityLabel;
14182
14542
  exports.groupMissionTasksByBucket = groupMissionTasksByBucket;
14183
14543
  exports.isTerminalMissionStatus = isTerminalMissionStatus;
14184
14544
  exports.missionTaskBucket = missionTaskBucket;
14545
+ exports.splitCitations = splitCitations;
14185
14546
  exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
14186
14547
  exports.useAgentChatRuntime = useAgentChatRuntime;
14187
14548
  exports.useChatViewMode = useChatViewMode;