@optilogic/chat 1.0.0-beta.7 → 1.0.0-beta.9
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 +93 -0
- package/dist/index.cjs +338 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +81 -1
- package/dist/index.d.ts +81 -1
- package/dist/index.js +322 -25
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/agent-response/AgentResponse.tsx +27 -1
- package/src/components/agent-response/components/HITLSection.tsx +95 -0
- package/src/components/agent-response/components/index.ts +3 -0
- package/src/components/agent-response/index.ts +2 -0
- package/src/components/hitl-interactions/HITLInteractionRecord.tsx +139 -0
- package/src/components/hitl-interactions/HITLQuestionPanel.tsx +256 -0
- package/src/components/hitl-interactions/index.ts +18 -0
- package/src/index.ts +13 -0
package/README.md
CHANGED
|
@@ -70,6 +70,77 @@ function ChatInput() {
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
### HITLQuestionPanel
|
|
74
|
+
|
|
75
|
+
Display an interactive panel for human-in-the-loop clarifying questions. Replaces the input area when the agent needs user clarification:
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { HITLQuestionPanel, type HITLQuestion } from '@optilogic/chat';
|
|
79
|
+
|
|
80
|
+
const question: HITLQuestion = {
|
|
81
|
+
reason: "I need clarification before proceeding with the data mapping.",
|
|
82
|
+
questions: [
|
|
83
|
+
"Which table should I load the shipment data into?",
|
|
84
|
+
"Should I overwrite existing rows or append?",
|
|
85
|
+
],
|
|
86
|
+
options: {
|
|
87
|
+
"Which table should I load the shipment data into?": ["Customers", "Demand", "Shipments"],
|
|
88
|
+
"Should I overwrite existing rows or append?": ["Overwrite", "Append"],
|
|
89
|
+
},
|
|
90
|
+
context: "Source file has 1,200 rows with columns: origin, destination, quantity, date",
|
|
91
|
+
timeoutSeconds: 300,
|
|
92
|
+
receivedAt: Date.now(),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function ChatInput() {
|
|
96
|
+
return (
|
|
97
|
+
<HITLQuestionPanel
|
|
98
|
+
question={question}
|
|
99
|
+
onSubmit={(response) => console.log('Response:', response)}
|
|
100
|
+
onStop={() => console.log('Agent stopped')}
|
|
101
|
+
/>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### HITLInteractionRecord
|
|
107
|
+
|
|
108
|
+
Display a completed HITL Q&A interaction in the chat history:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { HITLInteractionRecord, type HITLInteraction } from '@optilogic/chat';
|
|
112
|
+
|
|
113
|
+
const interaction: HITLInteraction = {
|
|
114
|
+
question: { /* HITLQuestion object */ },
|
|
115
|
+
response: "Q: Which table?\nA: Shipments",
|
|
116
|
+
respondedAt: Date.now(),
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
function CompletedInteraction() {
|
|
120
|
+
return <HITLInteractionRecord interaction={interaction} />;
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### AgentResponse with HITL Interactions
|
|
125
|
+
|
|
126
|
+
HITL interactions can also be displayed as a collapsible section within AgentResponse:
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import { AgentResponse, type HITLInteraction } from '@optilogic/chat';
|
|
130
|
+
|
|
131
|
+
function ChatView() {
|
|
132
|
+
const hitlInteractions: HITLInteraction[] = [/* completed interactions */];
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<AgentResponse
|
|
136
|
+
state={state}
|
|
137
|
+
hitlInteractions={hitlInteractions}
|
|
138
|
+
defaultHITLExpanded={false}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
73
144
|
## Components
|
|
74
145
|
|
|
75
146
|
### AgentResponse
|
|
@@ -79,6 +150,7 @@ Main component for displaying AI agent responses with:
|
|
|
79
150
|
- Tool call visualization
|
|
80
151
|
- Copy and feedback actions
|
|
81
152
|
- Metadata display (tokens, timing)
|
|
153
|
+
- Optional collapsible HITL interaction history
|
|
82
154
|
|
|
83
155
|
### UserPrompt
|
|
84
156
|
Component for displaying user messages in the chat interface.
|
|
@@ -86,6 +158,22 @@ Component for displaying user messages in the chat interface.
|
|
|
86
158
|
### UserPromptInput
|
|
87
159
|
Input component for composing user messages with tag support.
|
|
88
160
|
|
|
161
|
+
### HITLQuestionPanel
|
|
162
|
+
Interactive panel for displaying agent clarifying questions with:
|
|
163
|
+
- Predefined option buttons (toggleable selection)
|
|
164
|
+
- Free-form text input
|
|
165
|
+
- Countdown timer with visual warning state
|
|
166
|
+
- Keyboard support (Enter to submit, Shift+Enter for newlines)
|
|
167
|
+
|
|
168
|
+
### HITLInteractionRecord
|
|
169
|
+
Read-only display of a completed HITL Q&A interaction with:
|
|
170
|
+
- Per-question inline answers
|
|
171
|
+
- Context display
|
|
172
|
+
- Fallback for non-structured response formats
|
|
173
|
+
|
|
174
|
+
### HITLSection
|
|
175
|
+
Collapsible sub-component for rendering HITL interactions within AgentResponse. Follows the same expand/collapse pattern as ThinkingSection.
|
|
176
|
+
|
|
89
177
|
## Hooks
|
|
90
178
|
|
|
91
179
|
### useAgentResponseAccumulator
|
|
@@ -94,6 +182,11 @@ Manages state for streaming agent responses, handling incremental updates and me
|
|
|
94
182
|
### useThinkingTimer
|
|
95
183
|
Tracks elapsed time during agent thinking phases.
|
|
96
184
|
|
|
185
|
+
## Utilities
|
|
186
|
+
|
|
187
|
+
### buildResponseString
|
|
188
|
+
Combines selected options and free-form text into a single formatted string for the backend. Used internally by HITLQuestionPanel and can be used to construct responses programmatically.
|
|
189
|
+
|
|
97
190
|
## License
|
|
98
191
|
|
|
99
192
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React10 = require('react');
|
|
4
4
|
var core = require('@optilogic/core');
|
|
5
5
|
var lucideReact = require('lucide-react');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -24,10 +24,10 @@ function _interopNamespace(e) {
|
|
|
24
24
|
return Object.freeze(n);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
|
|
28
28
|
|
|
29
29
|
// src/components/agent-response/AgentResponse.tsx
|
|
30
|
-
var ActivityIndicators =
|
|
30
|
+
var ActivityIndicators = React10__namespace.forwardRef(
|
|
31
31
|
({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
|
|
32
32
|
const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
|
|
33
33
|
if (!hasAnyActivity) return null;
|
|
@@ -146,7 +146,7 @@ function formatTotalTime(seconds) {
|
|
|
146
146
|
const minutes = seconds / 60;
|
|
147
147
|
return `${minutes.toFixed(1)}m`;
|
|
148
148
|
}
|
|
149
|
-
var MetadataRow =
|
|
149
|
+
var MetadataRow = React10__namespace.forwardRef(
|
|
150
150
|
({
|
|
151
151
|
hasThinking,
|
|
152
152
|
isExpanded,
|
|
@@ -213,8 +213,8 @@ var MetadataRow = React7__namespace.forwardRef(
|
|
|
213
213
|
);
|
|
214
214
|
MetadataRow.displayName = "MetadataRow";
|
|
215
215
|
var ThinkingStepItem = ({ step, renderMarkdown }) => {
|
|
216
|
-
const [isCollapsed, setIsCollapsed] =
|
|
217
|
-
const toggleCollapse =
|
|
216
|
+
const [isCollapsed, setIsCollapsed] = React10.useState(step.isCollapsed ?? false);
|
|
217
|
+
const toggleCollapse = React10.useCallback(() => {
|
|
218
218
|
setIsCollapsed((prev) => !prev);
|
|
219
219
|
}, []);
|
|
220
220
|
const indentPadding = step.depth * 16;
|
|
@@ -241,7 +241,7 @@ var ThinkingStepItem = ({ step, renderMarkdown }) => {
|
|
|
241
241
|
)
|
|
242
242
|
] });
|
|
243
243
|
};
|
|
244
|
-
var ThinkingSection =
|
|
244
|
+
var ThinkingSection = React10__namespace.forwardRef(
|
|
245
245
|
({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
|
|
246
246
|
if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
|
|
247
247
|
return null;
|
|
@@ -266,7 +266,7 @@ var ThinkingSection = React7__namespace.forwardRef(
|
|
|
266
266
|
}
|
|
267
267
|
);
|
|
268
268
|
ThinkingSection.displayName = "ThinkingSection";
|
|
269
|
-
var ActionBar =
|
|
269
|
+
var ActionBar = React10__namespace.forwardRef(
|
|
270
270
|
({
|
|
271
271
|
response,
|
|
272
272
|
isVisible,
|
|
@@ -277,8 +277,8 @@ var ActionBar = React7__namespace.forwardRef(
|
|
|
277
277
|
className,
|
|
278
278
|
...props
|
|
279
279
|
}, ref) => {
|
|
280
|
-
const [copied, setCopied] =
|
|
281
|
-
const handleCopy =
|
|
280
|
+
const [copied, setCopied] = React10.useState(false);
|
|
281
|
+
const handleCopy = React10.useCallback(async () => {
|
|
282
282
|
try {
|
|
283
283
|
await navigator.clipboard.writeText(response);
|
|
284
284
|
setCopied(true);
|
|
@@ -288,11 +288,11 @@ var ActionBar = React7__namespace.forwardRef(
|
|
|
288
288
|
console.error("Failed to copy response:", err);
|
|
289
289
|
}
|
|
290
290
|
}, [response, onResponseCopy]);
|
|
291
|
-
const handleThumbsUp =
|
|
291
|
+
const handleThumbsUp = React10.useCallback(() => {
|
|
292
292
|
const newValue = feedback === "up" ? null : "up";
|
|
293
293
|
onFeedbackChange?.(newValue);
|
|
294
294
|
}, [feedback, onFeedbackChange]);
|
|
295
|
-
const handleThumbsDown =
|
|
295
|
+
const handleThumbsDown = React10.useCallback(() => {
|
|
296
296
|
const newValue = feedback === "down" ? null : "down";
|
|
297
297
|
onFeedbackChange?.(newValue);
|
|
298
298
|
}, [feedback, onFeedbackChange]);
|
|
@@ -355,13 +355,300 @@ var ActionBar = React7__namespace.forwardRef(
|
|
|
355
355
|
}
|
|
356
356
|
);
|
|
357
357
|
ActionBar.displayName = "ActionBar";
|
|
358
|
+
function buildResponseString(questions, selectedOptions, freeformText) {
|
|
359
|
+
const parts = [];
|
|
360
|
+
for (const q of questions) {
|
|
361
|
+
const answer = selectedOptions[q];
|
|
362
|
+
if (answer) {
|
|
363
|
+
parts.push(`Q: ${q}
|
|
364
|
+
A: ${answer}`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const trimmed = freeformText.trim();
|
|
368
|
+
if (trimmed) {
|
|
369
|
+
parts.push(`Additional context: ${trimmed}`);
|
|
370
|
+
}
|
|
371
|
+
return parts.join("\n\n");
|
|
372
|
+
}
|
|
373
|
+
var HITLQuestionPanel = React10__namespace.forwardRef(({ question, onSubmit, onStop, className, ...props }, ref) => {
|
|
374
|
+
const [freeformText, setFreeformText] = React10.useState("");
|
|
375
|
+
const [selectedOptions, setSelectedOptions] = React10.useState({});
|
|
376
|
+
const [secondsLeft, setSecondsLeft] = React10.useState(
|
|
377
|
+
() => Math.max(
|
|
378
|
+
0,
|
|
379
|
+
Math.round(
|
|
380
|
+
question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
);
|
|
384
|
+
const textareaRef = React10.useRef(null);
|
|
385
|
+
React10.useEffect(() => {
|
|
386
|
+
textareaRef.current?.focus();
|
|
387
|
+
}, []);
|
|
388
|
+
React10.useEffect(() => {
|
|
389
|
+
const interval = setInterval(() => {
|
|
390
|
+
const remaining = Math.max(
|
|
391
|
+
0,
|
|
392
|
+
Math.round(
|
|
393
|
+
question.timeoutSeconds - (Date.now() - question.receivedAt) / 1e3
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
setSecondsLeft(remaining);
|
|
397
|
+
if (remaining <= 0) clearInterval(interval);
|
|
398
|
+
}, 1e3);
|
|
399
|
+
return () => clearInterval(interval);
|
|
400
|
+
}, [question.timeoutSeconds, question.receivedAt]);
|
|
401
|
+
const timedOut = secondsLeft <= 0;
|
|
402
|
+
const questionsWithOptions = React10.useMemo(
|
|
403
|
+
() => question.questions.filter((q) => question.options?.[q]?.length),
|
|
404
|
+
[question.questions, question.options]
|
|
405
|
+
);
|
|
406
|
+
const allOptionsSelected = questionsWithOptions.length > 0 && questionsWithOptions.every((q) => selectedOptions[q]);
|
|
407
|
+
const hasFreeformText = freeformText.trim().length > 0;
|
|
408
|
+
const canSubmit = !timedOut && (allOptionsSelected || hasFreeformText);
|
|
409
|
+
const handleOptionClick = React10.useCallback(
|
|
410
|
+
(questionText, option) => {
|
|
411
|
+
if (timedOut) return;
|
|
412
|
+
setSelectedOptions((prev) => {
|
|
413
|
+
if (prev[questionText] === option) {
|
|
414
|
+
const next = { ...prev };
|
|
415
|
+
delete next[questionText];
|
|
416
|
+
return next;
|
|
417
|
+
}
|
|
418
|
+
return { ...prev, [questionText]: option };
|
|
419
|
+
});
|
|
420
|
+
},
|
|
421
|
+
[timedOut]
|
|
422
|
+
);
|
|
423
|
+
const handleSubmit = React10.useCallback(() => {
|
|
424
|
+
if (!canSubmit) return;
|
|
425
|
+
const combined = buildResponseString(
|
|
426
|
+
question.questions,
|
|
427
|
+
selectedOptions,
|
|
428
|
+
freeformText
|
|
429
|
+
);
|
|
430
|
+
onSubmit(combined);
|
|
431
|
+
}, [canSubmit, question.questions, selectedOptions, freeformText, onSubmit]);
|
|
432
|
+
const handleKeyDown = React10.useCallback(
|
|
433
|
+
(e) => {
|
|
434
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
435
|
+
e.preventDefault();
|
|
436
|
+
handleSubmit();
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
[handleSubmit]
|
|
440
|
+
);
|
|
441
|
+
const formatTime2 = (s) => {
|
|
442
|
+
const m = Math.floor(s / 60);
|
|
443
|
+
const sec = s % 60;
|
|
444
|
+
return `${m}:${sec.toString().padStart(2, "0")}`;
|
|
445
|
+
};
|
|
446
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
447
|
+
"div",
|
|
448
|
+
{
|
|
449
|
+
ref,
|
|
450
|
+
className: core.cn(
|
|
451
|
+
"rounded-lg border border-border bg-muted p-4 space-y-3",
|
|
452
|
+
className
|
|
453
|
+
),
|
|
454
|
+
...props,
|
|
455
|
+
children: [
|
|
456
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
|
|
457
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-foreground", children: question.reason }) }),
|
|
458
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
459
|
+
"span",
|
|
460
|
+
{
|
|
461
|
+
className: core.cn(
|
|
462
|
+
"text-xs font-mono whitespace-nowrap",
|
|
463
|
+
secondsLeft <= 30 ? "text-destructive" : "text-muted-foreground"
|
|
464
|
+
),
|
|
465
|
+
children: timedOut ? "Timed out" : formatTime2(secondsLeft)
|
|
466
|
+
}
|
|
467
|
+
)
|
|
468
|
+
] }),
|
|
469
|
+
question.context && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border max-h-24 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
|
|
470
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3", children: question.questions.map((q, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
471
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-foreground", children: q }),
|
|
472
|
+
question.options?.[q] && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-1.5", children: question.options[q].map((option, j) => {
|
|
473
|
+
const isSelected = selectedOptions[q] === option;
|
|
474
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
475
|
+
core.Button,
|
|
476
|
+
{
|
|
477
|
+
variant: isSelected ? "primary" : "outline",
|
|
478
|
+
size: "sm",
|
|
479
|
+
onClick: () => handleOptionClick(q, option),
|
|
480
|
+
disabled: timedOut,
|
|
481
|
+
children: option
|
|
482
|
+
},
|
|
483
|
+
j
|
|
484
|
+
);
|
|
485
|
+
}) })
|
|
486
|
+
] }, i)) }),
|
|
487
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
488
|
+
core.Textarea,
|
|
489
|
+
{
|
|
490
|
+
ref: textareaRef,
|
|
491
|
+
value: freeformText,
|
|
492
|
+
onChange: (e) => setFreeformText(e.target.value),
|
|
493
|
+
onKeyDown: handleKeyDown,
|
|
494
|
+
disabled: timedOut,
|
|
495
|
+
placeholder: "Add additional context or type a full response...",
|
|
496
|
+
rows: 2,
|
|
497
|
+
className: "resize-none"
|
|
498
|
+
}
|
|
499
|
+
),
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
501
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
502
|
+
core.Button,
|
|
503
|
+
{
|
|
504
|
+
variant: "ghost",
|
|
505
|
+
size: "sm",
|
|
506
|
+
onClick: onStop,
|
|
507
|
+
className: "text-destructive hover:text-destructive hover:bg-destructive/10",
|
|
508
|
+
children: "Stop agent"
|
|
509
|
+
}
|
|
510
|
+
),
|
|
511
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
512
|
+
core.Button,
|
|
513
|
+
{
|
|
514
|
+
variant: "primary",
|
|
515
|
+
size: "sm",
|
|
516
|
+
onClick: handleSubmit,
|
|
517
|
+
disabled: !canSubmit,
|
|
518
|
+
children: "Send response"
|
|
519
|
+
}
|
|
520
|
+
)
|
|
521
|
+
] })
|
|
522
|
+
]
|
|
523
|
+
}
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
HITLQuestionPanel.displayName = "HITLQuestionPanel";
|
|
527
|
+
function parseResponse(response) {
|
|
528
|
+
const answers = {};
|
|
529
|
+
let additionalContext = null;
|
|
530
|
+
const blocks = response.split("\n\n");
|
|
531
|
+
for (const block of blocks) {
|
|
532
|
+
const qaMatch = block.match(/^Q: (.+)\nA: (.+)$/s);
|
|
533
|
+
if (qaMatch) {
|
|
534
|
+
answers[qaMatch[1].trim()] = qaMatch[2].trim();
|
|
535
|
+
} else if (block.startsWith("Additional context: ")) {
|
|
536
|
+
additionalContext = block.slice("Additional context: ".length).trim();
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return { answers, additionalContext };
|
|
540
|
+
}
|
|
541
|
+
var HITLInteractionRecord = React10__namespace.forwardRef(({ interaction, className, ...props }, ref) => {
|
|
542
|
+
const { question, response, respondedAt } = interaction;
|
|
543
|
+
const timestamp = new Date(respondedAt).toLocaleTimeString([], {
|
|
544
|
+
hour: "2-digit",
|
|
545
|
+
minute: "2-digit"
|
|
546
|
+
});
|
|
547
|
+
const { answers, additionalContext } = React10.useMemo(
|
|
548
|
+
() => parseResponse(response),
|
|
549
|
+
[response]
|
|
550
|
+
);
|
|
551
|
+
const hasParsedAnswers = Object.keys(answers).length > 0;
|
|
552
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
553
|
+
"div",
|
|
554
|
+
{
|
|
555
|
+
ref,
|
|
556
|
+
className: core.cn(
|
|
557
|
+
"rounded-lg border border-border bg-muted p-3 space-y-2 text-sm",
|
|
558
|
+
className
|
|
559
|
+
),
|
|
560
|
+
...props,
|
|
561
|
+
children: [
|
|
562
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
563
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-muted-foreground", children: "Clarifying Question" }),
|
|
564
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: timestamp })
|
|
565
|
+
] }),
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground font-medium", children: question.reason }),
|
|
567
|
+
question.context && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-muted-foreground bg-background rounded p-2 border border-border", children: /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "whitespace-pre-wrap font-mono", children: question.context }) }),
|
|
568
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2", children: question.questions.map((q, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
569
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground", children: q }),
|
|
570
|
+
question.options?.[q] && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground ml-2", children: [
|
|
571
|
+
"Options: ",
|
|
572
|
+
question.options[q].join(", ")
|
|
573
|
+
] }),
|
|
574
|
+
hasParsedAnswers && answers[q] && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-foreground ml-2 mt-0.5 bg-background rounded px-2 py-1 border border-border", children: [
|
|
575
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Answer: " }),
|
|
576
|
+
answers[q]
|
|
577
|
+
] })
|
|
578
|
+
] }, i)) }),
|
|
579
|
+
hasParsedAnswers && additionalContext && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-2", children: [
|
|
580
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: "Additional context:" }),
|
|
581
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground mt-0.5", children: additionalContext })
|
|
582
|
+
] }),
|
|
583
|
+
!hasParsedAnswers && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-border pt-2", children: [
|
|
584
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: "Response:" }),
|
|
585
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-foreground mt-0.5", children: response })
|
|
586
|
+
] })
|
|
587
|
+
]
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
});
|
|
591
|
+
HITLInteractionRecord.displayName = "HITLInteractionRecord";
|
|
592
|
+
var HITLSection = React10__namespace.forwardRef(
|
|
593
|
+
({
|
|
594
|
+
interactions,
|
|
595
|
+
defaultExpanded = false,
|
|
596
|
+
isExpanded: controlledExpanded,
|
|
597
|
+
onExpandedChange,
|
|
598
|
+
className,
|
|
599
|
+
...props
|
|
600
|
+
}, ref) => {
|
|
601
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = React10.useState(defaultExpanded);
|
|
602
|
+
const isControlled = controlledExpanded !== void 0;
|
|
603
|
+
const isExpanded = isControlled ? controlledExpanded : uncontrolledExpanded;
|
|
604
|
+
const toggleExpanded = React10.useCallback(() => {
|
|
605
|
+
const newValue = !isExpanded;
|
|
606
|
+
if (isControlled) {
|
|
607
|
+
onExpandedChange?.(newValue);
|
|
608
|
+
} else {
|
|
609
|
+
setUncontrolledExpanded(newValue);
|
|
610
|
+
}
|
|
611
|
+
}, [isExpanded, isControlled, onExpandedChange]);
|
|
612
|
+
if (!interactions || interactions.length === 0) {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
616
|
+
"div",
|
|
617
|
+
{
|
|
618
|
+
ref,
|
|
619
|
+
className: core.cn("border-t border-border", className),
|
|
620
|
+
...props,
|
|
621
|
+
children: [
|
|
622
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
623
|
+
"button",
|
|
624
|
+
{
|
|
625
|
+
onClick: toggleExpanded,
|
|
626
|
+
className: "w-full flex items-center gap-2 py-2 px-3 hover:bg-muted/50 transition-colors text-left",
|
|
627
|
+
children: [
|
|
628
|
+
isExpanded ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
|
|
629
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageCircleQuestion, { className: "w-3.5 h-3.5 text-muted-foreground flex-shrink-0" }),
|
|
630
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium text-foreground/80", children: [
|
|
631
|
+
"Clarifying Questions (",
|
|
632
|
+
interactions.length,
|
|
633
|
+
")"
|
|
634
|
+
] })
|
|
635
|
+
]
|
|
636
|
+
}
|
|
637
|
+
),
|
|
638
|
+
isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 pb-3 space-y-2", children: interactions.map((interaction, i) => /* @__PURE__ */ jsxRuntime.jsx(HITLInteractionRecord, { interaction }, i)) })
|
|
639
|
+
]
|
|
640
|
+
}
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
HITLSection.displayName = "HITLSection";
|
|
358
645
|
function useThinkingTimer({
|
|
359
646
|
startTime,
|
|
360
647
|
endTime,
|
|
361
648
|
status
|
|
362
649
|
}) {
|
|
363
|
-
const [elapsed, setElapsed] =
|
|
364
|
-
|
|
650
|
+
const [elapsed, setElapsed] = React10.useState(0);
|
|
651
|
+
React10.useEffect(() => {
|
|
365
652
|
if (!startTime) {
|
|
366
653
|
setElapsed(0);
|
|
367
654
|
return;
|
|
@@ -398,9 +685,9 @@ var initialAgentResponseState = {
|
|
|
398
685
|
|
|
399
686
|
// src/components/agent-response/hooks/useAgentResponseAccumulator.ts
|
|
400
687
|
function useAgentResponseAccumulator(options) {
|
|
401
|
-
const [state, setState] =
|
|
688
|
+
const [state, setState] = React10.useState(initialAgentResponseState);
|
|
402
689
|
const topic = options?.topic;
|
|
403
|
-
const handleMessage =
|
|
690
|
+
const handleMessage = React10.useCallback(
|
|
404
691
|
(message) => {
|
|
405
692
|
let payload;
|
|
406
693
|
if (topic) {
|
|
@@ -539,12 +826,12 @@ function useAgentResponseAccumulator(options) {
|
|
|
539
826
|
},
|
|
540
827
|
[topic]
|
|
541
828
|
);
|
|
542
|
-
const reset =
|
|
829
|
+
const reset = React10.useCallback(() => {
|
|
543
830
|
setState(initialAgentResponseState);
|
|
544
831
|
}, []);
|
|
545
832
|
return { state, handleMessage, reset };
|
|
546
833
|
}
|
|
547
|
-
var AgentResponse =
|
|
834
|
+
var AgentResponse = React10__namespace.forwardRef(
|
|
548
835
|
({
|
|
549
836
|
state,
|
|
550
837
|
id,
|
|
@@ -556,15 +843,17 @@ var AgentResponse = React7__namespace.forwardRef(
|
|
|
556
843
|
thinkingExpanded: controlledThinkingExpanded,
|
|
557
844
|
onThinkingExpandedChange,
|
|
558
845
|
actionsVisible = "hover",
|
|
846
|
+
hitlInteractions,
|
|
847
|
+
defaultHITLExpanded = false,
|
|
559
848
|
renderMarkdown,
|
|
560
849
|
renderThinkingMarkdown,
|
|
561
850
|
className,
|
|
562
851
|
...props
|
|
563
852
|
}, ref) => {
|
|
564
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] =
|
|
853
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = React10.useState(defaultThinkingExpanded);
|
|
565
854
|
const isThinkingControlled = controlledThinkingExpanded !== void 0;
|
|
566
855
|
const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
|
|
567
|
-
const toggleThinking =
|
|
856
|
+
const toggleThinking = React10.useCallback(() => {
|
|
568
857
|
const newValue = !thinkingExpanded;
|
|
569
858
|
if (isThinkingControlled) {
|
|
570
859
|
onThinkingExpandedChange?.(newValue);
|
|
@@ -572,18 +861,19 @@ var AgentResponse = React7__namespace.forwardRef(
|
|
|
572
861
|
setUncontrolledExpanded(newValue);
|
|
573
862
|
}
|
|
574
863
|
}, [thinkingExpanded, isThinkingControlled, onThinkingExpandedChange]);
|
|
575
|
-
const [isHovered, setIsHovered] =
|
|
864
|
+
const [isHovered, setIsHovered] = React10.useState(false);
|
|
576
865
|
const elapsedTime = useThinkingTimer({
|
|
577
866
|
startTime: state.thinkingStartTime,
|
|
578
867
|
endTime: state.responseCompleteTime,
|
|
579
868
|
status: state.status
|
|
580
869
|
});
|
|
581
|
-
const totalTimeSeconds =
|
|
870
|
+
const totalTimeSeconds = React10.useMemo(() => {
|
|
582
871
|
if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
|
|
583
872
|
return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
|
|
584
873
|
}, [state.firstMessageTime, state.responseCompleteTime]);
|
|
585
874
|
const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || false;
|
|
586
|
-
const
|
|
875
|
+
const hasHITLInteractions = hitlInteractions && hitlInteractions.length > 0;
|
|
876
|
+
const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || hasHITLInteractions || state.response;
|
|
587
877
|
const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
|
|
588
878
|
const showActionBar = state.status === "complete" && state.response;
|
|
589
879
|
const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
|
|
@@ -624,6 +914,13 @@ var AgentResponse = React7__namespace.forwardRef(
|
|
|
624
914
|
}
|
|
625
915
|
)
|
|
626
916
|
] }),
|
|
917
|
+
hasHITLInteractions && /* @__PURE__ */ jsxRuntime.jsx(
|
|
918
|
+
HITLSection,
|
|
919
|
+
{
|
|
920
|
+
interactions: hitlInteractions,
|
|
921
|
+
defaultExpanded: defaultHITLExpanded
|
|
922
|
+
}
|
|
923
|
+
),
|
|
627
924
|
state.response && /* @__PURE__ */ jsxRuntime.jsx(
|
|
628
925
|
"div",
|
|
629
926
|
{
|
|
@@ -652,7 +949,7 @@ var AgentResponse = React7__namespace.forwardRef(
|
|
|
652
949
|
}
|
|
653
950
|
);
|
|
654
951
|
AgentResponse.displayName = "AgentResponse";
|
|
655
|
-
var UserPrompt =
|
|
952
|
+
var UserPrompt = React10__namespace.forwardRef(
|
|
656
953
|
({ content, timestamp, className, ...props }, ref) => {
|
|
657
954
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
658
955
|
"div",
|
|
@@ -751,7 +1048,7 @@ function CodeBlockLeaf({ attributes, children, leaf }) {
|
|
|
751
1048
|
}
|
|
752
1049
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { ...attributes, children });
|
|
753
1050
|
}
|
|
754
|
-
var UserPromptInput =
|
|
1051
|
+
var UserPromptInput = React10__namespace.forwardRef(
|
|
755
1052
|
({
|
|
756
1053
|
value = "",
|
|
757
1054
|
onChange,
|
|
@@ -774,14 +1071,14 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
774
1071
|
className,
|
|
775
1072
|
...props
|
|
776
1073
|
}, ref) => {
|
|
777
|
-
const editorRef =
|
|
778
|
-
const [internalValue, setInternalValue] =
|
|
779
|
-
const prevIsSubmitting =
|
|
780
|
-
const hasEmittedReady =
|
|
781
|
-
|
|
1074
|
+
const editorRef = React10__namespace.useRef(null);
|
|
1075
|
+
const [internalValue, setInternalValue] = React10__namespace.useState(value);
|
|
1076
|
+
const prevIsSubmitting = React10__namespace.useRef(isSubmitting);
|
|
1077
|
+
const hasEmittedReady = React10__namespace.useRef(false);
|
|
1078
|
+
React10__namespace.useEffect(() => {
|
|
782
1079
|
setInternalValue(value);
|
|
783
1080
|
}, [value]);
|
|
784
|
-
|
|
1081
|
+
React10__namespace.useEffect(() => {
|
|
785
1082
|
if (autoFocus) {
|
|
786
1083
|
requestAnimationFrame(() => {
|
|
787
1084
|
requestAnimationFrame(() => {
|
|
@@ -790,7 +1087,7 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
790
1087
|
});
|
|
791
1088
|
}
|
|
792
1089
|
}, [autoFocus]);
|
|
793
|
-
|
|
1090
|
+
React10__namespace.useEffect(() => {
|
|
794
1091
|
if (!hasEmittedReady.current && onReady) {
|
|
795
1092
|
requestAnimationFrame(() => {
|
|
796
1093
|
requestAnimationFrame(() => {
|
|
@@ -800,7 +1097,7 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
800
1097
|
});
|
|
801
1098
|
}
|
|
802
1099
|
}, [onReady]);
|
|
803
|
-
|
|
1100
|
+
React10__namespace.useEffect(() => {
|
|
804
1101
|
if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
|
|
805
1102
|
requestAnimationFrame(() => {
|
|
806
1103
|
editorRef.current?.focus();
|
|
@@ -808,7 +1105,7 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
808
1105
|
}
|
|
809
1106
|
prevIsSubmitting.current = isSubmitting;
|
|
810
1107
|
}, [isSubmitting, refocusAfterSubmit]);
|
|
811
|
-
|
|
1108
|
+
React10__namespace.useImperativeHandle(
|
|
812
1109
|
ref,
|
|
813
1110
|
() => ({
|
|
814
1111
|
focus: () => {
|
|
@@ -831,14 +1128,14 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
831
1128
|
}),
|
|
832
1129
|
[]
|
|
833
1130
|
);
|
|
834
|
-
const handleChange =
|
|
1131
|
+
const handleChange = React10__namespace.useCallback(
|
|
835
1132
|
(newValue) => {
|
|
836
1133
|
setInternalValue(newValue);
|
|
837
1134
|
onChange?.(newValue);
|
|
838
1135
|
},
|
|
839
1136
|
[onChange]
|
|
840
1137
|
);
|
|
841
|
-
const handleSubmit =
|
|
1138
|
+
const handleSubmit = React10__namespace.useCallback(
|
|
842
1139
|
(text) => {
|
|
843
1140
|
if (disabled || isSubmitting) return;
|
|
844
1141
|
if (!text.trim()) return;
|
|
@@ -850,7 +1147,7 @@ var UserPromptInput = React7__namespace.forwardRef(
|
|
|
850
1147
|
},
|
|
851
1148
|
[disabled, isSubmitting, onSubmit, clearOnSubmit]
|
|
852
1149
|
);
|
|
853
|
-
const handleSendClick =
|
|
1150
|
+
const handleSendClick = React10__namespace.useCallback(() => {
|
|
854
1151
|
const text = editorRef.current?.getText() ?? "";
|
|
855
1152
|
handleSubmit(text);
|
|
856
1153
|
}, [handleSubmit]);
|
|
@@ -918,10 +1215,14 @@ UserPromptInput.displayName = "UserPromptInput";
|
|
|
918
1215
|
exports.ActionBar = ActionBar;
|
|
919
1216
|
exports.ActivityIndicators = ActivityIndicators;
|
|
920
1217
|
exports.AgentResponse = AgentResponse;
|
|
1218
|
+
exports.HITLInteractionRecord = HITLInteractionRecord;
|
|
1219
|
+
exports.HITLQuestionPanel = HITLQuestionPanel;
|
|
1220
|
+
exports.HITLSection = HITLSection;
|
|
921
1221
|
exports.MetadataRow = MetadataRow;
|
|
922
1222
|
exports.ThinkingSection = ThinkingSection;
|
|
923
1223
|
exports.UserPrompt = UserPrompt;
|
|
924
1224
|
exports.UserPromptInput = UserPromptInput;
|
|
1225
|
+
exports.buildResponseString = buildResponseString;
|
|
925
1226
|
exports.formatTime = formatTime;
|
|
926
1227
|
exports.formatTotalTime = formatTotalTime;
|
|
927
1228
|
exports.initialAgentResponseState = initialAgentResponseState;
|