@optilogic/chat 1.0.0-beta.1 → 1.0.0-beta.11

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.
Files changed (38) hide show
  1. package/README.md +235 -0
  2. package/dist/index.cjs +1292 -43
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +524 -6
  5. package/dist/index.d.ts +524 -6
  6. package/dist/index.js +1267 -33
  7. package/dist/index.js.map +1 -1
  8. package/package.json +15 -9
  9. package/src/components/agent-response/AgentResponse.tsx +99 -10
  10. package/src/components/agent-response/components/ActivityIndicators.tsx +36 -4
  11. package/src/components/agent-response/components/HITLSection.tsx +95 -0
  12. package/src/components/agent-response/components/MetadataRow.tsx +21 -6
  13. package/src/components/agent-response/components/ThinkingSection.tsx +102 -10
  14. package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
  15. package/src/components/agent-response/components/index.ts +6 -0
  16. package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +79 -4
  17. package/src/components/agent-response/index.ts +23 -0
  18. package/src/components/agent-response/types.ts +96 -1
  19. package/src/components/agent-timeline/AgentTimeline.tsx +256 -0
  20. package/src/components/agent-timeline/TimelineAgentBlock.tsx +84 -0
  21. package/src/components/agent-timeline/TimelineItem.tsx +97 -0
  22. package/src/components/agent-timeline/index.ts +14 -0
  23. package/src/components/agent-timeline/types.ts +49 -0
  24. package/src/components/agent-timeline/utils.ts +167 -0
  25. package/src/components/hitl-interactions/HITLInteractionRecord.tsx +139 -0
  26. package/src/components/hitl-interactions/HITLQuestionPanel.tsx +270 -0
  27. package/src/components/hitl-interactions/index.ts +18 -0
  28. package/src/components/inline-actions/ActionMarkdownRenderer.tsx +60 -0
  29. package/src/components/inline-actions/index.ts +18 -0
  30. package/src/components/inline-actions/parseResponseSegments.ts +66 -0
  31. package/src/components/inline-actions/prompts.ts +41 -0
  32. package/src/components/inline-actions/types.ts +57 -0
  33. package/src/components/user-prompt/UserPrompt.tsx +60 -0
  34. package/src/components/user-prompt/index.ts +1 -0
  35. package/src/components/user-prompt-input/UserPromptInput.tsx +326 -0
  36. package/src/components/user-prompt-input/index.ts +2 -0
  37. package/src/components/user-prompt-input/types.ts +52 -0
  38. package/src/index.ts +54 -0
package/dist/index.cjs CHANGED
@@ -1,9 +1,10 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
3
+ var React11 = require('react');
4
4
  var core = require('@optilogic/core');
5
5
  var lucideReact = require('lucide-react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
+ var editor = require('@optilogic/editor');
7
8
 
8
9
  function _interopNamespace(e) {
9
10
  if (e && e.__esModule) return e;
@@ -23,14 +24,34 @@ function _interopNamespace(e) {
23
24
  return Object.freeze(n);
24
25
  }
25
26
 
26
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
27
+ var React11__namespace = /*#__PURE__*/_interopNamespace(React11);
27
28
 
28
29
  // src/components/agent-response/AgentResponse.tsx
29
- var ActivityIndicators = React__namespace.forwardRef(
30
- ({ toolCalls, knowledge, memory, className, ...props }, ref) => {
31
- const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
30
+ var ActivityIndicators = React11__namespace.forwardRef(
31
+ ({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
32
+ const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
32
33
  if (!hasAnyActivity) return null;
33
34
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: core.cn("flex items-center gap-2", className), ...props, children: [
35
+ statusUpdates.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { children: [
36
+ /* @__PURE__ */ jsxRuntime.jsx(core.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
37
+ "button",
38
+ {
39
+ className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
40
+ onClick: (e) => e.stopPropagation(),
41
+ children: [
42
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Activity, { className: "w-3.5 h-3.5" }),
43
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: statusUpdates.length })
44
+ ]
45
+ }
46
+ ) }),
47
+ /* @__PURE__ */ jsxRuntime.jsx(core.PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
48
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "font-medium text-sm", children: "Status Updates" }),
49
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: statusUpdates.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
50
+ item.agent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-medium text-muted-foreground", children: item.agent }),
51
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: item.agent ? "mt-1" : "", children: item.message })
52
+ ] }, item.id)) })
53
+ ] }) })
54
+ ] }),
34
55
  toolCalls.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(core.Popover, { children: [
35
56
  /* @__PURE__ */ jsxRuntime.jsx(core.PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
36
57
  "button",
@@ -125,7 +146,7 @@ function formatTotalTime(seconds) {
125
146
  const minutes = seconds / 60;
126
147
  return `${minutes.toFixed(1)}m`;
127
148
  }
128
- var MetadataRow = React__namespace.forwardRef(
149
+ var MetadataRow = React11__namespace.forwardRef(
129
150
  ({
130
151
  hasThinking,
131
152
  isExpanded,
@@ -133,6 +154,8 @@ var MetadataRow = React__namespace.forwardRef(
133
154
  toolCalls,
134
155
  knowledge,
135
156
  memory,
157
+ statusUpdates = [],
158
+ statusContent,
136
159
  status,
137
160
  elapsedTime,
138
161
  className,
@@ -140,7 +163,7 @@ var MetadataRow = React__namespace.forwardRef(
140
163
  }, ref) => {
141
164
  const isProcessing = status === "processing";
142
165
  const isComplete = status === "complete";
143
- const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
166
+ const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
144
167
  const renderLeftContent = () => {
145
168
  if (hasThinking) {
146
169
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -157,7 +180,7 @@ var MetadataRow = React__namespace.forwardRef(
157
180
  return null;
158
181
  };
159
182
  const leftContent = renderLeftContent();
160
- if (!leftContent && !hasActivity) {
183
+ if (!leftContent && !hasActivity && !statusContent) {
161
184
  return null;
162
185
  }
163
186
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -171,16 +194,18 @@ var MetadataRow = React__namespace.forwardRef(
171
194
  "button",
172
195
  {
173
196
  onClick: onToggle,
174
- className: "flex items-center gap-1.5 hover:bg-muted/50 -ml-1.5 pl-1.5 pr-2 py-0.5 rounded transition-colors",
197
+ className: "flex items-center gap-1.5 hover:bg-muted/50 -ml-1.5 pl-1.5 pr-2 py-0.5 rounded transition-colors shrink-0",
175
198
  children: leftContent
176
199
  }
177
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: leftContent }),
200
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5 shrink-0", children: leftContent }),
201
+ statusContent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0 mx-2", children: statusContent }),
178
202
  /* @__PURE__ */ jsxRuntime.jsx(
179
203
  ActivityIndicators,
180
204
  {
181
205
  toolCalls,
182
206
  knowledge,
183
- memory
207
+ memory,
208
+ statusUpdates
184
209
  }
185
210
  )
186
211
  ]
@@ -189,24 +214,61 @@ var MetadataRow = React__namespace.forwardRef(
189
214
  }
190
215
  );
191
216
  MetadataRow.displayName = "MetadataRow";
192
- var ThinkingSection = React__namespace.forwardRef(
193
- ({ content, isExpanded, className, ...props }, ref) => {
194
- if (!isExpanded || !content) {
217
+ var ThinkingStepItem = ({ step, renderMarkdown }) => {
218
+ const [isCollapsed, setIsCollapsed] = React11.useState(step.isCollapsed ?? false);
219
+ const toggleCollapse = React11.useCallback(() => {
220
+ setIsCollapsed((prev) => !prev);
221
+ }, []);
222
+ const indentPadding = step.depth * 16;
223
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border/50 last:border-b-0", children: [
224
+ /* @__PURE__ */ jsxRuntime.jsxs(
225
+ "button",
226
+ {
227
+ onClick: toggleCollapse,
228
+ className: "w-full flex items-center gap-1.5 py-1.5 px-2 hover:bg-muted/50 transition-colors text-left",
229
+ style: { paddingLeft: `${indentPadding + 8}px` },
230
+ children: [
231
+ isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }),
232
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground/80", children: step.label })
233
+ ]
234
+ }
235
+ ),
236
+ !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx(
237
+ "div",
238
+ {
239
+ className: "pb-2 px-2",
240
+ style: { paddingLeft: `${indentPadding + 28}px` },
241
+ children: renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(step.content) }) : /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: step.content })
242
+ }
243
+ )
244
+ ] });
245
+ };
246
+ var ThinkingSection = React11__namespace.forwardRef(
247
+ ({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
248
+ if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
195
249
  return null;
196
250
  }
251
+ const isStructured = Array.isArray(content);
197
252
  return /* @__PURE__ */ jsxRuntime.jsx(
198
253
  "div",
199
254
  {
200
255
  ref,
201
256
  className: core.cn("px-3 pb-3 border-t border-border", className),
202
257
  ...props,
203
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
258
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto scrollbar-thin", children: isStructured ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0", children: content.map((step) => /* @__PURE__ */ jsxRuntime.jsx(
259
+ ThinkingStepItem,
260
+ {
261
+ step,
262
+ renderMarkdown
263
+ },
264
+ step.id
265
+ )) }) : renderMarkdown ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(content) }) : /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
204
266
  }
205
267
  );
206
268
  }
207
269
  );
208
270
  ThinkingSection.displayName = "ThinkingSection";
209
- var ActionBar = React__namespace.forwardRef(
271
+ var ActionBar = React11__namespace.forwardRef(
210
272
  ({
211
273
  response,
212
274
  isVisible,
@@ -217,8 +279,8 @@ var ActionBar = React__namespace.forwardRef(
217
279
  className,
218
280
  ...props
219
281
  }, ref) => {
220
- const [copied, setCopied] = React.useState(false);
221
- const handleCopy = React.useCallback(async () => {
282
+ const [copied, setCopied] = React11.useState(false);
283
+ const handleCopy = React11.useCallback(async () => {
222
284
  try {
223
285
  await navigator.clipboard.writeText(response);
224
286
  setCopied(true);
@@ -228,11 +290,11 @@ var ActionBar = React__namespace.forwardRef(
228
290
  console.error("Failed to copy response:", err);
229
291
  }
230
292
  }, [response, onResponseCopy]);
231
- const handleThumbsUp = React.useCallback(() => {
293
+ const handleThumbsUp = React11.useCallback(() => {
232
294
  const newValue = feedback === "up" ? null : "up";
233
295
  onFeedbackChange?.(newValue);
234
296
  }, [feedback, onFeedbackChange]);
235
- const handleThumbsDown = React.useCallback(() => {
297
+ const handleThumbsDown = React11.useCallback(() => {
236
298
  const newValue = feedback === "down" ? null : "down";
237
299
  onFeedbackChange?.(newValue);
238
300
  }, [feedback, onFeedbackChange]);
@@ -295,13 +357,320 @@ var ActionBar = React__namespace.forwardRef(
295
357
  }
296
358
  );
297
359
  ActionBar.displayName = "ActionBar";
360
+ function buildResponseString(questions, selectedOptions, freeformText) {
361
+ const parts = [];
362
+ for (const q of questions) {
363
+ const answer = selectedOptions[q];
364
+ if (answer) {
365
+ parts.push(`Q: ${q}
366
+ A: ${answer}`);
367
+ }
368
+ }
369
+ const trimmed = freeformText.trim();
370
+ if (trimmed) {
371
+ parts.push(`Additional context: ${trimmed}`);
372
+ }
373
+ return parts.join("\n\n");
374
+ }
375
+ var HITLQuestionPanel = React11__namespace.forwardRef(({ question, onSubmit, onStop, className, ...props }, ref) => {
376
+ const [freeformText, setFreeformText] = React11.useState("");
377
+ const [selectedOptions, setSelectedOptions] = React11.useState({});
378
+ const hasTimeout = question.timeoutSeconds != null;
379
+ const [secondsLeft, setSecondsLeft] = React11.useState(
380
+ () => hasTimeout ? Math.max(
381
+ 0,
382
+ Math.round(
383
+ question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
384
+ )
385
+ ) : Infinity
386
+ );
387
+ const textareaRef = React11.useRef(null);
388
+ React11.useEffect(() => {
389
+ textareaRef.current?.focus();
390
+ }, []);
391
+ React11.useEffect(() => {
392
+ if (!hasTimeout) return;
393
+ const interval = setInterval(() => {
394
+ const remaining = Math.max(
395
+ 0,
396
+ Math.round(
397
+ question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
398
+ )
399
+ );
400
+ setSecondsLeft(remaining);
401
+ if (remaining <= 0) clearInterval(interval);
402
+ }, 1e3);
403
+ return () => clearInterval(interval);
404
+ }, [hasTimeout, question.timeoutSeconds, question.receivedAt]);
405
+ const timedOut = hasTimeout && secondsLeft <= 0;
406
+ const questionsWithOptions = React11.useMemo(
407
+ () => question.questions.filter((q) => question.options?.[q]?.length),
408
+ [question.questions, question.options]
409
+ );
410
+ const allOptionsSelected = questionsWithOptions.length > 0 && questionsWithOptions.every((q) => selectedOptions[q]);
411
+ const hasFreeformText = freeformText.trim().length > 0;
412
+ const canSubmit = !timedOut && (allOptionsSelected || hasFreeformText);
413
+ const handleOptionClick = React11.useCallback(
414
+ (questionText, option) => {
415
+ if (timedOut) return;
416
+ setSelectedOptions((prev) => {
417
+ if (prev[questionText] === option) {
418
+ const next = { ...prev };
419
+ delete next[questionText];
420
+ return next;
421
+ }
422
+ return { ...prev, [questionText]: option };
423
+ });
424
+ },
425
+ [timedOut]
426
+ );
427
+ const handleSubmit = React11.useCallback(() => {
428
+ if (!canSubmit) return;
429
+ const combined = buildResponseString(
430
+ question.questions,
431
+ selectedOptions,
432
+ freeformText
433
+ );
434
+ onSubmit(combined, { selectedOptions, freeformText });
435
+ }, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
436
+ const handleKeyDown = React11.useCallback(
437
+ (e) => {
438
+ if (e.key === "Enter" && !e.shiftKey) {
439
+ e.preventDefault();
440
+ handleSubmit();
441
+ }
442
+ },
443
+ [handleSubmit]
444
+ );
445
+ const formatTime2 = (s) => {
446
+ const m = Math.floor(s / 60);
447
+ const sec = s % 60;
448
+ return `${m}:${sec.toString().padStart(2, "0")}`;
449
+ };
450
+ return /* @__PURE__ */ jsxRuntime.jsxs(
451
+ "div",
452
+ {
453
+ ref,
454
+ className: core.cn(
455
+ "rounded-lg border border-border bg-muted p-4 space-y-3",
456
+ className
457
+ ),
458
+ ...props,
459
+ children: [
460
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
461
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: question.reason }) }),
462
+ hasTimeout && /* @__PURE__ */ jsxRuntime.jsx(
463
+ "span",
464
+ {
465
+ className: core.cn(
466
+ "text-xs font-mono whitespace-nowrap",
467
+ secondsLeft <= 30 ? "text-destructive" : "text-muted-foreground"
468
+ ),
469
+ children: timedOut ? "Timed out" : formatTime2(secondsLeft)
470
+ }
471
+ )
472
+ ] }),
473
+ question.context && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border max-h-24 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
474
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: question.questions.map((q, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
475
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: q }),
476
+ question.options?.[q] && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-1.5", children: question.options[q].map((option, j) => {
477
+ const isSelected = selectedOptions[q] === option;
478
+ return /* @__PURE__ */ jsxRuntime.jsx(
479
+ core.Button,
480
+ {
481
+ variant: isSelected ? "primary" : "outline",
482
+ size: "sm",
483
+ onClick: () => handleOptionClick(q, option),
484
+ disabled: timedOut,
485
+ children: option
486
+ },
487
+ j
488
+ );
489
+ }) })
490
+ ] }, i)) }),
491
+ /* @__PURE__ */ jsxRuntime.jsx(
492
+ core.Textarea,
493
+ {
494
+ ref: textareaRef,
495
+ value: freeformText,
496
+ onChange: (e) => setFreeformText(e.target.value),
497
+ onKeyDown: handleKeyDown,
498
+ disabled: timedOut,
499
+ placeholder: "Add additional context or type a full response...",
500
+ rows: 2,
501
+ className: "resize-none"
502
+ }
503
+ ),
504
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
505
+ /* @__PURE__ */ jsxRuntime.jsx(
506
+ core.Button,
507
+ {
508
+ variant: "ghost",
509
+ size: "sm",
510
+ onClick: onStop,
511
+ className: "text-destructive hover:text-destructive hover:bg-destructive/10",
512
+ children: "Stop agent"
513
+ }
514
+ ),
515
+ /* @__PURE__ */ jsxRuntime.jsx(
516
+ core.Button,
517
+ {
518
+ variant: "primary",
519
+ size: "sm",
520
+ onClick: handleSubmit,
521
+ disabled: !canSubmit,
522
+ children: "Send response"
523
+ }
524
+ )
525
+ ] })
526
+ ]
527
+ }
528
+ );
529
+ });
530
+ HITLQuestionPanel.displayName = "HITLQuestionPanel";
531
+ function parseResponse(response) {
532
+ const answers = {};
533
+ let additionalContext = null;
534
+ const blocks = response.split("\n\n");
535
+ for (const block of blocks) {
536
+ const qaMatch = block.match(/^Q: (.+)\nA: (.+)$/s);
537
+ if (qaMatch) {
538
+ answers[qaMatch[1].trim()] = qaMatch[2].trim();
539
+ } else if (block.startsWith("Additional context: ")) {
540
+ additionalContext = block.slice("Additional context: ".length).trim();
541
+ }
542
+ }
543
+ return { answers, additionalContext };
544
+ }
545
+ var HITLInteractionRecord = React11__namespace.forwardRef(({ interaction, className, ...props }, ref) => {
546
+ const { question, response, respondedAt } = interaction;
547
+ const timestamp = new Date(respondedAt).toLocaleTimeString([], {
548
+ hour: "2-digit",
549
+ minute: "2-digit"
550
+ });
551
+ const { answers, additionalContext } = React11.useMemo(
552
+ () => parseResponse(response),
553
+ [response]
554
+ );
555
+ const hasParsedAnswers = Object.keys(answers).length > 0;
556
+ return /* @__PURE__ */ jsxRuntime.jsxs(
557
+ "div",
558
+ {
559
+ ref,
560
+ className: core.cn(
561
+ "rounded-lg border border-border bg-muted p-3 space-y-2 text-sm",
562
+ className
563
+ ),
564
+ ...props,
565
+ children: [
566
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
567
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-muted-foreground", children: "Clarifying Question" }),
568
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: timestamp })
569
+ ] }),
570
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground font-medium", children: question.reason }),
571
+ question.context && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
572
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: question.questions.map((q, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
573
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground", children: q }),
574
+ question.options?.[q] && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
575
+ "Options: ",
576
+ question.options[q].join(", ")
577
+ ] }),
578
+ hasParsedAnswers && answers[q] && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-foreground ml-2 mt-0.5 bg-background rounded px-2 py-1 border border-border", children: [
579
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Answer: " }),
580
+ answers[q]
581
+ ] })
582
+ ] }, i)) }),
583
+ hasParsedAnswers && additionalContext && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-2", children: [
584
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: "Additional context:" }),
585
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground mt-0.5", children: additionalContext })
586
+ ] }),
587
+ !hasParsedAnswers && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-2", children: [
588
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: "Response:" }),
589
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground mt-0.5", children: response })
590
+ ] })
591
+ ]
592
+ }
593
+ );
594
+ });
595
+ HITLInteractionRecord.displayName = "HITLInteractionRecord";
596
+ var HITLSection = React11__namespace.forwardRef(
597
+ ({
598
+ interactions,
599
+ defaultExpanded = false,
600
+ isExpanded: controlledExpanded,
601
+ onExpandedChange,
602
+ className,
603
+ ...props
604
+ }, ref) => {
605
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React11.useState(defaultExpanded);
606
+ const isControlled = controlledExpanded !== void 0;
607
+ const isExpanded = isControlled ? controlledExpanded : uncontrolledExpanded;
608
+ const toggleExpanded = React11.useCallback(() => {
609
+ const newValue = !isExpanded;
610
+ if (isControlled) {
611
+ onExpandedChange?.(newValue);
612
+ } else {
613
+ setUncontrolledExpanded(newValue);
614
+ }
615
+ }, [isExpanded, isControlled, onExpandedChange]);
616
+ if (!interactions || interactions.length === 0) {
617
+ return null;
618
+ }
619
+ return /* @__PURE__ */ jsxRuntime.jsxs(
620
+ "div",
621
+ {
622
+ ref,
623
+ className: core.cn("border-t border-border", className),
624
+ ...props,
625
+ children: [
626
+ /* @__PURE__ */ jsxRuntime.jsxs(
627
+ "button",
628
+ {
629
+ onClick: toggleExpanded,
630
+ className: "w-full flex items-center gap-2 py-2 px-3 hover:bg-muted/50 transition-colors text-left",
631
+ children: [
632
+ isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
633
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageCircleQuestion, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
634
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground/80", children: [
635
+ "Clarifying Questions (",
636
+ interactions.length,
637
+ ")"
638
+ ] })
639
+ ]
640
+ }
641
+ ),
642
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-3 space-y-2", children: interactions.map((interaction, i) => /* @__PURE__ */ jsxRuntime.jsx(HITLInteractionRecord, { interaction }, i)) })
643
+ ]
644
+ }
645
+ );
646
+ }
647
+ );
648
+ HITLSection.displayName = "HITLSection";
649
+ var TruncatedMessage = React11__namespace.forwardRef(
650
+ ({ message, className, ...props }, ref) => {
651
+ return /* @__PURE__ */ jsxRuntime.jsx(
652
+ "div",
653
+ {
654
+ ref,
655
+ className: core.cn(
656
+ "text-xs text-muted-foreground truncate min-w-0",
657
+ className
658
+ ),
659
+ title: message,
660
+ ...props,
661
+ children: message
662
+ }
663
+ );
664
+ }
665
+ );
666
+ TruncatedMessage.displayName = "TruncatedMessage";
298
667
  function useThinkingTimer({
299
668
  startTime,
300
669
  endTime,
301
670
  status
302
671
  }) {
303
- const [elapsed, setElapsed] = React.useState(0);
304
- React.useEffect(() => {
672
+ const [elapsed, setElapsed] = React11.useState(0);
673
+ React11.useEffect(() => {
305
674
  if (!startTime) {
306
675
  setElapsed(0);
307
676
  return;
@@ -329,17 +698,138 @@ var initialAgentResponseState = {
329
698
  toolCalls: [],
330
699
  knowledge: [],
331
700
  memory: [],
701
+ statusUpdates: [],
332
702
  response: "",
333
703
  thinkingStartTime: null,
334
704
  responseCompleteTime: null,
335
705
  firstMessageTime: null
336
706
  };
337
707
 
708
+ // src/components/agent-timeline/utils.ts
709
+ function buildTimelineEntries(state) {
710
+ const entries = [];
711
+ if (state.thinkingSteps) {
712
+ let idx = 0;
713
+ for (const step of state.thinkingSteps) {
714
+ entries.push({
715
+ id: `tl-think-${idx++}`,
716
+ type: "thinking",
717
+ agentName: step.agentName ?? null,
718
+ parentAgent: step.parentAgent ?? null,
719
+ depth: step.depth ?? 0,
720
+ content: step.content,
721
+ title: step.label,
722
+ timestamp: step.timestamp ?? 0
723
+ });
724
+ }
725
+ } else if (state.thinking) {
726
+ entries.push({
727
+ id: "tl-think-0",
728
+ type: "thinking",
729
+ agentName: null,
730
+ parentAgent: null,
731
+ depth: 0,
732
+ content: state.thinking,
733
+ title: null,
734
+ timestamp: state.thinkingStartTime ?? 0
735
+ });
736
+ }
737
+ let toolIdx = 0;
738
+ for (const tool of state.toolCalls) {
739
+ entries.push({
740
+ id: `tl-tool-${toolIdx++}`,
741
+ type: "tool_call",
742
+ agentName: tool.agentName ?? null,
743
+ parentAgent: tool.parentAgent ?? null,
744
+ depth: tool.depth ?? 0,
745
+ content: tool.name,
746
+ title: null,
747
+ timestamp: tool.timestamp
748
+ });
749
+ }
750
+ let knowIdx = 0;
751
+ for (const item of state.knowledge) {
752
+ entries.push({
753
+ id: `tl-know-${knowIdx++}`,
754
+ type: "knowledge",
755
+ agentName: item.agentName ?? null,
756
+ parentAgent: item.parentAgent ?? null,
757
+ depth: item.depth ?? 0,
758
+ content: item.content,
759
+ title: item.source,
760
+ timestamp: item.timestamp
761
+ });
762
+ }
763
+ let memIdx = 0;
764
+ for (const item of state.memory) {
765
+ entries.push({
766
+ id: `tl-mem-${memIdx++}`,
767
+ type: "memory",
768
+ agentName: item.agentName ?? null,
769
+ parentAgent: item.parentAgent ?? null,
770
+ depth: item.depth ?? 0,
771
+ content: item.content,
772
+ title: item.type,
773
+ timestamp: item.timestamp
774
+ });
775
+ }
776
+ let statIdx = 0;
777
+ for (const item of state.statusUpdates) {
778
+ entries.push({
779
+ id: `tl-stat-${statIdx++}`,
780
+ type: "status_update",
781
+ agentName: item.agentName ?? item.agent ?? null,
782
+ parentAgent: item.parentAgent ?? null,
783
+ depth: item.depth ?? 0,
784
+ content: item.message,
785
+ title: null,
786
+ timestamp: item.timestamp
787
+ });
788
+ }
789
+ entries.sort((a, b) => a.timestamp - b.timestamp);
790
+ return entries;
791
+ }
792
+ function groupIntoAgentRuns(entries) {
793
+ const runs = [];
794
+ let currentRun = null;
795
+ for (const entry of entries) {
796
+ const name = entry.agentName || "Agent";
797
+ if (!currentRun || currentRun.agentName !== name) {
798
+ currentRun = {
799
+ agentName: name,
800
+ parentAgent: entry.parentAgent,
801
+ depth: entry.depth,
802
+ entries: []
803
+ };
804
+ runs.push(currentRun);
805
+ }
806
+ currentRun.entries.push({ entry, count: 1 });
807
+ }
808
+ for (const run of runs) {
809
+ run.entries = deduplicateEntries(run.entries);
810
+ }
811
+ return runs;
812
+ }
813
+ function deduplicateEntries(entries) {
814
+ if (entries.length === 0) return [];
815
+ const result = [entries[0]];
816
+ for (let i = 1; i < entries.length; i++) {
817
+ const prev = result[result.length - 1];
818
+ const curr = entries[i];
819
+ if (prev.entry.type === curr.entry.type && prev.entry.content === curr.entry.content) {
820
+ prev.count += curr.count;
821
+ } else {
822
+ result.push({ ...curr });
823
+ }
824
+ }
825
+ return result;
826
+ }
827
+
338
828
  // src/components/agent-response/hooks/useAgentResponseAccumulator.ts
339
829
  function useAgentResponseAccumulator(options) {
340
- const [state, setState] = React.useState(initialAgentResponseState);
830
+ const [state, setState] = React11.useState(initialAgentResponseState);
341
831
  const topic = options?.topic;
342
- const handleMessage = React.useCallback(
832
+ const handleMessage = React11.useCallback(
343
833
  (message) => {
344
834
  let payload;
345
835
  if (topic) {
@@ -363,16 +853,49 @@ function useAgentResponseAccumulator(options) {
363
853
  }
364
854
  return { ...prev, status: newStatus };
365
855
  case "thinking": {
856
+ if (payload.thinkingStep) {
857
+ const newStep = {
858
+ id: payload.thinkingStep.id || `step-${Date.now()}`,
859
+ label: payload.thinkingStep.label,
860
+ content: payload.thinkingStep.content,
861
+ depth: payload.thinkingStep.depth ?? payload.depth ?? 0,
862
+ isCollapsed: payload.thinkingStep.isCollapsed,
863
+ timestamp: Date.now(),
864
+ agentName: payload.agentName,
865
+ parentAgent: payload.parentAgent
866
+ };
867
+ const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
868
+ const next2 = {
869
+ ...prev,
870
+ status: newStatus,
871
+ thinkingSteps: [...prev.thinkingSteps || [], newStep],
872
+ thinkingStartTime: thinkingStartTime2,
873
+ firstMessageTime
874
+ };
875
+ return { ...next2, timelineEntries: buildTimelineEntries(next2) };
876
+ }
366
877
  const newThinking = payload.message || payload.content || "";
367
878
  const separator = prev.thinking && newThinking ? "\n\n" : "";
368
879
  const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
369
- return {
880
+ const prevSteps = prev.thinkingSteps || [];
881
+ const plainStep = {
882
+ id: `step-${prevSteps.length}`,
883
+ label: newThinking,
884
+ content: newThinking,
885
+ depth: payload.depth ?? 0,
886
+ timestamp: Date.now(),
887
+ agentName: payload.agentName,
888
+ parentAgent: payload.parentAgent
889
+ };
890
+ const next = {
370
891
  ...prev,
371
892
  status: newStatus,
372
893
  thinking: prev.thinking + separator + newThinking,
894
+ thinkingSteps: [...prevSteps, plainStep],
373
895
  thinkingStartTime,
374
896
  firstMessageTime
375
897
  };
898
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
376
899
  }
377
900
  case "tool_call": {
378
901
  const toolName = payload.message || payload.tool?.name;
@@ -381,14 +904,18 @@ function useAgentResponseAccumulator(options) {
381
904
  id: payload.tool?.id || `tool-${Date.now()}`,
382
905
  name: toolName,
383
906
  arguments: payload.tool?.arguments,
384
- timestamp: Date.now()
907
+ timestamp: Date.now(),
908
+ agentName: payload.agentName,
909
+ parentAgent: payload.parentAgent,
910
+ depth: payload.depth
385
911
  };
386
- return {
912
+ const next = {
387
913
  ...prev,
388
914
  status: newStatus,
389
915
  toolCalls: [...prev.toolCalls, newToolCall],
390
916
  firstMessageTime
391
917
  };
918
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
392
919
  }
393
920
  return { ...prev, status: newStatus, firstMessageTime };
394
921
  }
@@ -399,14 +926,18 @@ function useAgentResponseAccumulator(options) {
399
926
  id: payload.knowledge?.id || `knowledge-${Date.now()}`,
400
927
  source: payload.knowledge?.source || "unknown",
401
928
  content: knowledgeContent,
402
- timestamp: Date.now()
929
+ timestamp: Date.now(),
930
+ agentName: payload.agentName,
931
+ parentAgent: payload.parentAgent,
932
+ depth: payload.depth
403
933
  };
404
- return {
934
+ const next = {
405
935
  ...prev,
406
936
  status: newStatus,
407
937
  knowledge: [...prev.knowledge, newKnowledge],
408
938
  firstMessageTime
409
939
  };
940
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
410
941
  }
411
942
  return { ...prev, status: newStatus, firstMessageTime };
412
943
  }
@@ -417,14 +948,18 @@ function useAgentResponseAccumulator(options) {
417
948
  id: payload.memory?.id || `memory-${Date.now()}`,
418
949
  type: payload.memory?.type || "unknown",
419
950
  content: memoryContent,
420
- timestamp: Date.now()
951
+ timestamp: Date.now(),
952
+ agentName: payload.agentName,
953
+ parentAgent: payload.parentAgent,
954
+ depth: payload.depth
421
955
  };
422
- return {
956
+ const next = {
423
957
  ...prev,
424
958
  status: newStatus,
425
959
  memory: [...prev.memory, newMemory],
426
960
  firstMessageTime
427
961
  };
962
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
428
963
  }
429
964
  return { ...prev, status: newStatus, firstMessageTime };
430
965
  }
@@ -436,6 +971,28 @@ function useAgentResponseAccumulator(options) {
436
971
  responseCompleteTime: Date.now(),
437
972
  firstMessageTime: prev.firstMessageTime ?? Date.now()
438
973
  };
974
+ case "status_update": {
975
+ const statusMessage = payload.message || payload.statusUpdate?.message;
976
+ if (statusMessage) {
977
+ const newStatusItem = {
978
+ id: payload.statusUpdate?.id || `status-${Date.now()}`,
979
+ message: statusMessage,
980
+ agent: payload.statusUpdate?.agent,
981
+ timestamp: Date.now(),
982
+ agentName: payload.agentName,
983
+ parentAgent: payload.parentAgent,
984
+ depth: payload.depth
985
+ };
986
+ const next = {
987
+ ...prev,
988
+ status: newStatus,
989
+ statusUpdates: [...prev.statusUpdates, newStatusItem],
990
+ firstMessageTime
991
+ };
992
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
993
+ }
994
+ return { ...prev, status: newStatus, firstMessageTime };
995
+ }
439
996
  default:
440
997
  return { ...prev, status: newStatus, firstMessageTime };
441
998
  }
@@ -443,12 +1000,293 @@ function useAgentResponseAccumulator(options) {
443
1000
  },
444
1001
  [topic]
445
1002
  );
446
- const reset = React.useCallback(() => {
1003
+ const reset = React11.useCallback(() => {
447
1004
  setState(initialAgentResponseState);
448
1005
  }, []);
449
1006
  return { state, handleMessage, reset };
450
1007
  }
451
- var AgentResponse = React__namespace.forwardRef(
1008
+ var ICON_MAP = {
1009
+ thinking: lucideReact.Brain,
1010
+ tool_call: lucideReact.Wrench,
1011
+ knowledge: lucideReact.BookOpen,
1012
+ memory: lucideReact.HardDrive,
1013
+ status_update: lucideReact.Activity,
1014
+ ai_response: lucideReact.MessageSquare,
1015
+ error: lucideReact.AlertCircle
1016
+ };
1017
+ function TimelineItem({
1018
+ displayEntry,
1019
+ renderMarkdown,
1020
+ isExpanded,
1021
+ onToggleExpanded
1022
+ }) {
1023
+ const { entry, count } = displayEntry;
1024
+ const Icon = ICON_MAP[entry.type] ?? lucideReact.Activity;
1025
+ const isLong = entry.content.length > 200 || entry.content.split("\n").length > 3;
1026
+ const canExpand = isLong || entry.type === "ai_response";
1027
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-1 flex items-start gap-2 group", children: [
1028
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0 mt-0.5" }),
1029
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: isExpanded && entry.type === "ai_response" && renderMarkdown ? (
1030
+ // Expanded AI response: rendered markdown
1031
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1032
+ renderMarkdown(entry.content),
1033
+ /* @__PURE__ */ jsxRuntime.jsx(
1034
+ "button",
1035
+ {
1036
+ onClick: onToggleExpanded,
1037
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1038
+ children: "Show less"
1039
+ }
1040
+ )
1041
+ ] })
1042
+ ) : isExpanded ? (
1043
+ // Expanded non-AI: plain text
1044
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1045
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: entry.content }),
1046
+ /* @__PURE__ */ jsxRuntime.jsx(
1047
+ "button",
1048
+ {
1049
+ onClick: onToggleExpanded,
1050
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1051
+ children: "Show less"
1052
+ }
1053
+ )
1054
+ ] })
1055
+ ) : (
1056
+ // Collapsed: truncated with optional expand
1057
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-1.5 min-w-0", children: [
1058
+ /* @__PURE__ */ jsxRuntime.jsx(
1059
+ "div",
1060
+ {
1061
+ className: `text-xs text-muted-foreground min-w-0 ${canExpand ? "line-clamp-2 cursor-pointer hover:text-foreground/80" : ""}`,
1062
+ onClick: canExpand ? onToggleExpanded : void 0,
1063
+ children: entry.content
1064
+ }
1065
+ ),
1066
+ count > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground/60 whitespace-nowrap flex-shrink-0", children: [
1067
+ "(x",
1068
+ count,
1069
+ ")"
1070
+ ] })
1071
+ ] })
1072
+ ) })
1073
+ ] });
1074
+ }
1075
+ function TimelineAgentBlock({
1076
+ block,
1077
+ renderMarkdown,
1078
+ isSingleAgent,
1079
+ isCollapsed,
1080
+ onToggleCollapsed,
1081
+ expandedItems,
1082
+ onToggleItemExpanded
1083
+ }) {
1084
+ const indentPx = block.depth * 16;
1085
+ if (isSingleAgent && block.depth === 0) {
1086
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingLeft: `${indentPx}px` }, children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsxRuntime.jsx(
1087
+ TimelineItem,
1088
+ {
1089
+ displayEntry,
1090
+ renderMarkdown,
1091
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1092
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1093
+ },
1094
+ displayEntry.entry.id + "-" + i
1095
+ )) });
1096
+ }
1097
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { paddingLeft: `${indentPx}px` }, children: [
1098
+ /* @__PURE__ */ jsxRuntime.jsxs(
1099
+ "button",
1100
+ {
1101
+ onClick: onToggleCollapsed,
1102
+ className: "w-full flex items-center gap-1.5 py-1 hover:bg-muted/50 -ml-1 pl-1 pr-2 rounded transition-colors text-left",
1103
+ children: [
1104
+ isCollapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }),
1105
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground/80", children: block.agentName }),
1106
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground/60", children: [
1107
+ "(",
1108
+ block.entries.reduce((sum, e) => sum + e.count, 0),
1109
+ ")"
1110
+ ] })
1111
+ ]
1112
+ }
1113
+ ),
1114
+ !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-4", children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsxRuntime.jsx(
1115
+ TimelineItem,
1116
+ {
1117
+ displayEntry,
1118
+ renderMarkdown,
1119
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1120
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1121
+ },
1122
+ displayEntry.entry.id + "-" + i
1123
+ )) })
1124
+ ] });
1125
+ }
1126
+ function createTimelineUIState() {
1127
+ return {
1128
+ expandedItems: /* @__PURE__ */ new Set(),
1129
+ collapsedRuns: /* @__PURE__ */ new Set(),
1130
+ activeFilters: /* @__PURE__ */ new Set()
1131
+ };
1132
+ }
1133
+ var TYPE_CONFIG = [
1134
+ { type: "status_update", icon: lucideReact.Activity, label: "Status" },
1135
+ { type: "thinking", icon: lucideReact.Brain, label: "Thinking" },
1136
+ { type: "tool_call", icon: lucideReact.Wrench, label: "Tools" },
1137
+ { type: "knowledge", icon: lucideReact.BookOpen, label: "Knowledge" },
1138
+ { type: "memory", icon: lucideReact.HardDrive, label: "Memory" },
1139
+ { type: "ai_response", icon: lucideReact.MessageSquare, label: "AI" },
1140
+ { type: "error", icon: lucideReact.AlertCircle, label: "Errors" }
1141
+ ];
1142
+ function AgentTimeline({ entries, renderMarkdown, uiState, maxHeight = "300px" }) {
1143
+ const containerRef = React11.useRef(null);
1144
+ const [renderTick, setRenderTick] = React11.useState(0);
1145
+ const forceRender = React11.useCallback(() => setRenderTick((t) => t + 1), []);
1146
+ const [internalExpandedItems] = React11.useState(() => /* @__PURE__ */ new Set());
1147
+ const [internalCollapsedRuns] = React11.useState(() => /* @__PURE__ */ new Set());
1148
+ const [internalActiveFilters] = React11.useState(() => /* @__PURE__ */ new Set());
1149
+ const expandedItems = uiState?.expandedItems ?? internalExpandedItems;
1150
+ const collapsedRuns = uiState?.collapsedRuns ?? internalCollapsedRuns;
1151
+ const activeFilters = uiState?.activeFilters ?? internalActiveFilters;
1152
+ const availableTypes = React11.useMemo(() => {
1153
+ const types = /* @__PURE__ */ new Set();
1154
+ for (const entry of entries) {
1155
+ types.add(entry.type);
1156
+ }
1157
+ return types;
1158
+ }, [entries]);
1159
+ const filteredEntries = React11.useMemo(
1160
+ () => activeFilters.size === 0 ? entries : entries.filter((e) => activeFilters.has(e.type)),
1161
+ [entries, activeFilters, renderTick]
1162
+ );
1163
+ const agentRuns = React11.useMemo(() => groupIntoAgentRuns(filteredEntries), [filteredEntries, renderTick]);
1164
+ const toggleFilter = React11.useCallback((type) => {
1165
+ if (activeFilters.has(type)) {
1166
+ activeFilters.delete(type);
1167
+ } else {
1168
+ activeFilters.add(type);
1169
+ }
1170
+ forceRender();
1171
+ }, [activeFilters, forceRender]);
1172
+ const clearFilters = React11.useCallback(() => {
1173
+ activeFilters.clear();
1174
+ forceRender();
1175
+ }, [activeFilters, forceRender]);
1176
+ const toggleItemExpanded = React11.useCallback((entryId) => {
1177
+ if (expandedItems.has(entryId)) {
1178
+ expandedItems.delete(entryId);
1179
+ } else {
1180
+ expandedItems.add(entryId);
1181
+ }
1182
+ forceRender();
1183
+ }, [expandedItems, forceRender]);
1184
+ const collapseAll = React11.useCallback(() => {
1185
+ collapsedRuns.clear();
1186
+ agentRuns.forEach((run, i) => {
1187
+ collapsedRuns.add(`${run.agentName}-${i}`);
1188
+ });
1189
+ expandedItems.clear();
1190
+ forceRender();
1191
+ }, [agentRuns, collapsedRuns, expandedItems, forceRender]);
1192
+ const expandAll = React11.useCallback(() => {
1193
+ collapsedRuns.clear();
1194
+ agentRuns.forEach((run, i) => {
1195
+ collapsedRuns.add(`${run.agentName}-${i}:expanded`);
1196
+ });
1197
+ forceRender();
1198
+ }, [agentRuns, collapsedRuns, forceRender]);
1199
+ if (entries.length === 0) return null;
1200
+ const isSingle = agentRuns.length === 1;
1201
+ const hasActiveFilter = activeFilters.size > 0;
1202
+ const scrollStyle = maxHeight !== "none" ? { maxHeight } : void 0;
1203
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1204
+ "div",
1205
+ {
1206
+ ref: containerRef,
1207
+ className: `-mt-1 ${maxHeight !== "none" ? "overflow-y-auto scrollbar-thin" : ""}`,
1208
+ style: scrollStyle,
1209
+ children: [
1210
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-10 bg-background flex items-center gap-1 py-1.5 mb-1 border-b border-border/50 flex-wrap", children: [
1211
+ TYPE_CONFIG.filter((tc) => availableTypes.has(tc.type)).map((tc) => {
1212
+ const isActive = activeFilters.has(tc.type);
1213
+ const count = entries.filter((e) => e.type === tc.type).length;
1214
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1215
+ "button",
1216
+ {
1217
+ onClick: () => toggleFilter(tc.type),
1218
+ className: `inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] transition-colors ${isActive ? "bg-accent text-accent-foreground ring-1 ring-accent-foreground/20" : "text-muted-foreground/60 hover:text-muted-foreground hover:bg-muted/50"}`,
1219
+ title: `${isActive ? "Hide" : "Show only"} ${tc.label}`,
1220
+ children: [
1221
+ /* @__PURE__ */ jsxRuntime.jsx(tc.icon, { className: "w-3 h-3" }),
1222
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: count })
1223
+ ]
1224
+ },
1225
+ tc.type
1226
+ );
1227
+ }),
1228
+ hasActiveFilter && /* @__PURE__ */ jsxRuntime.jsx(
1229
+ "button",
1230
+ {
1231
+ onClick: clearFilters,
1232
+ className: "text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1",
1233
+ children: "Clear"
1234
+ }
1235
+ ),
1236
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
1237
+ !isSingle && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1238
+ /* @__PURE__ */ jsxRuntime.jsx(
1239
+ "button",
1240
+ {
1241
+ onClick: collapseAll,
1242
+ className: "inline-flex items-center gap-0.5 text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1 py-0.5 rounded hover:bg-muted/50 transition-colors",
1243
+ title: "Collapse all",
1244
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsDownUp, { className: "w-3 h-3" })
1245
+ }
1246
+ ),
1247
+ /* @__PURE__ */ jsxRuntime.jsx(
1248
+ "button",
1249
+ {
1250
+ onClick: expandAll,
1251
+ className: "inline-flex items-center gap-0.5 text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1 py-0.5 rounded hover:bg-muted/50 transition-colors",
1252
+ title: "Expand all",
1253
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "w-3 h-3" })
1254
+ }
1255
+ )
1256
+ ] })
1257
+ ] }),
1258
+ filteredEntries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-muted-foreground/50 py-2 text-center", children: "No entries match the selected filters" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0.5", children: agentRuns.map((run, i) => {
1259
+ const runKey = `${run.agentName}-${i}`;
1260
+ const defaultCollapsed = run.depth > 0;
1261
+ const isCollapsed = collapsedRuns.has(runKey) ? true : collapsedRuns.has(`${runKey}:expanded`) ? false : defaultCollapsed;
1262
+ return /* @__PURE__ */ jsxRuntime.jsx(
1263
+ TimelineAgentBlock,
1264
+ {
1265
+ block: run,
1266
+ renderMarkdown,
1267
+ isSingleAgent: isSingle,
1268
+ isCollapsed,
1269
+ onToggleCollapsed: () => {
1270
+ if (isCollapsed) {
1271
+ collapsedRuns.delete(runKey);
1272
+ collapsedRuns.add(`${runKey}:expanded`);
1273
+ } else {
1274
+ collapsedRuns.delete(`${runKey}:expanded`);
1275
+ collapsedRuns.add(runKey);
1276
+ }
1277
+ forceRender();
1278
+ },
1279
+ expandedItems,
1280
+ onToggleItemExpanded: toggleItemExpanded
1281
+ },
1282
+ runKey
1283
+ );
1284
+ }) })
1285
+ ]
1286
+ }
1287
+ );
1288
+ }
1289
+ var AgentResponse = React11__namespace.forwardRef(
452
1290
  ({
453
1291
  state,
454
1292
  id,
@@ -460,14 +1298,19 @@ var AgentResponse = React__namespace.forwardRef(
460
1298
  thinkingExpanded: controlledThinkingExpanded,
461
1299
  onThinkingExpandedChange,
462
1300
  actionsVisible = "hover",
1301
+ hitlInteractions,
1302
+ defaultHITLExpanded = false,
1303
+ statusContent,
463
1304
  renderMarkdown,
1305
+ renderThinkingMarkdown,
464
1306
  className,
465
1307
  ...props
466
1308
  }, ref) => {
467
- const [uncontrolledExpanded, setUncontrolledExpanded] = React.useState(defaultThinkingExpanded);
1309
+ const timelineUIStateRef = React11.useRef(createTimelineUIState());
1310
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React11.useState(defaultThinkingExpanded);
468
1311
  const isThinkingControlled = controlledThinkingExpanded !== void 0;
469
1312
  const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
470
- const toggleThinking = React.useCallback(() => {
1313
+ const toggleThinking = React11.useCallback(() => {
471
1314
  const newValue = !thinkingExpanded;
472
1315
  if (isThinkingControlled) {
473
1316
  onThinkingExpandedChange?.(newValue);
@@ -475,18 +1318,21 @@ var AgentResponse = React__namespace.forwardRef(
475
1318
  setUncontrolledExpanded(newValue);
476
1319
  }
477
1320
  }, [thinkingExpanded, isThinkingControlled, onThinkingExpandedChange]);
478
- const [isHovered, setIsHovered] = React.useState(false);
1321
+ const [isHovered, setIsHovered] = React11.useState(false);
479
1322
  const elapsedTime = useThinkingTimer({
480
1323
  startTime: state.thinkingStartTime,
481
1324
  endTime: state.responseCompleteTime,
482
1325
  status: state.status
483
1326
  });
484
- const totalTimeSeconds = React.useMemo(() => {
1327
+ const totalTimeSeconds = React11.useMemo(() => {
485
1328
  if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
486
1329
  return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
487
1330
  }, [state.firstMessageTime, state.responseCompleteTime]);
488
- const hasAnyContent = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.response;
489
- const showMetadataRow = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.status === "processing";
1331
+ const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
1332
+ const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
1333
+ const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
1334
+ const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
1335
+ const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
490
1336
  const showActionBar = state.status === "complete" && state.response;
491
1337
  const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
492
1338
  if (!hasAnyContent) {
@@ -506,24 +1352,41 @@ var AgentResponse = React__namespace.forwardRef(
506
1352
  /* @__PURE__ */ jsxRuntime.jsx(
507
1353
  MetadataRow,
508
1354
  {
509
- hasThinking: !!state.thinking,
1355
+ hasThinking: hasThinkingContent,
510
1356
  isExpanded: thinkingExpanded,
511
1357
  onToggle: toggleThinking,
512
1358
  toolCalls: state.toolCalls,
513
1359
  knowledge: state.knowledge,
514
1360
  memory: state.memory,
1361
+ statusUpdates: state.statusUpdates,
1362
+ statusContent,
515
1363
  status: state.status,
516
1364
  elapsedTime
517
1365
  }
518
1366
  ),
519
- /* @__PURE__ */ jsxRuntime.jsx(
1367
+ hasTimelineEntries ? thinkingExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-3 border-t border-border mt-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1368
+ AgentTimeline,
1369
+ {
1370
+ entries: state.timelineEntries,
1371
+ renderMarkdown: renderThinkingMarkdown,
1372
+ uiState: timelineUIStateRef.current
1373
+ }
1374
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
520
1375
  ThinkingSection,
521
1376
  {
522
- content: state.thinking,
523
- isExpanded: thinkingExpanded
1377
+ content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
1378
+ isExpanded: thinkingExpanded,
1379
+ renderMarkdown: renderThinkingMarkdown
524
1380
  }
525
1381
  )
526
1382
  ] }),
1383
+ hasHITLInteractions && /* @__PURE__ */ jsxRuntime.jsx(
1384
+ HITLSection,
1385
+ {
1386
+ interactions: hitlInteractions,
1387
+ defaultExpanded: defaultHITLExpanded
1388
+ }
1389
+ ),
527
1390
  state.response && /* @__PURE__ */ jsxRuntime.jsx(
528
1391
  "div",
529
1392
  {
@@ -552,15 +1415,401 @@ var AgentResponse = React__namespace.forwardRef(
552
1415
  }
553
1416
  );
554
1417
  AgentResponse.displayName = "AgentResponse";
1418
+ var UserPrompt = React11__namespace.forwardRef(
1419
+ ({ content, timestamp, className, ...props }, ref) => {
1420
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1421
+ "div",
1422
+ {
1423
+ ref,
1424
+ className: core.cn(
1425
+ "w-fit max-w-[80%] rounded-lg px-4 pt-3.5 pb-3",
1426
+ "bg-secondary text-secondary-foreground",
1427
+ className
1428
+ ),
1429
+ ...props,
1430
+ children: [
1431
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap", children: content }),
1432
+ timestamp && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-secondary-foreground/70 mt-1", children: timestamp.toLocaleTimeString([], {
1433
+ hour: "2-digit",
1434
+ minute: "2-digit"
1435
+ }) })
1436
+ ]
1437
+ }
1438
+ );
1439
+ }
1440
+ );
1441
+ UserPrompt.displayName = "UserPrompt";
1442
+ function createCodeBlockDecorate(entry) {
1443
+ const [node, path] = entry;
1444
+ const ranges = [];
1445
+ if (!editor.Text.isText(node)) {
1446
+ return ranges;
1447
+ }
1448
+ const { text } = node;
1449
+ const backtickPositions = [];
1450
+ let searchStart = 0;
1451
+ while (true) {
1452
+ const pos = text.indexOf("```", searchStart);
1453
+ if (pos === -1) break;
1454
+ backtickPositions.push(pos);
1455
+ searchStart = pos + 3;
1456
+ }
1457
+ let i = 0;
1458
+ while (i < backtickPositions.length) {
1459
+ const openPos = backtickPositions[i];
1460
+ const closePos = backtickPositions[i + 1];
1461
+ ranges.push({
1462
+ anchor: { path, offset: openPos },
1463
+ focus: { path, offset: openPos + 3 },
1464
+ codeDelimiter: true
1465
+ });
1466
+ if (closePos !== void 0) {
1467
+ if (closePos > openPos + 3) {
1468
+ ranges.push({
1469
+ anchor: { path, offset: openPos + 3 },
1470
+ focus: { path, offset: closePos },
1471
+ codeBlock: true
1472
+ });
1473
+ }
1474
+ ranges.push({
1475
+ anchor: { path, offset: closePos },
1476
+ focus: { path, offset: closePos + 3 },
1477
+ codeDelimiter: true
1478
+ });
1479
+ i += 2;
1480
+ } else {
1481
+ if (text.length > openPos + 3) {
1482
+ ranges.push({
1483
+ anchor: { path, offset: openPos + 3 },
1484
+ focus: { path, offset: text.length },
1485
+ codeBlock: true
1486
+ });
1487
+ }
1488
+ i += 1;
1489
+ }
1490
+ }
1491
+ return ranges;
1492
+ }
1493
+ function CodeBlockLeaf({ attributes, children, leaf }) {
1494
+ const leafAny = leaf;
1495
+ if (leafAny.codeBlock) {
1496
+ return /* @__PURE__ */ jsxRuntime.jsx(
1497
+ "span",
1498
+ {
1499
+ ...attributes,
1500
+ className: "bg-muted/50 text-muted-foreground font-mono text-sm rounded px-1",
1501
+ children
1502
+ }
1503
+ );
1504
+ }
1505
+ if (leafAny.codeDelimiter) {
1506
+ return /* @__PURE__ */ jsxRuntime.jsx(
1507
+ "span",
1508
+ {
1509
+ ...attributes,
1510
+ className: "text-muted-foreground/50 font-mono text-sm",
1511
+ children
1512
+ }
1513
+ );
1514
+ }
1515
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { ...attributes, children });
1516
+ }
1517
+ var UserPromptInput = React11__namespace.forwardRef(
1518
+ ({
1519
+ value = "",
1520
+ onChange,
1521
+ onSubmit,
1522
+ clearOnSubmit = true,
1523
+ placeholder = "Type your message...",
1524
+ disabled = false,
1525
+ isSubmitting = false,
1526
+ onStop,
1527
+ disableWhileSubmitting = true,
1528
+ autoFocus = false,
1529
+ refocusAfterSubmit = false,
1530
+ onReady,
1531
+ minRows = 1,
1532
+ maxRows = 6,
1533
+ renderActions,
1534
+ enableTags = false,
1535
+ onTagCreate,
1536
+ onTagDelete,
1537
+ className,
1538
+ ...props
1539
+ }, ref) => {
1540
+ const editorRef = React11__namespace.useRef(null);
1541
+ const [internalValue, setInternalValue] = React11__namespace.useState(value);
1542
+ const prevIsSubmitting = React11__namespace.useRef(isSubmitting);
1543
+ const hasEmittedReady = React11__namespace.useRef(false);
1544
+ React11__namespace.useEffect(() => {
1545
+ setInternalValue(value);
1546
+ }, [value]);
1547
+ React11__namespace.useEffect(() => {
1548
+ if (autoFocus) {
1549
+ requestAnimationFrame(() => {
1550
+ requestAnimationFrame(() => {
1551
+ editorRef.current?.focus();
1552
+ });
1553
+ });
1554
+ }
1555
+ }, [autoFocus]);
1556
+ React11__namespace.useEffect(() => {
1557
+ if (!hasEmittedReady.current && onReady) {
1558
+ requestAnimationFrame(() => {
1559
+ requestAnimationFrame(() => {
1560
+ hasEmittedReady.current = true;
1561
+ onReady();
1562
+ });
1563
+ });
1564
+ }
1565
+ }, [onReady]);
1566
+ React11__namespace.useEffect(() => {
1567
+ if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
1568
+ requestAnimationFrame(() => {
1569
+ editorRef.current?.focus();
1570
+ });
1571
+ }
1572
+ prevIsSubmitting.current = isSubmitting;
1573
+ }, [isSubmitting, refocusAfterSubmit]);
1574
+ React11__namespace.useImperativeHandle(
1575
+ ref,
1576
+ () => ({
1577
+ focus: () => {
1578
+ try {
1579
+ editorRef.current?.focus();
1580
+ } catch {
1581
+ requestAnimationFrame(() => {
1582
+ requestAnimationFrame(() => {
1583
+ editorRef.current?.focus();
1584
+ });
1585
+ });
1586
+ }
1587
+ },
1588
+ clear: () => {
1589
+ editorRef.current?.clear();
1590
+ setInternalValue("");
1591
+ },
1592
+ getText: () => editorRef.current?.getText() ?? "",
1593
+ insertText: (text) => editorRef.current?.insertText(text)
1594
+ }),
1595
+ []
1596
+ );
1597
+ const handleChange = React11__namespace.useCallback(
1598
+ (newValue) => {
1599
+ setInternalValue(newValue);
1600
+ onChange?.(newValue);
1601
+ },
1602
+ [onChange]
1603
+ );
1604
+ const handleSubmit = React11__namespace.useCallback(
1605
+ (text) => {
1606
+ if (disabled || isSubmitting) return;
1607
+ if (!text.trim()) return;
1608
+ onSubmit?.(text.trim());
1609
+ if (clearOnSubmit) {
1610
+ editorRef.current?.clear();
1611
+ setInternalValue("");
1612
+ }
1613
+ },
1614
+ [disabled, isSubmitting, onSubmit, clearOnSubmit]
1615
+ );
1616
+ const handleSendClick = React11__namespace.useCallback(() => {
1617
+ const text = editorRef.current?.getText() ?? "";
1618
+ handleSubmit(text);
1619
+ }, [handleSubmit]);
1620
+ const hasContent = internalValue.trim().length > 0;
1621
+ const canSubmit = hasContent && !disabled && !isSubmitting;
1622
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1623
+ "div",
1624
+ {
1625
+ className: core.cn(
1626
+ "rounded-lg border border-input bg-background",
1627
+ disabled && "opacity-50 cursor-not-allowed",
1628
+ className
1629
+ ),
1630
+ ...props,
1631
+ children: [
1632
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pl-2 pr-0 pt-1 pb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1633
+ editor.SlateEditor,
1634
+ {
1635
+ ref: editorRef,
1636
+ value: internalValue,
1637
+ onChange: handleChange,
1638
+ onSubmit: handleSubmit,
1639
+ clearOnSubmit: false,
1640
+ placeholder,
1641
+ disabled: disabled || disableWhileSubmitting && isSubmitting,
1642
+ enableTags,
1643
+ onTagCreate,
1644
+ onTagDelete,
1645
+ minRows,
1646
+ maxRows,
1647
+ decorate: createCodeBlockDecorate,
1648
+ renderLeaf: CodeBlockLeaf
1649
+ }
1650
+ ) }),
1651
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
1652
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
1653
+ isSubmitting && onStop ? /* @__PURE__ */ jsxRuntime.jsx(
1654
+ core.IconButton,
1655
+ {
1656
+ icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, {}),
1657
+ variant: "filled",
1658
+ size: "sm",
1659
+ "aria-label": "Stop",
1660
+ onClick: onStop
1661
+ }
1662
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1663
+ core.IconButton,
1664
+ {
1665
+ icon: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, {}),
1666
+ variant: "filled",
1667
+ size: "sm",
1668
+ "aria-label": isSubmitting ? "Sending..." : "Send message",
1669
+ disabled: !canSubmit,
1670
+ onClick: handleSendClick
1671
+ }
1672
+ )
1673
+ ] })
1674
+ ]
1675
+ }
1676
+ );
1677
+ }
1678
+ );
1679
+ UserPromptInput.displayName = "UserPromptInput";
1680
+
1681
+ // src/components/inline-actions/parseResponseSegments.ts
1682
+ var ACTION_BLOCK_REGEX = /```json:action\s*\n([\s\S]*?)```/g;
1683
+ function parseResponseSegments(text) {
1684
+ if (!text) return [];
1685
+ const segments = [];
1686
+ let lastIndex = 0;
1687
+ ACTION_BLOCK_REGEX.lastIndex = 0;
1688
+ let match;
1689
+ while ((match = ACTION_BLOCK_REGEX.exec(text)) !== null) {
1690
+ const before = text.slice(lastIndex, match.index);
1691
+ if (before.trim()) {
1692
+ segments.push({ kind: "markdown", content: before });
1693
+ }
1694
+ const jsonContent = match[1].trim();
1695
+ let parsed = null;
1696
+ try {
1697
+ parsed = JSON.parse(jsonContent);
1698
+ } catch {
1699
+ }
1700
+ if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
1701
+ segments.push({
1702
+ kind: "action",
1703
+ actionType: parsed.type,
1704
+ payload: parsed
1705
+ });
1706
+ } else {
1707
+ const rawBlock = match[0];
1708
+ segments.push({ kind: "markdown", content: rawBlock });
1709
+ }
1710
+ lastIndex = match.index + match[0].length;
1711
+ }
1712
+ const trailing = text.slice(lastIndex);
1713
+ if (trailing.trim()) {
1714
+ segments.push({ kind: "markdown", content: trailing });
1715
+ }
1716
+ return segments;
1717
+ }
1718
+ function ActionMarkdownRenderer({
1719
+ content,
1720
+ registry,
1721
+ renderMarkdown,
1722
+ onAction,
1723
+ isLatest
1724
+ }) {
1725
+ const segments = React11.useMemo(() => parseResponseSegments(content), [content]);
1726
+ if (segments.length === 1 && segments[0].kind === "markdown") {
1727
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderMarkdown(segments[0].content) });
1728
+ }
1729
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, index) => {
1730
+ if (segment.kind === "markdown") {
1731
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderMarkdown(segment.content) }, `md-${index}`);
1732
+ }
1733
+ const Component = registry[segment.actionType];
1734
+ if (!Component) {
1735
+ return /* @__PURE__ */ jsxRuntime.jsx(
1736
+ "pre",
1737
+ {
1738
+ className: "my-4 p-4 rounded-lg border border-border bg-muted text-sm font-mono overflow-x-auto",
1739
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: JSON.stringify(segment.payload, null, 2) })
1740
+ },
1741
+ `action-fallback-${index}`
1742
+ );
1743
+ }
1744
+ return /* @__PURE__ */ jsxRuntime.jsx(
1745
+ Component,
1746
+ {
1747
+ payload: segment.payload,
1748
+ onAction,
1749
+ isLatest
1750
+ },
1751
+ `action-${segment.actionType}-${index}`
1752
+ );
1753
+ }) });
1754
+ }
1755
+
1756
+ // src/components/inline-actions/prompts.ts
1757
+ var INLINE_ACTION_PROMPT = `
1758
+ <inline_actions>
1759
+ When your response should include interactive components (like query viewers,
1760
+ data tables, or executable actions), embed them as fenced code blocks using
1761
+ the \`json:action\` language tag:
1762
+
1763
+ \`\`\`json:action
1764
+ {
1765
+ "type": "action-type-here",
1766
+ ...action-specific fields
1767
+ }
1768
+ \`\`\`
1769
+
1770
+ Rules:
1771
+ - Each block must contain valid JSON with a "type" field.
1772
+ - The "type" must match a registered action component on the frontend.
1773
+ - Multiple action blocks per response are allowed.
1774
+ - Surround action blocks with normal markdown text for user context.
1775
+ - The action block is rendered as an interactive component in the chat UI.
1776
+ - SQL strings inside JSON must be properly escaped (newlines as \\n, quotes as \\").
1777
+
1778
+ Available action types:
1779
+
1780
+ - "optimap-query": Displays SQL queries with a button to execute them and
1781
+ update the 3D globe map.
1782
+ Required fields:
1783
+ - type: "optimap-query"
1784
+ - locations_sql: string (the validated locations SQL query)
1785
+ - routes_sql: string (the validated routes SQL query)
1786
+ - database_name: string (the target database name)
1787
+ </inline_actions>
1788
+ `;
555
1789
 
556
1790
  exports.ActionBar = ActionBar;
1791
+ exports.ActionMarkdownRenderer = ActionMarkdownRenderer;
557
1792
  exports.ActivityIndicators = ActivityIndicators;
558
1793
  exports.AgentResponse = AgentResponse;
1794
+ exports.AgentTimeline = AgentTimeline;
1795
+ exports.HITLInteractionRecord = HITLInteractionRecord;
1796
+ exports.HITLQuestionPanel = HITLQuestionPanel;
1797
+ exports.HITLSection = HITLSection;
1798
+ exports.INLINE_ACTION_PROMPT = INLINE_ACTION_PROMPT;
559
1799
  exports.MetadataRow = MetadataRow;
560
1800
  exports.ThinkingSection = ThinkingSection;
1801
+ exports.TruncatedMessage = TruncatedMessage;
1802
+ exports.UserPrompt = UserPrompt;
1803
+ exports.UserPromptInput = UserPromptInput;
1804
+ exports.buildResponseString = buildResponseString;
1805
+ exports.buildTimelineEntries = buildTimelineEntries;
1806
+ exports.createTimelineUIState = createTimelineUIState;
1807
+ exports.deduplicateEntries = deduplicateEntries;
561
1808
  exports.formatTime = formatTime;
562
1809
  exports.formatTotalTime = formatTotalTime;
1810
+ exports.groupIntoAgentRuns = groupIntoAgentRuns;
563
1811
  exports.initialAgentResponseState = initialAgentResponseState;
1812
+ exports.parseResponseSegments = parseResponseSegments;
564
1813
  exports.useAgentResponseAccumulator = useAgentResponseAccumulator;
565
1814
  exports.useThinkingTimer = useThinkingTimer;
566
1815
  //# sourceMappingURL=index.cjs.map