@optilogic/chat 1.0.0-beta.8 → 1.0.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 +136 -0
- package/dist/index.cjs +989 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +361 -2
- package/dist/index.d.ts +361 -2
- package/dist/index.js +964 -46
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/agent-response/AgentResponse.tsx +86 -14
- package/src/components/agent-response/components/HITLSection.tsx +95 -0
- 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 +6 -0
- package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +65 -8
- package/src/components/agent-response/index.ts +21 -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/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-input/UserPromptInput.tsx +13 -8
- package/src/components/user-prompt-input/types.ts +4 -0
- package/src/index.ts +42 -0
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { useState, useCallback,
|
|
3
|
-
import { cn, Popover, PopoverTrigger, PopoverContent, IconButton, LoadingSpinner } from '@optilogic/core';
|
|
4
|
-
import { Activity, Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, Square, Loader2, Send, ChevronUp,
|
|
1
|
+
import * as React11 from 'react';
|
|
2
|
+
import { useState, useCallback, useRef, useEffect, useMemo } from '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 = React7.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 = React7.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 = React7.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 = React7.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,
|
|
@@ -334,6 +336,313 @@ var ActionBar = React7.forwardRef(
|
|
|
334
336
|
}
|
|
335
337
|
);
|
|
336
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";
|
|
337
646
|
function useThinkingTimer({
|
|
338
647
|
startTime,
|
|
339
648
|
endTime,
|
|
@@ -369,12 +678,152 @@ var initialAgentResponseState = {
|
|
|
369
678
|
knowledge: [],
|
|
370
679
|
memory: [],
|
|
371
680
|
statusUpdates: [],
|
|
681
|
+
potentialResponses: [],
|
|
682
|
+
customTimelineEntries: [],
|
|
372
683
|
response: "",
|
|
373
684
|
thinkingStartTime: null,
|
|
374
685
|
responseCompleteTime: null,
|
|
375
686
|
firstMessageTime: null
|
|
376
687
|
};
|
|
377
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
|
+
|
|
378
827
|
// src/components/agent-response/hooks/useAgentResponseAccumulator.ts
|
|
379
828
|
function useAgentResponseAccumulator(options) {
|
|
380
829
|
const [state, setState] = useState(initialAgentResponseState);
|
|
@@ -408,28 +857,44 @@ function useAgentResponseAccumulator(options) {
|
|
|
408
857
|
id: payload.thinkingStep.id || `step-${Date.now()}`,
|
|
409
858
|
label: payload.thinkingStep.label,
|
|
410
859
|
content: payload.thinkingStep.content,
|
|
411
|
-
depth: payload.thinkingStep.depth ?? 0,
|
|
412
|
-
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
|
|
413
865
|
};
|
|
414
866
|
const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
|
|
415
|
-
|
|
867
|
+
const next2 = {
|
|
416
868
|
...prev,
|
|
417
869
|
status: newStatus,
|
|
418
870
|
thinkingSteps: [...prev.thinkingSteps || [], newStep],
|
|
419
871
|
thinkingStartTime: thinkingStartTime2,
|
|
420
872
|
firstMessageTime
|
|
421
873
|
};
|
|
874
|
+
return { ...next2, timelineEntries: buildTimelineEntries(next2) };
|
|
422
875
|
}
|
|
423
876
|
const newThinking = payload.message || payload.content || "";
|
|
424
877
|
const separator = prev.thinking && newThinking ? "\n\n" : "";
|
|
425
878
|
const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
|
|
426
|
-
|
|
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 = {
|
|
427
890
|
...prev,
|
|
428
891
|
status: newStatus,
|
|
429
892
|
thinking: prev.thinking + separator + newThinking,
|
|
893
|
+
thinkingSteps: [...prevSteps, plainStep],
|
|
430
894
|
thinkingStartTime,
|
|
431
895
|
firstMessageTime
|
|
432
896
|
};
|
|
897
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
433
898
|
}
|
|
434
899
|
case "tool_call": {
|
|
435
900
|
const toolName = payload.message || payload.tool?.name;
|
|
@@ -438,14 +903,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
438
903
|
id: payload.tool?.id || `tool-${Date.now()}`,
|
|
439
904
|
name: toolName,
|
|
440
905
|
arguments: payload.tool?.arguments,
|
|
441
|
-
timestamp: Date.now()
|
|
906
|
+
timestamp: Date.now(),
|
|
907
|
+
agentName: payload.agentName,
|
|
908
|
+
parentAgent: payload.parentAgent,
|
|
909
|
+
depth: payload.depth
|
|
442
910
|
};
|
|
443
|
-
|
|
911
|
+
const next = {
|
|
444
912
|
...prev,
|
|
445
913
|
status: newStatus,
|
|
446
914
|
toolCalls: [...prev.toolCalls, newToolCall],
|
|
447
915
|
firstMessageTime
|
|
448
916
|
};
|
|
917
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
449
918
|
}
|
|
450
919
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
451
920
|
}
|
|
@@ -456,14 +925,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
456
925
|
id: payload.knowledge?.id || `knowledge-${Date.now()}`,
|
|
457
926
|
source: payload.knowledge?.source || "unknown",
|
|
458
927
|
content: knowledgeContent,
|
|
459
|
-
timestamp: Date.now()
|
|
928
|
+
timestamp: Date.now(),
|
|
929
|
+
agentName: payload.agentName,
|
|
930
|
+
parentAgent: payload.parentAgent,
|
|
931
|
+
depth: payload.depth
|
|
460
932
|
};
|
|
461
|
-
|
|
933
|
+
const next = {
|
|
462
934
|
...prev,
|
|
463
935
|
status: newStatus,
|
|
464
936
|
knowledge: [...prev.knowledge, newKnowledge],
|
|
465
937
|
firstMessageTime
|
|
466
938
|
};
|
|
939
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
467
940
|
}
|
|
468
941
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
469
942
|
}
|
|
@@ -474,14 +947,18 @@ function useAgentResponseAccumulator(options) {
|
|
|
474
947
|
id: payload.memory?.id || `memory-${Date.now()}`,
|
|
475
948
|
type: payload.memory?.type || "unknown",
|
|
476
949
|
content: memoryContent,
|
|
477
|
-
timestamp: Date.now()
|
|
950
|
+
timestamp: Date.now(),
|
|
951
|
+
agentName: payload.agentName,
|
|
952
|
+
parentAgent: payload.parentAgent,
|
|
953
|
+
depth: payload.depth
|
|
478
954
|
};
|
|
479
|
-
|
|
955
|
+
const next = {
|
|
480
956
|
...prev,
|
|
481
957
|
status: newStatus,
|
|
482
958
|
memory: [...prev.memory, newMemory],
|
|
483
959
|
firstMessageTime
|
|
484
960
|
};
|
|
961
|
+
return { ...next, timelineEntries: buildTimelineEntries(next) };
|
|
485
962
|
}
|
|
486
963
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
487
964
|
}
|
|
@@ -500,14 +977,39 @@ function useAgentResponseAccumulator(options) {
|
|
|
500
977
|
id: payload.statusUpdate?.id || `status-${Date.now()}`,
|
|
501
978
|
message: statusMessage,
|
|
502
979
|
agent: payload.statusUpdate?.agent,
|
|
503
|
-
timestamp: Date.now()
|
|
980
|
+
timestamp: Date.now(),
|
|
981
|
+
agentName: payload.agentName,
|
|
982
|
+
parentAgent: payload.parentAgent,
|
|
983
|
+
depth: payload.depth
|
|
504
984
|
};
|
|
505
|
-
|
|
985
|
+
const next = {
|
|
506
986
|
...prev,
|
|
507
987
|
status: newStatus,
|
|
508
988
|
statusUpdates: [...prev.statusUpdates, newStatusItem],
|
|
509
989
|
firstMessageTime
|
|
510
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) };
|
|
511
1013
|
}
|
|
512
1014
|
return { ...prev, status: newStatus, firstMessageTime };
|
|
513
1015
|
}
|
|
@@ -523,7 +1025,288 @@ function useAgentResponseAccumulator(options) {
|
|
|
523
1025
|
}, []);
|
|
524
1026
|
return { state, handleMessage, reset };
|
|
525
1027
|
}
|
|
526
|
-
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(
|
|
527
1310
|
({
|
|
528
1311
|
state,
|
|
529
1312
|
id,
|
|
@@ -535,11 +1318,16 @@ var AgentResponse = React7.forwardRef(
|
|
|
535
1318
|
thinkingExpanded: controlledThinkingExpanded,
|
|
536
1319
|
onThinkingExpandedChange,
|
|
537
1320
|
actionsVisible = "hover",
|
|
1321
|
+
hitlInteractions,
|
|
1322
|
+
defaultHITLExpanded = false,
|
|
1323
|
+
statusContent,
|
|
538
1324
|
renderMarkdown,
|
|
539
1325
|
renderThinkingMarkdown,
|
|
1326
|
+
timelineMaxHeight,
|
|
540
1327
|
className,
|
|
541
1328
|
...props
|
|
542
1329
|
}, ref) => {
|
|
1330
|
+
const timelineUIStateRef = useRef(createTimelineUIState());
|
|
543
1331
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultThinkingExpanded);
|
|
544
1332
|
const isThinkingControlled = controlledThinkingExpanded !== void 0;
|
|
545
1333
|
const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
|
|
@@ -561,8 +1349,10 @@ var AgentResponse = React7.forwardRef(
|
|
|
561
1349
|
if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
|
|
562
1350
|
return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
|
|
563
1351
|
}, [state.firstMessageTime, state.responseCompleteTime]);
|
|
564
|
-
const
|
|
565
|
-
const
|
|
1352
|
+
const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
|
|
1353
|
+
const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || hasTimelineEntries || false;
|
|
1354
|
+
const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
|
|
1355
|
+
const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
|
|
566
1356
|
const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
|
|
567
1357
|
const showActionBar = state.status === "complete" && state.response;
|
|
568
1358
|
const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
|
|
@@ -590,11 +1380,20 @@ var AgentResponse = React7.forwardRef(
|
|
|
590
1380
|
knowledge: state.knowledge,
|
|
591
1381
|
memory: state.memory,
|
|
592
1382
|
statusUpdates: state.statusUpdates,
|
|
1383
|
+
statusContent,
|
|
593
1384
|
status: state.status,
|
|
594
1385
|
elapsedTime
|
|
595
1386
|
}
|
|
596
1387
|
),
|
|
597
|
-
/* @__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(
|
|
598
1397
|
ThinkingSection,
|
|
599
1398
|
{
|
|
600
1399
|
content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
|
|
@@ -603,6 +1402,13 @@ var AgentResponse = React7.forwardRef(
|
|
|
603
1402
|
}
|
|
604
1403
|
)
|
|
605
1404
|
] }),
|
|
1405
|
+
hasHITLInteractions && /* @__PURE__ */ jsx(
|
|
1406
|
+
HITLSection,
|
|
1407
|
+
{
|
|
1408
|
+
interactions: hitlInteractions,
|
|
1409
|
+
defaultExpanded: defaultHITLExpanded
|
|
1410
|
+
}
|
|
1411
|
+
),
|
|
606
1412
|
state.response && /* @__PURE__ */ jsx(
|
|
607
1413
|
"div",
|
|
608
1414
|
{
|
|
@@ -631,7 +1437,7 @@ var AgentResponse = React7.forwardRef(
|
|
|
631
1437
|
}
|
|
632
1438
|
);
|
|
633
1439
|
AgentResponse.displayName = "AgentResponse";
|
|
634
|
-
var UserPrompt =
|
|
1440
|
+
var UserPrompt = React11.forwardRef(
|
|
635
1441
|
({ content, timestamp, className, ...props }, ref) => {
|
|
636
1442
|
return /* @__PURE__ */ jsxs(
|
|
637
1443
|
"div",
|
|
@@ -730,7 +1536,7 @@ function CodeBlockLeaf({ attributes, children, leaf }) {
|
|
|
730
1536
|
}
|
|
731
1537
|
return /* @__PURE__ */ jsx("span", { ...attributes, children });
|
|
732
1538
|
}
|
|
733
|
-
var UserPromptInput =
|
|
1539
|
+
var UserPromptInput = React11.forwardRef(
|
|
734
1540
|
({
|
|
735
1541
|
value = "",
|
|
736
1542
|
onChange,
|
|
@@ -740,6 +1546,8 @@ var UserPromptInput = React7.forwardRef(
|
|
|
740
1546
|
disabled = false,
|
|
741
1547
|
isSubmitting = false,
|
|
742
1548
|
onStop,
|
|
1549
|
+
stopTooltip,
|
|
1550
|
+
stopClassName,
|
|
743
1551
|
disableWhileSubmitting = true,
|
|
744
1552
|
autoFocus = false,
|
|
745
1553
|
refocusAfterSubmit = false,
|
|
@@ -753,14 +1561,14 @@ var UserPromptInput = React7.forwardRef(
|
|
|
753
1561
|
className,
|
|
754
1562
|
...props
|
|
755
1563
|
}, ref) => {
|
|
756
|
-
const editorRef =
|
|
757
|
-
const [internalValue, setInternalValue] =
|
|
758
|
-
const prevIsSubmitting =
|
|
759
|
-
const hasEmittedReady =
|
|
760
|
-
|
|
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(() => {
|
|
761
1569
|
setInternalValue(value);
|
|
762
1570
|
}, [value]);
|
|
763
|
-
|
|
1571
|
+
React11.useEffect(() => {
|
|
764
1572
|
if (autoFocus) {
|
|
765
1573
|
requestAnimationFrame(() => {
|
|
766
1574
|
requestAnimationFrame(() => {
|
|
@@ -769,7 +1577,7 @@ var UserPromptInput = React7.forwardRef(
|
|
|
769
1577
|
});
|
|
770
1578
|
}
|
|
771
1579
|
}, [autoFocus]);
|
|
772
|
-
|
|
1580
|
+
React11.useEffect(() => {
|
|
773
1581
|
if (!hasEmittedReady.current && onReady) {
|
|
774
1582
|
requestAnimationFrame(() => {
|
|
775
1583
|
requestAnimationFrame(() => {
|
|
@@ -779,7 +1587,7 @@ var UserPromptInput = React7.forwardRef(
|
|
|
779
1587
|
});
|
|
780
1588
|
}
|
|
781
1589
|
}, [onReady]);
|
|
782
|
-
|
|
1590
|
+
React11.useEffect(() => {
|
|
783
1591
|
if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
|
|
784
1592
|
requestAnimationFrame(() => {
|
|
785
1593
|
editorRef.current?.focus();
|
|
@@ -787,7 +1595,7 @@ var UserPromptInput = React7.forwardRef(
|
|
|
787
1595
|
}
|
|
788
1596
|
prevIsSubmitting.current = isSubmitting;
|
|
789
1597
|
}, [isSubmitting, refocusAfterSubmit]);
|
|
790
|
-
|
|
1598
|
+
React11.useImperativeHandle(
|
|
791
1599
|
ref,
|
|
792
1600
|
() => ({
|
|
793
1601
|
focus: () => {
|
|
@@ -810,14 +1618,14 @@ var UserPromptInput = React7.forwardRef(
|
|
|
810
1618
|
}),
|
|
811
1619
|
[]
|
|
812
1620
|
);
|
|
813
|
-
const handleChange =
|
|
1621
|
+
const handleChange = React11.useCallback(
|
|
814
1622
|
(newValue) => {
|
|
815
1623
|
setInternalValue(newValue);
|
|
816
1624
|
onChange?.(newValue);
|
|
817
1625
|
},
|
|
818
1626
|
[onChange]
|
|
819
1627
|
);
|
|
820
|
-
const handleSubmit =
|
|
1628
|
+
const handleSubmit = React11.useCallback(
|
|
821
1629
|
(text) => {
|
|
822
1630
|
if (disabled || isSubmitting) return;
|
|
823
1631
|
if (!text.trim()) return;
|
|
@@ -829,7 +1637,7 @@ var UserPromptInput = React7.forwardRef(
|
|
|
829
1637
|
},
|
|
830
1638
|
[disabled, isSubmitting, onSubmit, clearOnSubmit]
|
|
831
1639
|
);
|
|
832
|
-
const handleSendClick =
|
|
1640
|
+
const handleSendClick = React11.useCallback(() => {
|
|
833
1641
|
const text = editorRef.current?.getText() ?? "";
|
|
834
1642
|
handleSubmit(text);
|
|
835
1643
|
}, [handleSubmit]);
|
|
@@ -866,16 +1674,17 @@ var UserPromptInput = React7.forwardRef(
|
|
|
866
1674
|
) }),
|
|
867
1675
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
|
|
868
1676
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
|
|
869
|
-
isSubmitting && onStop ? /* @__PURE__ */ jsx(
|
|
1677
|
+
isSubmitting && onStop ? /* @__PURE__ */ jsx(Tooltip, { content: stopTooltip, disabled: !stopTooltip, children: /* @__PURE__ */ jsx(
|
|
870
1678
|
IconButton,
|
|
871
1679
|
{
|
|
872
1680
|
icon: /* @__PURE__ */ jsx(Square, {}),
|
|
873
1681
|
variant: "filled",
|
|
874
1682
|
size: "sm",
|
|
875
|
-
"aria-label": "Stop",
|
|
876
|
-
onClick: onStop
|
|
1683
|
+
"aria-label": stopTooltip || "Stop",
|
|
1684
|
+
onClick: onStop,
|
|
1685
|
+
className: stopClassName
|
|
877
1686
|
}
|
|
878
|
-
) : /* @__PURE__ */ jsx(
|
|
1687
|
+
) }) : /* @__PURE__ */ jsx(
|
|
879
1688
|
IconButton,
|
|
880
1689
|
{
|
|
881
1690
|
icon: isSubmitting ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsx(Send, {}),
|
|
@@ -894,6 +1703,115 @@ var UserPromptInput = React7.forwardRef(
|
|
|
894
1703
|
);
|
|
895
1704
|
UserPromptInput.displayName = "UserPromptInput";
|
|
896
1705
|
|
|
897
|
-
|
|
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 };
|
|
898
1816
|
//# sourceMappingURL=index.js.map
|
|
899
1817
|
//# sourceMappingURL=index.js.map
|