@optilogic/chat 1.0.0-beta.1 → 1.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +235 -0
- package/dist/index.cjs +1292 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +524 -6
- package/dist/index.d.ts +524 -6
- package/dist/index.js +1267 -33
- package/dist/index.js.map +1 -1
- package/package.json +15 -9
- package/src/components/agent-response/AgentResponse.tsx +99 -10
- package/src/components/agent-response/components/ActivityIndicators.tsx +36 -4
- package/src/components/agent-response/components/HITLSection.tsx +95 -0
- package/src/components/agent-response/components/MetadataRow.tsx +21 -6
- package/src/components/agent-response/components/ThinkingSection.tsx +102 -10
- package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
- package/src/components/agent-response/components/index.ts +6 -0
- package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +79 -4
- package/src/components/agent-response/index.ts +23 -0
- package/src/components/agent-response/types.ts +96 -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 +167 -0
- package/src/components/hitl-interactions/HITLInteractionRecord.tsx +139 -0
- package/src/components/hitl-interactions/HITLQuestionPanel.tsx +270 -0
- package/src/components/hitl-interactions/index.ts +18 -0
- 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/UserPrompt.tsx +60 -0
- package/src/components/user-prompt/index.ts +1 -0
- package/src/components/user-prompt-input/UserPromptInput.tsx +326 -0
- package/src/components/user-prompt-input/index.ts +2 -0
- package/src/components/user-prompt-input/types.ts +52 -0
- package/src/index.ts +54 -0
package/dist/index.js
CHANGED
|
@@ -1,15 +1,36 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { useState, useCallback,
|
|
3
|
-
import { cn, Popover, PopoverTrigger, PopoverContent, LoadingSpinner } from '@optilogic/core';
|
|
4
|
-
import { Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, ChevronUp,
|
|
1
|
+
import * as React11 from 'react';
|
|
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, Brain, BookOpen, MessageSquare, AlertCircle, ChevronsDownUp, ChevronsUpDown } from 'lucide-react';
|
|
5
5
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
import { SlateEditor, Text } from '@optilogic/editor';
|
|
6
7
|
|
|
7
8
|
// src/components/agent-response/AgentResponse.tsx
|
|
8
|
-
var ActivityIndicators =
|
|
9
|
-
({ toolCalls, knowledge, memory, className, ...props }, ref) => {
|
|
10
|
-
const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
|
|
9
|
+
var ActivityIndicators = React11.forwardRef(
|
|
10
|
+
({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
|
|
11
|
+
const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
|
|
11
12
|
if (!hasAnyActivity) return null;
|
|
12
13
|
return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex items-center gap-2", className), ...props, children: [
|
|
14
|
+
statusUpdates.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
|
|
15
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
16
|
+
"button",
|
|
17
|
+
{
|
|
18
|
+
className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
|
|
19
|
+
onClick: (e) => e.stopPropagation(),
|
|
20
|
+
children: [
|
|
21
|
+
/* @__PURE__ */ jsx(Activity, { className: "w-3.5 h-3.5" }),
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs", children: statusUpdates.length })
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
) }),
|
|
26
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
27
|
+
/* @__PURE__ */ jsx("h4", { className: "font-medium text-sm", children: "Status Updates" }),
|
|
28
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: statusUpdates.map((item) => /* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
|
|
29
|
+
item.agent && /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground", children: item.agent }),
|
|
30
|
+
/* @__PURE__ */ jsx("div", { className: item.agent ? "mt-1" : "", children: item.message })
|
|
31
|
+
] }, item.id)) })
|
|
32
|
+
] }) })
|
|
33
|
+
] }),
|
|
13
34
|
toolCalls.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
|
|
14
35
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
15
36
|
"button",
|
|
@@ -104,7 +125,7 @@ function formatTotalTime(seconds) {
|
|
|
104
125
|
const minutes = seconds / 60;
|
|
105
126
|
return `${minutes.toFixed(1)}m`;
|
|
106
127
|
}
|
|
107
|
-
var MetadataRow =
|
|
128
|
+
var MetadataRow = React11.forwardRef(
|
|
108
129
|
({
|
|
109
130
|
hasThinking,
|
|
110
131
|
isExpanded,
|
|
@@ -112,6 +133,8 @@ var MetadataRow = React.forwardRef(
|
|
|
112
133
|
toolCalls,
|
|
113
134
|
knowledge,
|
|
114
135
|
memory,
|
|
136
|
+
statusUpdates = [],
|
|
137
|
+
statusContent,
|
|
115
138
|
status,
|
|
116
139
|
elapsedTime,
|
|
117
140
|
className,
|
|
@@ -119,7 +142,7 @@ var MetadataRow = React.forwardRef(
|
|
|
119
142
|
}, ref) => {
|
|
120
143
|
const isProcessing = status === "processing";
|
|
121
144
|
const isComplete = status === "complete";
|
|
122
|
-
const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
|
|
145
|
+
const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
|
|
123
146
|
const renderLeftContent = () => {
|
|
124
147
|
if (hasThinking) {
|
|
125
148
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
@@ -136,7 +159,7 @@ var MetadataRow = React.forwardRef(
|
|
|
136
159
|
return null;
|
|
137
160
|
};
|
|
138
161
|
const leftContent = renderLeftContent();
|
|
139
|
-
if (!leftContent && !hasActivity) {
|
|
162
|
+
if (!leftContent && !hasActivity && !statusContent) {
|
|
140
163
|
return null;
|
|
141
164
|
}
|
|
142
165
|
return /* @__PURE__ */ jsxs(
|
|
@@ -150,16 +173,18 @@ var MetadataRow = React.forwardRef(
|
|
|
150
173
|
"button",
|
|
151
174
|
{
|
|
152
175
|
onClick: onToggle,
|
|
153
|
-
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",
|
|
154
177
|
children: leftContent
|
|
155
178
|
}
|
|
156
|
-
) : /* @__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 }),
|
|
157
181
|
/* @__PURE__ */ jsx(
|
|
158
182
|
ActivityIndicators,
|
|
159
183
|
{
|
|
160
184
|
toolCalls,
|
|
161
185
|
knowledge,
|
|
162
|
-
memory
|
|
186
|
+
memory,
|
|
187
|
+
statusUpdates
|
|
163
188
|
}
|
|
164
189
|
)
|
|
165
190
|
]
|
|
@@ -168,24 +193,61 @@ var MetadataRow = React.forwardRef(
|
|
|
168
193
|
}
|
|
169
194
|
);
|
|
170
195
|
MetadataRow.displayName = "MetadataRow";
|
|
171
|
-
var
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
var ThinkingStepItem = ({ step, renderMarkdown }) => {
|
|
197
|
+
const [isCollapsed, setIsCollapsed] = useState(step.isCollapsed ?? false);
|
|
198
|
+
const toggleCollapse = useCallback(() => {
|
|
199
|
+
setIsCollapsed((prev) => !prev);
|
|
200
|
+
}, []);
|
|
201
|
+
const indentPadding = step.depth * 16;
|
|
202
|
+
return /* @__PURE__ */ jsxs("div", { className: "border-b border-border/50 last:border-b-0", children: [
|
|
203
|
+
/* @__PURE__ */ jsxs(
|
|
204
|
+
"button",
|
|
205
|
+
{
|
|
206
|
+
onClick: toggleCollapse,
|
|
207
|
+
className: "w-full flex items-center gap-1.5 py-1.5 px-2 hover:bg-muted/50 transition-colors text-left",
|
|
208
|
+
style: { paddingLeft: `${indentPadding + 8}px` },
|
|
209
|
+
children: [
|
|
210
|
+
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" }),
|
|
211
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground/80", children: step.label })
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
!isCollapsed && /* @__PURE__ */ jsx(
|
|
216
|
+
"div",
|
|
217
|
+
{
|
|
218
|
+
className: "pb-2 px-2",
|
|
219
|
+
style: { paddingLeft: `${indentPadding + 28}px` },
|
|
220
|
+
children: renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(step.content) }) : /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: step.content })
|
|
221
|
+
}
|
|
222
|
+
)
|
|
223
|
+
] });
|
|
224
|
+
};
|
|
225
|
+
var ThinkingSection = React11.forwardRef(
|
|
226
|
+
({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
|
|
227
|
+
if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
|
|
174
228
|
return null;
|
|
175
229
|
}
|
|
230
|
+
const isStructured = Array.isArray(content);
|
|
176
231
|
return /* @__PURE__ */ jsx(
|
|
177
232
|
"div",
|
|
178
233
|
{
|
|
179
234
|
ref,
|
|
180
235
|
className: cn("px-3 pb-3 border-t border-border", className),
|
|
181
236
|
...props,
|
|
182
|
-
children: /* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: /* @__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
|
+
ThinkingStepItem,
|
|
239
|
+
{
|
|
240
|
+
step,
|
|
241
|
+
renderMarkdown
|
|
242
|
+
},
|
|
243
|
+
step.id
|
|
244
|
+
)) }) : renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(content) }) : /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
|
|
183
245
|
}
|
|
184
246
|
);
|
|
185
247
|
}
|
|
186
248
|
);
|
|
187
249
|
ThinkingSection.displayName = "ThinkingSection";
|
|
188
|
-
var ActionBar =
|
|
250
|
+
var ActionBar = React11.forwardRef(
|
|
189
251
|
({
|
|
190
252
|
response,
|
|
191
253
|
isVisible,
|
|
@@ -274,6 +336,313 @@ var ActionBar = React.forwardRef(
|
|
|
274
336
|
}
|
|
275
337
|
);
|
|
276
338
|
ActionBar.displayName = "ActionBar";
|
|
339
|
+
function buildResponseString(questions, selectedOptions, freeformText) {
|
|
340
|
+
const parts = [];
|
|
341
|
+
for (const q of questions) {
|
|
342
|
+
const answer = selectedOptions[q];
|
|
343
|
+
if (answer) {
|
|
344
|
+
parts.push(`Q: ${q}
|
|
345
|
+
A: ${answer}`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const trimmed = freeformText.trim();
|
|
349
|
+
if (trimmed) {
|
|
350
|
+
parts.push(`Additional context: ${trimmed}`);
|
|
351
|
+
}
|
|
352
|
+
return parts.join("\n\n");
|
|
353
|
+
}
|
|
354
|
+
var HITLQuestionPanel = React11.forwardRef(({ question, onSubmit, onStop, className, ...props }, ref) => {
|
|
355
|
+
const [freeformText, setFreeformText] = useState("");
|
|
356
|
+
const [selectedOptions, setSelectedOptions] = useState({});
|
|
357
|
+
const hasTimeout = question.timeoutSeconds != null;
|
|
358
|
+
const [secondsLeft, setSecondsLeft] = useState(
|
|
359
|
+
() => hasTimeout ? Math.max(
|
|
360
|
+
0,
|
|
361
|
+
Math.round(
|
|
362
|
+
question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
|
|
363
|
+
)
|
|
364
|
+
) : Infinity
|
|
365
|
+
);
|
|
366
|
+
const textareaRef = useRef(null);
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
textareaRef.current?.focus();
|
|
369
|
+
}, []);
|
|
370
|
+
useEffect(() => {
|
|
371
|
+
if (!hasTimeout) return;
|
|
372
|
+
const interval = setInterval(() => {
|
|
373
|
+
const remaining = Math.max(
|
|
374
|
+
0,
|
|
375
|
+
Math.round(
|
|
376
|
+
question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
|
|
377
|
+
)
|
|
378
|
+
);
|
|
379
|
+
setSecondsLeft(remaining);
|
|
380
|
+
if (remaining <= 0) clearInterval(interval);
|
|
381
|
+
}, 1e3);
|
|
382
|
+
return () => clearInterval(interval);
|
|
383
|
+
}, [hasTimeout, question.timeoutSeconds, question.receivedAt]);
|
|
384
|
+
const timedOut = hasTimeout && secondsLeft <= 0;
|
|
385
|
+
const questionsWithOptions = useMemo(
|
|
386
|
+
() => question.questions.filter((q) => question.options?.[q]?.length),
|
|
387
|
+
[question.questions, question.options]
|
|
388
|
+
);
|
|
389
|
+
const allOptionsSelected = questionsWithOptions.length > 0 && questionsWithOptions.every((q) => selectedOptions[q]);
|
|
390
|
+
const hasFreeformText = freeformText.trim().length > 0;
|
|
391
|
+
const canSubmit = !timedOut && (allOptionsSelected || hasFreeformText);
|
|
392
|
+
const handleOptionClick = useCallback(
|
|
393
|
+
(questionText, option) => {
|
|
394
|
+
if (timedOut) return;
|
|
395
|
+
setSelectedOptions((prev) => {
|
|
396
|
+
if (prev[questionText] === option) {
|
|
397
|
+
const next = { ...prev };
|
|
398
|
+
delete next[questionText];
|
|
399
|
+
return next;
|
|
400
|
+
}
|
|
401
|
+
return { ...prev, [questionText]: option };
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
[timedOut]
|
|
405
|
+
);
|
|
406
|
+
const handleSubmit = useCallback(() => {
|
|
407
|
+
if (!canSubmit) return;
|
|
408
|
+
const combined = buildResponseString(
|
|
409
|
+
question.questions,
|
|
410
|
+
selectedOptions,
|
|
411
|
+
freeformText
|
|
412
|
+
);
|
|
413
|
+
onSubmit(combined, { selectedOptions, freeformText });
|
|
414
|
+
}, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
|
|
415
|
+
const handleKeyDown = useCallback(
|
|
416
|
+
(e) => {
|
|
417
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
418
|
+
e.preventDefault();
|
|
419
|
+
handleSubmit();
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
[handleSubmit]
|
|
423
|
+
);
|
|
424
|
+
const formatTime2 = (s) => {
|
|
425
|
+
const m = Math.floor(s / 60);
|
|
426
|
+
const sec = s % 60;
|
|
427
|
+
return `${m}:${sec.toString().padStart(2, "0")}`;
|
|
428
|
+
};
|
|
429
|
+
return /* @__PURE__ */ jsxs(
|
|
430
|
+
"div",
|
|
431
|
+
{
|
|
432
|
+
ref,
|
|
433
|
+
className: cn(
|
|
434
|
+
"rounded-lg border border-border bg-muted p-4 space-y-3",
|
|
435
|
+
className
|
|
436
|
+
),
|
|
437
|
+
...props,
|
|
438
|
+
children: [
|
|
439
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
440
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground", children: question.reason }) }),
|
|
441
|
+
hasTimeout && /* @__PURE__ */ jsx(
|
|
442
|
+
"span",
|
|
443
|
+
{
|
|
444
|
+
className: cn(
|
|
445
|
+
"text-xs font-mono whitespace-nowrap",
|
|
446
|
+
secondsLeft <= 30 ? "text-destructive" : "text-muted-foreground"
|
|
447
|
+
),
|
|
448
|
+
children: timedOut ? "Timed out" : formatTime2(secondsLeft)
|
|
449
|
+
}
|
|
450
|
+
)
|
|
451
|
+
] }),
|
|
452
|
+
question.context && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border max-h-24 overflow-y-auto", children: /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
|
|
453
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-3", children: question.questions.map((q, i) => /* @__PURE__ */ jsxs("div", { children: [
|
|
454
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-foreground", children: q }),
|
|
455
|
+
question.options?.[q] && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-1.5", children: question.options[q].map((option, j) => {
|
|
456
|
+
const isSelected = selectedOptions[q] === option;
|
|
457
|
+
return /* @__PURE__ */ jsx(
|
|
458
|
+
Button,
|
|
459
|
+
{
|
|
460
|
+
variant: isSelected ? "primary" : "outline",
|
|
461
|
+
size: "sm",
|
|
462
|
+
onClick: () => handleOptionClick(q, option),
|
|
463
|
+
disabled: timedOut,
|
|
464
|
+
children: option
|
|
465
|
+
},
|
|
466
|
+
j
|
|
467
|
+
);
|
|
468
|
+
}) })
|
|
469
|
+
] }, i)) }),
|
|
470
|
+
/* @__PURE__ */ jsx(
|
|
471
|
+
Textarea,
|
|
472
|
+
{
|
|
473
|
+
ref: textareaRef,
|
|
474
|
+
value: freeformText,
|
|
475
|
+
onChange: (e) => setFreeformText(e.target.value),
|
|
476
|
+
onKeyDown: handleKeyDown,
|
|
477
|
+
disabled: timedOut,
|
|
478
|
+
placeholder: "Add additional context or type a full response...",
|
|
479
|
+
rows: 2,
|
|
480
|
+
className: "resize-none"
|
|
481
|
+
}
|
|
482
|
+
),
|
|
483
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
484
|
+
/* @__PURE__ */ jsx(
|
|
485
|
+
Button,
|
|
486
|
+
{
|
|
487
|
+
variant: "ghost",
|
|
488
|
+
size: "sm",
|
|
489
|
+
onClick: onStop,
|
|
490
|
+
className: "text-destructive hover:text-destructive hover:bg-destructive/10",
|
|
491
|
+
children: "Stop agent"
|
|
492
|
+
}
|
|
493
|
+
),
|
|
494
|
+
/* @__PURE__ */ jsx(
|
|
495
|
+
Button,
|
|
496
|
+
{
|
|
497
|
+
variant: "primary",
|
|
498
|
+
size: "sm",
|
|
499
|
+
onClick: handleSubmit,
|
|
500
|
+
disabled: !canSubmit,
|
|
501
|
+
children: "Send response"
|
|
502
|
+
}
|
|
503
|
+
)
|
|
504
|
+
] })
|
|
505
|
+
]
|
|
506
|
+
}
|
|
507
|
+
);
|
|
508
|
+
});
|
|
509
|
+
HITLQuestionPanel.displayName = "HITLQuestionPanel";
|
|
510
|
+
function parseResponse(response) {
|
|
511
|
+
const answers = {};
|
|
512
|
+
let additionalContext = null;
|
|
513
|
+
const blocks = response.split("\n\n");
|
|
514
|
+
for (const block of blocks) {
|
|
515
|
+
const qaMatch = block.match(/^Q: (.+)\nA: (.+)$/s);
|
|
516
|
+
if (qaMatch) {
|
|
517
|
+
answers[qaMatch[1].trim()] = qaMatch[2].trim();
|
|
518
|
+
} else if (block.startsWith("Additional context: ")) {
|
|
519
|
+
additionalContext = block.slice("Additional context: ".length).trim();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return { answers, additionalContext };
|
|
523
|
+
}
|
|
524
|
+
var HITLInteractionRecord = React11.forwardRef(({ interaction, className, ...props }, ref) => {
|
|
525
|
+
const { question, response, respondedAt } = interaction;
|
|
526
|
+
const timestamp = new Date(respondedAt).toLocaleTimeString([], {
|
|
527
|
+
hour: "2-digit",
|
|
528
|
+
minute: "2-digit"
|
|
529
|
+
});
|
|
530
|
+
const { answers, additionalContext } = useMemo(
|
|
531
|
+
() => parseResponse(response),
|
|
532
|
+
[response]
|
|
533
|
+
);
|
|
534
|
+
const hasParsedAnswers = Object.keys(answers).length > 0;
|
|
535
|
+
return /* @__PURE__ */ jsxs(
|
|
536
|
+
"div",
|
|
537
|
+
{
|
|
538
|
+
ref,
|
|
539
|
+
className: cn(
|
|
540
|
+
"rounded-lg border border-border bg-muted p-3 space-y-2 text-sm",
|
|
541
|
+
className
|
|
542
|
+
),
|
|
543
|
+
...props,
|
|
544
|
+
children: [
|
|
545
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
546
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-muted-foreground", children: "Clarifying Question" }),
|
|
547
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: timestamp })
|
|
548
|
+
] }),
|
|
549
|
+
/* @__PURE__ */ jsx("p", { className: "text-foreground font-medium", children: question.reason }),
|
|
550
|
+
question.context && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border", children: /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
|
|
551
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: question.questions.map((q, i) => /* @__PURE__ */ jsxs("div", { children: [
|
|
552
|
+
/* @__PURE__ */ jsx("p", { className: "text-foreground", children: q }),
|
|
553
|
+
question.options?.[q] && /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
|
|
554
|
+
"Options: ",
|
|
555
|
+
question.options[q].join(", ")
|
|
556
|
+
] }),
|
|
557
|
+
hasParsedAnswers && answers[q] && /* @__PURE__ */ jsxs("p", { className: "text-xs text-foreground ml-2 mt-0.5 bg-background rounded px-2 py-1 border border-border", children: [
|
|
558
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Answer: " }),
|
|
559
|
+
answers[q]
|
|
560
|
+
] })
|
|
561
|
+
] }, i)) }),
|
|
562
|
+
hasParsedAnswers && additionalContext && /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-2", children: [
|
|
563
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "Additional context:" }),
|
|
564
|
+
/* @__PURE__ */ jsx("p", { className: "text-foreground mt-0.5", children: additionalContext })
|
|
565
|
+
] }),
|
|
566
|
+
!hasParsedAnswers && /* @__PURE__ */ jsxs("div", { className: "border-t border-border pt-2", children: [
|
|
567
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "Response:" }),
|
|
568
|
+
/* @__PURE__ */ jsx("p", { className: "text-foreground mt-0.5", children: response })
|
|
569
|
+
] })
|
|
570
|
+
]
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
});
|
|
574
|
+
HITLInteractionRecord.displayName = "HITLInteractionRecord";
|
|
575
|
+
var HITLSection = React11.forwardRef(
|
|
576
|
+
({
|
|
577
|
+
interactions,
|
|
578
|
+
defaultExpanded = false,
|
|
579
|
+
isExpanded: controlledExpanded,
|
|
580
|
+
onExpandedChange,
|
|
581
|
+
className,
|
|
582
|
+
...props
|
|
583
|
+
}, ref) => {
|
|
584
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultExpanded);
|
|
585
|
+
const isControlled = controlledExpanded !== void 0;
|
|
586
|
+
const isExpanded = isControlled ? controlledExpanded : uncontrolledExpanded;
|
|
587
|
+
const toggleExpanded = useCallback(() => {
|
|
588
|
+
const newValue = !isExpanded;
|
|
589
|
+
if (isControlled) {
|
|
590
|
+
onExpandedChange?.(newValue);
|
|
591
|
+
} else {
|
|
592
|
+
setUncontrolledExpanded(newValue);
|
|
593
|
+
}
|
|
594
|
+
}, [isExpanded, isControlled, onExpandedChange]);
|
|
595
|
+
if (!interactions || interactions.length === 0) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
return /* @__PURE__ */ jsxs(
|
|
599
|
+
"div",
|
|
600
|
+
{
|
|
601
|
+
ref,
|
|
602
|
+
className: cn("border-t border-border", className),
|
|
603
|
+
...props,
|
|
604
|
+
children: [
|
|
605
|
+
/* @__PURE__ */ jsxs(
|
|
606
|
+
"button",
|
|
607
|
+
{
|
|
608
|
+
onClick: toggleExpanded,
|
|
609
|
+
className: "w-full flex items-center gap-2 py-2 px-3 hover:bg-muted/50 transition-colors text-left",
|
|
610
|
+
children: [
|
|
611
|
+
isExpanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
|
|
612
|
+
/* @__PURE__ */ jsx(MessageCircleQuestion, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
|
|
613
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs font-medium text-foreground/80", children: [
|
|
614
|
+
"Clarifying Questions (",
|
|
615
|
+
interactions.length,
|
|
616
|
+
")"
|
|
617
|
+
] })
|
|
618
|
+
]
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
isExpanded && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 space-y-2", children: interactions.map((interaction, i) => /* @__PURE__ */ jsx(HITLInteractionRecord, { interaction }, i)) })
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
);
|
|
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";
|
|
277
646
|
function useThinkingTimer({
|
|
278
647
|
startTime,
|
|
279
648
|
endTime,
|
|
@@ -308,12 +677,133 @@ var initialAgentResponseState = {
|
|
|
308
677
|
toolCalls: [],
|
|
309
678
|
knowledge: [],
|
|
310
679
|
memory: [],
|
|
680
|
+
statusUpdates: [],
|
|
311
681
|
response: "",
|
|
312
682
|
thinkingStartTime: null,
|
|
313
683
|
responseCompleteTime: null,
|
|
314
684
|
firstMessageTime: null
|
|
315
685
|
};
|
|
316
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
|
+
|
|
317
807
|
// src/components/agent-response/hooks/useAgentResponseAccumulator.ts
|
|
318
808
|
function useAgentResponseAccumulator(options) {
|
|
319
809
|
const [state, setState] = useState(initialAgentResponseState);
|
|
@@ -342,16 +832,49 @@ function useAgentResponseAccumulator(options) {
|
|
|
342
832
|
}
|
|
343
833
|
return { ...prev, status: newStatus };
|
|
344
834
|
case "thinking": {
|
|
835
|
+
if (payload.thinkingStep) {
|
|
836
|
+
const newStep = {
|
|
837
|
+
id: payload.thinkingStep.id || `step-${Date.now()}`,
|
|
838
|
+
label: payload.thinkingStep.label,
|
|
839
|
+
content: payload.thinkingStep.content,
|
|
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
|
|
845
|
+
};
|
|
846
|
+
const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
|
|
847
|
+
const next2 = {
|
|
848
|
+
...prev,
|
|
849
|
+
status: newStatus,
|
|
850
|
+
thinkingSteps: [...prev.thinkingSteps || [], newStep],
|
|
851
|
+
thinkingStartTime: thinkingStartTime2,
|
|
852
|
+
firstMessageTime
|
|
853
|
+
};
|
|
854
|
+
return { ...next2, timelineEntries: buildTimelineEntries(next2) };
|
|
855
|
+
}
|
|
345
856
|
const newThinking = payload.message || payload.content || "";
|
|
346
857
|
const separator = prev.thinking && newThinking ? "\n\n" : "";
|
|
347
858
|
const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
|
|
348
|
-
|
|
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 = {
|
|
349
870
|
...prev,
|
|
350
871
|
status: newStatus,
|
|
351
872
|
thinking: prev.thinking + separator + newThinking,
|
|
873
|
+
thinkingSteps: [...prevSteps, plainStep],
|
|
352
874
|
thinkingStartTime,
|
|
353
875
|
firstMessageTime
|
|
354
876
|
};
|
|
877
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
355
878
|
}
|
|
356
879
|
case "tool_call": {
|
|
357
880
|
const toolName = payload.message || payload.tool?.name;
|
|
@@ -360,14 +883,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
360
883
|
id: payload.tool?.id || `tool-${Date.now()}`,
|
|
361
884
|
name: toolName,
|
|
362
885
|
arguments: payload.tool?.arguments,
|
|
363
|
-
timestamp: Date.now()
|
|
886
|
+
timestamp: Date.now(),
|
|
887
|
+
agentName: payload.agentName,
|
|
888
|
+
parentAgent: payload.parentAgent,
|
|
889
|
+
depth: payload.depth
|
|
364
890
|
};
|
|
365
|
-
|
|
891
|
+
const next = {
|
|
366
892
|
...prev,
|
|
367
893
|
status: newStatus,
|
|
368
894
|
toolCalls: [...prev.toolCalls, newToolCall],
|
|
369
895
|
firstMessageTime
|
|
370
896
|
};
|
|
897
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
371
898
|
}
|
|
372
899
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
373
900
|
}
|
|
@@ -378,14 +905,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
378
905
|
id: payload.knowledge?.id || `knowledge-${Date.now()}`,
|
|
379
906
|
source: payload.knowledge?.source || "unknown",
|
|
380
907
|
content: knowledgeContent,
|
|
381
|
-
timestamp: Date.now()
|
|
908
|
+
timestamp: Date.now(),
|
|
909
|
+
agentName: payload.agentName,
|
|
910
|
+
parentAgent: payload.parentAgent,
|
|
911
|
+
depth: payload.depth
|
|
382
912
|
};
|
|
383
|
-
|
|
913
|
+
const next = {
|
|
384
914
|
...prev,
|
|
385
915
|
status: newStatus,
|
|
386
916
|
knowledge: [...prev.knowledge, newKnowledge],
|
|
387
917
|
firstMessageTime
|
|
388
918
|
};
|
|
919
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
389
920
|
}
|
|
390
921
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
391
922
|
}
|
|
@@ -396,14 +927,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
396
927
|
id: payload.memory?.id || `memory-${Date.now()}`,
|
|
397
928
|
type: payload.memory?.type || "unknown",
|
|
398
929
|
content: memoryContent,
|
|
399
|
-
timestamp: Date.now()
|
|
930
|
+
timestamp: Date.now(),
|
|
931
|
+
agentName: payload.agentName,
|
|
932
|
+
parentAgent: payload.parentAgent,
|
|
933
|
+
depth: payload.depth
|
|
400
934
|
};
|
|
401
|
-
|
|
935
|
+
const next = {
|
|
402
936
|
...prev,
|
|
403
937
|
status: newStatus,
|
|
404
938
|
memory: [...prev.memory, newMemory],
|
|
405
939
|
firstMessageTime
|
|
406
940
|
};
|
|
941
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
407
942
|
}
|
|
408
943
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
409
944
|
}
|
|
@@ -415,6 +950,28 @@ function useAgentResponseAccumulator(options) {
|
|
|
415
950
|
responseCompleteTime: Date.now(),
|
|
416
951
|
firstMessageTime: prev.firstMessageTime ?? Date.now()
|
|
417
952
|
};
|
|
953
|
+
case "status_update": {
|
|
954
|
+
const statusMessage = payload.message || payload.statusUpdate?.message;
|
|
955
|
+
if (statusMessage) {
|
|
956
|
+
const newStatusItem = {
|
|
957
|
+
id: payload.statusUpdate?.id || `status-${Date.now()}`,
|
|
958
|
+
message: statusMessage,
|
|
959
|
+
agent: payload.statusUpdate?.agent,
|
|
960
|
+
timestamp: Date.now(),
|
|
961
|
+
agentName: payload.agentName,
|
|
962
|
+
parentAgent: payload.parentAgent,
|
|
963
|
+
depth: payload.depth
|
|
964
|
+
};
|
|
965
|
+
const next = {
|
|
966
|
+
...prev,
|
|
967
|
+
status: newStatus,
|
|
968
|
+
statusUpdates: [...prev.statusUpdates, newStatusItem],
|
|
969
|
+
firstMessageTime
|
|
970
|
+
};
|
|
971
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
972
|
+
}
|
|
973
|
+
return { ...prev, status: newStatus, firstMessageTime };
|
|
974
|
+
}
|
|
418
975
|
default:
|
|
419
976
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
420
977
|
}
|
|
@@ -427,7 +984,288 @@ function useAgentResponseAccumulator(options) {
|
|
|
427
984
|
}, []);
|
|
428
985
|
return { state, handleMessage, reset };
|
|
429
986
|
}
|
|
430
|
-
var
|
|
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
|
+
}
|
|
1268
|
+
var AgentResponse = React11.forwardRef(
|
|
431
1269
|
({
|
|
432
1270
|
state,
|
|
433
1271
|
id,
|
|
@@ -439,10 +1277,15 @@ var AgentResponse = React.forwardRef(
|
|
|
439
1277
|
thinkingExpanded: controlledThinkingExpanded,
|
|
440
1278
|
onThinkingExpandedChange,
|
|
441
1279
|
actionsVisible = "hover",
|
|
1280
|
+
hitlInteractions,
|
|
1281
|
+
defaultHITLExpanded = false,
|
|
1282
|
+
statusContent,
|
|
442
1283
|
renderMarkdown,
|
|
1284
|
+
renderThinkingMarkdown,
|
|
443
1285
|
className,
|
|
444
1286
|
...props
|
|
445
1287
|
}, ref) => {
|
|
1288
|
+
const timelineUIStateRef = useRef(createTimelineUIState());
|
|
446
1289
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultThinkingExpanded);
|
|
447
1290
|
const isThinkingControlled = controlledThinkingExpanded !== void 0;
|
|
448
1291
|
const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
|
|
@@ -464,8 +1307,11 @@ var AgentResponse = React.forwardRef(
|
|
|
464
1307
|
if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
|
|
465
1308
|
return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
|
|
466
1309
|
}, [state.firstMessageTime, state.responseCompleteTime]);
|
|
467
|
-
const
|
|
468
|
-
const
|
|
1310
|
+
const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
|
|
1311
|
+
const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
|
|
1312
|
+
const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
|
|
1313
|
+
const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
|
|
1314
|
+
const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
|
|
469
1315
|
const showActionBar = state.status === "complete" && state.response;
|
|
470
1316
|
const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
|
|
471
1317
|
if (!hasAnyContent) {
|
|
@@ -485,24 +1331,41 @@ var AgentResponse = React.forwardRef(
|
|
|
485
1331
|
/* @__PURE__ */ jsx(
|
|
486
1332
|
MetadataRow,
|
|
487
1333
|
{
|
|
488
|
-
hasThinking:
|
|
1334
|
+
hasThinking: hasThinkingContent,
|
|
489
1335
|
isExpanded: thinkingExpanded,
|
|
490
1336
|
onToggle: toggleThinking,
|
|
491
1337
|
toolCalls: state.toolCalls,
|
|
492
1338
|
knowledge: state.knowledge,
|
|
493
1339
|
memory: state.memory,
|
|
1340
|
+
statusUpdates: state.statusUpdates,
|
|
1341
|
+
statusContent,
|
|
494
1342
|
status: state.status,
|
|
495
1343
|
elapsedTime
|
|
496
1344
|
}
|
|
497
1345
|
),
|
|
498
|
-
/* @__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(
|
|
499
1354
|
ThinkingSection,
|
|
500
1355
|
{
|
|
501
|
-
content: state.thinking,
|
|
502
|
-
isExpanded: thinkingExpanded
|
|
1356
|
+
content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
|
|
1357
|
+
isExpanded: thinkingExpanded,
|
|
1358
|
+
renderMarkdown: renderThinkingMarkdown
|
|
503
1359
|
}
|
|
504
1360
|
)
|
|
505
1361
|
] }),
|
|
1362
|
+
hasHITLInteractions && /* @__PURE__ */ jsx(
|
|
1363
|
+
HITLSection,
|
|
1364
|
+
{
|
|
1365
|
+
interactions: hitlInteractions,
|
|
1366
|
+
defaultExpanded: defaultHITLExpanded
|
|
1367
|
+
}
|
|
1368
|
+
),
|
|
506
1369
|
state.response && /* @__PURE__ */ jsx(
|
|
507
1370
|
"div",
|
|
508
1371
|
{
|
|
@@ -531,7 +1394,378 @@ var AgentResponse = React.forwardRef(
|
|
|
531
1394
|
}
|
|
532
1395
|
);
|
|
533
1396
|
AgentResponse.displayName = "AgentResponse";
|
|
1397
|
+
var UserPrompt = React11.forwardRef(
|
|
1398
|
+
({ content, timestamp, className, ...props }, ref) => {
|
|
1399
|
+
return /* @__PURE__ */ jsxs(
|
|
1400
|
+
"div",
|
|
1401
|
+
{
|
|
1402
|
+
ref,
|
|
1403
|
+
className: cn(
|
|
1404
|
+
"w-fit max-w-[80%] rounded-lg px-4 pt-3.5 pb-3",
|
|
1405
|
+
"bg-secondary text-secondary-foreground",
|
|
1406
|
+
className
|
|
1407
|
+
),
|
|
1408
|
+
...props,
|
|
1409
|
+
children: [
|
|
1410
|
+
/* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap", children: content }),
|
|
1411
|
+
timestamp && /* @__PURE__ */ jsx("p", { className: "text-xs text-secondary-foreground/70 mt-1", children: timestamp.toLocaleTimeString([], {
|
|
1412
|
+
hour: "2-digit",
|
|
1413
|
+
minute: "2-digit"
|
|
1414
|
+
}) })
|
|
1415
|
+
]
|
|
1416
|
+
}
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
);
|
|
1420
|
+
UserPrompt.displayName = "UserPrompt";
|
|
1421
|
+
function createCodeBlockDecorate(entry) {
|
|
1422
|
+
const [node, path] = entry;
|
|
1423
|
+
const ranges = [];
|
|
1424
|
+
if (!Text.isText(node)) {
|
|
1425
|
+
return ranges;
|
|
1426
|
+
}
|
|
1427
|
+
const { text } = node;
|
|
1428
|
+
const backtickPositions = [];
|
|
1429
|
+
let searchStart = 0;
|
|
1430
|
+
while (true) {
|
|
1431
|
+
const pos = text.indexOf("```", searchStart);
|
|
1432
|
+
if (pos === -1) break;
|
|
1433
|
+
backtickPositions.push(pos);
|
|
1434
|
+
searchStart = pos + 3;
|
|
1435
|
+
}
|
|
1436
|
+
let i = 0;
|
|
1437
|
+
while (i < backtickPositions.length) {
|
|
1438
|
+
const openPos = backtickPositions[i];
|
|
1439
|
+
const closePos = backtickPositions[i + 1];
|
|
1440
|
+
ranges.push({
|
|
1441
|
+
anchor: { path, offset: openPos },
|
|
1442
|
+
focus: { path, offset: openPos + 3 },
|
|
1443
|
+
codeDelimiter: true
|
|
1444
|
+
});
|
|
1445
|
+
if (closePos !== void 0) {
|
|
1446
|
+
if (closePos > openPos + 3) {
|
|
1447
|
+
ranges.push({
|
|
1448
|
+
anchor: { path, offset: openPos + 3 },
|
|
1449
|
+
focus: { path, offset: closePos },
|
|
1450
|
+
codeBlock: true
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
ranges.push({
|
|
1454
|
+
anchor: { path, offset: closePos },
|
|
1455
|
+
focus: { path, offset: closePos + 3 },
|
|
1456
|
+
codeDelimiter: true
|
|
1457
|
+
});
|
|
1458
|
+
i += 2;
|
|
1459
|
+
} else {
|
|
1460
|
+
if (text.length > openPos + 3) {
|
|
1461
|
+
ranges.push({
|
|
1462
|
+
anchor: { path, offset: openPos + 3 },
|
|
1463
|
+
focus: { path, offset: text.length },
|
|
1464
|
+
codeBlock: true
|
|
1465
|
+
});
|
|
1466
|
+
}
|
|
1467
|
+
i += 1;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return ranges;
|
|
1471
|
+
}
|
|
1472
|
+
function CodeBlockLeaf({ attributes, children, leaf }) {
|
|
1473
|
+
const leafAny = leaf;
|
|
1474
|
+
if (leafAny.codeBlock) {
|
|
1475
|
+
return /* @__PURE__ */ jsx(
|
|
1476
|
+
"span",
|
|
1477
|
+
{
|
|
1478
|
+
...attributes,
|
|
1479
|
+
className: "bg-muted/50 text-muted-foreground font-mono text-sm rounded px-1",
|
|
1480
|
+
children
|
|
1481
|
+
}
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
if (leafAny.codeDelimiter) {
|
|
1485
|
+
return /* @__PURE__ */ jsx(
|
|
1486
|
+
"span",
|
|
1487
|
+
{
|
|
1488
|
+
...attributes,
|
|
1489
|
+
className: "text-muted-foreground/50 font-mono text-sm",
|
|
1490
|
+
children
|
|
1491
|
+
}
|
|
1492
|
+
);
|
|
1493
|
+
}
|
|
1494
|
+
return /* @__PURE__ */ jsx("span", { ...attributes, children });
|
|
1495
|
+
}
|
|
1496
|
+
var UserPromptInput = React11.forwardRef(
|
|
1497
|
+
({
|
|
1498
|
+
value = "",
|
|
1499
|
+
onChange,
|
|
1500
|
+
onSubmit,
|
|
1501
|
+
clearOnSubmit = true,
|
|
1502
|
+
placeholder = "Type your message...",
|
|
1503
|
+
disabled = false,
|
|
1504
|
+
isSubmitting = false,
|
|
1505
|
+
onStop,
|
|
1506
|
+
disableWhileSubmitting = true,
|
|
1507
|
+
autoFocus = false,
|
|
1508
|
+
refocusAfterSubmit = false,
|
|
1509
|
+
onReady,
|
|
1510
|
+
minRows = 1,
|
|
1511
|
+
maxRows = 6,
|
|
1512
|
+
renderActions,
|
|
1513
|
+
enableTags = false,
|
|
1514
|
+
onTagCreate,
|
|
1515
|
+
onTagDelete,
|
|
1516
|
+
className,
|
|
1517
|
+
...props
|
|
1518
|
+
}, ref) => {
|
|
1519
|
+
const editorRef = React11.useRef(null);
|
|
1520
|
+
const [internalValue, setInternalValue] = React11.useState(value);
|
|
1521
|
+
const prevIsSubmitting = React11.useRef(isSubmitting);
|
|
1522
|
+
const hasEmittedReady = React11.useRef(false);
|
|
1523
|
+
React11.useEffect(() => {
|
|
1524
|
+
setInternalValue(value);
|
|
1525
|
+
}, [value]);
|
|
1526
|
+
React11.useEffect(() => {
|
|
1527
|
+
if (autoFocus) {
|
|
1528
|
+
requestAnimationFrame(() => {
|
|
1529
|
+
requestAnimationFrame(() => {
|
|
1530
|
+
editorRef.current?.focus();
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
}
|
|
1534
|
+
}, [autoFocus]);
|
|
1535
|
+
React11.useEffect(() => {
|
|
1536
|
+
if (!hasEmittedReady.current && onReady) {
|
|
1537
|
+
requestAnimationFrame(() => {
|
|
1538
|
+
requestAnimationFrame(() => {
|
|
1539
|
+
hasEmittedReady.current = true;
|
|
1540
|
+
onReady();
|
|
1541
|
+
});
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
}, [onReady]);
|
|
1545
|
+
React11.useEffect(() => {
|
|
1546
|
+
if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
|
|
1547
|
+
requestAnimationFrame(() => {
|
|
1548
|
+
editorRef.current?.focus();
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
prevIsSubmitting.current = isSubmitting;
|
|
1552
|
+
}, [isSubmitting, refocusAfterSubmit]);
|
|
1553
|
+
React11.useImperativeHandle(
|
|
1554
|
+
ref,
|
|
1555
|
+
() => ({
|
|
1556
|
+
focus: () => {
|
|
1557
|
+
try {
|
|
1558
|
+
editorRef.current?.focus();
|
|
1559
|
+
} catch {
|
|
1560
|
+
requestAnimationFrame(() => {
|
|
1561
|
+
requestAnimationFrame(() => {
|
|
1562
|
+
editorRef.current?.focus();
|
|
1563
|
+
});
|
|
1564
|
+
});
|
|
1565
|
+
}
|
|
1566
|
+
},
|
|
1567
|
+
clear: () => {
|
|
1568
|
+
editorRef.current?.clear();
|
|
1569
|
+
setInternalValue("");
|
|
1570
|
+
},
|
|
1571
|
+
getText: () => editorRef.current?.getText() ?? "",
|
|
1572
|
+
insertText: (text) => editorRef.current?.insertText(text)
|
|
1573
|
+
}),
|
|
1574
|
+
[]
|
|
1575
|
+
);
|
|
1576
|
+
const handleChange = React11.useCallback(
|
|
1577
|
+
(newValue) => {
|
|
1578
|
+
setInternalValue(newValue);
|
|
1579
|
+
onChange?.(newValue);
|
|
1580
|
+
},
|
|
1581
|
+
[onChange]
|
|
1582
|
+
);
|
|
1583
|
+
const handleSubmit = React11.useCallback(
|
|
1584
|
+
(text) => {
|
|
1585
|
+
if (disabled || isSubmitting) return;
|
|
1586
|
+
if (!text.trim()) return;
|
|
1587
|
+
onSubmit?.(text.trim());
|
|
1588
|
+
if (clearOnSubmit) {
|
|
1589
|
+
editorRef.current?.clear();
|
|
1590
|
+
setInternalValue("");
|
|
1591
|
+
}
|
|
1592
|
+
},
|
|
1593
|
+
[disabled, isSubmitting, onSubmit, clearOnSubmit]
|
|
1594
|
+
);
|
|
1595
|
+
const handleSendClick = React11.useCallback(() => {
|
|
1596
|
+
const text = editorRef.current?.getText() ?? "";
|
|
1597
|
+
handleSubmit(text);
|
|
1598
|
+
}, [handleSubmit]);
|
|
1599
|
+
const hasContent = internalValue.trim().length > 0;
|
|
1600
|
+
const canSubmit = hasContent && !disabled && !isSubmitting;
|
|
1601
|
+
return /* @__PURE__ */ jsxs(
|
|
1602
|
+
"div",
|
|
1603
|
+
{
|
|
1604
|
+
className: cn(
|
|
1605
|
+
"rounded-lg border border-input bg-background",
|
|
1606
|
+
disabled && "opacity-50 cursor-not-allowed",
|
|
1607
|
+
className
|
|
1608
|
+
),
|
|
1609
|
+
...props,
|
|
1610
|
+
children: [
|
|
1611
|
+
/* @__PURE__ */ jsx("div", { className: "pl-2 pr-0 pt-1 pb-1", children: /* @__PURE__ */ jsx(
|
|
1612
|
+
SlateEditor,
|
|
1613
|
+
{
|
|
1614
|
+
ref: editorRef,
|
|
1615
|
+
value: internalValue,
|
|
1616
|
+
onChange: handleChange,
|
|
1617
|
+
onSubmit: handleSubmit,
|
|
1618
|
+
clearOnSubmit: false,
|
|
1619
|
+
placeholder,
|
|
1620
|
+
disabled: disabled || disableWhileSubmitting && isSubmitting,
|
|
1621
|
+
enableTags,
|
|
1622
|
+
onTagCreate,
|
|
1623
|
+
onTagDelete,
|
|
1624
|
+
minRows,
|
|
1625
|
+
maxRows,
|
|
1626
|
+
decorate: createCodeBlockDecorate,
|
|
1627
|
+
renderLeaf: CodeBlockLeaf
|
|
1628
|
+
}
|
|
1629
|
+
) }),
|
|
1630
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
|
|
1631
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
|
|
1632
|
+
isSubmitting && onStop ? /* @__PURE__ */ jsx(
|
|
1633
|
+
IconButton,
|
|
1634
|
+
{
|
|
1635
|
+
icon: /* @__PURE__ */ jsx(Square, {}),
|
|
1636
|
+
variant: "filled",
|
|
1637
|
+
size: "sm",
|
|
1638
|
+
"aria-label": "Stop",
|
|
1639
|
+
onClick: onStop
|
|
1640
|
+
}
|
|
1641
|
+
) : /* @__PURE__ */ jsx(
|
|
1642
|
+
IconButton,
|
|
1643
|
+
{
|
|
1644
|
+
icon: isSubmitting ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsx(Send, {}),
|
|
1645
|
+
variant: "filled",
|
|
1646
|
+
size: "sm",
|
|
1647
|
+
"aria-label": isSubmitting ? "Sending..." : "Send message",
|
|
1648
|
+
disabled: !canSubmit,
|
|
1649
|
+
onClick: handleSendClick
|
|
1650
|
+
}
|
|
1651
|
+
)
|
|
1652
|
+
] })
|
|
1653
|
+
]
|
|
1654
|
+
}
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
);
|
|
1658
|
+
UserPromptInput.displayName = "UserPromptInput";
|
|
1659
|
+
|
|
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
|
+
`;
|
|
534
1768
|
|
|
535
|
-
export { ActionBar, ActivityIndicators, AgentResponse, MetadataRow, ThinkingSection, formatTime, formatTotalTime, initialAgentResponseState, useAgentResponseAccumulator, useThinkingTimer };
|
|
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 };
|
|
536
1770
|
//# sourceMappingURL=index.js.map
|
|
537
1771
|
//# sourceMappingURL=index.js.map
|