@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.
- package/README.md +43 -0
- package/dist/index.cjs +709 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +283 -4
- package/dist/index.d.ts +283 -4
- package/dist/index.js +674 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/agent-response/AgentResponse.tsx +59 -13
- package/src/components/agent-response/components/MetadataRow.tsx +15 -4
- package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
- package/src/components/agent-response/components/index.ts +3 -0
- package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +65 -8
- package/src/components/agent-response/index.ts +19 -0
- package/src/components/agent-response/types.ts +61 -1
- package/src/components/agent-timeline/AgentTimeline.tsx +256 -0
- package/src/components/agent-timeline/TimelineAgentBlock.tsx +84 -0
- package/src/components/agent-timeline/TimelineItem.tsx +97 -0
- package/src/components/agent-timeline/index.ts +14 -0
- package/src/components/agent-timeline/types.ts +49 -0
- package/src/components/agent-timeline/utils.ts +189 -0
- package/src/components/hitl-interactions/HITLQuestionPanel.tsx +35 -21
- package/src/components/hitl-interactions/index.ts +1 -1
- package/src/components/inline-actions/ActionMarkdownRenderer.tsx +60 -0
- package/src/components/inline-actions/index.ts +18 -0
- package/src/components/inline-actions/parseResponseSegments.ts +66 -0
- package/src/components/inline-actions/prompts.ts +41 -0
- package/src/components/inline-actions/types.ts +57 -0
- package/src/components/user-prompt-input/UserPromptInput.tsx +13 -8
- package/src/components/user-prompt-input/types.ts +4 -0
- package/src/index.ts +29 -0
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as React11 from 'react';
|
|
2
2
|
import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
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';
|
|
3
|
+
import { cn, Popover, PopoverTrigger, PopoverContent, Button, Textarea, Tooltip, IconButton, LoadingSpinner } from '@optilogic/core';
|
|
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
|
|
|
8
8
|
// src/components/agent-response/AgentResponse.tsx
|
|
9
|
-
var ActivityIndicators =
|
|
9
|
+
var ActivityIndicators = React11.forwardRef(
|
|
10
10
|
({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
|
|
11
11
|
const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
|
|
12
12
|
if (!hasAnyActivity) return null;
|
|
@@ -125,7 +125,7 @@ function formatTotalTime(seconds) {
|
|
|
125
125
|
const minutes = seconds / 60;
|
|
126
126
|
return `${minutes.toFixed(1)}m`;
|
|
127
127
|
}
|
|
128
|
-
var MetadataRow =
|
|
128
|
+
var MetadataRow = React11.forwardRef(
|
|
129
129
|
({
|
|
130
130
|
hasThinking,
|
|
131
131
|
isExpanded,
|
|
@@ -134,6 +134,7 @@ var MetadataRow = React10.forwardRef(
|
|
|
134
134
|
knowledge,
|
|
135
135
|
memory,
|
|
136
136
|
statusUpdates = [],
|
|
137
|
+
statusContent,
|
|
137
138
|
status,
|
|
138
139
|
elapsedTime,
|
|
139
140
|
className,
|
|
@@ -158,7 +159,7 @@ var MetadataRow = React10.forwardRef(
|
|
|
158
159
|
return null;
|
|
159
160
|
};
|
|
160
161
|
const leftContent = renderLeftContent();
|
|
161
|
-
if (!leftContent && !hasActivity) {
|
|
162
|
+
if (!leftContent && !hasActivity && !statusContent) {
|
|
162
163
|
return null;
|
|
163
164
|
}
|
|
164
165
|
return /* @__PURE__ */ jsxs(
|
|
@@ -172,10 +173,11 @@ var MetadataRow = React10.forwardRef(
|
|
|
172
173
|
"button",
|
|
173
174
|
{
|
|
174
175
|
onClick: onToggle,
|
|
175
|
-
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",
|
|
176
|
+
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",
|
|
176
177
|
children: leftContent
|
|
177
178
|
}
|
|
178
|
-
) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: leftContent }),
|
|
179
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5 shrink-0", children: leftContent }),
|
|
180
|
+
statusContent && /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0 mx-2", children: statusContent }),
|
|
179
181
|
/* @__PURE__ */ jsx(
|
|
180
182
|
ActivityIndicators,
|
|
181
183
|
{
|
|
@@ -220,7 +222,7 @@ var ThinkingStepItem = ({ step, renderMarkdown }) => {
|
|
|
220
222
|
)
|
|
221
223
|
] });
|
|
222
224
|
};
|
|
223
|
-
var ThinkingSection =
|
|
225
|
+
var ThinkingSection = React11.forwardRef(
|
|
224
226
|
({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
|
|
225
227
|
if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
|
|
226
228
|
return null;
|
|
@@ -245,7 +247,7 @@ var ThinkingSection = React10.forwardRef(
|
|
|
245
247
|
}
|
|
246
248
|
);
|
|
247
249
|
ThinkingSection.displayName = "ThinkingSection";
|
|
248
|
-
var ActionBar =
|
|
250
|
+
var ActionBar = React11.forwardRef(
|
|
249
251
|
({
|
|
250
252
|
response,
|
|
251
253
|
isVisible,
|
|
@@ -349,22 +351,24 @@ A: ${answer}`);
|
|
|
349
351
|
}
|
|
350
352
|
return parts.join("\n\n");
|
|
351
353
|
}
|
|
352
|
-
var HITLQuestionPanel =
|
|
354
|
+
var HITLQuestionPanel = React11.forwardRef(({ question, onSubmit, onStop, className, ...props }, ref) => {
|
|
353
355
|
const [freeformText, setFreeformText] = useState("");
|
|
354
356
|
const [selectedOptions, setSelectedOptions] = useState({});
|
|
357
|
+
const hasTimeout = question.timeoutSeconds != null;
|
|
355
358
|
const [secondsLeft, setSecondsLeft] = useState(
|
|
356
|
-
() => Math.max(
|
|
359
|
+
() => hasTimeout ? Math.max(
|
|
357
360
|
0,
|
|
358
361
|
Math.round(
|
|
359
362
|
question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
|
|
360
363
|
)
|
|
361
|
-
)
|
|
364
|
+
) : Infinity
|
|
362
365
|
);
|
|
363
366
|
const textareaRef = useRef(null);
|
|
364
367
|
useEffect(() => {
|
|
365
368
|
textareaRef.current?.focus();
|
|
366
369
|
}, []);
|
|
367
370
|
useEffect(() => {
|
|
371
|
+
if (!hasTimeout) return;
|
|
368
372
|
const interval = setInterval(() => {
|
|
369
373
|
const remaining = Math.max(
|
|
370
374
|
0,
|
|
@@ -376,8 +380,8 @@ var HITLQuestionPanel = React10.forwardRef(({ question, onSubmit, onStop, classN
|
|
|
376
380
|
if (remaining <= 0) clearInterval(interval);
|
|
377
381
|
}, 1e3);
|
|
378
382
|
return () => clearInterval(interval);
|
|
379
|
-
}, [question.timeoutSeconds, question.receivedAt]);
|
|
380
|
-
const timedOut = secondsLeft <= 0;
|
|
383
|
+
}, [hasTimeout, question.timeoutSeconds, question.receivedAt]);
|
|
384
|
+
const timedOut = hasTimeout && secondsLeft <= 0;
|
|
381
385
|
const questionsWithOptions = useMemo(
|
|
382
386
|
() => question.questions.filter((q) => question.options?.[q]?.length),
|
|
383
387
|
[question.questions, question.options]
|
|
@@ -406,7 +410,7 @@ var HITLQuestionPanel = React10.forwardRef(({ question, onSubmit, onStop, classN
|
|
|
406
410
|
selectedOptions,
|
|
407
411
|
freeformText
|
|
408
412
|
);
|
|
409
|
-
onSubmit(combined);
|
|
413
|
+
onSubmit(combined, { selectedOptions, freeformText });
|
|
410
414
|
}, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
|
|
411
415
|
const handleKeyDown = useCallback(
|
|
412
416
|
(e) => {
|
|
@@ -434,7 +438,7 @@ var HITLQuestionPanel = React10.forwardRef(({ question, onSubmit, onStop, classN
|
|
|
434
438
|
children: [
|
|
435
439
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
436
440
|
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: question.reason }) }),
|
|
437
|
-
/* @__PURE__ */ jsx(
|
|
441
|
+
hasTimeout && /* @__PURE__ */ jsx(
|
|
438
442
|
"span",
|
|
439
443
|
{
|
|
440
444
|
className: cn(
|
|
@@ -517,7 +521,7 @@ function parseResponse(response) {
|
|
|
517
521
|
}
|
|
518
522
|
return { answers, additionalContext };
|
|
519
523
|
}
|
|
520
|
-
var HITLInteractionRecord =
|
|
524
|
+
var HITLInteractionRecord = React11.forwardRef(({ interaction, className, ...props }, ref) => {
|
|
521
525
|
const { question, response, respondedAt } = interaction;
|
|
522
526
|
const timestamp = new Date(respondedAt).toLocaleTimeString([], {
|
|
523
527
|
hour: "2-digit",
|
|
@@ -568,7 +572,7 @@ var HITLInteractionRecord = React10.forwardRef(({ interaction, className, ...pro
|
|
|
568
572
|
);
|
|
569
573
|
});
|
|
570
574
|
HITLInteractionRecord.displayName = "HITLInteractionRecord";
|
|
571
|
-
var HITLSection =
|
|
575
|
+
var HITLSection = React11.forwardRef(
|
|
572
576
|
({
|
|
573
577
|
interactions,
|
|
574
578
|
defaultExpanded = false,
|
|
@@ -621,6 +625,24 @@ var HITLSection = React10.forwardRef(
|
|
|
621
625
|
}
|
|
622
626
|
);
|
|
623
627
|
HITLSection.displayName = "HITLSection";
|
|
628
|
+
var TruncatedMessage = React11.forwardRef(
|
|
629
|
+
({ message, className, ...props }, ref) => {
|
|
630
|
+
return /* @__PURE__ */ jsx(
|
|
631
|
+
"div",
|
|
632
|
+
{
|
|
633
|
+
ref,
|
|
634
|
+
className: cn(
|
|
635
|
+
"text-xs text-muted-foreground truncate min-w-0",
|
|
636
|
+
className
|
|
637
|
+
),
|
|
638
|
+
title: message,
|
|
639
|
+
...props,
|
|
640
|
+
children: message
|
|
641
|
+
}
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
);
|
|
645
|
+
TruncatedMessage.displayName = "TruncatedMessage";
|
|
624
646
|
function useThinkingTimer({
|
|
625
647
|
startTime,
|
|
626
648
|
endTime,
|
|
@@ -656,12 +678,152 @@ var initialAgentResponseState = {
|
|
|
656
678
|
knowledge: [],
|
|
657
679
|
memory: [],
|
|
658
680
|
statusUpdates: [],
|
|
681
|
+
potentialResponses: [],
|
|
682
|
+
customTimelineEntries: [],
|
|
659
683
|
response: "",
|
|
660
684
|
thinkingStartTime: null,
|
|
661
685
|
responseCompleteTime: null,
|
|
662
686
|
firstMessageTime: null
|
|
663
687
|
};
|
|
664
688
|
|
|
689
|
+
// src/components/agent-timeline/utils.ts
|
|
690
|
+
function buildTimelineEntries(state) {
|
|
691
|
+
const entries = [];
|
|
692
|
+
if (state.thinkingSteps) {
|
|
693
|
+
let idx = 0;
|
|
694
|
+
for (const step of state.thinkingSteps) {
|
|
695
|
+
entries.push({
|
|
696
|
+
id: `tl-think-${idx++}`,
|
|
697
|
+
type: "thinking",
|
|
698
|
+
agentName: step.agentName ?? null,
|
|
699
|
+
parentAgent: step.parentAgent ?? null,
|
|
700
|
+
depth: step.depth ?? 0,
|
|
701
|
+
content: step.content,
|
|
702
|
+
title: step.label,
|
|
703
|
+
timestamp: step.timestamp ?? 0
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
} else if (state.thinking) {
|
|
707
|
+
entries.push({
|
|
708
|
+
id: "tl-think-0",
|
|
709
|
+
type: "thinking",
|
|
710
|
+
agentName: null,
|
|
711
|
+
parentAgent: null,
|
|
712
|
+
depth: 0,
|
|
713
|
+
content: state.thinking,
|
|
714
|
+
title: null,
|
|
715
|
+
timestamp: state.thinkingStartTime ?? 0
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
let toolIdx = 0;
|
|
719
|
+
for (const tool of state.toolCalls) {
|
|
720
|
+
entries.push({
|
|
721
|
+
id: `tl-tool-${toolIdx++}`,
|
|
722
|
+
type: "tool_call",
|
|
723
|
+
agentName: tool.agentName ?? null,
|
|
724
|
+
parentAgent: tool.parentAgent ?? null,
|
|
725
|
+
depth: tool.depth ?? 0,
|
|
726
|
+
content: tool.name,
|
|
727
|
+
title: null,
|
|
728
|
+
timestamp: tool.timestamp
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
let knowIdx = 0;
|
|
732
|
+
for (const item of state.knowledge) {
|
|
733
|
+
entries.push({
|
|
734
|
+
id: `tl-know-${knowIdx++}`,
|
|
735
|
+
type: "knowledge",
|
|
736
|
+
agentName: item.agentName ?? null,
|
|
737
|
+
parentAgent: item.parentAgent ?? null,
|
|
738
|
+
depth: item.depth ?? 0,
|
|
739
|
+
content: item.content,
|
|
740
|
+
title: item.source,
|
|
741
|
+
timestamp: item.timestamp
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
let memIdx = 0;
|
|
745
|
+
for (const item of state.memory) {
|
|
746
|
+
entries.push({
|
|
747
|
+
id: `tl-mem-${memIdx++}`,
|
|
748
|
+
type: "memory",
|
|
749
|
+
agentName: item.agentName ?? null,
|
|
750
|
+
parentAgent: item.parentAgent ?? null,
|
|
751
|
+
depth: item.depth ?? 0,
|
|
752
|
+
content: item.content,
|
|
753
|
+
title: item.type,
|
|
754
|
+
timestamp: item.timestamp
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
let statIdx = 0;
|
|
758
|
+
for (const item of state.statusUpdates) {
|
|
759
|
+
entries.push({
|
|
760
|
+
id: `tl-stat-${statIdx++}`,
|
|
761
|
+
type: "status_update",
|
|
762
|
+
agentName: item.agentName ?? item.agent ?? null,
|
|
763
|
+
parentAgent: item.parentAgent ?? null,
|
|
764
|
+
depth: item.depth ?? 0,
|
|
765
|
+
content: item.message,
|
|
766
|
+
title: null,
|
|
767
|
+
timestamp: item.timestamp
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
if (state.potentialResponses) {
|
|
771
|
+
let respIdx = 0;
|
|
772
|
+
for (const resp of state.potentialResponses) {
|
|
773
|
+
entries.push({
|
|
774
|
+
id: `tl-resp-${respIdx++}`,
|
|
775
|
+
type: "ai_response",
|
|
776
|
+
agentName: resp.agentName ?? null,
|
|
777
|
+
parentAgent: resp.parentAgent ?? null,
|
|
778
|
+
depth: resp.depth ?? 0,
|
|
779
|
+
content: resp.content,
|
|
780
|
+
title: null,
|
|
781
|
+
timestamp: resp.timestamp
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (state.customTimelineEntries) {
|
|
786
|
+
entries.push(...state.customTimelineEntries);
|
|
787
|
+
}
|
|
788
|
+
entries.sort((a, b) => a.timestamp - b.timestamp);
|
|
789
|
+
return entries;
|
|
790
|
+
}
|
|
791
|
+
function groupIntoAgentRuns(entries) {
|
|
792
|
+
const runs = [];
|
|
793
|
+
let currentRun = null;
|
|
794
|
+
for (const entry of entries) {
|
|
795
|
+
const name = entry.agentName || "Agent";
|
|
796
|
+
if (!currentRun || currentRun.agentName !== name) {
|
|
797
|
+
currentRun = {
|
|
798
|
+
agentName: name,
|
|
799
|
+
parentAgent: entry.parentAgent,
|
|
800
|
+
depth: entry.depth,
|
|
801
|
+
entries: []
|
|
802
|
+
};
|
|
803
|
+
runs.push(currentRun);
|
|
804
|
+
}
|
|
805
|
+
currentRun.entries.push({ entry, count: 1 });
|
|
806
|
+
}
|
|
807
|
+
for (const run of runs) {
|
|
808
|
+
run.entries = deduplicateEntries(run.entries);
|
|
809
|
+
}
|
|
810
|
+
return runs;
|
|
811
|
+
}
|
|
812
|
+
function deduplicateEntries(entries) {
|
|
813
|
+
if (entries.length === 0) return [];
|
|
814
|
+
const result = [entries[0]];
|
|
815
|
+
for (let i = 1; i < entries.length; i++) {
|
|
816
|
+
const prev = result[result.length - 1];
|
|
817
|
+
const curr = entries[i];
|
|
818
|
+
if (prev.entry.type === curr.entry.type && prev.entry.content === curr.entry.content) {
|
|
819
|
+
prev.count += curr.count;
|
|
820
|
+
} else {
|
|
821
|
+
result.push({ ...curr });
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
|
|
665
827
|
// src/components/agent-response/hooks/useAgentResponseAccumulator.ts
|
|
666
828
|
function useAgentResponseAccumulator(options) {
|
|
667
829
|
const [state, setState] = useState(initialAgentResponseState);
|
|
@@ -695,28 +857,44 @@ function useAgentResponseAccumulator(options) {
|
|
|
695
857
|
id: payload.thinkingStep.id || `step-${Date.now()}`,
|
|
696
858
|
label: payload.thinkingStep.label,
|
|
697
859
|
content: payload.thinkingStep.content,
|
|
698
|
-
depth: payload.thinkingStep.depth ?? 0,
|
|
699
|
-
isCollapsed: payload.thinkingStep.isCollapsed
|
|
860
|
+
depth: payload.thinkingStep.depth ?? payload.depth ?? 0,
|
|
861
|
+
isCollapsed: payload.thinkingStep.isCollapsed,
|
|
862
|
+
timestamp: Date.now(),
|
|
863
|
+
agentName: payload.agentName,
|
|
864
|
+
parentAgent: payload.parentAgent
|
|
700
865
|
};
|
|
701
866
|
const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
|
|
702
|
-
|
|
867
|
+
const next2 = {
|
|
703
868
|
...prev,
|
|
704
869
|
status: newStatus,
|
|
705
870
|
thinkingSteps: [...prev.thinkingSteps || [], newStep],
|
|
706
871
|
thinkingStartTime: thinkingStartTime2,
|
|
707
872
|
firstMessageTime
|
|
708
873
|
};
|
|
874
|
+
return { ...next2, timelineEntries: buildTimelineEntries(next2) };
|
|
709
875
|
}
|
|
710
876
|
const newThinking = payload.message || payload.content || "";
|
|
711
877
|
const separator = prev.thinking && newThinking ? "\n\n" : "";
|
|
712
878
|
const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
|
|
713
|
-
|
|
879
|
+
const prevSteps = prev.thinkingSteps || [];
|
|
880
|
+
const plainStep = {
|
|
881
|
+
id: `step-${prevSteps.length}`,
|
|
882
|
+
label: newThinking,
|
|
883
|
+
content: newThinking,
|
|
884
|
+
depth: payload.depth ?? 0,
|
|
885
|
+
timestamp: Date.now(),
|
|
886
|
+
agentName: payload.agentName,
|
|
887
|
+
parentAgent: payload.parentAgent
|
|
888
|
+
};
|
|
889
|
+
const next = {
|
|
714
890
|
...prev,
|
|
715
891
|
status: newStatus,
|
|
716
892
|
thinking: prev.thinking + separator + newThinking,
|
|
893
|
+
thinkingSteps: [...prevSteps, plainStep],
|
|
717
894
|
thinkingStartTime,
|
|
718
895
|
firstMessageTime
|
|
719
896
|
};
|
|
897
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
720
898
|
}
|
|
721
899
|
case "tool_call": {
|
|
722
900
|
const toolName = payload.message || payload.tool?.name;
|
|
@@ -725,14 +903,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
725
903
|
id: payload.tool?.id || `tool-${Date.now()}`,
|
|
726
904
|
name: toolName,
|
|
727
905
|
arguments: payload.tool?.arguments,
|
|
728
|
-
timestamp: Date.now()
|
|
906
|
+
timestamp: Date.now(),
|
|
907
|
+
agentName: payload.agentName,
|
|
908
|
+
parentAgent: payload.parentAgent,
|
|
909
|
+
depth: payload.depth
|
|
729
910
|
};
|
|
730
|
-
|
|
911
|
+
const next = {
|
|
731
912
|
...prev,
|
|
732
913
|
status: newStatus,
|
|
733
914
|
toolCalls: [...prev.toolCalls, newToolCall],
|
|
734
915
|
firstMessageTime
|
|
735
916
|
};
|
|
917
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
736
918
|
}
|
|
737
919
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
738
920
|
}
|
|
@@ -743,14 +925,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
743
925
|
id: payload.knowledge?.id || `knowledge-${Date.now()}`,
|
|
744
926
|
source: payload.knowledge?.source || "unknown",
|
|
745
927
|
content: knowledgeContent,
|
|
746
|
-
timestamp: Date.now()
|
|
928
|
+
timestamp: Date.now(),
|
|
929
|
+
agentName: payload.agentName,
|
|
930
|
+
parentAgent: payload.parentAgent,
|
|
931
|
+
depth: payload.depth
|
|
747
932
|
};
|
|
748
|
-
|
|
933
|
+
const next = {
|
|
749
934
|
...prev,
|
|
750
935
|
status: newStatus,
|
|
751
936
|
knowledge: [...prev.knowledge, newKnowledge],
|
|
752
937
|
firstMessageTime
|
|
753
938
|
};
|
|
939
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
754
940
|
}
|
|
755
941
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
756
942
|
}
|
|
@@ -761,14 +947,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
761
947
|
id: payload.memory?.id || `memory-${Date.now()}`,
|
|
762
948
|
type: payload.memory?.type || "unknown",
|
|
763
949
|
content: memoryContent,
|
|
764
|
-
timestamp: Date.now()
|
|
950
|
+
timestamp: Date.now(),
|
|
951
|
+
agentName: payload.agentName,
|
|
952
|
+
parentAgent: payload.parentAgent,
|
|
953
|
+
depth: payload.depth
|
|
765
954
|
};
|
|
766
|
-
|
|
955
|
+
const next = {
|
|
767
956
|
...prev,
|
|
768
957
|
status: newStatus,
|
|
769
958
|
memory: [...prev.memory, newMemory],
|
|
770
959
|
firstMessageTime
|
|
771
960
|
};
|
|
961
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
772
962
|
}
|
|
773
963
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
774
964
|
}
|
|
@@ -787,14 +977,39 @@ function useAgentResponseAccumulator(options) {
|
|
|
787
977
|
id: payload.statusUpdate?.id || `status-${Date.now()}`,
|
|
788
978
|
message: statusMessage,
|
|
789
979
|
agent: payload.statusUpdate?.agent,
|
|
790
|
-
timestamp: Date.now()
|
|
980
|
+
timestamp: Date.now(),
|
|
981
|
+
agentName: payload.agentName,
|
|
982
|
+
parentAgent: payload.parentAgent,
|
|
983
|
+
depth: payload.depth
|
|
791
984
|
};
|
|
792
|
-
|
|
985
|
+
const next = {
|
|
793
986
|
...prev,
|
|
794
987
|
status: newStatus,
|
|
795
988
|
statusUpdates: [...prev.statusUpdates, newStatusItem],
|
|
796
989
|
firstMessageTime
|
|
797
990
|
};
|
|
991
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
992
|
+
}
|
|
993
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
994
|
+
}
|
|
995
|
+
case "potential_response": {
|
|
996
|
+
const respContent = payload.message || payload.content || "";
|
|
997
|
+
if (respContent) {
|
|
998
|
+
const newResp = {
|
|
999
|
+
id: `resp-${Date.now()}`,
|
|
1000
|
+
content: respContent,
|
|
1001
|
+
timestamp: Date.now(),
|
|
1002
|
+
agentName: payload.agentName,
|
|
1003
|
+
parentAgent: payload.parentAgent,
|
|
1004
|
+
depth: payload.depth
|
|
1005
|
+
};
|
|
1006
|
+
const next = {
|
|
1007
|
+
...prev,
|
|
1008
|
+
status: newStatus,
|
|
1009
|
+
potentialResponses: [...prev.potentialResponses || [], newResp],
|
|
1010
|
+
firstMessageTime
|
|
1011
|
+
};
|
|
1012
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
798
1013
|
}
|
|
799
1014
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
800
1015
|
}
|
|
@@ -810,7 +1025,288 @@ function useAgentResponseAccumulator(options) {
|
|
|
810
1025
|
}, []);
|
|
811
1026
|
return { state, handleMessage, reset };
|
|
812
1027
|
}
|
|
813
|
-
var
|
|
1028
|
+
var ICON_MAP = {
|
|
1029
|
+
thinking: Brain,
|
|
1030
|
+
tool_call: Wrench,
|
|
1031
|
+
knowledge: BookOpen,
|
|
1032
|
+
memory: HardDrive,
|
|
1033
|
+
status_update: Activity,
|
|
1034
|
+
ai_response: MessageSquare,
|
|
1035
|
+
error: AlertCircle
|
|
1036
|
+
};
|
|
1037
|
+
function TimelineItem({
|
|
1038
|
+
displayEntry,
|
|
1039
|
+
renderMarkdown,
|
|
1040
|
+
isExpanded,
|
|
1041
|
+
onToggleExpanded
|
|
1042
|
+
}) {
|
|
1043
|
+
const { entry, count } = displayEntry;
|
|
1044
|
+
const Icon = ICON_MAP[entry.type] ?? Activity;
|
|
1045
|
+
const isLong = entry.content.length > 200 || entry.content.split("\n").length > 3;
|
|
1046
|
+
const canExpand = isLong || entry.type === "ai_response";
|
|
1047
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-1 flex items-start gap-2 group", children: [
|
|
1048
|
+
/* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0 mt-0.5" }),
|
|
1049
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: isExpanded && entry.type === "ai_response" && renderMarkdown ? (
|
|
1050
|
+
// Expanded AI response: rendered markdown
|
|
1051
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1052
|
+
renderMarkdown(entry.content),
|
|
1053
|
+
/* @__PURE__ */ jsx(
|
|
1054
|
+
"button",
|
|
1055
|
+
{
|
|
1056
|
+
onClick: onToggleExpanded,
|
|
1057
|
+
className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
|
|
1058
|
+
children: "Show less"
|
|
1059
|
+
}
|
|
1060
|
+
)
|
|
1061
|
+
] })
|
|
1062
|
+
) : isExpanded ? (
|
|
1063
|
+
// Expanded non-AI: plain text
|
|
1064
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1065
|
+
/* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: entry.content }),
|
|
1066
|
+
/* @__PURE__ */ jsx(
|
|
1067
|
+
"button",
|
|
1068
|
+
{
|
|
1069
|
+
onClick: onToggleExpanded,
|
|
1070
|
+
className: "text-[10px] text-muted-foreground/70 hover:text-muted-foreground mt-1",
|
|
1071
|
+
children: "Show less"
|
|
1072
|
+
}
|
|
1073
|
+
)
|
|
1074
|
+
] })
|
|
1075
|
+
) : (
|
|
1076
|
+
// Collapsed: truncated with optional expand
|
|
1077
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5 min-w-0", children: [
|
|
1078
|
+
/* @__PURE__ */ jsx(
|
|
1079
|
+
"div",
|
|
1080
|
+
{
|
|
1081
|
+
className: `text-xs text-muted-foreground min-w-0 ${canExpand ? "line-clamp-2 cursor-pointer hover:text-foreground/80" : ""}`,
|
|
1082
|
+
onClick: canExpand ? onToggleExpanded : void 0,
|
|
1083
|
+
children: entry.content
|
|
1084
|
+
}
|
|
1085
|
+
),
|
|
1086
|
+
count > 1 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground/60 whitespace-nowrap flex-shrink-0", children: [
|
|
1087
|
+
"(x",
|
|
1088
|
+
count,
|
|
1089
|
+
")"
|
|
1090
|
+
] })
|
|
1091
|
+
] })
|
|
1092
|
+
) })
|
|
1093
|
+
] });
|
|
1094
|
+
}
|
|
1095
|
+
function TimelineAgentBlock({
|
|
1096
|
+
block,
|
|
1097
|
+
renderMarkdown,
|
|
1098
|
+
isSingleAgent,
|
|
1099
|
+
isCollapsed,
|
|
1100
|
+
onToggleCollapsed,
|
|
1101
|
+
expandedItems,
|
|
1102
|
+
onToggleItemExpanded
|
|
1103
|
+
}) {
|
|
1104
|
+
const indentPx = block.depth * 16;
|
|
1105
|
+
if (isSingleAgent && block.depth === 0) {
|
|
1106
|
+
return /* @__PURE__ */ jsx("div", { style: { paddingLeft: `${indentPx}px` }, children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsx(
|
|
1107
|
+
TimelineItem,
|
|
1108
|
+
{
|
|
1109
|
+
displayEntry,
|
|
1110
|
+
renderMarkdown,
|
|
1111
|
+
isExpanded: expandedItems.has(displayEntry.entry.id),
|
|
1112
|
+
onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
|
|
1113
|
+
},
|
|
1114
|
+
displayEntry.entry.id + "-" + i
|
|
1115
|
+
)) });
|
|
1116
|
+
}
|
|
1117
|
+
return /* @__PURE__ */ jsxs("div", { style: { paddingLeft: `${indentPx}px` }, children: [
|
|
1118
|
+
/* @__PURE__ */ jsxs(
|
|
1119
|
+
"button",
|
|
1120
|
+
{
|
|
1121
|
+
onClick: onToggleCollapsed,
|
|
1122
|
+
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",
|
|
1123
|
+
children: [
|
|
1124
|
+
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" }),
|
|
1125
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground/80", children: block.agentName }),
|
|
1126
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground/60", children: [
|
|
1127
|
+
"(",
|
|
1128
|
+
block.entries.reduce((sum, e) => sum + e.count, 0),
|
|
1129
|
+
")"
|
|
1130
|
+
] })
|
|
1131
|
+
]
|
|
1132
|
+
}
|
|
1133
|
+
),
|
|
1134
|
+
!isCollapsed && /* @__PURE__ */ jsx("div", { className: "ml-4", children: block.entries.map((displayEntry, i) => /* @__PURE__ */ jsx(
|
|
1135
|
+
TimelineItem,
|
|
1136
|
+
{
|
|
1137
|
+
displayEntry,
|
|
1138
|
+
renderMarkdown,
|
|
1139
|
+
isExpanded: expandedItems.has(displayEntry.entry.id),
|
|
1140
|
+
onToggleExpanded: () => onToggleItemExpanded(displayEntry.entry.id)
|
|
1141
|
+
},
|
|
1142
|
+
displayEntry.entry.id + "-" + i
|
|
1143
|
+
)) })
|
|
1144
|
+
] });
|
|
1145
|
+
}
|
|
1146
|
+
function createTimelineUIState() {
|
|
1147
|
+
return {
|
|
1148
|
+
expandedItems: /* @__PURE__ */ new Set(),
|
|
1149
|
+
collapsedRuns: /* @__PURE__ */ new Set(),
|
|
1150
|
+
activeFilters: /* @__PURE__ */ new Set()
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
var TYPE_CONFIG = [
|
|
1154
|
+
{ type: "status_update", icon: Activity, label: "Status" },
|
|
1155
|
+
{ type: "thinking", icon: Brain, label: "Thinking" },
|
|
1156
|
+
{ type: "tool_call", icon: Wrench, label: "Tools" },
|
|
1157
|
+
{ type: "knowledge", icon: BookOpen, label: "Knowledge" },
|
|
1158
|
+
{ type: "memory", icon: HardDrive, label: "Memory" },
|
|
1159
|
+
{ type: "ai_response", icon: MessageSquare, label: "AI" },
|
|
1160
|
+
{ type: "error", icon: AlertCircle, label: "Errors" }
|
|
1161
|
+
];
|
|
1162
|
+
function AgentTimeline({ entries, renderMarkdown, uiState, maxHeight = "300px" }) {
|
|
1163
|
+
const containerRef = useRef(null);
|
|
1164
|
+
const [renderTick, setRenderTick] = useState(0);
|
|
1165
|
+
const forceRender = useCallback(() => setRenderTick((t) => t + 1), []);
|
|
1166
|
+
const [internalExpandedItems] = useState(() => /* @__PURE__ */ new Set());
|
|
1167
|
+
const [internalCollapsedRuns] = useState(() => /* @__PURE__ */ new Set());
|
|
1168
|
+
const [internalActiveFilters] = useState(() => /* @__PURE__ */ new Set());
|
|
1169
|
+
const expandedItems = uiState?.expandedItems ?? internalExpandedItems;
|
|
1170
|
+
const collapsedRuns = uiState?.collapsedRuns ?? internalCollapsedRuns;
|
|
1171
|
+
const activeFilters = uiState?.activeFilters ?? internalActiveFilters;
|
|
1172
|
+
const availableTypes = useMemo(() => {
|
|
1173
|
+
const types = /* @__PURE__ */ new Set();
|
|
1174
|
+
for (const entry of entries) {
|
|
1175
|
+
types.add(entry.type);
|
|
1176
|
+
}
|
|
1177
|
+
return types;
|
|
1178
|
+
}, [entries]);
|
|
1179
|
+
const filteredEntries = useMemo(
|
|
1180
|
+
() => activeFilters.size === 0 ? entries : entries.filter((e) => activeFilters.has(e.type)),
|
|
1181
|
+
[entries, activeFilters, renderTick]
|
|
1182
|
+
);
|
|
1183
|
+
const agentRuns = useMemo(() => groupIntoAgentRuns(filteredEntries), [filteredEntries, renderTick]);
|
|
1184
|
+
const toggleFilter = useCallback((type) => {
|
|
1185
|
+
if (activeFilters.has(type)) {
|
|
1186
|
+
activeFilters.delete(type);
|
|
1187
|
+
} else {
|
|
1188
|
+
activeFilters.add(type);
|
|
1189
|
+
}
|
|
1190
|
+
forceRender();
|
|
1191
|
+
}, [activeFilters, forceRender]);
|
|
1192
|
+
const clearFilters = useCallback(() => {
|
|
1193
|
+
activeFilters.clear();
|
|
1194
|
+
forceRender();
|
|
1195
|
+
}, [activeFilters, forceRender]);
|
|
1196
|
+
const toggleItemExpanded = useCallback((entryId) => {
|
|
1197
|
+
if (expandedItems.has(entryId)) {
|
|
1198
|
+
expandedItems.delete(entryId);
|
|
1199
|
+
} else {
|
|
1200
|
+
expandedItems.add(entryId);
|
|
1201
|
+
}
|
|
1202
|
+
forceRender();
|
|
1203
|
+
}, [expandedItems, forceRender]);
|
|
1204
|
+
const collapseAll = useCallback(() => {
|
|
1205
|
+
collapsedRuns.clear();
|
|
1206
|
+
agentRuns.forEach((run, i) => {
|
|
1207
|
+
collapsedRuns.add(`${run.agentName}-${i}`);
|
|
1208
|
+
});
|
|
1209
|
+
expandedItems.clear();
|
|
1210
|
+
forceRender();
|
|
1211
|
+
}, [agentRuns, collapsedRuns, expandedItems, forceRender]);
|
|
1212
|
+
const expandAll = useCallback(() => {
|
|
1213
|
+
collapsedRuns.clear();
|
|
1214
|
+
agentRuns.forEach((run, i) => {
|
|
1215
|
+
collapsedRuns.add(`${run.agentName}-${i}:expanded`);
|
|
1216
|
+
});
|
|
1217
|
+
forceRender();
|
|
1218
|
+
}, [agentRuns, collapsedRuns, forceRender]);
|
|
1219
|
+
if (entries.length === 0) return null;
|
|
1220
|
+
const isSingle = agentRuns.length === 1;
|
|
1221
|
+
const hasActiveFilter = activeFilters.size > 0;
|
|
1222
|
+
const scrollStyle = maxHeight !== "none" ? { maxHeight } : void 0;
|
|
1223
|
+
return /* @__PURE__ */ jsxs(
|
|
1224
|
+
"div",
|
|
1225
|
+
{
|
|
1226
|
+
ref: containerRef,
|
|
1227
|
+
className: maxHeight !== "none" ? "overflow-y-auto" : "",
|
|
1228
|
+
style: scrollStyle,
|
|
1229
|
+
children: [
|
|
1230
|
+
/* @__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 pl-2", children: [
|
|
1231
|
+
TYPE_CONFIG.filter((tc) => availableTypes.has(tc.type)).map((tc) => {
|
|
1232
|
+
const isActive = activeFilters.has(tc.type);
|
|
1233
|
+
const count = entries.filter((e) => e.type === tc.type).length;
|
|
1234
|
+
return /* @__PURE__ */ jsxs(
|
|
1235
|
+
"button",
|
|
1236
|
+
{
|
|
1237
|
+
onClick: () => toggleFilter(tc.type),
|
|
1238
|
+
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"}`,
|
|
1239
|
+
title: `${isActive ? "Hide" : "Show only"} ${tc.label}`,
|
|
1240
|
+
children: [
|
|
1241
|
+
/* @__PURE__ */ jsx(tc.icon, { className: "w-3 h-3" }),
|
|
1242
|
+
/* @__PURE__ */ jsx("span", { children: count })
|
|
1243
|
+
]
|
|
1244
|
+
},
|
|
1245
|
+
tc.type
|
|
1246
|
+
);
|
|
1247
|
+
}),
|
|
1248
|
+
hasActiveFilter && /* @__PURE__ */ jsx(
|
|
1249
|
+
"button",
|
|
1250
|
+
{
|
|
1251
|
+
onClick: clearFilters,
|
|
1252
|
+
className: "text-[10px] text-muted-foreground/60 hover:text-muted-foreground px-1",
|
|
1253
|
+
children: "Clear"
|
|
1254
|
+
}
|
|
1255
|
+
),
|
|
1256
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
1257
|
+
!isSingle && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1258
|
+
/* @__PURE__ */ jsx(
|
|
1259
|
+
"button",
|
|
1260
|
+
{
|
|
1261
|
+
onClick: collapseAll,
|
|
1262
|
+
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",
|
|
1263
|
+
title: "Collapse all",
|
|
1264
|
+
children: /* @__PURE__ */ jsx(ChevronsDownUp, { className: "w-3 h-3" })
|
|
1265
|
+
}
|
|
1266
|
+
),
|
|
1267
|
+
/* @__PURE__ */ jsx(
|
|
1268
|
+
"button",
|
|
1269
|
+
{
|
|
1270
|
+
onClick: expandAll,
|
|
1271
|
+
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",
|
|
1272
|
+
title: "Expand all",
|
|
1273
|
+
children: /* @__PURE__ */ jsx(ChevronsUpDown, { className: "w-3 h-3" })
|
|
1274
|
+
}
|
|
1275
|
+
)
|
|
1276
|
+
] })
|
|
1277
|
+
] }),
|
|
1278
|
+
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) => {
|
|
1279
|
+
const runKey = `${run.agentName}-${i}`;
|
|
1280
|
+
const defaultCollapsed = run.depth > 0;
|
|
1281
|
+
const isCollapsed = collapsedRuns.has(runKey) ? true : collapsedRuns.has(`${runKey}:expanded`) ? false : defaultCollapsed;
|
|
1282
|
+
return /* @__PURE__ */ jsx(
|
|
1283
|
+
TimelineAgentBlock,
|
|
1284
|
+
{
|
|
1285
|
+
block: run,
|
|
1286
|
+
renderMarkdown,
|
|
1287
|
+
isSingleAgent: isSingle,
|
|
1288
|
+
isCollapsed,
|
|
1289
|
+
onToggleCollapsed: () => {
|
|
1290
|
+
if (isCollapsed) {
|
|
1291
|
+
collapsedRuns.delete(runKey);
|
|
1292
|
+
collapsedRuns.add(`${runKey}:expanded`);
|
|
1293
|
+
} else {
|
|
1294
|
+
collapsedRuns.delete(`${runKey}:expanded`);
|
|
1295
|
+
collapsedRuns.add(runKey);
|
|
1296
|
+
}
|
|
1297
|
+
forceRender();
|
|
1298
|
+
},
|
|
1299
|
+
expandedItems,
|
|
1300
|
+
onToggleItemExpanded: toggleItemExpanded
|
|
1301
|
+
},
|
|
1302
|
+
runKey
|
|
1303
|
+
);
|
|
1304
|
+
}) })
|
|
1305
|
+
]
|
|
1306
|
+
}
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
var AgentResponse = React11.forwardRef(
|
|
814
1310
|
({
|
|
815
1311
|
state,
|
|
816
1312
|
id,
|
|
@@ -824,11 +1320,14 @@ var AgentResponse = React10.forwardRef(
|
|
|
824
1320
|
actionsVisible = "hover",
|
|
825
1321
|
hitlInteractions,
|
|
826
1322
|
defaultHITLExpanded = false,
|
|
1323
|
+
statusContent,
|
|
827
1324
|
renderMarkdown,
|
|
828
1325
|
renderThinkingMarkdown,
|
|
1326
|
+
timelineMaxHeight,
|
|
829
1327
|
className,
|
|
830
1328
|
...props
|
|
831
1329
|
}, ref) => {
|
|
1330
|
+
const timelineUIStateRef = useRef(createTimelineUIState());
|
|
832
1331
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultThinkingExpanded);
|
|
833
1332
|
const isThinkingControlled = controlledThinkingExpanded !== void 0;
|
|
834
1333
|
const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
|
|
@@ -850,7 +1349,8 @@ var AgentResponse = React10.forwardRef(
|
|
|
850
1349
|
if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
|
|
851
1350
|
return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
|
|
852
1351
|
}, [state.firstMessageTime, state.responseCompleteTime]);
|
|
853
|
-
const
|
|
1352
|
+
const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
|
|
1353
|
+
const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
|
|
854
1354
|
const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
|
|
855
1355
|
const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
|
|
856
1356
|
const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
|
|
@@ -880,11 +1380,20 @@ var AgentResponse = React10.forwardRef(
|
|
|
880
1380
|
knowledge: state.knowledge,
|
|
881
1381
|
memory: state.memory,
|
|
882
1382
|
statusUpdates: state.statusUpdates,
|
|
1383
|
+
statusContent,
|
|
883
1384
|
status: state.status,
|
|
884
1385
|
elapsedTime
|
|
885
1386
|
}
|
|
886
1387
|
),
|
|
887
|
-
/* @__PURE__ */ jsx(
|
|
1388
|
+
hasTimelineEntries ? thinkingExpanded && /* @__PURE__ */ jsx("div", { className: "pb-3 border-t border-border", children: /* @__PURE__ */ jsx(
|
|
1389
|
+
AgentTimeline,
|
|
1390
|
+
{
|
|
1391
|
+
entries: state.timelineEntries,
|
|
1392
|
+
renderMarkdown: renderThinkingMarkdown,
|
|
1393
|
+
uiState: timelineUIStateRef.current,
|
|
1394
|
+
maxHeight: timelineMaxHeight
|
|
1395
|
+
}
|
|
1396
|
+
) }) : /* @__PURE__ */ jsx(
|
|
888
1397
|
ThinkingSection,
|
|
889
1398
|
{
|
|
890
1399
|
content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
|
|
@@ -928,7 +1437,7 @@ var AgentResponse = React10.forwardRef(
|
|
|
928
1437
|
}
|
|
929
1438
|
);
|
|
930
1439
|
AgentResponse.displayName = "AgentResponse";
|
|
931
|
-
var UserPrompt =
|
|
1440
|
+
var UserPrompt = React11.forwardRef(
|
|
932
1441
|
({ content, timestamp, className, ...props }, ref) => {
|
|
933
1442
|
return /* @__PURE__ */ jsxs(
|
|
934
1443
|
"div",
|
|
@@ -1027,7 +1536,7 @@ function CodeBlockLeaf({ attributes, children, leaf }) {
|
|
|
1027
1536
|
}
|
|
1028
1537
|
return /* @__PURE__ */ jsx("span", { ...attributes, children });
|
|
1029
1538
|
}
|
|
1030
|
-
var UserPromptInput =
|
|
1539
|
+
var UserPromptInput = React11.forwardRef(
|
|
1031
1540
|
({
|
|
1032
1541
|
value = "",
|
|
1033
1542
|
onChange,
|
|
@@ -1037,6 +1546,8 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1037
1546
|
disabled = false,
|
|
1038
1547
|
isSubmitting = false,
|
|
1039
1548
|
onStop,
|
|
1549
|
+
stopTooltip,
|
|
1550
|
+
stopClassName,
|
|
1040
1551
|
disableWhileSubmitting = true,
|
|
1041
1552
|
autoFocus = false,
|
|
1042
1553
|
refocusAfterSubmit = false,
|
|
@@ -1050,14 +1561,14 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1050
1561
|
className,
|
|
1051
1562
|
...props
|
|
1052
1563
|
}, ref) => {
|
|
1053
|
-
const editorRef =
|
|
1054
|
-
const [internalValue, setInternalValue] =
|
|
1055
|
-
const prevIsSubmitting =
|
|
1056
|
-
const hasEmittedReady =
|
|
1057
|
-
|
|
1564
|
+
const editorRef = React11.useRef(null);
|
|
1565
|
+
const [internalValue, setInternalValue] = React11.useState(value);
|
|
1566
|
+
const prevIsSubmitting = React11.useRef(isSubmitting);
|
|
1567
|
+
const hasEmittedReady = React11.useRef(false);
|
|
1568
|
+
React11.useEffect(() => {
|
|
1058
1569
|
setInternalValue(value);
|
|
1059
1570
|
}, [value]);
|
|
1060
|
-
|
|
1571
|
+
React11.useEffect(() => {
|
|
1061
1572
|
if (autoFocus) {
|
|
1062
1573
|
requestAnimationFrame(() => {
|
|
1063
1574
|
requestAnimationFrame(() => {
|
|
@@ -1066,7 +1577,7 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1066
1577
|
});
|
|
1067
1578
|
}
|
|
1068
1579
|
}, [autoFocus]);
|
|
1069
|
-
|
|
1580
|
+
React11.useEffect(() => {
|
|
1070
1581
|
if (!hasEmittedReady.current && onReady) {
|
|
1071
1582
|
requestAnimationFrame(() => {
|
|
1072
1583
|
requestAnimationFrame(() => {
|
|
@@ -1076,7 +1587,7 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1076
1587
|
});
|
|
1077
1588
|
}
|
|
1078
1589
|
}, [onReady]);
|
|
1079
|
-
|
|
1590
|
+
React11.useEffect(() => {
|
|
1080
1591
|
if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
|
|
1081
1592
|
requestAnimationFrame(() => {
|
|
1082
1593
|
editorRef.current?.focus();
|
|
@@ -1084,7 +1595,7 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1084
1595
|
}
|
|
1085
1596
|
prevIsSubmitting.current = isSubmitting;
|
|
1086
1597
|
}, [isSubmitting, refocusAfterSubmit]);
|
|
1087
|
-
|
|
1598
|
+
React11.useImperativeHandle(
|
|
1088
1599
|
ref,
|
|
1089
1600
|
() => ({
|
|
1090
1601
|
focus: () => {
|
|
@@ -1107,14 +1618,14 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1107
1618
|
}),
|
|
1108
1619
|
[]
|
|
1109
1620
|
);
|
|
1110
|
-
const handleChange =
|
|
1621
|
+
const handleChange = React11.useCallback(
|
|
1111
1622
|
(newValue) => {
|
|
1112
1623
|
setInternalValue(newValue);
|
|
1113
1624
|
onChange?.(newValue);
|
|
1114
1625
|
},
|
|
1115
1626
|
[onChange]
|
|
1116
1627
|
);
|
|
1117
|
-
const handleSubmit =
|
|
1628
|
+
const handleSubmit = React11.useCallback(
|
|
1118
1629
|
(text) => {
|
|
1119
1630
|
if (disabled || isSubmitting) return;
|
|
1120
1631
|
if (!text.trim()) return;
|
|
@@ -1126,7 +1637,7 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1126
1637
|
},
|
|
1127
1638
|
[disabled, isSubmitting, onSubmit, clearOnSubmit]
|
|
1128
1639
|
);
|
|
1129
|
-
const handleSendClick =
|
|
1640
|
+
const handleSendClick = React11.useCallback(() => {
|
|
1130
1641
|
const text = editorRef.current?.getText() ?? "";
|
|
1131
1642
|
handleSubmit(text);
|
|
1132
1643
|
}, [handleSubmit]);
|
|
@@ -1163,16 +1674,17 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1163
1674
|
) }),
|
|
1164
1675
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
|
|
1165
1676
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
|
|
1166
|
-
isSubmitting && onStop ? /* @__PURE__ */ jsx(
|
|
1677
|
+
isSubmitting && onStop ? /* @__PURE__ */ jsx(Tooltip, { content: stopTooltip, disabled: !stopTooltip, children: /* @__PURE__ */ jsx(
|
|
1167
1678
|
IconButton,
|
|
1168
1679
|
{
|
|
1169
1680
|
icon: /* @__PURE__ */ jsx(Square, {}),
|
|
1170
1681
|
variant: "filled",
|
|
1171
1682
|
size: "sm",
|
|
1172
|
-
"aria-label": "Stop",
|
|
1173
|
-
onClick: onStop
|
|
1683
|
+
"aria-label": stopTooltip || "Stop",
|
|
1684
|
+
onClick: onStop,
|
|
1685
|
+
className: stopClassName
|
|
1174
1686
|
}
|
|
1175
|
-
) : /* @__PURE__ */ jsx(
|
|
1687
|
+
) }) : /* @__PURE__ */ jsx(
|
|
1176
1688
|
IconButton,
|
|
1177
1689
|
{
|
|
1178
1690
|
icon: isSubmitting ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsx(Send, {}),
|
|
@@ -1191,6 +1703,115 @@ var UserPromptInput = React10.forwardRef(
|
|
|
1191
1703
|
);
|
|
1192
1704
|
UserPromptInput.displayName = "UserPromptInput";
|
|
1193
1705
|
|
|
1194
|
-
|
|
1706
|
+
// src/components/inline-actions/parseResponseSegments.ts
|
|
1707
|
+
var ACTION_BLOCK_REGEX = /```json:action\s*\n([\s\S]*?)```/g;
|
|
1708
|
+
function parseResponseSegments(text) {
|
|
1709
|
+
if (!text) return [];
|
|
1710
|
+
const segments = [];
|
|
1711
|
+
let lastIndex = 0;
|
|
1712
|
+
ACTION_BLOCK_REGEX.lastIndex = 0;
|
|
1713
|
+
let match;
|
|
1714
|
+
while ((match = ACTION_BLOCK_REGEX.exec(text)) !== null) {
|
|
1715
|
+
const before = text.slice(lastIndex, match.index);
|
|
1716
|
+
if (before.trim()) {
|
|
1717
|
+
segments.push({ kind: "markdown", content: before });
|
|
1718
|
+
}
|
|
1719
|
+
const jsonContent = match[1].trim();
|
|
1720
|
+
let parsed = null;
|
|
1721
|
+
try {
|
|
1722
|
+
parsed = JSON.parse(jsonContent);
|
|
1723
|
+
} catch {
|
|
1724
|
+
}
|
|
1725
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
1726
|
+
segments.push({
|
|
1727
|
+
kind: "action",
|
|
1728
|
+
actionType: parsed.type,
|
|
1729
|
+
payload: parsed
|
|
1730
|
+
});
|
|
1731
|
+
} else {
|
|
1732
|
+
const rawBlock = match[0];
|
|
1733
|
+
segments.push({ kind: "markdown", content: rawBlock });
|
|
1734
|
+
}
|
|
1735
|
+
lastIndex = match.index + match[0].length;
|
|
1736
|
+
}
|
|
1737
|
+
const trailing = text.slice(lastIndex);
|
|
1738
|
+
if (trailing.trim()) {
|
|
1739
|
+
segments.push({ kind: "markdown", content: trailing });
|
|
1740
|
+
}
|
|
1741
|
+
return segments;
|
|
1742
|
+
}
|
|
1743
|
+
function ActionMarkdownRenderer({
|
|
1744
|
+
content,
|
|
1745
|
+
registry,
|
|
1746
|
+
renderMarkdown,
|
|
1747
|
+
onAction,
|
|
1748
|
+
isLatest
|
|
1749
|
+
}) {
|
|
1750
|
+
const segments = useMemo(() => parseResponseSegments(content), [content]);
|
|
1751
|
+
if (segments.length === 1 && segments[0].kind === "markdown") {
|
|
1752
|
+
return /* @__PURE__ */ jsx(Fragment, { children: renderMarkdown(segments[0].content) });
|
|
1753
|
+
}
|
|
1754
|
+
return /* @__PURE__ */ jsx(Fragment, { children: segments.map((segment, index) => {
|
|
1755
|
+
if (segment.kind === "markdown") {
|
|
1756
|
+
return /* @__PURE__ */ jsx("div", { children: renderMarkdown(segment.content) }, `md-${index}`);
|
|
1757
|
+
}
|
|
1758
|
+
const Component = registry[segment.actionType];
|
|
1759
|
+
if (!Component) {
|
|
1760
|
+
return /* @__PURE__ */ jsx(
|
|
1761
|
+
"pre",
|
|
1762
|
+
{
|
|
1763
|
+
className: "my-4 p-4 rounded-lg border border-border bg-muted text-sm font-mono overflow-x-auto",
|
|
1764
|
+
children: /* @__PURE__ */ jsx("code", { children: JSON.stringify(segment.payload, null, 2) })
|
|
1765
|
+
},
|
|
1766
|
+
`action-fallback-${index}`
|
|
1767
|
+
);
|
|
1768
|
+
}
|
|
1769
|
+
return /* @__PURE__ */ jsx(
|
|
1770
|
+
Component,
|
|
1771
|
+
{
|
|
1772
|
+
payload: segment.payload,
|
|
1773
|
+
onAction,
|
|
1774
|
+
isLatest
|
|
1775
|
+
},
|
|
1776
|
+
`action-${segment.actionType}-${index}`
|
|
1777
|
+
);
|
|
1778
|
+
}) });
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
// src/components/inline-actions/prompts.ts
|
|
1782
|
+
var INLINE_ACTION_PROMPT = `
|
|
1783
|
+
<inline_actions>
|
|
1784
|
+
When your response should include interactive components (like query viewers,
|
|
1785
|
+
data tables, or executable actions), embed them as fenced code blocks using
|
|
1786
|
+
the \`json:action\` language tag:
|
|
1787
|
+
|
|
1788
|
+
\`\`\`json:action
|
|
1789
|
+
{
|
|
1790
|
+
"type": "action-type-here",
|
|
1791
|
+
...action-specific fields
|
|
1792
|
+
}
|
|
1793
|
+
\`\`\`
|
|
1794
|
+
|
|
1795
|
+
Rules:
|
|
1796
|
+
- Each block must contain valid JSON with a "type" field.
|
|
1797
|
+
- The "type" must match a registered action component on the frontend.
|
|
1798
|
+
- Multiple action blocks per response are allowed.
|
|
1799
|
+
- Surround action blocks with normal markdown text for user context.
|
|
1800
|
+
- The action block is rendered as an interactive component in the chat UI.
|
|
1801
|
+
- SQL strings inside JSON must be properly escaped (newlines as \\n, quotes as \\").
|
|
1802
|
+
|
|
1803
|
+
Available action types:
|
|
1804
|
+
|
|
1805
|
+
- "optimap-query": Displays SQL queries with a button to execute them and
|
|
1806
|
+
update the 3D globe map.
|
|
1807
|
+
Required fields:
|
|
1808
|
+
- type: "optimap-query"
|
|
1809
|
+
- locations_sql: string (the validated locations SQL query)
|
|
1810
|
+
- routes_sql: string (the validated routes SQL query)
|
|
1811
|
+
- database_name: string (the target database name)
|
|
1812
|
+
</inline_actions>
|
|
1813
|
+
`;
|
|
1814
|
+
|
|
1815
|
+
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 };
|
|
1195
1816
|
//# sourceMappingURL=index.js.map
|
|
1196
1817
|
//# sourceMappingURL=index.js.map
|