@optilogic/chat 1.0.0-beta.1 → 1.0.0-beta.10

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.
@@ -5,9 +5,9 @@
5
5
  */
6
6
 
7
7
  import * as React from "react";
8
- import { Wrench, Book, HardDrive } from "lucide-react";
8
+ import { Wrench, Book, HardDrive, Activity } from "lucide-react";
9
9
  import { cn, Popover, PopoverTrigger, PopoverContent } from "@optilogic/core";
10
- import type { ToolCall, KnowledgeItem, MemoryItem } from "../types";
10
+ import type { ToolCall, KnowledgeItem, MemoryItem, StatusItem } from "../types";
11
11
 
12
12
  export interface ActivityIndicatorsProps extends React.HTMLAttributes<HTMLDivElement> {
13
13
  /** Tool calls to display */
@@ -16,6 +16,8 @@ export interface ActivityIndicatorsProps extends React.HTMLAttributes<HTMLDivEle
16
16
  knowledge: KnowledgeItem[];
17
17
  /** Memory items to display */
18
18
  memory: MemoryItem[];
19
+ /** Status updates to display */
20
+ statusUpdates?: StatusItem[];
19
21
  }
20
22
 
21
23
  /**
@@ -32,14 +34,44 @@ export interface ActivityIndicatorsProps extends React.HTMLAttributes<HTMLDivEle
32
34
  * />
33
35
  */
34
36
  const ActivityIndicators = React.forwardRef<HTMLDivElement, ActivityIndicatorsProps>(
35
- ({ toolCalls, knowledge, memory, className, ...props }, ref) => {
37
+ ({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
36
38
  const hasAnyActivity =
37
- toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
39
+ toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
38
40
 
39
41
  if (!hasAnyActivity) return null;
40
42
 
41
43
  return (
42
44
  <div ref={ref} className={cn("flex items-center gap-2", className)} {...props}>
45
+ {/* Status Updates */}
46
+ {statusUpdates.length > 0 && (
47
+ <Popover>
48
+ <PopoverTrigger asChild>
49
+ <button
50
+ className="flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors"
51
+ onClick={(e) => e.stopPropagation()}
52
+ >
53
+ <Activity className="w-3.5 h-3.5" />
54
+ <span className="text-xs">{statusUpdates.length}</span>
55
+ </button>
56
+ </PopoverTrigger>
57
+ <PopoverContent className="w-80">
58
+ <div className="space-y-2">
59
+ <h4 className="font-medium text-sm">Status Updates</h4>
60
+ <div className="space-y-2 max-h-60 overflow-auto">
61
+ {statusUpdates.map((item) => (
62
+ <div key={item.id} className="p-2 bg-muted rounded text-xs">
63
+ {item.agent && (
64
+ <div className="font-medium text-muted-foreground">{item.agent}</div>
65
+ )}
66
+ <div className={item.agent ? "mt-1" : ""}>{item.message}</div>
67
+ </div>
68
+ ))}
69
+ </div>
70
+ </div>
71
+ </PopoverContent>
72
+ </Popover>
73
+ )}
74
+
43
75
  {/* Tool Calls */}
44
76
  {toolCalls.length > 0 && (
45
77
  <Popover>
@@ -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 };
@@ -9,7 +9,7 @@ import { ChevronDown, ChevronUp } from "lucide-react";
9
9
  import { cn, LoadingSpinner } from "@optilogic/core";
10
10
  import { ActivityIndicators } from "./ActivityIndicators";
11
11
  import { formatTime } from "../utils";
12
- import type { AgentResponseStatus, ToolCall, KnowledgeItem, MemoryItem } from "../types";
12
+ import type { AgentResponseStatus, ToolCall, KnowledgeItem, MemoryItem, StatusItem } from "../types";
13
13
 
14
14
  export interface MetadataRowProps extends React.HTMLAttributes<HTMLDivElement> {
15
15
  /** Whether there is thinking content */
@@ -24,6 +24,10 @@ export interface MetadataRowProps extends React.HTMLAttributes<HTMLDivElement> {
24
24
  knowledge: KnowledgeItem[];
25
25
  /** Memory items to display */
26
26
  memory: MemoryItem[];
27
+ /** Status updates to display */
28
+ statusUpdates?: StatusItem[];
29
+ /** Optional content to display in the middle area between left content and activity indicators */
30
+ statusContent?: React.ReactNode;
27
31
  /** Current response status */
28
32
  status: AgentResponseStatus;
29
33
  /** Elapsed time in seconds */
@@ -57,6 +61,8 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
57
61
  toolCalls,
58
62
  knowledge,
59
63
  memory,
64
+ statusUpdates = [],
65
+ statusContent,
60
66
  status,
61
67
  elapsedTime,
62
68
  className,
@@ -67,7 +73,7 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
67
73
  const isProcessing = status === "processing";
68
74
  const isComplete = status === "complete";
69
75
  const hasActivity =
70
- toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
76
+ toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
71
77
 
72
78
  // Determine what to show on the left side
73
79
  const renderLeftContent = () => {
@@ -105,8 +111,8 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
105
111
 
106
112
  const leftContent = renderLeftContent();
107
113
 
108
- // If nothing to show (no thinking, not processing, no activity), hide the row
109
- 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) {
110
116
  return null;
111
117
  }
112
118
 
@@ -122,19 +128,28 @@ const MetadataRow = React.forwardRef<HTMLDivElement, MetadataRowProps>(
122
128
  {hasThinking ? (
123
129
  <button
124
130
  onClick={onToggle}
125
- 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"
126
132
  >
127
133
  {leftContent}
128
134
  </button>
129
135
  ) : (
130
- <div className="flex items-center gap-1.5">
136
+ <div className="flex items-center gap-1.5 shrink-0">
131
137
  {leftContent}
132
138
  </div>
133
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
+
134
148
  <ActivityIndicators
135
149
  toolCalls={toolCalls}
136
150
  knowledge={knowledge}
137
151
  memory={memory}
152
+ statusUpdates={statusUpdates}
138
153
  />
139
154
  </div>
140
155
  );
@@ -1,33 +1,109 @@
1
1
  /**
2
2
  * Thinking Section Component
3
3
  *
4
- * Collapsible section for displaying agent thinking/reasoning content
4
+ * Collapsible section for displaying agent thinking/reasoning content.
5
+ * Supports both plain text and structured collapsible sub-sections.
5
6
  */
6
7
 
7
8
  import * as React from "react";
9
+ import { useState, useCallback } from "react";
10
+ import { ChevronDown, ChevronRight } from "lucide-react";
8
11
  import { cn } from "@optilogic/core";
12
+ import type { ThinkingStep } from "../types";
9
13
 
10
- export interface ThinkingSectionProps extends React.HTMLAttributes<HTMLDivElement> {
11
- /** The thinking content to display */
12
- content: string;
14
+ export interface ThinkingSectionProps
15
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "content"> {
16
+ /** The thinking content to display (string or structured steps) */
17
+ content: string | ThinkingStep[];
13
18
  /** Whether the section is expanded */
14
19
  isExpanded: boolean;
20
+ /**
21
+ * Custom markdown renderer for the thinking content.
22
+ * If not provided, the content will be rendered as plain preformatted text.
23
+ */
24
+ renderMarkdown?: (content: string) => React.ReactNode;
15
25
  }
16
26
 
27
+ /**
28
+ * Internal component for rendering a collapsible thinking step
29
+ */
30
+ interface ThinkingStepItemProps {
31
+ step: ThinkingStep;
32
+ renderMarkdown?: (content: string) => React.ReactNode;
33
+ }
34
+
35
+ const ThinkingStepItem: React.FC<ThinkingStepItemProps> = ({ step, renderMarkdown }) => {
36
+ const [isCollapsed, setIsCollapsed] = useState(step.isCollapsed ?? false);
37
+
38
+ const toggleCollapse = useCallback(() => {
39
+ setIsCollapsed((prev) => !prev);
40
+ }, []);
41
+
42
+ const indentPadding = step.depth * 16;
43
+
44
+ return (
45
+ <div className="border-b border-border/50 last:border-b-0">
46
+ <button
47
+ onClick={toggleCollapse}
48
+ className="w-full flex items-center gap-1.5 py-1.5 px-2 hover:bg-muted/50 transition-colors text-left"
49
+ style={{ paddingLeft: `${indentPadding + 8}px` }}
50
+ >
51
+ {isCollapsed ? (
52
+ <ChevronRight className="w-3 h-3 text-muted-foreground flex-shrink-0" />
53
+ ) : (
54
+ <ChevronDown className="w-3 h-3 text-muted-foreground flex-shrink-0" />
55
+ )}
56
+ <span className="text-xs font-medium text-foreground/80">{step.label}</span>
57
+ </button>
58
+
59
+ {!isCollapsed && (
60
+ <div
61
+ className="pb-2 px-2"
62
+ style={{ paddingLeft: `${indentPadding + 28}px` }}
63
+ >
64
+ {renderMarkdown ? (
65
+ <div className="text-xs text-muted-foreground">
66
+ {renderMarkdown(step.content)}
67
+ </div>
68
+ ) : (
69
+ <pre className="text-xs text-muted-foreground whitespace-pre-wrap font-mono">
70
+ {step.content}
71
+ </pre>
72
+ )}
73
+ </div>
74
+ )}
75
+ </div>
76
+ );
77
+ };
78
+
17
79
  /**
18
80
  * ThinkingSection Component
19
81
  *
20
82
  * Displays the agent's thinking/reasoning content in a collapsible panel.
83
+ * Supports both plain text content and structured collapsible sub-sections.
21
84
  *
22
85
  * @example
86
+ * // Plain text content
23
87
  * <ThinkingSection content={state.thinking} isExpanded={isExpanded} />
88
+ *
89
+ * @example
90
+ * // Structured content with sub-sections
91
+ * <ThinkingSection
92
+ * content={[
93
+ * { id: "1", label: "Analysis", content: "...", depth: 0 },
94
+ * { id: "2", label: "Sub-analysis", content: "...", depth: 1 },
95
+ * ]}
96
+ * isExpanded={isExpanded}
97
+ * />
24
98
  */
25
99
  const ThinkingSection = React.forwardRef<HTMLDivElement, ThinkingSectionProps>(
26
- ({ content, isExpanded, className, ...props }, ref) => {
27
- if (!isExpanded || !content) {
100
+ ({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
101
+ if (!isExpanded || !content || (Array.isArray(content) && content.length === 0)) {
28
102
  return null;
29
103
  }
30
104
 
105
+ const isStructured = Array.isArray(content);
106
+
31
107
  return (
32
108
  <div
33
109
  ref={ref}
@@ -35,9 +111,25 @@ const ThinkingSection = React.forwardRef<HTMLDivElement, ThinkingSectionProps>(
35
111
  {...props}
36
112
  >
37
113
  <div className="mt-2 max-h-[200px] overflow-y-auto">
38
- <pre className="text-xs text-muted-foreground whitespace-pre-wrap font-mono">
39
- {content}
40
- </pre>
114
+ {isStructured ? (
115
+ <div className="space-y-0">
116
+ {content.map((step) => (
117
+ <ThinkingStepItem
118
+ key={step.id}
119
+ step={step}
120
+ renderMarkdown={renderMarkdown}
121
+ />
122
+ ))}
123
+ </div>
124
+ ) : renderMarkdown ? (
125
+ <div className="text-xs text-muted-foreground">
126
+ {renderMarkdown(content)}
127
+ </div>
128
+ ) : (
129
+ <pre className="text-xs text-muted-foreground whitespace-pre-wrap font-mono">
130
+ {content}
131
+ </pre>
132
+ )}
41
133
  </div>
42
134
  </div>
43
135
  );
@@ -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";
@@ -13,6 +13,8 @@ import {
13
13
  type ToolCall,
14
14
  type KnowledgeItem,
15
15
  type MemoryItem,
16
+ type StatusItem,
17
+ type ThinkingStep,
16
18
  } from "../types";
17
19
 
18
20
  export interface UseAgentResponseAccumulatorOptions {
@@ -86,6 +88,26 @@ export function useAgentResponseAccumulator(
86
88
  return { ...prev, status: newStatus };
87
89
 
88
90
  case "thinking": {
91
+ // Check if this is a structured thinking step
92
+ if (payload.thinkingStep) {
93
+ const newStep: ThinkingStep = {
94
+ id: payload.thinkingStep.id || `step-${Date.now()}`,
95
+ label: payload.thinkingStep.label,
96
+ content: payload.thinkingStep.content,
97
+ depth: payload.thinkingStep.depth ?? 0,
98
+ isCollapsed: payload.thinkingStep.isCollapsed,
99
+ };
100
+ const thinkingStartTime = prev.thinkingStartTime ?? Date.now();
101
+ return {
102
+ ...prev,
103
+ status: newStatus,
104
+ thinkingSteps: [...(prev.thinkingSteps || []), newStep],
105
+ thinkingStartTime,
106
+ firstMessageTime,
107
+ };
108
+ }
109
+
110
+ // Plain text thinking (existing behavior)
89
111
  const newThinking = payload.message || payload.content || "";
90
112
  // Add line break between thinking messages
91
113
  const separator = prev.thinking && newThinking ? "\n\n" : "";
@@ -170,6 +192,25 @@ export function useAgentResponseAccumulator(
170
192
  firstMessageTime: prev.firstMessageTime ?? Date.now(),
171
193
  };
172
194
 
195
+ case "status_update": {
196
+ const statusMessage = payload.message || payload.statusUpdate?.message;
197
+ if (statusMessage) {
198
+ const newStatusItem: StatusItem = {
199
+ id: payload.statusUpdate?.id || `status-${Date.now()}`,
200
+ message: statusMessage,
201
+ agent: payload.statusUpdate?.agent,
202
+ timestamp: Date.now(),
203
+ };
204
+ return {
205
+ ...prev,
206
+ status: newStatus,
207
+ statusUpdates: [...prev.statusUpdates, newStatusItem],
208
+ firstMessageTime,
209
+ };
210
+ }
211
+ return { ...prev, status: newStatus, firstMessageTime };
212
+ }
213
+
173
214
  default:
174
215
  return { ...prev, status: newStatus, firstMessageTime };
175
216
  }
@@ -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
@@ -38,6 +42,9 @@ export type {
38
42
  ToolCall,
39
43
  KnowledgeItem,
40
44
  MemoryItem,
45
+ StatusItem,
46
+ ThinkingStep,
47
+ ThinkingContent,
41
48
  AgentMessage,
42
49
  GenericWebSocketMessage,
43
50
  } from "./types";
@@ -44,6 +44,40 @@ export interface MemoryItem {
44
44
  timestamp: number;
45
45
  }
46
46
 
47
+ /**
48
+ * Status update information from the agent
49
+ */
50
+ export interface StatusItem {
51
+ id: string;
52
+ message: string;
53
+ timestamp: number;
54
+ /** Optional agent name if in multi-agent scenario */
55
+ agent?: string;
56
+ }
57
+
58
+ /**
59
+ * A single step in structured thinking content
60
+ */
61
+ export interface ThinkingStep {
62
+ /** Unique identifier for the step */
63
+ id: string;
64
+ /** Label/title shown in the collapsible header */
65
+ label: string;
66
+ /** Content of the thinking step */
67
+ content: string;
68
+ /** Nesting depth (0 = root level, 1 = first indent, etc.) */
69
+ depth: number;
70
+ /** Whether this step should start collapsed (default: false) */
71
+ isCollapsed?: boolean;
72
+ }
73
+
74
+ /**
75
+ * Union type for thinking content
76
+ * - string: plain text (backward compatible)
77
+ * - ThinkingStep[]: structured with collapsible sub-sections
78
+ */
79
+ export type ThinkingContent = string | ThinkingStep[];
80
+
47
81
  /**
48
82
  * State shape for the agent response component
49
83
  */
@@ -52,12 +86,16 @@ export interface AgentResponseState {
52
86
  status: AgentResponseStatus;
53
87
  /** Accumulated thinking/reasoning text */
54
88
  thinking: string;
89
+ /** Structured thinking steps (if provided, takes precedence over thinking string) */
90
+ thinkingSteps?: ThinkingStep[];
55
91
  /** Tool calls made during processing */
56
92
  toolCalls: ToolCall[];
57
93
  /** Knowledge items retrieved */
58
94
  knowledge: KnowledgeItem[];
59
95
  /** Memory items accessed */
60
96
  memory: MemoryItem[];
97
+ /** Status updates from the agent */
98
+ statusUpdates: StatusItem[];
61
99
  /** Final response text */
62
100
  response: string;
63
101
  /** Timestamp when first thinking message was received (for timer) */
@@ -72,7 +110,7 @@ export interface AgentResponseState {
72
110
  * WebSocket message payload for agent responses
73
111
  */
74
112
  export interface AgentMessage {
75
- type: "status" | "thinking" | "tool_call" | "knowledge" | "memory" | "response";
113
+ type: "status" | "thinking" | "tool_call" | "knowledge" | "memory" | "response" | "status_update";
76
114
  /** Message content - for simple string payloads */
77
115
  message?: string;
78
116
  /** Alternative content field */
@@ -97,6 +135,20 @@ export interface AgentMessage {
97
135
  type: string;
98
136
  content: string;
99
137
  };
138
+ /** For status_update messages */
139
+ statusUpdate?: {
140
+ id: string;
141
+ message: string;
142
+ agent?: string;
143
+ };
144
+ /** For structured thinking step messages */
145
+ thinkingStep?: {
146
+ id?: string;
147
+ label: string;
148
+ content: string;
149
+ depth?: number;
150
+ isCollapsed?: boolean;
151
+ };
100
152
  }
101
153
 
102
154
  /**
@@ -117,6 +169,7 @@ export const initialAgentResponseState: AgentResponseState = {
117
169
  toolCalls: [],
118
170
  knowledge: [],
119
171
  memory: [],
172
+ statusUpdates: [],
120
173
  response: "",
121
174
  thinkingStartTime: null,
122
175
  responseCompleteTime: null,