@optilogic/chat 1.0.0-beta.10 → 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.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React11 from 'react';
2
2
  import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
3
3
  import { cn, Popover, PopoverTrigger, PopoverContent, Button, Textarea, IconButton, LoadingSpinner } from '@optilogic/core';
4
- import { Activity, Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, ChevronDown, ChevronRight, MessageCircleQuestion, Square, Loader2, Send, ChevronUp } from 'lucide-react';
4
+ import { Activity, Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, ChevronDown, ChevronRight, MessageCircleQuestion, Square, Loader2, Send, ChevronUp, Brain, BookOpen, MessageSquare, AlertCircle, ChevronsDownUp, ChevronsUpDown } from 'lucide-react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
  import { SlateEditor, Text } from '@optilogic/editor';
7
7
 
@@ -234,7 +234,7 @@ var ThinkingSection = React11.forwardRef(
234
234
  ref,
235
235
  className: cn("px-3 pb-3 border-t border-border", className),
236
236
  ...props,
237
- children: /* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: isStructured ? /* @__PURE__ */ jsx("div", { className: "space-y-0", children: content.map((step) => /* @__PURE__ */ jsx(
237
+ children: /* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto scrollbar-thin", children: isStructured ? /* @__PURE__ */ jsx("div", { className: "space-y-0", children: content.map((step) => /* @__PURE__ */ jsx(
238
238
  ThinkingStepItem,
239
239
  {
240
240
  step,
@@ -410,7 +410,7 @@ var HITLQuestionPanel = React11.forwardRef(({ question, onSubmit, onStop, classN
410
410
  selectedOptions,
411
411
  freeformText
412
412
  );
413
- onSubmit(combined);
413
+ onSubmit(combined, { selectedOptions, freeformText });
414
414
  }, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
415
415
  const handleKeyDown = useCallback(
416
416
  (e) => {
@@ -684,6 +684,126 @@ var initialAgentResponseState = {
684
684
  firstMessageTime: null
685
685
  };
686
686
 
687
+ // src/components/agent-timeline/utils.ts
688
+ function buildTimelineEntries(state) {
689
+ const entries = [];
690
+ if (state.thinkingSteps) {
691
+ let idx = 0;
692
+ for (const step of state.thinkingSteps) {
693
+ entries.push({
694
+ id: `tl-think-${idx++}`,
695
+ type: "thinking",
696
+ agentName: step.agentName ?? null,
697
+ parentAgent: step.parentAgent ?? null,
698
+ depth: step.depth ?? 0,
699
+ content: step.content,
700
+ title: step.label,
701
+ timestamp: step.timestamp ?? 0
702
+ });
703
+ }
704
+ } else if (state.thinking) {
705
+ entries.push({
706
+ id: "tl-think-0",
707
+ type: "thinking",
708
+ agentName: null,
709
+ parentAgent: null,
710
+ depth: 0,
711
+ content: state.thinking,
712
+ title: null,
713
+ timestamp: state.thinkingStartTime ?? 0
714
+ });
715
+ }
716
+ let toolIdx = 0;
717
+ for (const tool of state.toolCalls) {
718
+ entries.push({
719
+ id: `tl-tool-${toolIdx++}`,
720
+ type: "tool_call",
721
+ agentName: tool.agentName ?? null,
722
+ parentAgent: tool.parentAgent ?? null,
723
+ depth: tool.depth ?? 0,
724
+ content: tool.name,
725
+ title: null,
726
+ timestamp: tool.timestamp
727
+ });
728
+ }
729
+ let knowIdx = 0;
730
+ for (const item of state.knowledge) {
731
+ entries.push({
732
+ id: `tl-know-${knowIdx++}`,
733
+ type: "knowledge",
734
+ agentName: item.agentName ?? null,
735
+ parentAgent: item.parentAgent ?? null,
736
+ depth: item.depth ?? 0,
737
+ content: item.content,
738
+ title: item.source,
739
+ timestamp: item.timestamp
740
+ });
741
+ }
742
+ let memIdx = 0;
743
+ for (const item of state.memory) {
744
+ entries.push({
745
+ id: `tl-mem-${memIdx++}`,
746
+ type: "memory",
747
+ agentName: item.agentName ?? null,
748
+ parentAgent: item.parentAgent ?? null,
749
+ depth: item.depth ?? 0,
750
+ content: item.content,
751
+ title: item.type,
752
+ timestamp: item.timestamp
753
+ });
754
+ }
755
+ let statIdx = 0;
756
+ for (const item of state.statusUpdates) {
757
+ entries.push({
758
+ id: `tl-stat-${statIdx++}`,
759
+ type: "status_update",
760
+ agentName: item.agentName ?? item.agent ?? null,
761
+ parentAgent: item.parentAgent ?? null,
762
+ depth: item.depth ?? 0,
763
+ content: item.message,
764
+ title: null,
765
+ timestamp: item.timestamp
766
+ });
767
+ }
768
+ entries.sort((a, b) => a.timestamp - b.timestamp);
769
+ return entries;
770
+ }
771
+ function groupIntoAgentRuns(entries) {
772
+ const runs = [];
773
+ let currentRun = null;
774
+ for (const entry of entries) {
775
+ const name = entry.agentName || "Agent";
776
+ if (!currentRun || currentRun.agentName !== name) {
777
+ currentRun = {
778
+ agentName: name,
779
+ parentAgent: entry.parentAgent,
780
+ depth: entry.depth,
781
+ entries: []
782
+ };
783
+ runs.push(currentRun);
784
+ }
785
+ currentRun.entries.push({ entry, count: 1 });
786
+ }
787
+ for (const run of runs) {
788
+ run.entries = deduplicateEntries(run.entries);
789
+ }
790
+ return runs;
791
+ }
792
+ function deduplicateEntries(entries) {
793
+ if (entries.length === 0) return [];
794
+ const result = [entries[0]];
795
+ for (let i = 1; i < entries.length; i++) {
796
+ const prev = result[result.length - 1];
797
+ const curr = entries[i];
798
+ if (prev.entry.type === curr.entry.type && prev.entry.content === curr.entry.content) {
799
+ prev.count += curr.count;
800
+ } else {
801
+ result.push({ ...curr });
802
+ }
803
+ }
804
+ return result;
805
+ }
806
+
687
807
  // src/components/agent-response/hooks/useAgentResponseAccumulator.ts
688
808
  function useAgentResponseAccumulator(options) {
689
809
  const [state, setState] = useState(initialAgentResponseState);
@@ -717,28 +837,44 @@ function useAgentResponseAccumulator(options) {
717
837
  id: payload.thinkingStep.id || `step-${Date.now()}`,
718
838
  label: payload.thinkingStep.label,
719
839
  content: payload.thinkingStep.content,
720
- depth: payload.thinkingStep.depth ?? 0,
721
- isCollapsed: payload.thinkingStep.isCollapsed
840
+ depth: payload.thinkingStep.depth ?? payload.depth ?? 0,
841
+ isCollapsed: payload.thinkingStep.isCollapsed,
842
+ timestamp: Date.now(),
843
+ agentName: payload.agentName,
844
+ parentAgent: payload.parentAgent
722
845
  };
723
846
  const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
724
- return {
847
+ const next2 = {
725
848
  ...prev,
726
849
  status: newStatus,
727
850
  thinkingSteps: [...prev.thinkingSteps || [], newStep],
728
851
  thinkingStartTime: thinkingStartTime2,
729
852
  firstMessageTime
730
853
  };
854
+ return { ...next2, timelineEntries: buildTimelineEntries(next2) };
731
855
  }
732
856
  const newThinking = payload.message || payload.content || "";
733
857
  const separator = prev.thinking && newThinking ? "\n\n" : "";
734
858
  const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
735
- return {
859
+ const prevSteps = prev.thinkingSteps || [];
860
+ const plainStep = {
861
+ id: `step-${prevSteps.length}`,
862
+ label: newThinking,
863
+ content: newThinking,
864
+ depth: payload.depth ?? 0,
865
+ timestamp: Date.now(),
866
+ agentName: payload.agentName,
867
+ parentAgent: payload.parentAgent
868
+ };
869
+ const next = {
736
870
  ...prev,
737
871
  status: newStatus,
738
872
  thinking: prev.thinking + separator + newThinking,
873
+ thinkingSteps: [...prevSteps, plainStep],
739
874
  thinkingStartTime,
740
875
  firstMessageTime
741
876
  };
877
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
742
878
  }
743
879
  case "tool_call": {
744
880
  const toolName = payload.message || payload.tool?.name;
@@ -747,14 +883,18 @@ function useAgentResponseAccumulator(options) {
747
883
  id: payload.tool?.id || `tool-${Date.now()}`,
748
884
  name: toolName,
749
885
  arguments: payload.tool?.arguments,
750
- timestamp: Date.now()
886
+ timestamp: Date.now(),
887
+ agentName: payload.agentName,
888
+ parentAgent: payload.parentAgent,
889
+ depth: payload.depth
751
890
  };
752
- return {
891
+ const next = {
753
892
  ...prev,
754
893
  status: newStatus,
755
894
  toolCalls: [...prev.toolCalls, newToolCall],
756
895
  firstMessageTime
757
896
  };
897
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
758
898
  }
759
899
  return { ...prev, status: newStatus, firstMessageTime };
760
900
  }
@@ -765,14 +905,18 @@ function useAgentResponseAccumulator(options) {
765
905
  id: payload.knowledge?.id || `knowledge-${Date.now()}`,
766
906
  source: payload.knowledge?.source || "unknown",
767
907
  content: knowledgeContent,
768
- timestamp: Date.now()
908
+ timestamp: Date.now(),
909
+ agentName: payload.agentName,
910
+ parentAgent: payload.parentAgent,
911
+ depth: payload.depth
769
912
  };
770
- return {
913
+ const next = {
771
914
  ...prev,
772
915
  status: newStatus,
773
916
  knowledge: [...prev.knowledge, newKnowledge],
774
917
  firstMessageTime
775
918
  };
919
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
776
920
  }
777
921
  return { ...prev, status: newStatus, firstMessageTime };
778
922
  }
@@ -783,14 +927,18 @@ function useAgentResponseAccumulator(options) {
783
927
  id: payload.memory?.id || `memory-${Date.now()}`,
784
928
  type: payload.memory?.type || "unknown",
785
929
  content: memoryContent,
786
- timestamp: Date.now()
930
+ timestamp: Date.now(),
931
+ agentName: payload.agentName,
932
+ parentAgent: payload.parentAgent,
933
+ depth: payload.depth
787
934
  };
788
- return {
935
+ const next = {
789
936
  ...prev,
790
937
  status: newStatus,
791
938
  memory: [...prev.memory, newMemory],
792
939
  firstMessageTime
793
940
  };
941
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
794
942
  }
795
943
  return { ...prev, status: newStatus, firstMessageTime };
796
944
  }
@@ -809,14 +957,18 @@ function useAgentResponseAccumulator(options) {
809
957
  id: payload.statusUpdate?.id || `status-${Date.now()}`,
810
958
  message: statusMessage,
811
959
  agent: payload.statusUpdate?.agent,
812
- timestamp: Date.now()
960
+ timestamp: Date.now(),
961
+ agentName: payload.agentName,
962
+ parentAgent: payload.parentAgent,
963
+ depth: payload.depth
813
964
  };
814
- return {
965
+ const next = {
815
966
  ...prev,
816
967
  status: newStatus,
817
968
  statusUpdates: [...prev.statusUpdates, newStatusItem],
818
969
  firstMessageTime
819
970
  };
971
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
820
972
  }
821
973
  return { ...prev, status: newStatus, firstMessageTime };
822
974
  }
@@ -832,6 +984,287 @@ function useAgentResponseAccumulator(options) {
832
984
  }, []);
833
985
  return { state, handleMessage, reset };
834
986
  }
987
+ var ICON_MAP = {
988
+ thinking: Brain,
989
+ tool_call: Wrench,
990
+ knowledge: BookOpen,
991
+ memory: HardDrive,
992
+ status_update: Activity,
993
+ ai_response: MessageSquare,
994
+ error: AlertCircle
995
+ };
996
+ function TimelineItem({
997
+ displayEntry,
998
+ renderMarkdown,
999
+ isExpanded,
1000
+ onToggleExpanded
1001
+ }) {
1002
+ const { entry, count } = displayEntry;
1003
+ const Icon = ICON_MAP[entry.type] ?? Activity;
1004
+ const isLong = entry.content.length > 200 || entry.content.split("\n").length > 3;
1005
+ const canExpand = isLong || entry.type === "ai_response";
1006
+ return /* @__PURE__ */ jsxs("div", { className: "py-1 flex items-start gap-2 group", children: [
1007
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0 mt-0.5" }),
1008
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: isExpanded && entry.type === "ai_response" && renderMarkdown ? (
1009
+ // Expanded AI response: rendered markdown
1010
+ /* @__PURE__ */ jsxs("div", { children: [
1011
+ renderMarkdown(entry.content),
1012
+ /* @__PURE__ */ jsx(
1013
+ "button",
1014
+ {
1015
+ onClick: onToggleExpanded,
1016
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1017
+ children: "Show less"
1018
+ }
1019
+ )
1020
+ ] })
1021
+ ) : isExpanded ? (
1022
+ // Expanded non-AI: plain text
1023
+ /* @__PURE__ */ jsxs("div", { children: [
1024
+ /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: entry.content }),
1025
+ /* @__PURE__ */ jsx(
1026
+ "button",
1027
+ {
1028
+ onClick: onToggleExpanded,
1029
+ className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
1030
+ children: "Show less"
1031
+ }
1032
+ )
1033
+ ] })
1034
+ ) : (
1035
+ // Collapsed: truncated with optional expand
1036
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5 min-w-0", children: [
1037
+ /* @__PURE__ */ jsx(
1038
+ "div",
1039
+ {
1040
+ className: `text-xs text-muted-foreground min-w-0 ${canExpand ? "line-clamp-2 cursor-pointer hover:text-foreground/80" : ""}`,
1041
+ onClick: canExpand ? onToggleExpanded : void 0,
1042
+ children: entry.content
1043
+ }
1044
+ ),
1045
+ count > 1 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground/60 whitespace-nowrap flex-shrink-0", children: [
1046
+ "(x",
1047
+ count,
1048
+ ")"
1049
+ ] })
1050
+ ] })
1051
+ ) })
1052
+ ] });
1053
+ }
1054
+ function TimelineAgentBlock({
1055
+ block,
1056
+ renderMarkdown,
1057
+ isSingleAgent,
1058
+ isCollapsed,
1059
+ onToggleCollapsed,
1060
+ expandedItems,
1061
+ onToggleItemExpanded
1062
+ }) {
1063
+ const indentPx = block.depth * 16;
1064
+ if (isSingleAgent && block.depth === 0) {
1065
+ return /* @__PURE__ */ jsx("div", { style: { paddingLeft: `${indentPx}px` }, children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsx(
1066
+ TimelineItem,
1067
+ {
1068
+ displayEntry,
1069
+ renderMarkdown,
1070
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1071
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1072
+ },
1073
+ displayEntry.entry.id + "-" + i
1074
+ )) });
1075
+ }
1076
+ return /* @__PURE__ */ jsxs("div", { style: { paddingLeft: `${indentPx}px` }, children: [
1077
+ /* @__PURE__ */ jsxs(
1078
+ "button",
1079
+ {
1080
+ onClick: onToggleCollapsed,
1081
+ 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",
1082
+ children: [
1083
+ isCollapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }),
1084
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground/80", children: block.agentName }),
1085
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground/60", children: [
1086
+ "(",
1087
+ block.entries.reduce((sum, e) => sum + e.count, 0),
1088
+ ")"
1089
+ ] })
1090
+ ]
1091
+ }
1092
+ ),
1093
+ !isCollapsed && /* @__PURE__ */ jsx("div", { className: "ml-4", children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsx(
1094
+ TimelineItem,
1095
+ {
1096
+ displayEntry,
1097
+ renderMarkdown,
1098
+ isExpanded: expandedItems.has(displayEntry.entry.id),
1099
+ onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
1100
+ },
1101
+ displayEntry.entry.id + "-" + i
1102
+ )) })
1103
+ ] });
1104
+ }
1105
+ function createTimelineUIState() {
1106
+ return {
1107
+ expandedItems: /* @__PURE__ */ new Set(),
1108
+ collapsedRuns: /* @__PURE__ */ new Set(),
1109
+ activeFilters: /* @__PURE__ */ new Set()
1110
+ };
1111
+ }
1112
+ var TYPE_CONFIG = [
1113
+ { type: "status_update", icon: Activity, label: "Status" },
1114
+ { type: "thinking", icon: Brain, label: "Thinking" },
1115
+ { type: "tool_call", icon: Wrench, label: "Tools" },
1116
+ { type: "knowledge", icon: BookOpen, label: "Knowledge" },
1117
+ { type: "memory", icon: HardDrive, label: "Memory" },
1118
+ { type: "ai_response", icon: MessageSquare, label: "AI" },
1119
+ { type: "error", icon: AlertCircle, label: "Errors" }
1120
+ ];
1121
+ function AgentTimeline({ entries, renderMarkdown, uiState, maxHeight = "300px" }) {
1122
+ const containerRef = useRef(null);
1123
+ const [renderTick, setRenderTick] = useState(0);
1124
+ const forceRender = useCallback(() => setRenderTick((t) => t + 1), []);
1125
+ const [internalExpandedItems] = useState(() => /* @__PURE__ */ new Set());
1126
+ const [internalCollapsedRuns] = useState(() => /* @__PURE__ */ new Set());
1127
+ const [internalActiveFilters] = useState(() => /* @__PURE__ */ new Set());
1128
+ const expandedItems = uiState?.expandedItems ?? internalExpandedItems;
1129
+ const collapsedRuns = uiState?.collapsedRuns ?? internalCollapsedRuns;
1130
+ const activeFilters = uiState?.activeFilters ?? internalActiveFilters;
1131
+ const availableTypes = useMemo(() => {
1132
+ const types = /* @__PURE__ */ new Set();
1133
+ for (const entry of entries) {
1134
+ types.add(entry.type);
1135
+ }
1136
+ return types;
1137
+ }, [entries]);
1138
+ const filteredEntries = useMemo(
1139
+ () => activeFilters.size === 0 ? entries : entries.filter((e) => activeFilters.has(e.type)),
1140
+ [entries, activeFilters, renderTick]
1141
+ );
1142
+ const agentRuns = useMemo(() => groupIntoAgentRuns(filteredEntries), [filteredEntries, renderTick]);
1143
+ const toggleFilter = useCallback((type) => {
1144
+ if (activeFilters.has(type)) {
1145
+ activeFilters.delete(type);
1146
+ } else {
1147
+ activeFilters.add(type);
1148
+ }
1149
+ forceRender();
1150
+ }, [activeFilters, forceRender]);
1151
+ const clearFilters = useCallback(() => {
1152
+ activeFilters.clear();
1153
+ forceRender();
1154
+ }, [activeFilters, forceRender]);
1155
+ const toggleItemExpanded = useCallback((entryId) => {
1156
+ if (expandedItems.has(entryId)) {
1157
+ expandedItems.delete(entryId);
1158
+ } else {
1159
+ expandedItems.add(entryId);
1160
+ }
1161
+ forceRender();
1162
+ }, [expandedItems, forceRender]);
1163
+ const collapseAll = useCallback(() => {
1164
+ collapsedRuns.clear();
1165
+ agentRuns.forEach((run, i) => {
1166
+ collapsedRuns.add(`${run.agentName}-${i}`);
1167
+ });
1168
+ expandedItems.clear();
1169
+ forceRender();
1170
+ }, [agentRuns, collapsedRuns, expandedItems, forceRender]);
1171
+ const expandAll = useCallback(() => {
1172
+ collapsedRuns.clear();
1173
+ agentRuns.forEach((run, i) => {
1174
+ collapsedRuns.add(`${run.agentName}-${i}:expanded`);
1175
+ });
1176
+ forceRender();
1177
+ }, [agentRuns, collapsedRuns, forceRender]);
1178
+ if (entries.length === 0) return null;
1179
+ const isSingle = agentRuns.length === 1;
1180
+ const hasActiveFilter = activeFilters.size > 0;
1181
+ const scrollStyle = maxHeight !== "none" ? { maxHeight } : void 0;
1182
+ return /* @__PURE__ */ jsxs(
1183
+ "div",
1184
+ {
1185
+ ref: containerRef,
1186
+ className: `-mt-1 ${maxHeight !== "none" ? "overflow-y-auto scrollbar-thin" : ""}`,
1187
+ style: scrollStyle,
1188
+ children: [
1189
+ /* @__PURE__ */ 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: [
1190
+ TYPE_CONFIG.filter((tc) => availableTypes.has(tc.type)).map((tc) => {
1191
+ const isActive = activeFilters.has(tc.type);
1192
+ const count = entries.filter((e) => e.type === tc.type).length;
1193
+ return /* @__PURE__ */ jsxs(
1194
+ "button",
1195
+ {
1196
+ onClick: () => toggleFilter(tc.type),
1197
+ 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"}`,
1198
+ title: `${isActive ? "Hide" : "Show only"} ${tc.label}`,
1199
+ children: [
1200
+ /* @__PURE__ */ jsx(tc.icon, { className: "w-3 h-3" }),
1201
+ /* @__PURE__ */ jsx("span", { children: count })
1202
+ ]
1203
+ },
1204
+ tc.type
1205
+ );
1206
+ }),
1207
+ hasActiveFilter && /* @__PURE__ */ jsx(
1208
+ "button",
1209
+ {
1210
+ onClick: clearFilters,
1211
+ className: "text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1",
1212
+ children: "Clear"
1213
+ }
1214
+ ),
1215
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
1216
+ !isSingle && /* @__PURE__ */ jsxs(Fragment, { children: [
1217
+ /* @__PURE__ */ jsx(
1218
+ "button",
1219
+ {
1220
+ onClick: collapseAll,
1221
+ 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",
1222
+ title: "Collapse all",
1223
+ children: /* @__PURE__ */ jsx(ChevronsDownUp, { className: "w-3 h-3" })
1224
+ }
1225
+ ),
1226
+ /* @__PURE__ */ jsx(
1227
+ "button",
1228
+ {
1229
+ onClick: expandAll,
1230
+ 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",
1231
+ title: "Expand all",
1232
+ children: /* @__PURE__ */ jsx(ChevronsUpDown, { className: "w-3 h-3" })
1233
+ }
1234
+ )
1235
+ ] })
1236
+ ] }),
1237
+ filteredEntries.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-[10px] text-muted-foreground/50 py-2 text-center", children: "No entries match the selected filters" }) : /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: agentRuns.map((run, i) => {
1238
+ const runKey = `${run.agentName}-${i}`;
1239
+ const defaultCollapsed = run.depth > 0;
1240
+ const isCollapsed = collapsedRuns.has(runKey) ? true : collapsedRuns.has(`${runKey}:expanded`) ? false : defaultCollapsed;
1241
+ return /* @__PURE__ */ jsx(
1242
+ TimelineAgentBlock,
1243
+ {
1244
+ block: run,
1245
+ renderMarkdown,
1246
+ isSingleAgent: isSingle,
1247
+ isCollapsed,
1248
+ onToggleCollapsed: () => {
1249
+ if (isCollapsed) {
1250
+ collapsedRuns.delete(runKey);
1251
+ collapsedRuns.add(`${runKey}:expanded`);
1252
+ } else {
1253
+ collapsedRuns.delete(`${runKey}:expanded`);
1254
+ collapsedRuns.add(runKey);
1255
+ }
1256
+ forceRender();
1257
+ },
1258
+ expandedItems,
1259
+ onToggleItemExpanded: toggleItemExpanded
1260
+ },
1261
+ runKey
1262
+ );
1263
+ }) })
1264
+ ]
1265
+ }
1266
+ );
1267
+ }
835
1268
  var AgentResponse = React11.forwardRef(
836
1269
  ({
837
1270
  state,
@@ -852,6 +1285,7 @@ var AgentResponse = React11.forwardRef(
852
1285
  className,
853
1286
  ...props
854
1287
  }, ref) => {
1288
+ const timelineUIStateRef = useRef(createTimelineUIState());
855
1289
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultThinkingExpanded);
856
1290
  const isThinkingControlled = controlledThinkingExpanded !== void 0;
857
1291
  const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
@@ -873,7 +1307,8 @@ var AgentResponse = React11.forwardRef(
873
1307
  if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
874
1308
  return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
875
1309
  }, [state.firstMessageTime, state.responseCompleteTime]);
876
- const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || false;
1310
+ const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
1311
+ const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
877
1312
  const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
878
1313
  const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
879
1314
  const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
@@ -908,7 +1343,14 @@ var AgentResponse = React11.forwardRef(
908
1343
  elapsedTime
909
1344
  }
910
1345
  ),
911
- /* @__PURE__ */ jsx(
1346
+ hasTimelineEntries ? thinkingExpanded && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 border-t border-border mt-2", children: /* @__PURE__ */ jsx(
1347
+ AgentTimeline,
1348
+ {
1349
+ entries: state.timelineEntries,
1350
+ renderMarkdown: renderThinkingMarkdown,
1351
+ uiState: timelineUIStateRef.current
1352
+ }
1353
+ ) }) : /* @__PURE__ */ jsx(
912
1354
  ThinkingSection,
913
1355
  {
914
1356
  content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
@@ -1215,6 +1657,115 @@ var UserPromptInput = React11.forwardRef(
1215
1657
  );
1216
1658
  UserPromptInput.displayName = "UserPromptInput";
1217
1659
 
1218
- export { ActionBar, ActivityIndicators, AgentResponse, HITLInteractionRecord, HITLQuestionPanel, HITLSection, MetadataRow, ThinkingSection, TruncatedMessage, UserPrompt, UserPromptInput, buildResponseString, formatTime, formatTotalTime, initialAgentResponseState, useAgentResponseAccumulator, useThinkingTimer };
1660
+ // src/components/inline-actions/parseResponseSegments.ts
1661
+ var ACTION_BLOCK_REGEX = /```json:action\s*\n([\s\S]*?)```/g;
1662
+ function parseResponseSegments(text) {
1663
+ if (!text) return [];
1664
+ const segments = [];
1665
+ let lastIndex = 0;
1666
+ ACTION_BLOCK_REGEX.lastIndex = 0;
1667
+ let match;
1668
+ while ((match = ACTION_BLOCK_REGEX.exec(text)) !== null) {
1669
+ const before = text.slice(lastIndex, match.index);
1670
+ if (before.trim()) {
1671
+ segments.push({ kind: "markdown", content: before });
1672
+ }
1673
+ const jsonContent = match[1].trim();
1674
+ let parsed = null;
1675
+ try {
1676
+ parsed = JSON.parse(jsonContent);
1677
+ } catch {
1678
+ }
1679
+ if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
1680
+ segments.push({
1681
+ kind: "action",
1682
+ actionType: parsed.type,
1683
+ payload: parsed
1684
+ });
1685
+ } else {
1686
+ const rawBlock = match[0];
1687
+ segments.push({ kind: "markdown", content: rawBlock });
1688
+ }
1689
+ lastIndex = match.index + match[0].length;
1690
+ }
1691
+ const trailing = text.slice(lastIndex);
1692
+ if (trailing.trim()) {
1693
+ segments.push({ kind: "markdown", content: trailing });
1694
+ }
1695
+ return segments;
1696
+ }
1697
+ function ActionMarkdownRenderer({
1698
+ content,
1699
+ registry,
1700
+ renderMarkdown,
1701
+ onAction,
1702
+ isLatest
1703
+ }) {
1704
+ const segments = useMemo(() => parseResponseSegments(content), [content]);
1705
+ if (segments.length === 1 && segments[0].kind === "markdown") {
1706
+ return /* @__PURE__ */ jsx(Fragment, { children: renderMarkdown(segments[0].content) });
1707
+ }
1708
+ return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, index) => {
1709
+ if (segment.kind === "markdown") {
1710
+ return /* @__PURE__ */ jsx("div", { children: renderMarkdown(segment.content) }, `md-${index}`);
1711
+ }
1712
+ const Component = registry[segment.actionType];
1713
+ if (!Component) {
1714
+ return /* @__PURE__ */ jsx(
1715
+ "pre",
1716
+ {
1717
+ className: "my-4 p-4 rounded-lg border border-border bg-muted text-sm font-mono overflow-x-auto",
1718
+ children: /* @__PURE__ */ jsx("code", { children: JSON.stringify(segment.payload, null, 2) })
1719
+ },
1720
+ `action-fallback-${index}`
1721
+ );
1722
+ }
1723
+ return /* @__PURE__ */ jsx(
1724
+ Component,
1725
+ {
1726
+ payload: segment.payload,
1727
+ onAction,
1728
+ isLatest
1729
+ },
1730
+ `action-${segment.actionType}-${index}`
1731
+ );
1732
+ }) });
1733
+ }
1734
+
1735
+ // src/components/inline-actions/prompts.ts
1736
+ var INLINE_ACTION_PROMPT = `
1737
+ <inline_actions>
1738
+ When your response should include interactive components (like query viewers,
1739
+ data tables, or executable actions), embed them as fenced code blocks using
1740
+ the \`json:action\` language tag:
1741
+
1742
+ \`\`\`json:action
1743
+ {
1744
+ "type": "action-type-here",
1745
+ ...action-specific fields
1746
+ }
1747
+ \`\`\`
1748
+
1749
+ Rules:
1750
+ - Each block must contain valid JSON with a "type" field.
1751
+ - The "type" must match a registered action component on the frontend.
1752
+ - Multiple action blocks per response are allowed.
1753
+ - Surround action blocks with normal markdown text for user context.
1754
+ - The action block is rendered as an interactive component in the chat UI.
1755
+ - SQL strings inside JSON must be properly escaped (newlines as \\n, quotes as \\").
1756
+
1757
+ Available action types:
1758
+
1759
+ - "optimap-query": Displays SQL queries with a button to execute them and
1760
+ update the 3D globe map.
1761
+ Required fields:
1762
+ - type: "optimap-query"
1763
+ - locations_sql: string (the validated locations SQL query)
1764
+ - routes_sql: string (the validated routes SQL query)
1765
+ - database_name: string (the target database name)
1766
+ </inline_actions>
1767
+ `;
1768
+
1769
+ export { ActionBar, ActionMarkdownRenderer, ActivityIndicators, AgentResponse, AgentTimeline, HITLInteractionRecord, HITLQuestionPanel, HITLSection, INLINE_ACTION_PROMPT, MetadataRow, ThinkingSection, TruncatedMessage, UserPrompt, UserPromptInput, buildResponseString, buildTimelineEntries, createTimelineUIState, deduplicateEntries, formatTime, formatTotalTime, groupIntoAgentRuns, initialAgentResponseState, parseResponseSegments, useAgentResponseAccumulator, useThinkingTimer };
1219
1770
  //# sourceMappingURL=index.js.map
1220
1771
  //# sourceMappingURL=index.js.map