@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.cjs CHANGED
@@ -255,7 +255,7 @@ var ThinkingSection = React11__namespace.forwardRef(
255
255
  ref,
256
256
  className: core.cn("px-3 pb-3 border-t border-border", className),
257
257
  ...props,
258
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: isStructured ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0", children: content.map((step) => /* @__PURE__ */ jsxRuntime.jsx(
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
259
  ThinkingStepItem,
260
260
  {
261
261
  step,
@@ -431,7 +431,7 @@ var HITLQuestionPanel = React11__namespace.forwardRef(({ question, onSubmit, onS
431
431
  selectedOptions,
432
432
  freeformText
433
433
  );
434
- onSubmit(combined);
434
+ onSubmit(combined, { selectedOptions, freeformText });
435
435
  }, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
436
436
  const handleKeyDown = React11.useCallback(
437
437
  (e) => {
@@ -705,6 +705,126 @@ var initialAgentResponseState = {
705
705
  firstMessageTime: null
706
706
  };
707
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
+
708
828
  // src/components/agent-response/hooks/useAgentResponseAccumulator.ts
709
829
  function useAgentResponseAccumulator(options) {
710
830
  const [state, setState] = React11.useState(initialAgentResponseState);
@@ -738,28 +858,44 @@ function useAgentResponseAccumulator(options) {
738
858
  id: payload.thinkingStep.id || `step-${Date.now()}`,
739
859
  label: payload.thinkingStep.label,
740
860
  content: payload.thinkingStep.content,
741
- depth: payload.thinkingStep.depth ?? 0,
742
- isCollapsed: payload.thinkingStep.isCollapsed
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
743
866
  };
744
867
  const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
745
- return {
868
+ const next2 = {
746
869
  ...prev,
747
870
  status: newStatus,
748
871
  thinkingSteps: [...prev.thinkingSteps || [], newStep],
749
872
  thinkingStartTime: thinkingStartTime2,
750
873
  firstMessageTime
751
874
  };
875
+ return { ...next2, timelineEntries: buildTimelineEntries(next2) };
752
876
  }
753
877
  const newThinking = payload.message || payload.content || "";
754
878
  const separator = prev.thinking && newThinking ? "\n\n" : "";
755
879
  const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
756
- 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 = {
757
891
  ...prev,
758
892
  status: newStatus,
759
893
  thinking: prev.thinking + separator + newThinking,
894
+ thinkingSteps: [...prevSteps, plainStep],
760
895
  thinkingStartTime,
761
896
  firstMessageTime
762
897
  };
898
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
763
899
  }
764
900
  case "tool_call": {
765
901
  const toolName = payload.message || payload.tool?.name;
@@ -768,14 +904,18 @@ function useAgentResponseAccumulator(options) {
768
904
  id: payload.tool?.id || `tool-${Date.now()}`,
769
905
  name: toolName,
770
906
  arguments: payload.tool?.arguments,
771
- timestamp: Date.now()
907
+ timestamp: Date.now(),
908
+ agentName: payload.agentName,
909
+ parentAgent: payload.parentAgent,
910
+ depth: payload.depth
772
911
  };
773
- return {
912
+ const next = {
774
913
  ...prev,
775
914
  status: newStatus,
776
915
  toolCalls: [...prev.toolCalls, newToolCall],
777
916
  firstMessageTime
778
917
  };
918
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
779
919
  }
780
920
  return { ...prev, status: newStatus, firstMessageTime };
781
921
  }
@@ -786,14 +926,18 @@ function useAgentResponseAccumulator(options) {
786
926
  id: payload.knowledge?.id || `knowledge-${Date.now()}`,
787
927
  source: payload.knowledge?.source || "unknown",
788
928
  content: knowledgeContent,
789
- timestamp: Date.now()
929
+ timestamp: Date.now(),
930
+ agentName: payload.agentName,
931
+ parentAgent: payload.parentAgent,
932
+ depth: payload.depth
790
933
  };
791
- return {
934
+ const next = {
792
935
  ...prev,
793
936
  status: newStatus,
794
937
  knowledge: [...prev.knowledge, newKnowledge],
795
938
  firstMessageTime
796
939
  };
940
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
797
941
  }
798
942
  return { ...prev, status: newStatus, firstMessageTime };
799
943
  }
@@ -804,14 +948,18 @@ function useAgentResponseAccumulator(options) {
804
948
  id: payload.memory?.id || `memory-${Date.now()}`,
805
949
  type: payload.memory?.type || "unknown",
806
950
  content: memoryContent,
807
- timestamp: Date.now()
951
+ timestamp: Date.now(),
952
+ agentName: payload.agentName,
953
+ parentAgent: payload.parentAgent,
954
+ depth: payload.depth
808
955
  };
809
- return {
956
+ const next = {
810
957
  ...prev,
811
958
  status: newStatus,
812
959
  memory: [...prev.memory, newMemory],
813
960
  firstMessageTime
814
961
  };
962
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
815
963
  }
816
964
  return { ...prev, status: newStatus, firstMessageTime };
817
965
  }
@@ -830,14 +978,18 @@ function useAgentResponseAccumulator(options) {
830
978
  id: payload.statusUpdate?.id || `status-${Date.now()}`,
831
979
  message: statusMessage,
832
980
  agent: payload.statusUpdate?.agent,
833
- timestamp: Date.now()
981
+ timestamp: Date.now(),
982
+ agentName: payload.agentName,
983
+ parentAgent: payload.parentAgent,
984
+ depth: payload.depth
834
985
  };
835
- return {
986
+ const next = {
836
987
  ...prev,
837
988
  status: newStatus,
838
989
  statusUpdates: [...prev.statusUpdates, newStatusItem],
839
990
  firstMessageTime
840
991
  };
992
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
841
993
  }
842
994
  return { ...prev, status: newStatus, firstMessageTime };
843
995
  }
@@ -853,6 +1005,287 @@ function useAgentResponseAccumulator(options) {
853
1005
  }, []);
854
1006
  return { state, handleMessage, reset };
855
1007
  }
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
+ }
856
1289
  var AgentResponse = React11__namespace.forwardRef(
857
1290
  ({
858
1291
  state,
@@ -873,6 +1306,7 @@ var AgentResponse = React11__namespace.forwardRef(
873
1306
  className,
874
1307
  ...props
875
1308
  }, ref) => {
1309
+ const timelineUIStateRef = React11.useRef(createTimelineUIState());
876
1310
  const [uncontrolledExpanded, setUncontrolledExpanded] = React11.useState(defaultThinkingExpanded);
877
1311
  const isThinkingControlled = controlledThinkingExpanded !== void 0;
878
1312
  const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
@@ -894,7 +1328,8 @@ var AgentResponse = React11__namespace.forwardRef(
894
1328
  if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
895
1329
  return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
896
1330
  }, [state.firstMessageTime, state.responseCompleteTime]);
897
- const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || false;
1331
+ const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
1332
+ const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
898
1333
  const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
899
1334
  const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
900
1335
  const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
@@ -929,7 +1364,14 @@ var AgentResponse = React11__namespace.forwardRef(
929
1364
  elapsedTime
930
1365
  }
931
1366
  ),
932
- /* @__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(
933
1375
  ThinkingSection,
934
1376
  {
935
1377
  content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
@@ -1236,21 +1678,138 @@ var UserPromptInput = React11__namespace.forwardRef(
1236
1678
  );
1237
1679
  UserPromptInput.displayName = "UserPromptInput";
1238
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
+ `;
1789
+
1239
1790
  exports.ActionBar = ActionBar;
1791
+ exports.ActionMarkdownRenderer = ActionMarkdownRenderer;
1240
1792
  exports.ActivityIndicators = ActivityIndicators;
1241
1793
  exports.AgentResponse = AgentResponse;
1794
+ exports.AgentTimeline = AgentTimeline;
1242
1795
  exports.HITLInteractionRecord = HITLInteractionRecord;
1243
1796
  exports.HITLQuestionPanel = HITLQuestionPanel;
1244
1797
  exports.HITLSection = HITLSection;
1798
+ exports.INLINE_ACTION_PROMPT = INLINE_ACTION_PROMPT;
1245
1799
  exports.MetadataRow = MetadataRow;
1246
1800
  exports.ThinkingSection = ThinkingSection;
1247
1801
  exports.TruncatedMessage = TruncatedMessage;
1248
1802
  exports.UserPrompt = UserPrompt;
1249
1803
  exports.UserPromptInput = UserPromptInput;
1250
1804
  exports.buildResponseString = buildResponseString;
1805
+ exports.buildTimelineEntries = buildTimelineEntries;
1806
+ exports.createTimelineUIState = createTimelineUIState;
1807
+ exports.deduplicateEntries = deduplicateEntries;
1251
1808
  exports.formatTime = formatTime;
1252
1809
  exports.formatTotalTime = formatTotalTime;
1810
+ exports.groupIntoAgentRuns = groupIntoAgentRuns;
1253
1811
  exports.initialAgentResponseState = initialAgentResponseState;
1812
+ exports.parseResponseSegments = parseResponseSegments;
1254
1813
  exports.useAgentResponseAccumulator = useAgentResponseAccumulator;
1255
1814
  exports.useThinkingTimer = useThinkingTimer;
1256
1815
  //# sourceMappingURL=index.cjs.map