@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 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 React7 = require('react');
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 React7__namespace = /*#__PURE__*/_interopNamespace(React7);
27
+ var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
28
28
 
29
29
  // src/components/agent-response/AgentResponse.tsx
30
- var ActivityIndicators = React7__namespace.forwardRef(
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 = React7__namespace.forwardRef(
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] = React7.useState(step.isCollapsed ?? false);
217
- const toggleCollapse = React7.useCallback(() => {
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 = React7__namespace.forwardRef(
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 = React7__namespace.forwardRef(
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] = React7.useState(false);
281
- const handleCopy = React7.useCallback(async () => {
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 = React7.useCallback(() => {
291
+ const handleThumbsUp = React10.useCallback(() => {
292
292
  const newValue = feedback === "up" ? null : "up";
293
293
  onFeedbackChange?.(newValue);
294
294
  }, [feedback, onFeedbackChange]);
295
- const handleThumbsDown = React7.useCallback(() => {
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] = React7.useState(0);
364
- React7.useEffect(() => {
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] = React7.useState(initialAgentResponseState);
688
+ const [state, setState] = React10.useState(initialAgentResponseState);
402
689
  const topic = options?.topic;
403
- const handleMessage = React7.useCallback(
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 = React7.useCallback(() => {
829
+ const reset = React10.useCallback(() => {
543
830
  setState(initialAgentResponseState);
544
831
  }, []);
545
832
  return { state, handleMessage, reset };
546
833
  }
547
- var AgentResponse = React7__namespace.forwardRef(
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] = React7.useState(defaultThinkingExpanded);
853
+ const [uncontrolledExpanded, setUncontrolledExpanded] = React10.useState(defaultThinkingExpanded);
565
854
  const isThinkingControlled = controlledThinkingExpanded !== void 0;
566
855
  const thinkingExpanded = isThinkingControlled ? controlledThinkingExpanded : uncontrolledExpanded;
567
- const toggleThinking = React7.useCallback(() => {
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] = React7.useState(false);
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 = React7.useMemo(() => {
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 hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.response;
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 = React7__namespace.forwardRef(
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 = React7__namespace.forwardRef(
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 = React7__namespace.useRef(null);
778
- const [internalValue, setInternalValue] = React7__namespace.useState(value);
779
- const prevIsSubmitting = React7__namespace.useRef(isSubmitting);
780
- const hasEmittedReady = React7__namespace.useRef(false);
781
- React7__namespace.useEffect(() => {
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
- React7__namespace.useEffect(() => {
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
- React7__namespace.useEffect(() => {
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
- React7__namespace.useEffect(() => {
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
- React7__namespace.useImperativeHandle(
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 = React7__namespace.useCallback(
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 = React7__namespace.useCallback(
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 = React7__namespace.useCallback(() => {
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;