@optilogic/chat 1.0.0-beta.9 → 1.1.0

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 (31) hide show
  1. package/README.md +43 -0
  2. package/dist/index.cjs +709 -79
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +283 -4
  5. package/dist/index.d.ts +283 -4
  6. package/dist/index.js +674 -53
  7. package/dist/index.js.map +1 -1
  8. package/package.json +3 -3
  9. package/src/components/agent-response/AgentResponse.tsx +59 -13
  10. package/src/components/agent-response/components/MetadataRow.tsx +15 -4
  11. package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
  12. package/src/components/agent-response/components/index.ts +3 -0
  13. package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +65 -8
  14. package/src/components/agent-response/index.ts +19 -0
  15. package/src/components/agent-response/types.ts +61 -1
  16. package/src/components/agent-timeline/AgentTimeline.tsx +256 -0
  17. package/src/components/agent-timeline/TimelineAgentBlock.tsx +84 -0
  18. package/src/components/agent-timeline/TimelineItem.tsx +97 -0
  19. package/src/components/agent-timeline/index.ts +14 -0
  20. package/src/components/agent-timeline/types.ts +49 -0
  21. package/src/components/agent-timeline/utils.ts +189 -0
  22. package/src/components/hitl-interactions/HITLQuestionPanel.tsx +35 -21
  23. package/src/components/hitl-interactions/index.ts +1 -1
  24. package/src/components/inline-actions/ActionMarkdownRenderer.tsx +60 -0
  25. package/src/components/inline-actions/index.ts +18 -0
  26. package/src/components/inline-actions/parseResponseSegments.ts +66 -0
  27. package/src/components/inline-actions/prompts.ts +41 -0
  28. package/src/components/inline-actions/types.ts +57 -0
  29. package/src/components/user-prompt-input/UserPromptInput.tsx +13 -8
  30. package/src/components/user-prompt-input/types.ts +4 -0
  31. package/src/index.ts +29 -0
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var React10 = 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');
@@ -24,10 +24,10 @@ function _interopNamespace(e) {
24
24
  return Object.freeze(n);
25
25
  }
26
26
 
27
- var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
27
+ var React11__namespace = /*#__PURE__*/_interopNamespace(React11);
28
28
 
29
29
  // src/components/agent-response/AgentResponse.tsx
30
- var ActivityIndicators = React10__namespace.forwardRef(
30
+ var ActivityIndicators = React11__namespace.forwardRef(
31
31
  ({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
32
32
  const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
33
33
  if (!hasAnyActivity) return null;
@@ -146,7 +146,7 @@ function formatTotalTime(seconds) {
146
146
  const minutes = seconds / 60;
147
147
  return `${minutes.toFixed(1)}m`;
148
148
  }
149
- var MetadataRow = React10__namespace.forwardRef(
149
+ var MetadataRow = React11__namespace.forwardRef(
150
150
  ({
151
151
  hasThinking,
152
152
  isExpanded,
@@ -155,6 +155,7 @@ var MetadataRow = React10__namespace.forwardRef(
155
155
  knowledge,
156
156
  memory,
157
157
  statusUpdates = [],
158
+ statusContent,
158
159
  status,
159
160
  elapsedTime,
160
161
  className,
@@ -179,7 +180,7 @@ var MetadataRow = React10__namespace.forwardRef(
179
180
  return null;
180
181
  };
181
182
  const leftContent = renderLeftContent();
182
- if (!leftContent && !hasActivity) {
183
+ if (!leftContent && !hasActivity && !statusContent) {
183
184
  return null;
184
185
  }
185
186
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -193,10 +194,11 @@ var MetadataRow = React10__namespace.forwardRef(
193
194
  "button",
194
195
  {
195
196
  onClick: onToggle,
196
- 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",
197
198
  children: leftContent
198
199
  }
199
- ) : /* @__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 }),
200
202
  /* @__PURE__ */ jsxRuntime.jsx(
201
203
  ActivityIndicators,
202
204
  {
@@ -213,8 +215,8 @@ var MetadataRow = React10__namespace.forwardRef(
213
215
  );
214
216
  MetadataRow.displayName = "MetadataRow";
215
217
  var ThinkingStepItem = ({ step, renderMarkdown }) => {
216
- const [isCollapsed, setIsCollapsed] = React10.useState(step.isCollapsed ?? false);
217
- const toggleCollapse = React10.useCallback(() => {
218
+ const [isCollapsed, setIsCollapsed] = React11.useState(step.isCollapsed ?? false);
219
+ const toggleCollapse = React11.useCallback(() => {
218
220
  setIsCollapsed((prev) => !prev);
219
221
  }, []);
220
222
  const indentPadding = step.depth * 16;
@@ -241,7 +243,7 @@ var ThinkingStepItem = ({ step, renderMarkdown }) => {
241
243
  )
242
244
  ] });
243
245
  };
244
- var ThinkingSection = React10__namespace.forwardRef(
246
+ var ThinkingSection = React11__namespace.forwardRef(
245
247
  ({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
246
248
  if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
247
249
  return null;
@@ -266,7 +268,7 @@ var ThinkingSection = React10__namespace.forwardRef(
266
268
  }
267
269
  );
268
270
  ThinkingSection.displayName = "ThinkingSection";
269
- var ActionBar = React10__namespace.forwardRef(
271
+ var ActionBar = React11__namespace.forwardRef(
270
272
  ({
271
273
  response,
272
274
  isVisible,
@@ -277,8 +279,8 @@ var ActionBar = React10__namespace.forwardRef(
277
279
  className,
278
280
  ...props
279
281
  }, ref) => {
280
- const [copied, setCopied] = React10.useState(false);
281
- const handleCopy = React10.useCallback(async () => {
282
+ const [copied, setCopied] = React11.useState(false);
283
+ const handleCopy = React11.useCallback(async () => {
282
284
  try {
283
285
  await navigator.clipboard.writeText(response);
284
286
  setCopied(true);
@@ -288,11 +290,11 @@ var ActionBar = React10__namespace.forwardRef(
288
290
  console.error("Failed to copy response:", err);
289
291
  }
290
292
  }, [response, onResponseCopy]);
291
- const handleThumbsUp = React10.useCallback(() => {
293
+ const handleThumbsUp = React11.useCallback(() => {
292
294
  const newValue = feedback === "up" ? null : "up";
293
295
  onFeedbackChange?.(newValue);
294
296
  }, [feedback, onFeedbackChange]);
295
- const handleThumbsDown = React10.useCallback(() => {
297
+ const handleThumbsDown = React11.useCallback(() => {
296
298
  const newValue = feedback === "down" ? null : "down";
297
299
  onFeedbackChange?.(newValue);
298
300
  }, [feedback, onFeedbackChange]);
@@ -370,22 +372,24 @@ A: ${answer}`);
370
372
  }
371
373
  return parts.join("\n\n");
372
374
  }
373
- var HITLQuestionPanel = React10__namespace.forwardRef(({ question, onSubmit, onStop, className, ...props }, ref) => {
374
- const [freeformText, setFreeformText] = React10.useState("");
375
- const [selectedOptions, setSelectedOptions] = React10.useState({});
376
- const [secondsLeft, setSecondsLeft] = React10.useState(
377
- () => Math.max(
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(
378
381
  0,
379
382
  Math.round(
380
383
  question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
381
384
  )
382
- )
385
+ ) : Infinity
383
386
  );
384
- const textareaRef = React10.useRef(null);
385
- React10.useEffect(() => {
387
+ const textareaRef = React11.useRef(null);
388
+ React11.useEffect(() => {
386
389
  textareaRef.current?.focus();
387
390
  }, []);
388
- React10.useEffect(() => {
391
+ React11.useEffect(() => {
392
+ if (!hasTimeout) return;
389
393
  const interval = setInterval(() => {
390
394
  const remaining = Math.max(
391
395
  0,
@@ -397,16 +401,16 @@ var HITLQuestionPanel = React10__namespace.forwardRef(({ question, onSubmit, onS
397
401
  if (remaining <= 0) clearInterval(interval);
398
402
  }, 1e3);
399
403
  return () => clearInterval(interval);
400
- }, [question.timeoutSeconds, question.receivedAt]);
401
- const timedOut = secondsLeft <= 0;
402
- const questionsWithOptions = React10.useMemo(
404
+ }, [hasTimeout, question.timeoutSeconds, question.receivedAt]);
405
+ const timedOut = hasTimeout && secondsLeft <= 0;
406
+ const questionsWithOptions = React11.useMemo(
403
407
  () => question.questions.filter((q) => question.options?.[q]?.length),
404
408
  [question.questions, question.options]
405
409
  );
406
410
  const allOptionsSelected = questionsWithOptions.length > 0 && questionsWithOptions.every((q) => selectedOptions[q]);
407
411
  const hasFreeformText = freeformText.trim().length > 0;
408
412
  const canSubmit = !timedOut && (allOptionsSelected || hasFreeformText);
409
- const handleOptionClick = React10.useCallback(
413
+ const handleOptionClick = React11.useCallback(
410
414
  (questionText, option) => {
411
415
  if (timedOut) return;
412
416
  setSelectedOptions((prev) => {
@@ -420,16 +424,16 @@ var HITLQuestionPanel = React10__namespace.forwardRef(({ question, onSubmit, onS
420
424
  },
421
425
  [timedOut]
422
426
  );
423
- const handleSubmit = React10.useCallback(() => {
427
+ const handleSubmit = React11.useCallback(() => {
424
428
  if (!canSubmit) return;
425
429
  const combined = buildResponseString(
426
430
  question.questions,
427
431
  selectedOptions,
428
432
  freeformText
429
433
  );
430
- onSubmit(combined);
434
+ onSubmit(combined, { selectedOptions, freeformText });
431
435
  }, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
432
- const handleKeyDown = React10.useCallback(
436
+ const handleKeyDown = React11.useCallback(
433
437
  (e) => {
434
438
  if (e.key === "Enter" && !e.shiftKey) {
435
439
  e.preventDefault();
@@ -455,7 +459,7 @@ var HITLQuestionPanel = React10__namespace.forwardRef(({ question, onSubmit, onS
455
459
  children: [
456
460
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
457
461
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: question.reason }) }),
458
- /* @__PURE__ */ jsxRuntime.jsx(
462
+ hasTimeout && /* @__PURE__ */ jsxRuntime.jsx(
459
463
  "span",
460
464
  {
461
465
  className: core.cn(
@@ -538,13 +542,13 @@ function parseResponse(response) {
538
542
  }
539
543
  return { answers, additionalContext };
540
544
  }
541
- var HITLInteractionRecord = React10__namespace.forwardRef(({ interaction, className, ...props }, ref) => {
545
+ var HITLInteractionRecord = React11__namespace.forwardRef(({ interaction, className, ...props }, ref) => {
542
546
  const { question, response, respondedAt } = interaction;
543
547
  const timestamp = new Date(respondedAt).toLocaleTimeString([], {
544
548
  hour: "2-digit",
545
549
  minute: "2-digit"
546
550
  });
547
- const { answers, additionalContext } = React10.useMemo(
551
+ const { answers, additionalContext } = React11.useMemo(
548
552
  () => parseResponse(response),
549
553
  [response]
550
554
  );
@@ -589,7 +593,7 @@ var HITLInteractionRecord = React10__namespace.forwardRef(({ interaction, classN
589
593
  );
590
594
  });
591
595
  HITLInteractionRecord.displayName = "HITLInteractionRecord";
592
- var HITLSection = React10__namespace.forwardRef(
596
+ var HITLSection = React11__namespace.forwardRef(
593
597
  ({
594
598
  interactions,
595
599
  defaultExpanded = false,
@@ -598,10 +602,10 @@ var HITLSection = React10__namespace.forwardRef(
598
602
  className,
599
603
  ...props
600
604
  }, ref) => {
601
- const [uncontrolledExpanded, setUncontrolledExpanded] = React10.useState(defaultExpanded);
605
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React11.useState(defaultExpanded);
602
606
  const isControlled = controlledExpanded !== void 0;
603
607
  const isExpanded = isControlled ? controlledExpanded : uncontrolledExpanded;
604
- const toggleExpanded = React10.useCallback(() => {
608
+ const toggleExpanded = React11.useCallback(() => {
605
609
  const newValue = !isExpanded;
606
610
  if (isControlled) {
607
611
  onExpandedChange?.(newValue);
@@ -642,13 +646,31 @@ var HITLSection = React10__namespace.forwardRef(
642
646
  }
643
647
  );
644
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";
645
667
  function useThinkingTimer({
646
668
  startTime,
647
669
  endTime,
648
670
  status
649
671
  }) {
650
- const [elapsed, setElapsed] = React10.useState(0);
651
- React10.useEffect(() => {
672
+ const [elapsed, setElapsed] = React11.useState(0);
673
+ React11.useEffect(() => {
652
674
  if (!startTime) {
653
675
  setElapsed(0);
654
676
  return;
@@ -677,17 +699,157 @@ var initialAgentResponseState = {
677
699
  knowledge: [],
678
700
  memory: [],
679
701
  statusUpdates: [],
702
+ potentialResponses: [],
703
+ customTimelineEntries: [],
680
704
  response: "",
681
705
  thinkingStartTime: null,
682
706
  responseCompleteTime: null,
683
707
  firstMessageTime: null
684
708
  };
685
709
 
710
+ // src/components/agent-timeline/utils.ts
711
+ function buildTimelineEntries(state) {
712
+ const entries = [];
713
+ if (state.thinkingSteps) {
714
+ let idx = 0;
715
+ for (const step of state.thinkingSteps) {
716
+ entries.push({
717
+ id: `tl-think-${idx++}`,
718
+ type: "thinking",
719
+ agentName: step.agentName ?? null,
720
+ parentAgent: step.parentAgent ?? null,
721
+ depth: step.depth ?? 0,
722
+ content: step.content,
723
+ title: step.label,
724
+ timestamp: step.timestamp ?? 0
725
+ });
726
+ }
727
+ } else if (state.thinking) {
728
+ entries.push({
729
+ id: "tl-think-0",
730
+ type: "thinking",
731
+ agentName: null,
732
+ parentAgent: null,
733
+ depth: 0,
734
+ content: state.thinking,
735
+ title: null,
736
+ timestamp: state.thinkingStartTime ?? 0
737
+ });
738
+ }
739
+ let toolIdx = 0;
740
+ for (const tool of state.toolCalls) {
741
+ entries.push({
742
+ id: `tl-tool-${toolIdx++}`,
743
+ type: "tool_call",
744
+ agentName: tool.agentName ?? null,
745
+ parentAgent: tool.parentAgent ?? null,
746
+ depth: tool.depth ?? 0,
747
+ content: tool.name,
748
+ title: null,
749
+ timestamp: tool.timestamp
750
+ });
751
+ }
752
+ let knowIdx = 0;
753
+ for (const item of state.knowledge) {
754
+ entries.push({
755
+ id: `tl-know-${knowIdx++}`,
756
+ type: "knowledge",
757
+ agentName: item.agentName ?? null,
758
+ parentAgent: item.parentAgent ?? null,
759
+ depth: item.depth ?? 0,
760
+ content: item.content,
761
+ title: item.source,
762
+ timestamp: item.timestamp
763
+ });
764
+ }
765
+ let memIdx = 0;
766
+ for (const item of state.memory) {
767
+ entries.push({
768
+ id: `tl-mem-${memIdx++}`,
769
+ type: "memory",
770
+ agentName: item.agentName ?? null,
771
+ parentAgent: item.parentAgent ?? null,
772
+ depth: item.depth ?? 0,
773
+ content: item.content,
774
+ title: item.type,
775
+ timestamp: item.timestamp
776
+ });
777
+ }
778
+ let statIdx = 0;
779
+ for (const item of state.statusUpdates) {
780
+ entries.push({
781
+ id: `tl-stat-${statIdx++}`,
782
+ type: "status_update",
783
+ agentName: item.agentName ?? item.agent ?? null,
784
+ parentAgent: item.parentAgent ?? null,
785
+ depth: item.depth ?? 0,
786
+ content: item.message,
787
+ title: null,
788
+ timestamp: item.timestamp
789
+ });
790
+ }
791
+ if (state.potentialResponses) {
792
+ let respIdx = 0;
793
+ for (const resp of state.potentialResponses) {
794
+ entries.push({
795
+ id: `tl-resp-${respIdx++}`,
796
+ type: "ai_response",
797
+ agentName: resp.agentName ?? null,
798
+ parentAgent: resp.parentAgent ?? null,
799
+ depth: resp.depth ?? 0,
800
+ content: resp.content,
801
+ title: null,
802
+ timestamp: resp.timestamp
803
+ });
804
+ }
805
+ }
806
+ if (state.customTimelineEntries) {
807
+ entries.push(...state.customTimelineEntries);
808
+ }
809
+ entries.sort((a, b) => a.timestamp - b.timestamp);
810
+ return entries;
811
+ }
812
+ function groupIntoAgentRuns(entries) {
813
+ const runs = [];
814
+ let currentRun = null;
815
+ for (const entry of entries) {
816
+ const name = entry.agentName || "Agent";
817
+ if (!currentRun || currentRun.agentName !== name) {
818
+ currentRun = {
819
+ agentName: name,
820
+ parentAgent: entry.parentAgent,
821
+ depth: entry.depth,
822
+ entries: []
823
+ };
824
+ runs.push(currentRun);
825
+ }
826
+ currentRun.entries.push({ entry, count: 1 });
827
+ }
828
+ for (const run of runs) {
829
+ run.entries = deduplicateEntries(run.entries);
830
+ }
831
+ return runs;
832
+ }
833
+ function deduplicateEntries(entries) {
834
+ if (entries.length === 0) return [];
835
+ const result = [entries[0]];
836
+ for (let i = 1; i < entries.length; i++) {
837
+ const prev = result[result.length - 1];
838
+ const curr = entries[i];
839
+ if (prev.entry.type === curr.entry.type && prev.entry.content === curr.entry.content) {
840
+ prev.count += curr.count;
841
+ } else {
842
+ result.push({ ...curr });
843
+ }
844
+ }
845
+ return result;
846
+ }
847
+
686
848
  // src/components/agent-response/hooks/useAgentResponseAccumulator.ts
687
849
  function useAgentResponseAccumulator(options) {
688
- const [state, setState] = React10.useState(initialAgentResponseState);
850
+ const [state, setState] = React11.useState(initialAgentResponseState);
689
851
  const topic = options?.topic;
690
- const handleMessage = React10.useCallback(
852
+ const handleMessage = React11.useCallback(
691
853
  (message) => {
692
854
  let payload;
693
855
  if (topic) {
@@ -716,28 +878,44 @@ function useAgentResponseAccumulator(options) {
716
878
  id: payload.thinkingStep.id || `step-${Date.now()}`,
717
879
  label: payload.thinkingStep.label,
718
880
  content: payload.thinkingStep.content,
719
- depth: payload.thinkingStep.depth ?? 0,
720
- isCollapsed: payload.thinkingStep.isCollapsed
881
+ depth: payload.thinkingStep.depth ?? payload.depth ?? 0,
882
+ isCollapsed: payload.thinkingStep.isCollapsed,
883
+ timestamp: Date.now(),
884
+ agentName: payload.agentName,
885
+ parentAgent: payload.parentAgent
721
886
  };
722
887
  const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
723
- return {
888
+ const next2 = {
724
889
  ...prev,
725
890
  status: newStatus,
726
891
  thinkingSteps: [...prev.thinkingSteps || [], newStep],
727
892
  thinkingStartTime: thinkingStartTime2,
728
893
  firstMessageTime
729
894
  };
895
+ return { ...next2, timelineEntries: buildTimelineEntries(next2) };
730
896
  }
731
897
  const newThinking = payload.message || payload.content || "";
732
898
  const separator = prev.thinking && newThinking ? "\n\n" : "";
733
899
  const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
734
- return {
900
+ const prevSteps = prev.thinkingSteps || [];
901
+ const plainStep = {
902
+ id: `step-${prevSteps.length}`,
903
+ label: newThinking,
904
+ content: newThinking,
905
+ depth: payload.depth ?? 0,
906
+ timestamp: Date.now(),
907
+ agentName: payload.agentName,
908
+ parentAgent: payload.parentAgent
909
+ };
910
+ const next = {
735
911
  ...prev,
736
912
  status: newStatus,
737
913
  thinking: prev.thinking + separator + newThinking,
914
+ thinkingSteps: [...prevSteps, plainStep],
738
915
  thinkingStartTime,
739
916
  firstMessageTime
740
917
  };
918
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
741
919
  }
742
920
  case "tool_call": {
743
921
  const toolName = payload.message || payload.tool?.name;
@@ -746,14 +924,18 @@ function useAgentResponseAccumulator(options) {
746
924
  id: payload.tool?.id || `tool-${Date.now()}`,
747
925
  name: toolName,
748
926
  arguments: payload.tool?.arguments,
749
- timestamp: Date.now()
927
+ timestamp: Date.now(),
928
+ agentName: payload.agentName,
929
+ parentAgent: payload.parentAgent,
930
+ depth: payload.depth
750
931
  };
751
- return {
932
+ const next = {
752
933
  ...prev,
753
934
  status: newStatus,
754
935
  toolCalls: [...prev.toolCalls, newToolCall],
755
936
  firstMessageTime
756
937
  };
938
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
757
939
  }
758
940
  return { ...prev, status: newStatus, firstMessageTime };
759
941
  }
@@ -764,14 +946,18 @@ function useAgentResponseAccumulator(options) {
764
946
  id: payload.knowledge?.id || `knowledge-${Date.now()}`,
765
947
  source: payload.knowledge?.source || "unknown",
766
948
  content: knowledgeContent,
767
- timestamp: Date.now()
949
+ timestamp: Date.now(),
950
+ agentName: payload.agentName,
951
+ parentAgent: payload.parentAgent,
952
+ depth: payload.depth
768
953
  };
769
- return {
954
+ const next = {
770
955
  ...prev,
771
956
  status: newStatus,
772
957
  knowledge: [...prev.knowledge, newKnowledge],
773
958
  firstMessageTime
774
959
  };
960
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
775
961
  }
776
962
  return { ...prev, status: newStatus, firstMessageTime };
777
963
  }
@@ -782,14 +968,18 @@ function useAgentResponseAccumulator(options) {
782
968
  id: payload.memory?.id || `memory-${Date.now()}`,
783
969
  type: payload.memory?.type || "unknown",
784
970
  content: memoryContent,
785
- timestamp: Date.now()
971
+ timestamp: Date.now(),
972
+ agentName: payload.agentName,
973
+ parentAgent: payload.parentAgent,
974
+ depth: payload.depth
786
975
  };
787
- return {
976
+ const next = {
788
977
  ...prev,
789
978
  status: newStatus,
790
979
  memory: [...prev.memory, newMemory],
791
980
  firstMessageTime
792
981
  };
982
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
793
983
  }
794
984
  return { ...prev, status: newStatus, firstMessageTime };
795
985
  }
@@ -808,14 +998,39 @@ function useAgentResponseAccumulator(options) {
808
998
  id: payload.statusUpdate?.id || `status-${Date.now()}`,
809
999
  message: statusMessage,
810
1000
  agent: payload.statusUpdate?.agent,
811
- timestamp: Date.now()
1001
+ timestamp: Date.now(),
1002
+ agentName: payload.agentName,
1003
+ parentAgent: payload.parentAgent,
1004
+ depth: payload.depth
812
1005
  };
813
- return {
1006
+ const next = {
814
1007
  ...prev,
815
1008
  status: newStatus,
816
1009
  statusUpdates: [...prev.statusUpdates, newStatusItem],
817
1010
  firstMessageTime
818
1011
  };
1012
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
1013
+ }
1014
+ return { ...prev, status: newStatus, firstMessageTime };
1015
+ }
1016
+ case "potential_response": {
1017
+ const respContent = payload.message || payload.content || "";
1018
+ if (respContent) {
1019
+ const newResp = {
1020
+ id: `resp-${Date.now()}`,
1021
+ content: respContent,
1022
+ timestamp: Date.now(),
1023
+ agentName: payload.agentName,
1024
+ parentAgent: payload.parentAgent,
1025
+ depth: payload.depth
1026
+ };
1027
+ const next = {
1028
+ ...prev,
1029
+ status: newStatus,
1030
+ potentialResponses: [...prev.potentialResponses || [], newResp],
1031
+ firstMessageTime
1032
+ };
1033
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
819
1034
  }
820
1035
  return { ...prev, status: newStatus, firstMessageTime };
821
1036
  }
@@ -826,12 +1041,293 @@ function useAgentResponseAccumulator(options) {
826
1041
  },
827
1042
  [topic]
828
1043
  );
829
- const reset = React10.useCallback(() => {
1044
+ const reset = React11.useCallback(() => {
830
1045
  setState(initialAgentResponseState);
831
1046
  }, []);
832
1047
  return { state, handleMessage, reset };
833
1048
  }
834
- var AgentResponse = React10__namespace.forwardRef(
1049
+ var ICON_MAP = {
1050
+ thinking: lucideReact.Brain,
1051
+ tool_call: lucideReact.Wrench,
1052
+ knowledge: lucideReact.BookOpen,
1053
+ memory: lucideReact.HardDrive,
1054
+ status_update: lucideReact.Activity,
1055
+ ai_response: lucideReact.MessageSquare,
1056
+ error: lucideReact.AlertCircle
1057
+ };
1058
+ function TimelineItem({
1059
+ displayEntry,
1060
+ renderMarkdown,
1061
+ isExpanded,
1062
+ onToggleExpanded
1063
+ }) {
1064
+ const { entry, count } = displayEntry;
1065
+ const Icon = ICON_MAP[entry.type] ?? lucideReact.Activity;
1066
+ const isLong = entry.content.length > 200 || entry.content.split("\n").length > 3;
1067
+ const canExpand = isLong || entry.type === "ai_response";
1068
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-1 flex items-start gap-2 group", children: [
1069
+ /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0 mt-0.5" }),
1070
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: isExpanded && entry.type === "ai_response" && renderMarkdown ? (
1071
+ // Expanded AI response: rendered markdown
1072
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1073
+ renderMarkdown(entry.content),
1074
+ /* @__PURE__ */ jsxRuntime.jsx(
1075
+ "button",
1076
+ {
1077
+ onClick: onToggleExpanded,
1078
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1079
+ children: "Show less"
1080
+ }
1081
+ )
1082
+ ] })
1083
+ ) : isExpanded ? (
1084
+ // Expanded non-AI: plain text
1085
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1086
+ /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: entry.content }),
1087
+ /* @__PURE__ */ jsxRuntime.jsx(
1088
+ "button",
1089
+ {
1090
+ onClick: onToggleExpanded,
1091
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1092
+ children: "Show less"
1093
+ }
1094
+ )
1095
+ ] })
1096
+ ) : (
1097
+ // Collapsed: truncated with optional expand
1098
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-1.5 min-w-0", children: [
1099
+ /* @__PURE__ */ jsxRuntime.jsx(
1100
+ "div",
1101
+ {
1102
+ className: `text-xs text-muted-foreground min-w-0 ${canExpand ? "line-clamp-2 cursor-pointer hover:text-foreground/80" : ""}`,
1103
+ onClick: canExpand ? onToggleExpanded : void 0,
1104
+ children: entry.content
1105
+ }
1106
+ ),
1107
+ count > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground/60 whitespace-nowrap flex-shrink-0", children: [
1108
+ "(x",
1109
+ count,
1110
+ ")"
1111
+ ] })
1112
+ ] })
1113
+ ) })
1114
+ ] });
1115
+ }
1116
+ function TimelineAgentBlock({
1117
+ block,
1118
+ renderMarkdown,
1119
+ isSingleAgent,
1120
+ isCollapsed,
1121
+ onToggleCollapsed,
1122
+ expandedItems,
1123
+ onToggleItemExpanded
1124
+ }) {
1125
+ const indentPx = block.depth * 16;
1126
+ if (isSingleAgent && block.depth === 0) {
1127
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingLeft: `${indentPx}px` }, children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsxRuntime.jsx(
1128
+ TimelineItem,
1129
+ {
1130
+ displayEntry,
1131
+ renderMarkdown,
1132
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1133
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1134
+ },
1135
+ displayEntry.entry.id + "-" + i
1136
+ )) });
1137
+ }
1138
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { paddingLeft: `${indentPx}px` }, children: [
1139
+ /* @__PURE__ */ jsxRuntime.jsxs(
1140
+ "button",
1141
+ {
1142
+ onClick: onToggleCollapsed,
1143
+ 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",
1144
+ children: [
1145
+ 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" }),
1146
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-foreground/80", children: block.agentName }),
1147
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground/60", children: [
1148
+ "(",
1149
+ block.entries.reduce((sum, e) => sum + e.count, 0),
1150
+ ")"
1151
+ ] })
1152
+ ]
1153
+ }
1154
+ ),
1155
+ !isCollapsed && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-4", children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsxRuntime.jsx(
1156
+ TimelineItem,
1157
+ {
1158
+ displayEntry,
1159
+ renderMarkdown,
1160
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1161
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1162
+ },
1163
+ displayEntry.entry.id + "-" + i
1164
+ )) })
1165
+ ] });
1166
+ }
1167
+ function createTimelineUIState() {
1168
+ return {
1169
+ expandedItems: /* @__PURE__ */ new Set(),
1170
+ collapsedRuns: /* @__PURE__ */ new Set(),
1171
+ activeFilters: /* @__PURE__ */ new Set()
1172
+ };
1173
+ }
1174
+ var TYPE_CONFIG = [
1175
+ { type: "status_update", icon: lucideReact.Activity, label: "Status" },
1176
+ { type: "thinking", icon: lucideReact.Brain, label: "Thinking" },
1177
+ { type: "tool_call", icon: lucideReact.Wrench, label: "Tools" },
1178
+ { type: "knowledge", icon: lucideReact.BookOpen, label: "Knowledge" },
1179
+ { type: "memory", icon: lucideReact.HardDrive, label: "Memory" },
1180
+ { type: "ai_response", icon: lucideReact.MessageSquare, label: "AI" },
1181
+ { type: "error", icon: lucideReact.AlertCircle, label: "Errors" }
1182
+ ];
1183
+ function AgentTimeline({ entries, renderMarkdown, uiState, maxHeight = "300px" }) {
1184
+ const containerRef = React11.useRef(null);
1185
+ const [renderTick, setRenderTick] = React11.useState(0);
1186
+ const forceRender = React11.useCallback(() => setRenderTick((t) => t + 1), []);
1187
+ const [internalExpandedItems] = React11.useState(() => /* @__PURE__ */ new Set());
1188
+ const [internalCollapsedRuns] = React11.useState(() => /* @__PURE__ */ new Set());
1189
+ const [internalActiveFilters] = React11.useState(() => /* @__PURE__ */ new Set());
1190
+ const expandedItems = uiState?.expandedItems ?? internalExpandedItems;
1191
+ const collapsedRuns = uiState?.collapsedRuns ?? internalCollapsedRuns;
1192
+ const activeFilters = uiState?.activeFilters ?? internalActiveFilters;
1193
+ const availableTypes = React11.useMemo(() => {
1194
+ const types = /* @__PURE__ */ new Set();
1195
+ for (const entry of entries) {
1196
+ types.add(entry.type);
1197
+ }
1198
+ return types;
1199
+ }, [entries]);
1200
+ const filteredEntries = React11.useMemo(
1201
+ () => activeFilters.size === 0 ? entries : entries.filter((e) => activeFilters.has(e.type)),
1202
+ [entries, activeFilters, renderTick]
1203
+ );
1204
+ const agentRuns = React11.useMemo(() => groupIntoAgentRuns(filteredEntries), [filteredEntries, renderTick]);
1205
+ const toggleFilter = React11.useCallback((type) => {
1206
+ if (activeFilters.has(type)) {
1207
+ activeFilters.delete(type);
1208
+ } else {
1209
+ activeFilters.add(type);
1210
+ }
1211
+ forceRender();
1212
+ }, [activeFilters, forceRender]);
1213
+ const clearFilters = React11.useCallback(() => {
1214
+ activeFilters.clear();
1215
+ forceRender();
1216
+ }, [activeFilters, forceRender]);
1217
+ const toggleItemExpanded = React11.useCallback((entryId) => {
1218
+ if (expandedItems.has(entryId)) {
1219
+ expandedItems.delete(entryId);
1220
+ } else {
1221
+ expandedItems.add(entryId);
1222
+ }
1223
+ forceRender();
1224
+ }, [expandedItems, forceRender]);
1225
+ const collapseAll = React11.useCallback(() => {
1226
+ collapsedRuns.clear();
1227
+ agentRuns.forEach((run, i) => {
1228
+ collapsedRuns.add(`${run.agentName}-${i}`);
1229
+ });
1230
+ expandedItems.clear();
1231
+ forceRender();
1232
+ }, [agentRuns, collapsedRuns, expandedItems, forceRender]);
1233
+ const expandAll = React11.useCallback(() => {
1234
+ collapsedRuns.clear();
1235
+ agentRuns.forEach((run, i) => {
1236
+ collapsedRuns.add(`${run.agentName}-${i}:expanded`);
1237
+ });
1238
+ forceRender();
1239
+ }, [agentRuns, collapsedRuns, forceRender]);
1240
+ if (entries.length === 0) return null;
1241
+ const isSingle = agentRuns.length === 1;
1242
+ const hasActiveFilter = activeFilters.size > 0;
1243
+ const scrollStyle = maxHeight !== "none" ? { maxHeight } : void 0;
1244
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1245
+ "div",
1246
+ {
1247
+ ref: containerRef,
1248
+ className: maxHeight !== "none" ? "overflow-y-auto" : "",
1249
+ style: scrollStyle,
1250
+ children: [
1251
+ /* @__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 pl-2", children: [
1252
+ TYPE_CONFIG.filter((tc) => availableTypes.has(tc.type)).map((tc) => {
1253
+ const isActive = activeFilters.has(tc.type);
1254
+ const count = entries.filter((e) => e.type === tc.type).length;
1255
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1256
+ "button",
1257
+ {
1258
+ onClick: () => toggleFilter(tc.type),
1259
+ 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"}`,
1260
+ title: `${isActive ? "Hide" : "Show only"} ${tc.label}`,
1261
+ children: [
1262
+ /* @__PURE__ */ jsxRuntime.jsx(tc.icon, { className: "w-3 h-3" }),
1263
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: count })
1264
+ ]
1265
+ },
1266
+ tc.type
1267
+ );
1268
+ }),
1269
+ hasActiveFilter && /* @__PURE__ */ jsxRuntime.jsx(
1270
+ "button",
1271
+ {
1272
+ onClick: clearFilters,
1273
+ className: "text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1",
1274
+ children: "Clear"
1275
+ }
1276
+ ),
1277
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
1278
+ !isSingle && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1279
+ /* @__PURE__ */ jsxRuntime.jsx(
1280
+ "button",
1281
+ {
1282
+ onClick: collapseAll,
1283
+ 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",
1284
+ title: "Collapse all",
1285
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsDownUp, { className: "w-3 h-3" })
1286
+ }
1287
+ ),
1288
+ /* @__PURE__ */ jsxRuntime.jsx(
1289
+ "button",
1290
+ {
1291
+ onClick: expandAll,
1292
+ 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",
1293
+ title: "Expand all",
1294
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "w-3 h-3" })
1295
+ }
1296
+ )
1297
+ ] })
1298
+ ] }),
1299
+ 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) => {
1300
+ const runKey = `${run.agentName}-${i}`;
1301
+ const defaultCollapsed = run.depth > 0;
1302
+ const isCollapsed = collapsedRuns.has(runKey) ? true : collapsedRuns.has(`${runKey}:expanded`) ? false : defaultCollapsed;
1303
+ return /* @__PURE__ */ jsxRuntime.jsx(
1304
+ TimelineAgentBlock,
1305
+ {
1306
+ block: run,
1307
+ renderMarkdown,
1308
+ isSingleAgent: isSingle,
1309
+ isCollapsed,
1310
+ onToggleCollapsed: () => {
1311
+ if (isCollapsed) {
1312
+ collapsedRuns.delete(runKey);
1313
+ collapsedRuns.add(`${runKey}:expanded`);
1314
+ } else {
1315
+ collapsedRuns.delete(`${runKey}:expanded`);
1316
+ collapsedRuns.add(runKey);
1317
+ }
1318
+ forceRender();
1319
+ },
1320
+ expandedItems,
1321
+ onToggleItemExpanded: toggleItemExpanded
1322
+ },
1323
+ runKey
1324
+ );
1325
+ }) })
1326
+ ]
1327
+ }
1328
+ );
1329
+ }
1330
+ var AgentResponse = React11__namespace.forwardRef(
835
1331
  ({
836
1332
  state,
837
1333
  id,
@@ -845,15 +1341,18 @@ var AgentResponse = React10__namespace.forwardRef(
845
1341
  actionsVisible = "hover",
846
1342
  hitlInteractions,
847
1343
  defaultHITLExpanded = false,
1344
+ statusContent,
848
1345
  renderMarkdown,
849
1346
  renderThinkingMarkdown,
1347
+ timelineMaxHeight,
850
1348
  className,
851
1349
  ...props
852
1350
  }, ref) => {
853
- const [uncontrolledExpanded, setUncontrolledExpanded] = React10.useState(defaultThinkingExpanded);
1351
+ const timelineUIStateRef = React11.useRef(createTimelineUIState());
1352
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React11.useState(defaultThinkingExpanded);
854
1353
  const isThinkingControlled = controlledThinkingExpanded !== void 0;
855
1354
  const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
856
- const toggleThinking = React10.useCallback(() => {
1355
+ const toggleThinking = React11.useCallback(() => {
857
1356
  const newValue = !thinkingExpanded;
858
1357
  if (isThinkingControlled) {
859
1358
  onThinkingExpandedChange?.(newValue);
@@ -861,17 +1360,18 @@ var AgentResponse = React10__namespace.forwardRef(
861
1360
  setUncontrolledExpanded(newValue);
862
1361
  }
863
1362
  }, [thinkingExpanded, isThinkingControlled, onThinkingExpandedChange]);
864
- const [isHovered, setIsHovered] = React10.useState(false);
1363
+ const [isHovered, setIsHovered] = React11.useState(false);
865
1364
  const elapsedTime = useThinkingTimer({
866
1365
  startTime: state.thinkingStartTime,
867
1366
  endTime: state.responseCompleteTime,
868
1367
  status: state.status
869
1368
  });
870
- const totalTimeSeconds = React10.useMemo(() => {
1369
+ const totalTimeSeconds = React11.useMemo(() => {
871
1370
  if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
872
1371
  return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
873
1372
  }, [state.firstMessageTime, state.responseCompleteTime]);
874
- const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || false;
1373
+ const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
1374
+ const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
875
1375
  const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
876
1376
  const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
877
1377
  const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
@@ -901,11 +1401,20 @@ var AgentResponse = React10__namespace.forwardRef(
901
1401
  knowledge: state.knowledge,
902
1402
  memory: state.memory,
903
1403
  statusUpdates: state.statusUpdates,
1404
+ statusContent,
904
1405
  status: state.status,
905
1406
  elapsedTime
906
1407
  }
907
1408
  ),
908
- /* @__PURE__ */ jsxRuntime.jsx(
1409
+ hasTimelineEntries ? thinkingExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pb-3 border-t border-border", children: /* @__PURE__ */ jsxRuntime.jsx(
1410
+ AgentTimeline,
1411
+ {
1412
+ entries: state.timelineEntries,
1413
+ renderMarkdown: renderThinkingMarkdown,
1414
+ uiState: timelineUIStateRef.current,
1415
+ maxHeight: timelineMaxHeight
1416
+ }
1417
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
909
1418
  ThinkingSection,
910
1419
  {
911
1420
  content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
@@ -949,7 +1458,7 @@ var AgentResponse = React10__namespace.forwardRef(
949
1458
  }
950
1459
  );
951
1460
  AgentResponse.displayName = "AgentResponse";
952
- var UserPrompt = React10__namespace.forwardRef(
1461
+ var UserPrompt = React11__namespace.forwardRef(
953
1462
  ({ content, timestamp, className, ...props }, ref) => {
954
1463
  return /* @__PURE__ */ jsxRuntime.jsxs(
955
1464
  "div",
@@ -1048,7 +1557,7 @@ function CodeBlockLeaf({ attributes, children, leaf }) {
1048
1557
  }
1049
1558
  return /* @__PURE__ */ jsxRuntime.jsx("span", { ...attributes, children });
1050
1559
  }
1051
- var UserPromptInput = React10__namespace.forwardRef(
1560
+ var UserPromptInput = React11__namespace.forwardRef(
1052
1561
  ({
1053
1562
  value = "",
1054
1563
  onChange,
@@ -1058,6 +1567,8 @@ var UserPromptInput = React10__namespace.forwardRef(
1058
1567
  disabled = false,
1059
1568
  isSubmitting = false,
1060
1569
  onStop,
1570
+ stopTooltip,
1571
+ stopClassName,
1061
1572
  disableWhileSubmitting = true,
1062
1573
  autoFocus = false,
1063
1574
  refocusAfterSubmit = false,
@@ -1071,14 +1582,14 @@ var UserPromptInput = React10__namespace.forwardRef(
1071
1582
  className,
1072
1583
  ...props
1073
1584
  }, ref) => {
1074
- const editorRef = React10__namespace.useRef(null);
1075
- const [internalValue, setInternalValue] = React10__namespace.useState(value);
1076
- const prevIsSubmitting = React10__namespace.useRef(isSubmitting);
1077
- const hasEmittedReady = React10__namespace.useRef(false);
1078
- React10__namespace.useEffect(() => {
1585
+ const editorRef = React11__namespace.useRef(null);
1586
+ const [internalValue, setInternalValue] = React11__namespace.useState(value);
1587
+ const prevIsSubmitting = React11__namespace.useRef(isSubmitting);
1588
+ const hasEmittedReady = React11__namespace.useRef(false);
1589
+ React11__namespace.useEffect(() => {
1079
1590
  setInternalValue(value);
1080
1591
  }, [value]);
1081
- React10__namespace.useEffect(() => {
1592
+ React11__namespace.useEffect(() => {
1082
1593
  if (autoFocus) {
1083
1594
  requestAnimationFrame(() => {
1084
1595
  requestAnimationFrame(() => {
@@ -1087,7 +1598,7 @@ var UserPromptInput = React10__namespace.forwardRef(
1087
1598
  });
1088
1599
  }
1089
1600
  }, [autoFocus]);
1090
- React10__namespace.useEffect(() => {
1601
+ React11__namespace.useEffect(() => {
1091
1602
  if (!hasEmittedReady.current && onReady) {
1092
1603
  requestAnimationFrame(() => {
1093
1604
  requestAnimationFrame(() => {
@@ -1097,7 +1608,7 @@ var UserPromptInput = React10__namespace.forwardRef(
1097
1608
  });
1098
1609
  }
1099
1610
  }, [onReady]);
1100
- React10__namespace.useEffect(() => {
1611
+ React11__namespace.useEffect(() => {
1101
1612
  if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
1102
1613
  requestAnimationFrame(() => {
1103
1614
  editorRef.current?.focus();
@@ -1105,7 +1616,7 @@ var UserPromptInput = React10__namespace.forwardRef(
1105
1616
  }
1106
1617
  prevIsSubmitting.current = isSubmitting;
1107
1618
  }, [isSubmitting, refocusAfterSubmit]);
1108
- React10__namespace.useImperativeHandle(
1619
+ React11__namespace.useImperativeHandle(
1109
1620
  ref,
1110
1621
  () => ({
1111
1622
  focus: () => {
@@ -1128,14 +1639,14 @@ var UserPromptInput = React10__namespace.forwardRef(
1128
1639
  }),
1129
1640
  []
1130
1641
  );
1131
- const handleChange = React10__namespace.useCallback(
1642
+ const handleChange = React11__namespace.useCallback(
1132
1643
  (newValue) => {
1133
1644
  setInternalValue(newValue);
1134
1645
  onChange?.(newValue);
1135
1646
  },
1136
1647
  [onChange]
1137
1648
  );
1138
- const handleSubmit = React10__namespace.useCallback(
1649
+ const handleSubmit = React11__namespace.useCallback(
1139
1650
  (text) => {
1140
1651
  if (disabled || isSubmitting) return;
1141
1652
  if (!text.trim()) return;
@@ -1147,7 +1658,7 @@ var UserPromptInput = React10__namespace.forwardRef(
1147
1658
  },
1148
1659
  [disabled, isSubmitting, onSubmit, clearOnSubmit]
1149
1660
  );
1150
- const handleSendClick = React10__namespace.useCallback(() => {
1661
+ const handleSendClick = React11__namespace.useCallback(() => {
1151
1662
  const text = editorRef.current?.getText() ?? "";
1152
1663
  handleSubmit(text);
1153
1664
  }, [handleSubmit]);
@@ -1184,16 +1695,17 @@ var UserPromptInput = React10__namespace.forwardRef(
1184
1695
  ) }),
1185
1696
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
1186
1697
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
1187
- isSubmitting && onStop ? /* @__PURE__ */ jsxRuntime.jsx(
1698
+ isSubmitting && onStop ? /* @__PURE__ */ jsxRuntime.jsx(core.Tooltip, { content: stopTooltip, disabled: !stopTooltip, children: /* @__PURE__ */ jsxRuntime.jsx(
1188
1699
  core.IconButton,
1189
1700
  {
1190
1701
  icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, {}),
1191
1702
  variant: "filled",
1192
1703
  size: "sm",
1193
- "aria-label": "Stop",
1194
- onClick: onStop
1704
+ "aria-label": stopTooltip || "Stop",
1705
+ onClick: onStop,
1706
+ className: stopClassName
1195
1707
  }
1196
- ) : /* @__PURE__ */ jsxRuntime.jsx(
1708
+ ) }) : /* @__PURE__ */ jsxRuntime.jsx(
1197
1709
  core.IconButton,
1198
1710
  {
1199
1711
  icon: isSubmitting ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, {}),
@@ -1212,20 +1724,138 @@ var UserPromptInput = React10__namespace.forwardRef(
1212
1724
  );
1213
1725
  UserPromptInput.displayName = "UserPromptInput";
1214
1726
 
1727
+ // src/components/inline-actions/parseResponseSegments.ts
1728
+ var ACTION_BLOCK_REGEX = /```json:action\s*\n([\s\S]*?)```/g;
1729
+ function parseResponseSegments(text) {
1730
+ if (!text) return [];
1731
+ const segments = [];
1732
+ let lastIndex = 0;
1733
+ ACTION_BLOCK_REGEX.lastIndex = 0;
1734
+ let match;
1735
+ while ((match = ACTION_BLOCK_REGEX.exec(text)) !== null) {
1736
+ const before = text.slice(lastIndex, match.index);
1737
+ if (before.trim()) {
1738
+ segments.push({ kind: "markdown", content: before });
1739
+ }
1740
+ const jsonContent = match[1].trim();
1741
+ let parsed = null;
1742
+ try {
1743
+ parsed = JSON.parse(jsonContent);
1744
+ } catch {
1745
+ }
1746
+ if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
1747
+ segments.push({
1748
+ kind: "action",
1749
+ actionType: parsed.type,
1750
+ payload: parsed
1751
+ });
1752
+ } else {
1753
+ const rawBlock = match[0];
1754
+ segments.push({ kind: "markdown", content: rawBlock });
1755
+ }
1756
+ lastIndex = match.index + match[0].length;
1757
+ }
1758
+ const trailing = text.slice(lastIndex);
1759
+ if (trailing.trim()) {
1760
+ segments.push({ kind: "markdown", content: trailing });
1761
+ }
1762
+ return segments;
1763
+ }
1764
+ function ActionMarkdownRenderer({
1765
+ content,
1766
+ registry,
1767
+ renderMarkdown,
1768
+ onAction,
1769
+ isLatest
1770
+ }) {
1771
+ const segments = React11.useMemo(() => parseResponseSegments(content), [content]);
1772
+ if (segments.length === 1 && segments[0].kind === "markdown") {
1773
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderMarkdown(segments[0].content) });
1774
+ }
1775
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: segments.map((segment, index) => {
1776
+ if (segment.kind === "markdown") {
1777
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderMarkdown(segment.content) }, `md-${index}`);
1778
+ }
1779
+ const Component = registry[segment.actionType];
1780
+ if (!Component) {
1781
+ return /* @__PURE__ */ jsxRuntime.jsx(
1782
+ "pre",
1783
+ {
1784
+ className: "my-4 p-4 rounded-lg border border-border bg-muted text-sm font-mono overflow-x-auto",
1785
+ children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: JSON.stringify(segment.payload, null, 2) })
1786
+ },
1787
+ `action-fallback-${index}`
1788
+ );
1789
+ }
1790
+ return /* @__PURE__ */ jsxRuntime.jsx(
1791
+ Component,
1792
+ {
1793
+ payload: segment.payload,
1794
+ onAction,
1795
+ isLatest
1796
+ },
1797
+ `action-${segment.actionType}-${index}`
1798
+ );
1799
+ }) });
1800
+ }
1801
+
1802
+ // src/components/inline-actions/prompts.ts
1803
+ var INLINE_ACTION_PROMPT = `
1804
+ <inline_actions>
1805
+ When your response should include interactive components (like query viewers,
1806
+ data tables, or executable actions), embed them as fenced code blocks using
1807
+ the \`json:action\` language tag:
1808
+
1809
+ \`\`\`json:action
1810
+ {
1811
+ "type": "action-type-here",
1812
+ ...action-specific fields
1813
+ }
1814
+ \`\`\`
1815
+
1816
+ Rules:
1817
+ - Each block must contain valid JSON with a "type" field.
1818
+ - The "type" must match a registered action component on the frontend.
1819
+ - Multiple action blocks per response are allowed.
1820
+ - Surround action blocks with normal markdown text for user context.
1821
+ - The action block is rendered as an interactive component in the chat UI.
1822
+ - SQL strings inside JSON must be properly escaped (newlines as \\n, quotes as \\").
1823
+
1824
+ Available action types:
1825
+
1826
+ - "optimap-query": Displays SQL queries with a button to execute them and
1827
+ update the 3D globe map.
1828
+ Required fields:
1829
+ - type: "optimap-query"
1830
+ - locations_sql: string (the validated locations SQL query)
1831
+ - routes_sql: string (the validated routes SQL query)
1832
+ - database_name: string (the target database name)
1833
+ </inline_actions>
1834
+ `;
1835
+
1215
1836
  exports.ActionBar = ActionBar;
1837
+ exports.ActionMarkdownRenderer = ActionMarkdownRenderer;
1216
1838
  exports.ActivityIndicators = ActivityIndicators;
1217
1839
  exports.AgentResponse = AgentResponse;
1840
+ exports.AgentTimeline = AgentTimeline;
1218
1841
  exports.HITLInteractionRecord = HITLInteractionRecord;
1219
1842
  exports.HITLQuestionPanel = HITLQuestionPanel;
1220
1843
  exports.HITLSection = HITLSection;
1844
+ exports.INLINE_ACTION_PROMPT = INLINE_ACTION_PROMPT;
1221
1845
  exports.MetadataRow = MetadataRow;
1222
1846
  exports.ThinkingSection = ThinkingSection;
1847
+ exports.TruncatedMessage = TruncatedMessage;
1223
1848
  exports.UserPrompt = UserPrompt;
1224
1849
  exports.UserPromptInput = UserPromptInput;
1225
1850
  exports.buildResponseString = buildResponseString;
1851
+ exports.buildTimelineEntries = buildTimelineEntries;
1852
+ exports.createTimelineUIState = createTimelineUIState;
1853
+ exports.deduplicateEntries = deduplicateEntries;
1226
1854
  exports.formatTime = formatTime;
1227
1855
  exports.formatTotalTime = formatTotalTime;
1856
+ exports.groupIntoAgentRuns = groupIntoAgentRuns;
1228
1857
  exports.initialAgentResponseState = initialAgentResponseState;
1858
+ exports.parseResponseSegments = parseResponseSegments;
1229
1859
  exports.useAgentResponseAccumulator = useAgentResponseAccumulator;
1230
1860
  exports.useThinkingTimer = useThinkingTimer;
1231
1861
  //# sourceMappingURL=index.cjs.map