@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.
Files changed (33) hide show
  1. package/README.md +136 -0
  2. package/dist/index.cjs +989 -58
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +361 -2
  5. package/dist/index.d.ts +361 -2
  6. package/dist/index.js +964 -46
  7. package/dist/index.js.map +1 -1
  8. package/package.json +3 -3
  9. package/src/components/agent-response/AgentResponse.tsx +86 -14
  10. package/src/components/agent-response/components/HITLSection.tsx +95 -0
  11. package/src/components/agent-response/components/MetadataRow.tsx +15 -4
  12. package/src/components/agent-response/components/TruncatedMessage.tsx +52 -0
  13. package/src/components/agent-response/components/index.ts +6 -0
  14. package/src/components/agent-response/hooks/useAgentResponseAccumulator.ts +65 -8
  15. package/src/components/agent-response/index.ts +21 -0
  16. package/src/components/agent-response/types.ts +61 -1
  17. package/src/components/agent-timeline/AgentTimeline.tsx +256 -0
  18. package/src/components/agent-timeline/TimelineAgentBlock.tsx +84 -0
  19. package/src/components/agent-timeline/TimelineItem.tsx +97 -0
  20. package/src/components/agent-timeline/index.ts +14 -0
  21. package/src/components/agent-timeline/types.ts +49 -0
  22. package/src/components/agent-timeline/utils.ts +189 -0
  23. package/src/components/hitl-interactions/HITLInteractionRecord.tsx +139 -0
  24. package/src/components/hitl-interactions/HITLQuestionPanel.tsx +270 -0
  25. package/src/components/hitl-interactions/index.ts +18 -0
  26. package/src/components/inline-actions/ActionMarkdownRenderer.tsx +60 -0
  27. package/src/components/inline-actions/index.ts +18 -0
  28. package/src/components/inline-actions/parseResponseSegments.ts +66 -0
  29. package/src/components/inline-actions/prompts.ts +41 -0
  30. package/src/components/inline-actions/types.ts +57 -0
  31. package/src/components/user-prompt-input/UserPromptInput.tsx +13 -8
  32. package/src/components/user-prompt-input/types.ts +4 -0
  33. package/src/index.ts +42 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optilogic/chat",
3
- "version": "1.0.0-beta.8",
3
+ "version": "1.0.0",
4
4
  "description": "Chat UI components for Optilogic - AgentResponse and related components for LLM interactions",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -24,8 +24,8 @@
24
24
  "README.md"
25
25
  ],
26
26
  "dependencies": {
27
- "@optilogic/core": "1.0.0-beta.8",
28
- "@optilogic/editor": "1.0.0-beta.8"
27
+ "@optilogic/core": "1.0.0",
28
+ "@optilogic/editor": "1.0.0"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "react": "^18.0.0 || ^19.0.0",
@@ -6,11 +6,14 @@
6
6
  */
7
7
 
8
8
  import * as React from "react";
9
- import { useState, useMemo, useCallback } from "react";
9
+ import { useState, useRef, useMemo, useCallback } from "react";
10
10
  import { cn } from "@optilogic/core";
11
- import { MetadataRow, ThinkingSection, ActionBar } from "./components";
11
+ import { MetadataRow, ThinkingSection, ActionBar, HITLSection } from "./components";
12
12
  import { useThinkingTimer } from "./hooks";
13
13
  import type { AgentResponseState, FeedbackValue } from "./types";
14
+ import type { HITLInteraction } from "../hitl-interactions";
15
+ import { AgentTimeline, createTimelineUIState } from "../agent-timeline";
16
+ import type { TimelineUIState } from "../agent-timeline";
14
17
 
15
18
  export interface AgentResponseProps extends React.HTMLAttributes<HTMLDivElement> {
16
19
  /** The response state to render */
@@ -37,6 +40,35 @@ export interface AgentResponseProps extends React.HTMLAttributes<HTMLDivElement>
37
40
  /** Action bar visibility mode */
38
41
  actionsVisible?: boolean | "hover";
39
42
 
43
+ /**
44
+ * Optional HITL (Human-in-the-Loop) interactions to display as a
45
+ * collapsible section within the response. When provided, a "Clarifying
46
+ * Questions" section appears between the thinking/metadata area and
47
+ * the response content.
48
+ */
49
+ hitlInteractions?: HITLInteraction[];
50
+
51
+ /** Whether the HITL section starts expanded (default: false) */
52
+ defaultHITLExpanded?: boolean;
53
+
54
+ /**
55
+ * Optional content to display in the MetadataRow's middle area,
56
+ * between the thinking toggle (left) and activity indicators (right).
57
+ * Typically used for ephemeral status messages during processing.
58
+ * The parent is responsible for setting and clearing this content.
59
+ *
60
+ * @example
61
+ * <AgentResponse
62
+ * state={state}
63
+ * statusContent={
64
+ * state.status !== 'complete'
65
+ * ? <TruncatedMessage message="Analyzing data..." />
66
+ * : undefined
67
+ * }
68
+ * />
69
+ */
70
+ statusContent?: React.ReactNode;
71
+
40
72
  /**
41
73
  * Custom markdown renderer for the response content.
42
74
  * If not provided, the response will be rendered as plain text.
@@ -60,6 +92,12 @@ export interface AgentResponseProps extends React.HTMLAttributes<HTMLDivElement>
60
92
  * />
61
93
  */
62
94
  renderThinkingMarkdown?: (content: string) => React.ReactNode;
95
+
96
+ /**
97
+ * Maximum height of the AgentTimeline scrollable container.
98
+ * Defaults to "300px". Set to "none" to disable the constraint.
99
+ */
100
+ timelineMaxHeight?: string;
63
101
  }
64
102
 
65
103
  /**
@@ -107,13 +145,20 @@ const AgentResponse = React.forwardRef<HTMLDivElement, AgentResponseProps>(
107
145
  thinkingExpanded: controlledThinkingExpanded,
108
146
  onThinkingExpandedChange,
109
147
  actionsVisible = "hover",
148
+ hitlInteractions,
149
+ defaultHITLExpanded = false,
150
+ statusContent,
110
151
  renderMarkdown,
111
152
  renderThinkingMarkdown,
153
+ timelineMaxHeight,
112
154
  className,
113
155
  ...props
114
156
  },
115
157
  ref
116
158
  ) => {
159
+ // Ref-backed timeline UI state (survives remounts during streaming)
160
+ const timelineUIStateRef = useRef<TimelineUIState>(createTimelineUIState());
161
+
117
162
  // Uncontrolled thinking expanded state
118
163
  const [uncontrolledExpanded, setUncontrolledExpanded] = useState(defaultThinkingExpanded);
119
164
 
@@ -149,9 +194,13 @@ const AgentResponse = React.forwardRef<HTMLDivElement, AgentResponseProps>(
149
194
  return (state.responseCompleteTime - state.firstMessageTime) / 1000;
150
195
  }, [state.firstMessageTime, state.responseCompleteTime]);
151
196
 
152
- // Check if we have any thinking content (plain text or structured)
197
+ // Check if we have any thinking content (plain text, structured, or timeline)
198
+ const hasTimelineEntries = !!(state.timelineEntries && state.timelineEntries.length > 0);
153
199
  const hasThinkingContent =
154
- !!state.thinking || (state.thinkingSteps && state.thinkingSteps.length > 0) || false;
200
+ !!state.thinking || (state.thinkingSteps && state.thinkingSteps.length > 0) || hasTimelineEntries || false;
201
+
202
+ const hasHITLInteractions =
203
+ hitlInteractions && hitlInteractions.length > 0;
155
204
 
156
205
  // Derived state: has any content been received?
157
206
  const hasAnyContent =
@@ -160,6 +209,7 @@ const AgentResponse = React.forwardRef<HTMLDivElement, AgentResponseProps>(
160
209
  state.knowledge.length > 0 ||
161
210
  state.memory.length > 0 ||
162
211
  state.statusUpdates.length > 0 ||
212
+ hasHITLInteractions ||
163
213
  state.response;
164
214
 
165
215
  // Derived state: should show metadata row?
@@ -203,23 +253,45 @@ const AgentResponse = React.forwardRef<HTMLDivElement, AgentResponseProps>(
203
253
  knowledge={state.knowledge}
204
254
  memory={state.memory}
205
255
  statusUpdates={state.statusUpdates}
256
+ statusContent={statusContent}
206
257
  status={state.status}
207
258
  elapsedTime={elapsedTime}
208
259
  />
209
260
 
210
- {/* Thinking Content - collapsible with max-height */}
211
- <ThinkingSection
212
- content={
213
- state.thinkingSteps && state.thinkingSteps.length > 0
214
- ? state.thinkingSteps
215
- : state.thinking
216
- }
217
- isExpanded={thinkingExpanded}
218
- renderMarkdown={renderThinkingMarkdown}
219
- />
261
+ {/* Thinking Content - AgentTimeline when timeline entries exist, ThinkingSection otherwise */}
262
+ {hasTimelineEntries ? (
263
+ thinkingExpanded && (
264
+ <div className="pb-3 border-t border-border">
265
+ <AgentTimeline
266
+ entries={state.timelineEntries!}
267
+ renderMarkdown={renderThinkingMarkdown}
268
+ uiState={timelineUIStateRef.current}
269
+ maxHeight={timelineMaxHeight}
270
+ />
271
+ </div>
272
+ )
273
+ ) : (
274
+ <ThinkingSection
275
+ content={
276
+ state.thinkingSteps && state.thinkingSteps.length > 0
277
+ ? state.thinkingSteps
278
+ : state.thinking
279
+ }
280
+ isExpanded={thinkingExpanded}
281
+ renderMarkdown={renderThinkingMarkdown}
282
+ />
283
+ )}
220
284
  </>
221
285
  )}
222
286
 
287
+ {/* HITL Interactions - collapsible section */}
288
+ {hasHITLInteractions && (
289
+ <HITLSection
290
+ interactions={hitlInteractions}
291
+ defaultExpanded={defaultHITLExpanded}
292
+ />
293
+ )}
294
+
223
295
  {/* Response Section */}
224
296
  {state.response && (
225
297
  <div
@@ -0,0 +1,95 @@
1
+ /**
2
+ * HITL Section Component
3
+ *
4
+ * Collapsible section for displaying completed HITL (Human-in-the-Loop)
5
+ * interactions within an AgentResponse. Follows the same pattern as
6
+ * ThinkingSection for consistent UX.
7
+ */
8
+
9
+ import * as React from "react";
10
+ import { useState, useCallback } from "react";
11
+ import { ChevronDown, ChevronRight, MessageCircleQuestion } from "lucide-react";
12
+ import { cn } from "@optilogic/core";
13
+ import { HITLInteractionRecord } from "../../hitl-interactions";
14
+ import type { HITLInteraction } from "../../hitl-interactions";
15
+
16
+ export interface HITLSectionProps
17
+ extends React.HTMLAttributes<HTMLDivElement> {
18
+ /** The HITL interactions to display */
19
+ interactions: HITLInteraction[];
20
+ /** Whether the section starts expanded (uncontrolled) */
21
+ defaultExpanded?: boolean;
22
+ /** Whether the section is expanded (controlled) */
23
+ isExpanded?: boolean;
24
+ /** Callback when expansion state changes (controlled) */
25
+ onExpandedChange?: (expanded: boolean) => void;
26
+ }
27
+
28
+ const HITLSection = React.forwardRef<HTMLDivElement, HITLSectionProps>(
29
+ (
30
+ {
31
+ interactions,
32
+ defaultExpanded = false,
33
+ isExpanded: controlledExpanded,
34
+ onExpandedChange,
35
+ className,
36
+ ...props
37
+ },
38
+ ref
39
+ ) => {
40
+ const [uncontrolledExpanded, setUncontrolledExpanded] =
41
+ useState(defaultExpanded);
42
+
43
+ const isControlled = controlledExpanded !== undefined;
44
+ const isExpanded = isControlled ? controlledExpanded : uncontrolledExpanded;
45
+
46
+ const toggleExpanded = useCallback(() => {
47
+ const newValue = !isExpanded;
48
+ if (isControlled) {
49
+ onExpandedChange?.(newValue);
50
+ } else {
51
+ setUncontrolledExpanded(newValue);
52
+ }
53
+ }, [isExpanded, isControlled, onExpandedChange]);
54
+
55
+ if (!interactions || interactions.length === 0) {
56
+ return null;
57
+ }
58
+
59
+ return (
60
+ <div
61
+ ref={ref}
62
+ className={cn("border-t border-border", className)}
63
+ {...props}
64
+ >
65
+ {/* Collapsible header */}
66
+ <button
67
+ onClick={toggleExpanded}
68
+ className="w-full flex items-center gap-2 py-2 px-3 hover:bg-muted/50 transition-colors text-left"
69
+ >
70
+ {isExpanded ? (
71
+ <ChevronDown className="w-3.5 h-3.5 text-muted-foreground flex-shrink-0" />
72
+ ) : (
73
+ <ChevronRight className="w-3.5 h-3.5 text-muted-foreground flex-shrink-0" />
74
+ )}
75
+ <MessageCircleQuestion className="w-3.5 h-3.5 text-muted-foreground flex-shrink-0" />
76
+ <span className="text-xs font-medium text-foreground/80">
77
+ Clarifying Questions ({interactions.length})
78
+ </span>
79
+ </button>
80
+
81
+ {/* Expanded content */}
82
+ {isExpanded && (
83
+ <div className="px-3 pb-3 space-y-2">
84
+ {interactions.map((interaction, i) => (
85
+ <HITLInteractionRecord key={i} interaction={interaction} />
86
+ ))}
87
+ </div>
88
+ )}
89
+ </div>
90
+ );
91
+ }
92
+ );
93
+ HITLSection.displayName = "HITLSection";
94
+
95
+ export { HITLSection };
@@ -26,6 +26,8 @@ export interface MetadataRowProps extends React.HTMLAttributes<HTMLDivElement> {
26
26
  memory: MemoryItem[];
27
27
  /** Status updates to display */
28
28
  statusUpdates?: StatusItem[];
29
+ /** Optional content to display in the middle area between left content and activity indicators */
30
+ statusContent?: React.ReactNode;
29
31
  /** Current response status */
30
32
  status: AgentResponseStatus;
31
33
  /** Elapsed time in seconds */
@@ -60,6 +62,7 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
60
62
  knowledge,
61
63
  memory,
62
64
  statusUpdates = [],
65
+ statusContent,
63
66
  status,
64
67
  elapsedTime,
65
68
  className,
@@ -108,8 +111,8 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
108
111
 
109
112
  const leftContent = renderLeftContent();
110
113
 
111
- // If nothing to show (no thinking, not processing, no activity), hide the row
112
- if (!leftContent && !hasActivity) {
114
+ // If nothing to show (no thinking, not processing, no activity, no status content), hide the row
115
+ if (!leftContent && !hasActivity && !statusContent) {
113
116
  return null;
114
117
  }
115
118
 
@@ -125,15 +128,23 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
125
128
  {hasThinking ? (
126
129
  <button
127
130
  onClick={onToggle}
128
- 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"
131
+ 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"
129
132
  >
130
133
  {leftContent}
131
134
  </button>
132
135
  ) : (
133
- <div className="flex items-center gap-1.5">
136
+ <div className="flex items-center gap-1.5 shrink-0">
134
137
  {leftContent}
135
138
  </div>
136
139
  )}
140
+
141
+ {/* Middle content - status content slot */}
142
+ {statusContent && (
143
+ <div className="flex-1 min-w-0 mx-2">
144
+ {statusContent}
145
+ </div>
146
+ )}
147
+
137
148
  <ActivityIndicators
138
149
  toolCalls={toolCalls}
139
150
  knowledge={knowledge}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Truncated Message Component
3
+ *
4
+ * Renders a single-line text message with CSS-based truncation (text-overflow: ellipsis).
5
+ * Designed as a standalone utility that can be used anywhere, including as
6
+ * the statusContent slot in MetadataRow.
7
+ */
8
+
9
+ import * as React from "react";
10
+ import { cn } from "@optilogic/core";
11
+
12
+ export interface TruncatedMessageProps extends React.HTMLAttributes<HTMLDivElement> {
13
+ /** The message string to display (truncated with ellipsis if it overflows) */
14
+ message: string;
15
+ }
16
+
17
+ /**
18
+ * TruncatedMessage Component
19
+ *
20
+ * Displays a single-line text message that truncates with an ellipsis when
21
+ * it overflows its container. Uses CSS text-overflow for zero-JS truncation.
22
+ *
23
+ * @example
24
+ * <TruncatedMessage message="Searching the knowledge base for relevant documents..." />
25
+ *
26
+ * @example
27
+ * // Inside MetadataRow's statusContent slot
28
+ * <AgentResponse
29
+ * state={state}
30
+ * statusContent={<TruncatedMessage message="Running analysis..." />}
31
+ * />
32
+ */
33
+ const TruncatedMessage = React.forwardRef<HTMLDivElement, TruncatedMessageProps>(
34
+ ({ message, className, ...props }, ref) => {
35
+ return (
36
+ <div
37
+ ref={ref}
38
+ className={cn(
39
+ "text-xs text-muted-foreground truncate min-w-0",
40
+ className
41
+ )}
42
+ title={message}
43
+ {...props}
44
+ >
45
+ {message}
46
+ </div>
47
+ );
48
+ }
49
+ );
50
+ TruncatedMessage.displayName = "TruncatedMessage";
51
+
52
+ export { TruncatedMessage };
@@ -13,3 +13,9 @@ export type { ThinkingSectionProps } from "./ThinkingSection";
13
13
 
14
14
  export { ActionBar } from "./ActionBar";
15
15
  export type { ActionBarProps } from "./ActionBar";
16
+
17
+ export { HITLSection } from "./HITLSection";
18
+ export type { HITLSectionProps } from "./HITLSection";
19
+
20
+ export { TruncatedMessage } from "./TruncatedMessage";
21
+ export type { TruncatedMessageProps } from "./TruncatedMessage";
@@ -15,7 +15,9 @@ import {
15
15
  type MemoryItem,
16
16
  type StatusItem,
17
17
  type ThinkingStep,
18
+ type PotentialResponse,
18
19
  } from "../types";
20
+ import { buildTimelineEntries } from "../../agent-timeline/utils";
19
21
 
20
22
  export interface UseAgentResponseAccumulatorOptions {
21
23
  /** WebSocket topic to filter messages (optional, for convenience) */
@@ -94,33 +96,50 @@ export function useAgentResponseAccumulator(
94
96
  id: payload.thinkingStep.id || `step-${Date.now()}`,
95
97
  label: payload.thinkingStep.label,
96
98
  content: payload.thinkingStep.content,
97
- depth: payload.thinkingStep.depth ?? 0,
99
+ depth: payload.thinkingStep.depth ?? payload.depth ?? 0,
98
100
  isCollapsed: payload.thinkingStep.isCollapsed,
101
+ timestamp: Date.now(),
102
+ agentName: payload.agentName,
103
+ parentAgent: payload.parentAgent,
99
104
  };
100
105
  const thinkingStartTime = prev.thinkingStartTime ?? Date.now();
101
- return {
106
+ const next = {
102
107
  ...prev,
103
108
  status: newStatus,
104
109
  thinkingSteps: [...(prev.thinkingSteps || []), newStep],
105
110
  thinkingStartTime,
106
111
  firstMessageTime,
107
112
  };
113
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
108
114
  }
109
115
 
110
- // Plain text thinking (existing behavior)
116
+ // Plain text thinking concatenate for backward compat AND
117
+ // push a ThinkingStep so the timeline gets individual entries.
111
118
  const newThinking = payload.message || payload.content || "";
112
119
  // Add line break between thinking messages
113
120
  const separator = prev.thinking && newThinking ? "\n\n" : "";
114
121
  // Set thinkingStartTime on first thinking message
115
122
  const thinkingStartTime =
116
123
  prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
117
- return {
124
+ const prevSteps = prev.thinkingSteps || [];
125
+ const plainStep: ThinkingStep = {
126
+ id: `step-${prevSteps.length}`,
127
+ label: newThinking,
128
+ content: newThinking,
129
+ depth: payload.depth ?? 0,
130
+ timestamp: Date.now(),
131
+ agentName: payload.agentName,
132
+ parentAgent: payload.parentAgent,
133
+ };
134
+ const next = {
118
135
  ...prev,
119
136
  status: newStatus,
120
137
  thinking: prev.thinking + separator + newThinking,
138
+ thinkingSteps: [...prevSteps, plainStep],
121
139
  thinkingStartTime,
122
140
  firstMessageTime,
123
141
  };
142
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
124
143
  }
125
144
 
126
145
  case "tool_call": {
@@ -132,13 +151,17 @@ export function useAgentResponseAccumulator(
132
151
  name: toolName,
133
152
  arguments: payload.tool?.arguments,
134
153
  timestamp: Date.now(),
154
+ agentName: payload.agentName,
155
+ parentAgent: payload.parentAgent,
156
+ depth: payload.depth,
135
157
  };
136
- return {
158
+ const next = {
137
159
  ...prev,
138
160
  status: newStatus,
139
161
  toolCalls: [...prev.toolCalls, newToolCall],
140
162
  firstMessageTime,
141
163
  };
164
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
142
165
  }
143
166
  return { ...prev, status: newStatus, firstMessageTime };
144
167
  }
@@ -152,13 +175,17 @@ export function useAgentResponseAccumulator(
152
175
  source: payload.knowledge?.source || "unknown",
153
176
  content: knowledgeContent,
154
177
  timestamp: Date.now(),
178
+ agentName: payload.agentName,
179
+ parentAgent: payload.parentAgent,
180
+ depth: payload.depth,
155
181
  };
156
- return {
182
+ const next = {
157
183
  ...prev,
158
184
  status: newStatus,
159
185
  knowledge: [...prev.knowledge, newKnowledge],
160
186
  firstMessageTime,
161
187
  };
188
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
162
189
  }
163
190
  return { ...prev, status: newStatus, firstMessageTime };
164
191
  }
@@ -172,13 +199,17 @@ export function useAgentResponseAccumulator(
172
199
  type: payload.memory?.type || "unknown",
173
200
  content: memoryContent,
174
201
  timestamp: Date.now(),
202
+ agentName: payload.agentName,
203
+ parentAgent: payload.parentAgent,
204
+ depth: payload.depth,
175
205
  };
176
- return {
206
+ const next = {
177
207
  ...prev,
178
208
  status: newStatus,
179
209
  memory: [...prev.memory, newMemory],
180
210
  firstMessageTime,
181
211
  };
212
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
182
213
  }
183
214
  return { ...prev, status: newStatus, firstMessageTime };
184
215
  }
@@ -200,13 +231,39 @@ export function useAgentResponseAccumulator(
200
231
  message: statusMessage,
201
232
  agent: payload.statusUpdate?.agent,
202
233
  timestamp: Date.now(),
234
+ agentName: payload.agentName,
235
+ parentAgent: payload.parentAgent,
236
+ depth: payload.depth,
203
237
  };
204
- return {
238
+ const next = {
205
239
  ...prev,
206
240
  status: newStatus,
207
241
  statusUpdates: [...prev.statusUpdates, newStatusItem],
208
242
  firstMessageTime,
209
243
  };
244
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
245
+ }
246
+ return { ...prev, status: newStatus, firstMessageTime };
247
+ }
248
+
249
+ case "potential_response": {
250
+ const respContent = payload.message || payload.content || "";
251
+ if (respContent) {
252
+ const newResp: PotentialResponse = {
253
+ id: `resp-${Date.now()}`,
254
+ content: respContent,
255
+ timestamp: Date.now(),
256
+ agentName: payload.agentName,
257
+ parentAgent: payload.parentAgent,
258
+ depth: payload.depth,
259
+ };
260
+ const next = {
261
+ ...prev,
262
+ status: newStatus,
263
+ potentialResponses: [...(prev.potentialResponses || []), newResp],
264
+ firstMessageTime,
265
+ };
266
+ return { ...next, timelineEntries: buildTimelineEntries(next) };
210
267
  }
211
268
  return { ...prev, status: newStatus, firstMessageTime };
212
269
  }
@@ -15,10 +15,14 @@ export {
15
15
  MetadataRow,
16
16
  ThinkingSection,
17
17
  ActionBar,
18
+ HITLSection,
19
+ TruncatedMessage,
18
20
  type ActivityIndicatorsProps,
19
21
  type MetadataRowProps,
20
22
  type ThinkingSectionProps,
21
23
  type ActionBarProps,
24
+ type HITLSectionProps,
25
+ type TruncatedMessageProps,
22
26
  } from "./components";
23
27
 
24
28
  // Hooks
@@ -41,6 +45,7 @@ export type {
41
45
  StatusItem,
42
46
  ThinkingStep,
43
47
  ThinkingContent,
48
+ PotentialResponse,
44
49
  AgentMessage,
45
50
  GenericWebSocketMessage,
46
51
  } from "./types";
@@ -49,3 +54,19 @@ export { initialAgentResponseState } from "./types";
49
54
 
50
55
  // Utilities
51
56
  export { formatTime, formatTotalTime } from "./utils";
57
+
58
+ // Agent Timeline (replaces ThinkingSection for rich execution visibility)
59
+ export {
60
+ AgentTimeline,
61
+ createTimelineUIState,
62
+ buildTimelineEntries,
63
+ groupIntoAgentRuns,
64
+ deduplicateEntries,
65
+ } from "../agent-timeline";
66
+ export type {
67
+ TimelineUIState,
68
+ TimelineEntry,
69
+ TimelineEntryType,
70
+ AgentRun,
71
+ DisplayEntry,
72
+ } from "../agent-timeline";