@jazzmind/busibox-app 3.0.38 → 3.0.39

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 (32) hide show
  1. package/dist/components/chat/ChatContainer.d.ts.map +1 -1
  2. package/dist/components/chat/ChatContainer.js +122 -602
  3. package/dist/components/chat/ChatContainer.js.map +1 -1
  4. package/dist/components/chat/ChatInterface.d.ts +0 -1
  5. package/dist/components/chat/ChatInterface.d.ts.map +1 -1
  6. package/dist/components/chat/ChatInterface.js +159 -302
  7. package/dist/components/chat/ChatInterface.js.map +1 -1
  8. package/dist/components/chat/MessageList.d.ts +2 -1
  9. package/dist/components/chat/MessageList.d.ts.map +1 -1
  10. package/dist/components/chat/MessageList.js +33 -17
  11. package/dist/components/chat/MessageList.js.map +1 -1
  12. package/dist/components/chat/ThinkingToggle.d.ts.map +1 -1
  13. package/dist/components/chat/ThinkingToggle.js +15 -1
  14. package/dist/components/chat/ThinkingToggle.js.map +1 -1
  15. package/dist/lib/agent/agent-api-base.d.ts.map +1 -1
  16. package/dist/lib/agent/agent-api-base.js +0 -1
  17. package/dist/lib/agent/agent-api-base.js.map +1 -1
  18. package/dist/lib/agent/chat-client.d.ts.map +1 -1
  19. package/dist/lib/agent/chat-client.js +0 -1
  20. package/dist/lib/agent/chat-client.js.map +1 -1
  21. package/dist/lib/agent/index.d.ts +2 -0
  22. package/dist/lib/agent/index.d.ts.map +1 -1
  23. package/dist/lib/agent/index.js +2 -0
  24. package/dist/lib/agent/index.js.map +1 -1
  25. package/dist/lib/agent/stream-event-processor.d.ts +37 -0
  26. package/dist/lib/agent/stream-event-processor.d.ts.map +1 -0
  27. package/dist/lib/agent/stream-event-processor.js +204 -0
  28. package/dist/lib/agent/stream-event-processor.js.map +1 -0
  29. package/dist/lib/hooks/useChatStream.d.ts.map +1 -1
  30. package/dist/lib/hooks/useChatStream.js +27 -173
  31. package/dist/lib/hooks/useChatStream.js.map +1 -1
  32. package/package.json +1 -1
@@ -27,21 +27,13 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
27
27
  * />
28
28
  * ```
29
29
  */
30
- import { useState, useRef, useEffect } from 'react';
30
+ import { useState, useRef, useEffect, useCallback } from 'react';
31
31
  import { Send, Bot, Loader2, Paperclip, Plus, Trash2, Volume2, X } from 'lucide-react';
32
32
  import toast from 'react-hot-toast';
33
- import ReactMarkdown from 'react-markdown';
34
- import remarkGfm from 'remark-gfm';
35
- import remarkMath from 'remark-math';
36
- import rehypeKatex from 'rehype-katex';
37
- import 'katex/dist/katex.min.css';
38
33
  import { MessageList } from './MessageList';
39
- import { ThinkingToggle } from './ThinkingToggle';
40
- import { ThinkingStream } from './ThinkingStream';
41
- import { StepTimeline } from './StepTimeline';
42
- import { StreamingToolCard } from './StreamingToolCard';
43
- import { stripThinkTags, extractThinkContent, preprocessLatex, streamingMarkdownComponents } from './chat-utils';
34
+ import { stripThinkTags } from './chat-utils';
44
35
  import { sendChatMessage, streamChatMessageAgentic, getConversationHistory } from '../../lib/agent/chat-client';
36
+ import { createAccumulator, processStreamEvent } from '../../lib/agent/stream-event-processor';
45
37
  export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = false, enableDocSearch = false, allowAttachments = false, placeholder = 'Type your message...', welcomeMessage, model = 'auto', useStreaming: _useStreaming = true, useAgenticStreaming = true, className = '', onMessageSent, onResponseReceived, initialConversationId, metadata, onConversationDeleted, }) {
46
38
  const [messages, setMessages] = useState([]);
47
39
  const [input, setInput] = useState('');
@@ -61,29 +53,31 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
61
53
  const messagesEndRef = useRef(null);
62
54
  const fileInputRef = useRef(null);
63
55
  const textareaRef = useRef(null);
56
+ const tokenRef = useRef(token);
57
+ useEffect(() => {
58
+ tokenRef.current = token;
59
+ }, [token]);
64
60
  const scrollToBottom = () => {
65
61
  messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
66
62
  };
67
63
  useEffect(() => {
68
64
  scrollToBottom();
69
65
  }, [messages, streamingContent]);
70
- // Load conversation history when initialConversationId is provided
71
- useEffect(() => {
72
- if (initialConversationId && token) {
73
- loadConversationHistory(initialConversationId);
74
- }
75
- }, [initialConversationId, token]);
76
- const loadConversationHistory = async (convId) => {
66
+ const loadConversationHistory = useCallback(async (convId) => {
77
67
  setLoadingHistory(true);
78
68
  try {
79
- const history = await getConversationHistory(convId, { token, agentUrl });
80
- // Convert to DisplayMessage format
81
- const displayMessages = history.map(msg => ({
82
- id: msg.id,
83
- role: msg.role,
84
- content: msg.content,
85
- createdAt: new Date(msg.created_at || msg.createdAt || Date.now()),
86
- }));
69
+ const history = await getConversationHistory(convId, { token: tokenRef.current, agentUrl });
70
+ const displayMessages = history.map(msg => {
71
+ const raw = msg;
72
+ const thoughts = raw.routing_decision?.thoughts || raw.thoughts;
73
+ return {
74
+ id: msg.id,
75
+ role: msg.role,
76
+ content: msg.content,
77
+ createdAt: new Date(raw.created_at || raw.createdAt || Date.now()),
78
+ thoughts: thoughts?.length > 0 ? thoughts : undefined,
79
+ };
80
+ });
87
81
  setMessages(displayMessages);
88
82
  setConversationId(convId);
89
83
  }
@@ -94,7 +88,13 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
94
88
  finally {
95
89
  setLoadingHistory(false);
96
90
  }
97
- };
91
+ }, [agentUrl]);
92
+ // Load conversation history when initialConversationId changes (not on token refresh)
93
+ useEffect(() => {
94
+ if (initialConversationId && tokenRef.current) {
95
+ loadConversationHistory(initialConversationId);
96
+ }
97
+ }, [initialConversationId, loadConversationHistory]);
98
98
  const handleFileSelect = async (e) => {
99
99
  const files = e.target.files;
100
100
  if (!files || files.length === 0)
@@ -120,11 +120,10 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
120
120
  const handleSubmit = async (e, overrideMessage) => {
121
121
  e?.preventDefault();
122
122
  const messageText = overrideMessage ?? input.trim();
123
- // Allow submission when promptActive even if isLoading is still true
124
- if (!messageText || (isLoading && !promptActive))
123
+ if (!messageText)
125
124
  return;
126
- // If we're submitting during a prompt-active state, abort the lingering stream
127
- if (isLoading && promptActive && abortController) {
125
+ // Abort any in-flight stream so the new message can proceed
126
+ if (isLoading && abortController) {
128
127
  abortController.abort();
129
128
  }
130
129
  const userMessage = messageText;
@@ -165,282 +164,142 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
165
164
  setInterimMessages([]);
166
165
  setStreamingAgentName(undefined);
167
166
  setStreamingParts([]);
168
- let fullContent = '';
169
- let collectedThoughts = [];
170
- let collectedParts = [];
167
+ const accumulated = createAccumulator();
171
168
  let hasAddedMessage = false;
172
- // Track in-flight tool calls by source so we can update their status
173
- const pendingTools = new Map(); // source -> index in collectedParts
174
- for await (const event of streamChatMessageAgentic(request, { token, agentUrl, signal: controller.signal })) {
175
- const newEvent = {
176
- type: event.type,
177
- source: event.data?.source,
178
- message: event.data?.message,
179
- data: event.data,
180
- timestamp: new Date(),
181
- };
182
- if (event.data?.source && !event.data.source.includes('dispatcher')) {
183
- setStreamingAgentName(event.data.source);
184
- }
185
- switch (event.type) {
186
- case 'conversation_created':
187
- setConversationId(event.data.conversation_id);
188
- break;
189
- case 'thought':
190
- case 'plan':
191
- case 'progress':
192
- collectedThoughts = [...collectedThoughts, newEvent];
193
- setThoughts(collectedThoughts);
194
- break;
195
- case 'tool_start':
196
- {
197
- collectedThoughts = [...collectedThoughts, newEvent];
198
- setThoughts(collectedThoughts);
199
- const toolSource = event.data?.source || 'tool';
200
- const toolName = String(event.data?.data?.tool_name || event.data?.data?.display_name || toolSource);
201
- const toolPart = {
202
- type: 'tool_call',
203
- id: `tool-${Date.now()}-${toolName}`,
204
- name: toolName,
205
- displayName: String(event.data?.data?.display_name || event.data?.message || toolName),
206
- status: 'running',
207
- input: (event.data?.data || undefined),
208
- startedAt: new Date(),
169
+ for await (const event of streamChatMessageAgentic(request, { token: tokenRef.current, agentUrl, signal: controller.signal })) {
170
+ const parsed = event.data;
171
+ // message_complete and error have ChatInterface-specific side effects
172
+ // (creating DisplayMessages, toasts) so they're handled inline.
173
+ if (event.type === 'message_complete') {
174
+ if (!hasAddedMessage) {
175
+ setConversationId(parsed.conversation_id);
176
+ const cleanedContent = stripThinkTags(accumulated.fullContent);
177
+ if (cleanedContent) {
178
+ const finalParts = [
179
+ ...accumulated.parts,
180
+ { type: 'text', content: cleanedContent },
181
+ ];
182
+ const assistantMessage = {
183
+ role: 'assistant',
184
+ content: cleanedContent,
185
+ timestamp: new Date(),
186
+ thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
187
+ agentName: accumulated.agentName,
188
+ parts: finalParts,
209
189
  };
210
- pendingTools.set(toolSource, collectedParts.length);
211
- collectedParts = [...collectedParts, toolPart];
212
- setStreamingParts(collectedParts);
213
- }
214
- break;
215
- case 'tool_result':
216
- {
217
- collectedThoughts = [...collectedThoughts, newEvent];
218
- setThoughts(collectedThoughts);
219
- const resultSource = event.data?.source || 'tool';
220
- const idx = pendingTools.get(resultSource);
221
- if (idx !== undefined && collectedParts[idx]?.type === 'tool_call') {
222
- const existing = collectedParts[idx];
223
- const updated = {
224
- ...existing,
225
- status: event.data?.data?.success === false ? 'error' : 'completed',
226
- output: event.data?.message || undefined,
227
- error: event.data?.data?.success === false ? String(event.data?.message || 'Failed') : undefined,
228
- completedAt: new Date(),
229
- };
230
- collectedParts = [...collectedParts];
231
- collectedParts[idx] = updated;
232
- pendingTools.delete(resultSource);
233
- }
234
- else {
235
- // No matching tool_start; append as a standalone completed tool
236
- const toolName = String(event.data?.data?.tool_name || event.data?.data?.display_name || resultSource);
237
- collectedParts = [...collectedParts, {
238
- type: 'tool_call',
239
- id: `tool-${Date.now()}-${toolName}`,
240
- name: toolName,
241
- displayName: String(event.data?.data?.display_name || toolName),
242
- status: event.data?.data?.success === false ? 'error' : 'completed',
243
- output: event.data?.message || undefined,
244
- completedAt: new Date(),
245
- }];
246
- }
247
- setStreamingParts(collectedParts);
248
- }
249
- break;
250
- case 'interim':
251
- {
252
- const payload = event.data || {};
253
- const nested = payload.data || {};
254
- const interimMessage = String(payload.message || '').trim();
255
- const audioUrl = typeof nested.audio_url === 'string' ? nested.audio_url : '';
256
- const rendered = audioUrl
257
- ? `${interimMessage || 'Voice output ready'} (${audioUrl})`
258
- : interimMessage;
259
- if (rendered) {
260
- setInterimMessages(prev => [...prev, rendered]);
261
- }
190
+ setMessages((prev) => [...prev, assistantMessage]);
191
+ hasAddedMessage = true;
262
192
  }
263
- break;
264
- case 'content':
265
- {
266
- const contentData = event.data?.data || {};
267
- const msgText = event.data?.message || '';
268
- if (contentData.streaming && contentData.partial) {
269
- fullContent += msgText;
270
- }
271
- else if (contentData.complete) {
272
- // Final marker
273
- }
274
- else if (msgText) {
275
- fullContent = msgText;
276
- }
277
- const thinkTexts = extractThinkContent(fullContent);
278
- if (thinkTexts.length > 0) {
279
- const thinkThought = {
280
- type: 'thought',
281
- source: 'model',
282
- message: thinkTexts.join('\n\n'),
283
- data: event.data,
284
- timestamp: new Date(),
285
- };
286
- const hasModelThought = collectedThoughts.some(t => t.source === 'model' && t.type === 'thought');
287
- if (hasModelThought) {
288
- collectedThoughts = collectedThoughts.map(t => t.source === 'model' && t.type === 'thought' ? thinkThought : t);
289
- }
290
- else {
291
- collectedThoughts = [...collectedThoughts, thinkThought];
292
- }
293
- setThoughts(collectedThoughts);
294
- }
295
- setStreamingContent(stripThinkTags(fullContent));
193
+ setStreamingContent('');
194
+ setThoughts([]);
195
+ setInterimMessages([]);
196
+ setStreamingAgentName(undefined);
197
+ setStreamingParts([]);
198
+ setIsLoading(false);
199
+ if (cleanedContent) {
200
+ onResponseReceived?.(cleanedContent);
296
201
  }
297
- break;
298
- case 'clarify_parallel':
299
- {
300
- // Agent is asking a question while continuing work in the background
301
- const bgStatus = event.data?.data?.background_status || event.data?.background_status;
302
- const clarifyMsg = event.data?.message || '';
303
- if (clarifyMsg) {
304
- fullContent += clarifyMsg + '\n\n';
305
- setStreamingContent(stripThinkTags(fullContent));
306
- }
307
- if (bgStatus) {
308
- collectedThoughts = [...collectedThoughts, {
309
- type: 'progress',
310
- source: event.data?.source,
311
- message: String(bgStatus),
312
- data: { phase: 'background_work' },
313
- timestamp: new Date(),
314
- }];
315
- setThoughts(collectedThoughts);
316
- }
317
- const clarifyOptions = event.data?.data?.options || event.data?.options;
318
- if (clarifyOptions && Array.isArray(clarifyOptions) && clarifyOptions.length > 0) {
319
- setQuickReplies(clarifyOptions);
320
- setPromptActive(true);
321
- }
322
- }
323
- break;
324
- case 'prompt':
325
- {
326
- const promptOptions = event.data?.data?.options || event.data?.options;
327
- if (promptOptions && Array.isArray(promptOptions)) {
328
- setQuickReplies(promptOptions);
329
- setPromptActive(true);
330
- // Add a prompt part for rendering
331
- const promptType = (event.data?.data?.prompt_type || 'choice');
332
- collectedParts = [...collectedParts, { type: 'prompt', options: promptOptions, promptType }];
333
- setStreamingParts(collectedParts);
334
- // Finalize accumulated content as a completed message
335
- const cleanedSoFar = stripThinkTags(fullContent);
336
- if (cleanedSoFar && !hasAddedMessage) {
337
- const assistantMessage = {
338
- role: 'assistant',
339
- content: cleanedSoFar,
340
- timestamp: new Date(),
341
- thoughts: collectedThoughts.length > 0 ? collectedThoughts : undefined,
342
- agentName: streamingAgentName,
343
- parts: collectedParts,
344
- };
345
- setMessages((prev) => [...prev, assistantMessage]);
346
- hasAddedMessage = true;
347
- }
348
- setStreamingContent('');
349
- setThoughts([]);
350
- setInterimMessages([]);
351
- setStreamingParts([]);
352
- }
202
+ }
203
+ continue;
204
+ }
205
+ if (event.type === 'error') {
206
+ const errorMessage = parsed?.message || parsed?.error || 'An error occurred';
207
+ const errorSource = parsed?.source || parsed?.data?.source || '';
208
+ const isToolError = errorSource && !errorSource.includes('agent') && !errorSource.includes('dispatcher');
209
+ if (isToolError) {
210
+ accumulated.thoughts = [...accumulated.thoughts, {
211
+ type: 'error',
212
+ source: errorSource,
213
+ message: `Tool error (${errorSource}): ${errorMessage}`,
214
+ data: parsed,
215
+ timestamp: new Date(),
216
+ }];
217
+ setThoughts(accumulated.thoughts);
218
+ const errIdx = accumulated.pendingTools.get(errorSource);
219
+ if (errIdx !== undefined && accumulated.parts[errIdx]?.type === 'tool_call') {
220
+ const existing = accumulated.parts[errIdx];
221
+ accumulated.parts = [...accumulated.parts];
222
+ accumulated.parts[errIdx] = { ...existing, status: 'error', error: errorMessage, completedAt: new Date() };
223
+ accumulated.pendingTools.delete(errorSource);
224
+ setStreamingParts(accumulated.parts);
353
225
  }
354
- break;
355
- case 'complete':
356
- break;
357
- case 'message_complete':
226
+ }
227
+ else {
228
+ toast.error(errorMessage);
358
229
  if (!hasAddedMessage) {
359
- setConversationId(event.data.conversation_id);
360
- const cleanedContent = stripThinkTags(fullContent);
361
- if (cleanedContent) {
362
- // Build final text part if there's content not yet in parts
363
- const finalParts = [
364
- ...collectedParts,
365
- { type: 'text', content: cleanedContent },
366
- ];
367
- const assistantMessage = {
368
- role: 'assistant',
369
- content: cleanedContent,
370
- timestamp: new Date(),
371
- thoughts: collectedThoughts.length > 0 ? collectedThoughts : undefined,
372
- agentName: streamingAgentName,
373
- parts: finalParts,
374
- };
375
- setMessages((prev) => [...prev, assistantMessage]);
376
- hasAddedMessage = true;
377
- }
378
- setStreamingContent('');
379
- setThoughts([]);
380
- setInterimMessages([]);
381
- setStreamingAgentName(undefined);
382
- setStreamingParts([]);
383
- setIsLoading(false);
384
- if (cleanedContent) {
385
- onResponseReceived?.(cleanedContent);
386
- }
387
- }
388
- break;
389
- case 'error':
390
- {
391
- const errorMessage = event.data?.message || event.data?.error || 'An error occurred';
392
- const errorSource = event.data?.source || event.data?.data?.source || '';
393
- const isToolError = errorSource && !errorSource.includes('agent') && !errorSource.includes('dispatcher');
394
- if (isToolError) {
395
- collectedThoughts = [...collectedThoughts, {
396
- type: 'error',
397
- source: errorSource,
398
- message: `Tool error (${errorSource}): ${errorMessage}`,
399
- data: event.data,
400
- timestamp: new Date(),
401
- }];
402
- setThoughts(collectedThoughts);
403
- // Update matching pending tool part to error status
404
- const errIdx = pendingTools.get(errorSource);
405
- if (errIdx !== undefined && collectedParts[errIdx]?.type === 'tool_call') {
406
- const existing = collectedParts[errIdx];
407
- collectedParts = [...collectedParts];
408
- collectedParts[errIdx] = { ...existing, status: 'error', error: errorMessage, completedAt: new Date() };
409
- pendingTools.delete(errorSource);
410
- setStreamingParts(collectedParts);
411
- }
412
- }
413
- else {
414
- toast.error(errorMessage);
415
- if (!hasAddedMessage) {
416
- const errorContent = fullContent.trim()
417
- ? `${fullContent}\n\n**Error:** ${errorMessage}`
418
- : `**Error:** ${errorMessage}`;
419
- const errorAssistantMessage = {
420
- role: 'assistant',
421
- content: errorContent,
422
- timestamp: new Date(),
423
- thoughts: collectedThoughts.length > 0 ? collectedThoughts : undefined,
424
- agentName: streamingAgentName,
425
- parts: collectedParts,
426
- };
427
- setMessages((prev) => [...prev, errorAssistantMessage]);
428
- hasAddedMessage = true;
429
- }
430
- setStreamingContent('');
431
- setThoughts([]);
432
- setInterimMessages([]);
433
- setStreamingAgentName(undefined);
434
- setStreamingParts([]);
435
- }
230
+ const errorContent = accumulated.fullContent.trim()
231
+ ? `${accumulated.fullContent}\n\n**Error:** ${errorMessage}`
232
+ : `**Error:** ${errorMessage}`;
233
+ const errorAssistantMessage = {
234
+ role: 'assistant',
235
+ content: errorContent,
236
+ timestamp: new Date(),
237
+ thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
238
+ agentName: accumulated.agentName,
239
+ parts: accumulated.parts,
240
+ };
241
+ setMessages((prev) => [...prev, errorAssistantMessage]);
242
+ hasAddedMessage = true;
436
243
  }
437
- break;
244
+ setStreamingContent('');
245
+ setThoughts([]);
246
+ setInterimMessages([]);
247
+ setStreamingAgentName(undefined);
248
+ setStreamingParts([]);
249
+ }
250
+ continue;
251
+ }
252
+ // All other events go through the shared processor
253
+ const result = processStreamEvent(event.type, parsed, accumulated);
254
+ if (result.conversationId && !result.titleUpdate) {
255
+ setConversationId(result.conversationId);
256
+ }
257
+ if (accumulated.agentName) {
258
+ setStreamingAgentName(accumulated.agentName);
259
+ }
260
+ if (result.content !== undefined) {
261
+ setStreamingContent(result.content);
262
+ }
263
+ if (result.thoughts) {
264
+ setThoughts(result.thoughts);
265
+ }
266
+ if (result.parts) {
267
+ setStreamingParts(result.parts);
268
+ }
269
+ if (result.interimMessages) {
270
+ setInterimMessages(result.interimMessages);
271
+ }
272
+ if (result.quickReplies) {
273
+ setQuickReplies(result.quickReplies);
274
+ }
275
+ if (result.promptActive !== undefined) {
276
+ setPromptActive(result.promptActive);
277
+ }
278
+ // prompt event: finalize accumulated content as a completed message
279
+ if (event.type === 'prompt' && result.promptActive) {
280
+ const cleanedSoFar = stripThinkTags(accumulated.fullContent);
281
+ if (cleanedSoFar && !hasAddedMessage) {
282
+ const assistantMessage = {
283
+ role: 'assistant',
284
+ content: cleanedSoFar,
285
+ timestamp: new Date(),
286
+ thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
287
+ agentName: accumulated.agentName,
288
+ parts: accumulated.parts,
289
+ };
290
+ setMessages((prev) => [...prev, assistantMessage]);
291
+ hasAddedMessage = true;
292
+ }
293
+ setStreamingContent('');
294
+ setThoughts([]);
295
+ setInterimMessages([]);
296
+ setStreamingParts([]);
438
297
  }
439
298
  }
440
299
  }
441
300
  else {
442
301
  // Non-streaming fallback
443
- const response = await sendChatMessage(request, { token, agentUrl });
302
+ const response = await sendChatMessage(request, { token: tokenRef.current, agentUrl });
444
303
  setConversationId(response.conversation_id);
445
304
  const assistantMessage = {
446
305
  role: 'assistant',
@@ -533,7 +392,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
533
392
  const response = await fetch(`${agentUrl || ''}/chat/${conversationId}/messages/${messageId}`, {
534
393
  method: 'DELETE',
535
394
  headers: {
536
- 'Authorization': `Bearer ${token}`,
395
+ 'Authorization': `Bearer ${tokenRef.current}`,
537
396
  'Content-Type': 'application/json',
538
397
  },
539
398
  });
@@ -561,7 +420,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
561
420
  const response = await fetch(`${agentUrl || ''}/conversations/${conversationId}`, {
562
421
  method: 'DELETE',
563
422
  headers: {
564
- 'Authorization': `Bearer ${token}`,
423
+ 'Authorization': `Bearer ${tokenRef.current}`,
565
424
  'Content-Type': 'application/json',
566
425
  },
567
426
  });
@@ -630,9 +489,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
630
489
  });
631
490
  }
632
491
  return displayMessages;
633
- })(), streamingContent: undefined, streamingAgentName: streamingAgentName, isLoading: false, onDeleteMessage: handleDeleteMessage, onRetryMessage: handleRetryMessage, onSuggestedAction: (action) => handleSubmit(null, action) })), (isLoading || streamingContent) && !promptActive && (_jsxs("div", { className: "flex gap-4 justify-start", children: [_jsxs("div", { className: "flex flex-col items-center gap-1", children: [_jsx("div", { className: "w-8 h-8 rounded-full bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center flex-shrink-0 text-white font-semibold text-sm", children: streamingAgentName?.charAt(0).toUpperCase() || 'A' }), streamingAgentName && (_jsx("span", { className: "text-[10px] text-gray-500 dark:text-gray-400 font-medium capitalize", children: streamingAgentName }))] }), _jsx("div", { className: "max-w-3xl flex-1", children: _jsxs("div", { className: "rounded-lg px-4 py-3 bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100", children: [(thoughts.length > 0 || streamingParts.length > 0) && (_jsx(StepTimeline, { thoughts: thoughts, parts: streamingParts, isActive: isLoading })), _jsx(ThinkingStream, { thoughts: thoughts, isActive: isLoading && !streamingContent }), thoughts.filter(t => t.data?.phase !== 'model_reasoning').length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mb-2 text-xs", children: _jsx(ThinkingToggle, { thoughts: thoughts.filter(t => t.data?.phase !== 'model_reasoning'), isActive: isLoading && !streamingContent }) })), streamingParts.filter(p => p.type === 'tool_call').length > 0 && (_jsx("div", { className: "mb-2", children: streamingParts
634
- .filter((p) => p.type === 'tool_call')
635
- .map((part) => (_jsx(StreamingToolCard, { part: part }, part.id))) })), interimMessages.length > 0 && (_jsx("div", { className: "mb-3 space-y-1", children: interimMessages.map((msg, idx) => (_jsx("div", { className: "text-xs rounded border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900/20 px-2 py-1 text-amber-800 dark:text-amber-200", children: msg }, `interim-${idx}`))) })), streamingContent ? (_jsxs("div", { className: "prose prose-sm dark:prose-invert max-w-none prose-headings:font-semibold prose-h1:text-xl prose-h1:mt-4 prose-h1:mb-3 prose-h2:text-lg prose-h2:mt-4 prose-h2:mb-2 prose-h3:text-base prose-h3:mt-3 prose-h3:mb-2 prose-p:my-2 prose-p:leading-relaxed prose-ul:my-3 prose-ol:my-3 prose-li:my-0.5 prose-hr:my-6 prose-strong:font-semibold prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-pre:border prose-blockquote:border-l-4 prose-blockquote:pl-4 prose-blockquote:italic prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-a:no-underline hover:prose-a:underline", children: [_jsx(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkMath], rehypePlugins: [rehypeKatex], components: streamingMarkdownComponents, children: preprocessLatex(streamingContent) }), _jsx("span", { className: "inline-block w-2 h-4 bg-gray-400 dark:bg-gray-500 animate-pulse ml-1" })] })) : (_jsxs("div", { className: "flex items-center gap-2 text-gray-500 dark:text-gray-400", children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), _jsx("span", { children: "Thinking..." })] }))] }) })] })), _jsx("div", { ref: messagesEndRef })] }), attachments.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: _jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex items-center gap-2 bg-white dark:bg-gray-700 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-600", children: [_jsx("span", { className: "text-sm text-gray-700 dark:text-gray-200", children: attachment.name }), _jsx("button", { onClick: () => handleRemoveAttachment(index), className: "text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400", children: "\u00D7" })] }, index))) }) })), quickReplies.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-3 pt-2 pb-1 flex flex-wrap gap-2 justify-center border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: quickReplies.map((reply) => (_jsx("button", { type: "button", onClick: () => handleQuickReply(reply), className: "px-4 py-1.5 text-sm font-medium rounded-full border border-blue-500 dark:border-blue-400 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors", children: reply }, reply))) })), _jsx("form", { onSubmit: handleSubmit, className: "flex-shrink-0 p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: _jsxs("div", { className: "flex gap-2 items-end", children: [allowAttachments && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileSelect, className: "hidden" }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isLoading, className: "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0", title: "Attach files", children: _jsx(Paperclip, { className: "w-5 h-5" }) })] })), _jsx("textarea", { ref: textareaRef, value: input, onChange: (e) => {
492
+ })(), streamingContent: streamingContent || undefined, streamingAgentName: streamingAgentName, streamingThoughts: thoughts, streamingParts: streamingParts, isLoading: isLoading, onDeleteMessage: handleDeleteMessage, onRetryMessage: handleRetryMessage, onSuggestedAction: (action) => handleSubmit(null, action) })), _jsx("div", { ref: messagesEndRef })] }), attachments.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: _jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex items-center gap-2 bg-white dark:bg-gray-700 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-600", children: [_jsx("span", { className: "text-sm text-gray-700 dark:text-gray-200", children: attachment.name }), _jsx("button", { onClick: () => handleRemoveAttachment(index), className: "text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400", children: "\u00D7" })] }, index))) }) })), quickReplies.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-3 pt-2 pb-1 flex flex-wrap gap-2 justify-center border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: quickReplies.map((reply) => (_jsx("button", { type: "button", onClick: () => handleQuickReply(reply), className: "px-4 py-1.5 text-sm font-medium rounded-full border border-blue-500 dark:border-blue-400 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors", children: reply }, reply))) })), _jsx("form", { onSubmit: handleSubmit, className: "flex-shrink-0 p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: _jsxs("div", { className: "flex gap-2 items-end", children: [allowAttachments && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileSelect, className: "hidden" }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isLoading, className: "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0", title: "Attach files", children: _jsx(Paperclip, { className: "w-5 h-5" }) })] })), _jsx("textarea", { ref: textareaRef, value: input, onChange: (e) => {
636
493
  setInput(e.target.value);
637
494
  if (textareaRef.current) {
638
495
  textareaRef.current.style.height = 'auto';
@@ -641,10 +498,10 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
641
498
  }, onKeyDown: (e) => {
642
499
  if (e.key === 'Enter' && !e.shiftKey) {
643
500
  e.preventDefault();
644
- if (input.trim() && (!isLoading || promptActive)) {
501
+ if (input.trim()) {
645
502
  handleSubmit(e);
646
503
  }
647
504
  }
648
- }, placeholder: promptActive ? 'Type your reply...' : placeholder, rows: 1, className: "flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent resize-none", style: { minHeight: '40px', maxHeight: '200px' }, disabled: isLoading && !promptActive }), isLoading && !promptActive ? (_jsx("button", { type: "button", onClick: handleCancel, className: "bg-red-600 dark:bg-red-500 hover:bg-red-700 dark:hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium flex-shrink-0", title: "Cancel", children: _jsx("span", { className: "w-5 h-5 flex items-center justify-center", children: "\u23F9" }) })) : (_jsx("button", { type: "submit", disabled: !input.trim(), className: "bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium disabled:cursor-not-allowed flex-shrink-0", children: _jsx(Send, { className: "w-5 h-5" }) }))] }) }), showVoiceComingSoon && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4", children: _jsxs("div", { className: "w-full max-w-sm rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-xl p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Voice Mode" }), _jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-300", children: "Voice mode is coming soon. Chat remains text-only for now." })] }), _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "p-1 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700", "aria-label": "Close voice mode modal", children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "mt-4 flex justify-end", children: _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "px-3 py-1.5 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600", children: "Got it" }) })] }) }))] }));
505
+ }, placeholder: promptActive ? 'Type your reply...' : placeholder, rows: 1, className: "flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent resize-none", style: { minHeight: '40px', maxHeight: '200px' }, disabled: false }), isLoading && !promptActive && (_jsx("button", { type: "button", onClick: handleCancel, className: "bg-red-600 dark:bg-red-500 hover:bg-red-700 dark:hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium flex-shrink-0", title: "Cancel", children: _jsx("span", { className: "w-5 h-5 flex items-center justify-center", children: "\u23F9" }) })), _jsx("button", { type: "submit", disabled: !input.trim(), className: "bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium disabled:cursor-not-allowed flex-shrink-0", children: _jsx(Send, { className: "w-5 h-5" }) })] }) }), showVoiceComingSoon && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4", children: _jsxs("div", { className: "w-full max-w-sm rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-xl p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Voice Mode" }), _jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-300", children: "Voice mode is coming soon. Chat remains text-only for now." })] }), _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "p-1 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700", "aria-label": "Close voice mode modal", children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "mt-4 flex justify-end", children: _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "px-3 py-1.5 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600", children: "Got it" }) })] }) }))] }));
649
506
  }
650
507
  //# sourceMappingURL=ChatInterface.js.map