@devicai/ui 0.10.2 → 0.10.4

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 (42) hide show
  1. package/dist/cjs/api/client.js +6 -1
  2. package/dist/cjs/api/client.js.map +1 -1
  3. package/dist/cjs/components/ChatDrawer/ChatDrawer.js +2 -0
  4. package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
  5. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js +9 -6
  6. package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  7. package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js +1 -1
  8. package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  9. package/dist/cjs/hooks/useDevicChat.js +89 -27
  10. package/dist/cjs/hooks/useDevicChat.js.map +1 -1
  11. package/dist/cjs/hooks/usePolling.js +16 -12
  12. package/dist/cjs/hooks/usePolling.js.map +1 -1
  13. package/dist/cjs/provider/DevicProvider.js +3 -2
  14. package/dist/cjs/provider/DevicProvider.js.map +1 -1
  15. package/dist/cjs/utils/index.js.map +1 -1
  16. package/dist/cjs/utils/logger.js +17 -0
  17. package/dist/cjs/utils/logger.js.map +1 -0
  18. package/dist/esm/api/client.js +6 -1
  19. package/dist/esm/api/client.js.map +1 -1
  20. package/dist/esm/components/ChatDrawer/ChatDrawer.js +2 -0
  21. package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
  22. package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +6 -0
  23. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js +10 -7
  24. package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
  25. package/dist/esm/components/ThreadStateTag/ThreadStateTag.js +1 -1
  26. package/dist/esm/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
  27. package/dist/esm/hooks/useDevicChat.d.ts +6 -0
  28. package/dist/esm/hooks/useDevicChat.js +90 -28
  29. package/dist/esm/hooks/useDevicChat.js.map +1 -1
  30. package/dist/esm/hooks/usePolling.d.ts +5 -0
  31. package/dist/esm/hooks/usePolling.js +17 -13
  32. package/dist/esm/hooks/usePolling.js.map +1 -1
  33. package/dist/esm/provider/DevicProvider.d.ts +1 -1
  34. package/dist/esm/provider/DevicProvider.js +3 -2
  35. package/dist/esm/provider/DevicProvider.js.map +1 -1
  36. package/dist/esm/provider/types.d.ts +9 -0
  37. package/dist/esm/utils/index.d.ts +2 -0
  38. package/dist/esm/utils/index.js.map +1 -1
  39. package/dist/esm/utils/logger.d.ts +10 -0
  40. package/dist/esm/utils/logger.js +15 -0
  41. package/dist/esm/utils/logger.js.map +1 -0
  42. package/package.json +1 -1
@@ -57,6 +57,12 @@ export interface UseDevicChatOptions {
57
57
  * Callback when a new chat is created
58
58
  */
59
59
  onChatCreated?: (chatUid: string) => void;
60
+ /**
61
+ * Enable debug logging to the browser console.
62
+ * Overrides the provider-level debug setting when provided.
63
+ * @default false
64
+ */
65
+ debug?: boolean;
60
66
  }
61
67
  export interface UseDevicChatResult {
62
68
  /**
@@ -1,11 +1,11 @@
1
- import { useState, useRef, useEffect, useCallback } from 'react';
1
+ import { useMemo, useRef, useState, useEffect, useCallback } from 'react';
2
2
  import 'react/jsx-runtime';
3
3
  import { useOptionalDevicContext } from '../provider/DevicContext.js';
4
4
  import { DevicApiClient } from '../api/client.js';
5
5
  import { usePolling } from './usePolling.js';
6
6
  import { useModelInterface } from './useModelInterface.js';
7
+ import { createLogger } from '../utils/logger.js';
7
8
 
8
- console.log('[devic-ui] Version: 0.6.1');
9
9
  /**
10
10
  * Main hook for managing chat with a Devic assistant
11
11
  *
@@ -29,7 +29,7 @@ console.log('[devic-ui] Version: 0.6.1');
29
29
  * ```
30
30
  */
31
31
  function useDevicChat(options) {
32
- const { assistantId, chatUid: initialChatUid, apiKey: propsApiKey, baseUrl: propsBaseUrl, tenantId, tenantMetadata, enabledTools, modelInterfaceTools = [], pollingInterval = 1000, onMessageSent, onMessageReceived, onToolCall, onError, onChatCreated, } = options;
32
+ const { assistantId, chatUid: initialChatUid, apiKey: propsApiKey, baseUrl: propsBaseUrl, tenantId, tenantMetadata, enabledTools, modelInterfaceTools = [], pollingInterval = 1000, onMessageSent, onMessageReceived, onToolCall, onError, onChatCreated, debug: propsDebug, } = options;
33
33
  // Get context (may be null if not wrapped in provider)
34
34
  const context = useOptionalDevicContext();
35
35
  // Resolve configuration
@@ -37,6 +37,10 @@ function useDevicChat(options) {
37
37
  const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';
38
38
  const resolvedTenantId = tenantId || context?.tenantId;
39
39
  const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };
40
+ const debug = propsDebug ?? context?.debug ?? false;
41
+ const log = useMemo(() => createLogger(debug), [debug]);
42
+ const logRef = useRef(log);
43
+ logRef.current = log;
40
44
  // State
41
45
  const [messages, setMessages] = useState([]);
42
46
  const [chatUid, setChatUid] = useState(initialChatUid || null);
@@ -72,6 +76,50 @@ function useDevicChat(options) {
72
76
  clientRef.current.setConfig({ apiKey, baseUrl });
73
77
  }
74
78
  }, [apiKey, baseUrl]);
79
+ // Resume chat state based on realtime status.
80
+ // Called after loading chat history to detect in-progress conversations.
81
+ const resumeFromRealtimeStatus = useCallback(async (targetChatUid) => {
82
+ if (!clientRef.current)
83
+ return;
84
+ try {
85
+ const realtime = await clientRef.current.getRealtimeHistory(assistantId, targetChatUid);
86
+ logRef.current.log('[useDevicChat] resumeFromRealtimeStatus:', realtime.status);
87
+ // Update messages with realtime data (may be fresher than static history)
88
+ if (realtime.chatHistory?.length) {
89
+ setMessages(realtime.chatHistory);
90
+ }
91
+ setStatus(realtime.status);
92
+ if (realtime.status === 'processing') {
93
+ // Chat is still processing — resume polling
94
+ setIsLoading(true);
95
+ setShouldPoll(true);
96
+ }
97
+ else if (realtime.status === 'waiting_for_tool_response') {
98
+ // Chat is waiting for tool response — resume polling to trigger tool handling
99
+ setIsLoading(true);
100
+ setShouldPoll(true);
101
+ }
102
+ else if (realtime.status === 'handed_off') {
103
+ // Chat has an active handoff
104
+ setIsLoading(true);
105
+ setHandedOff(true);
106
+ const subThreadId = realtime.handedOffSubThreadId || null;
107
+ logRef.current.log('[useDevicChat] Resuming handoff state:', { subThreadId });
108
+ if (subThreadId) {
109
+ setHandedOffSubThreadId(subThreadId);
110
+ }
111
+ }
112
+ else {
113
+ // completed or error — just stop
114
+ setIsLoading(false);
115
+ }
116
+ }
117
+ catch (err) {
118
+ // If realtime fetch fails (e.g. chat has no realtime entry), just stay idle
119
+ logRef.current.warn('[useDevicChat] resumeFromRealtimeStatus failed:', err);
120
+ setIsLoading(false);
121
+ }
122
+ }, [assistantId]);
75
123
  // Load initial chat history if chatUid prop is provided
76
124
  // This runs once on mount (or when initialChatUid changes) to fetch existing conversation
77
125
  const initialChatLoadedRef = useRef(false);
@@ -85,41 +133,40 @@ function useDevicChat(options) {
85
133
  const history = await clientRef.current.getChatHistory(assistantId, initialChatUid, { tenantId: resolvedTenantId });
86
134
  setMessages(history.chatContent);
87
135
  setChatUid(initialChatUid);
88
- setStatus('completed');
136
+ // Check realtime status to resume in-progress conversations
137
+ await resumeFromRealtimeStatus(initialChatUid);
89
138
  }
90
139
  catch (err) {
91
140
  const error = err instanceof Error ? err : new Error(String(err));
92
141
  setError(error);
93
142
  onErrorRef.current?.(error);
94
- }
95
- finally {
96
143
  setIsLoading(false);
97
144
  }
98
145
  };
99
146
  loadInitialChat();
100
147
  }
101
- }, [initialChatUid, assistantId, resolvedTenantId]);
148
+ }, [initialChatUid, assistantId, resolvedTenantId, resumeFromRealtimeStatus]);
102
149
  // Model interface hook
103
150
  const { toolSchemas, handleToolCalls, extractPendingToolCalls, } = useModelInterface({
104
151
  tools: modelInterfaceTools,
105
152
  onToolExecute: onToolCall,
106
153
  });
107
154
  // Polling hook - uses callbacks for side effects, return value not needed
108
- console.log('[useDevicChat] Render - shouldPoll:', shouldPoll, 'chatUid:', chatUid);
155
+ logRef.current.log('[useDevicChat] Render - shouldPoll:', shouldPoll, 'chatUid:', chatUid);
109
156
  usePolling(shouldPoll ? chatUid : null, async () => {
110
- console.log('[useDevicChat] fetchFn called, chatUid:', chatUid);
157
+ logRef.current.log('[useDevicChat] fetchFn called, chatUid:', chatUid);
111
158
  if (!clientRef.current || !chatUid) {
112
159
  throw new Error('Cannot poll without client or chatUid');
113
160
  }
114
161
  const result = await clientRef.current.getRealtimeHistory(assistantId, chatUid);
115
- console.log('[useDevicChat] getRealtimeHistory result:', result);
162
+ logRef.current.log('[useDevicChat] getRealtimeHistory result:', result);
116
163
  return result;
117
164
  }, {
118
165
  interval: pollingInterval,
119
166
  enabled: shouldPoll,
120
167
  stopStatuses: ['completed', 'error', 'waiting_for_tool_response', 'handed_off'],
121
168
  onUpdate: async (data) => {
122
- console.log('[useDevicChat] onUpdate called, status:', data.status);
169
+ logRef.current.log('[useDevicChat] onUpdate called, status:', data.status);
123
170
  // Merge realtime data with optimistic messages
124
171
  setMessages((prev) => {
125
172
  const realtimeUIDs = new Set(data.chatHistory.map((m) => m.uid));
@@ -148,7 +195,7 @@ function useDevicChat(options) {
148
195
  }
149
196
  },
150
197
  onStop: (data) => {
151
- console.log('[useDevicChat] onStop called, status:', data?.status);
198
+ logRef.current.log('[useDevicChat] onStop called, status:', data?.status);
152
199
  setShouldPoll(false);
153
200
  if (data?.status === 'error') {
154
201
  setIsLoading(false);
@@ -164,7 +211,7 @@ function useDevicChat(options) {
164
211
  // Set handoff state directly from the realtime status.
165
212
  setHandedOff(true);
166
213
  const subThreadId = data.handedOffSubThreadId || null;
167
- console.log('[useDevicChat] Handoff state set:', { handedOff: true, subThreadId });
214
+ logRef.current.log('[useDevicChat] Handoff state set:', { handedOff: true, subThreadId });
168
215
  if (subThreadId) {
169
216
  setHandedOffSubThreadId(subThreadId);
170
217
  }
@@ -172,12 +219,13 @@ function useDevicChat(options) {
172
219
  // Note: waiting_for_tool_response is handled in onUpdate to avoid double execution
173
220
  },
174
221
  onError: (err) => {
175
- console.error('[useDevicChat] onError called:', err);
222
+ logRef.current.error('[useDevicChat] onError called:', err);
176
223
  setError(err);
177
224
  setIsLoading(false);
178
225
  setShouldPoll(false);
179
226
  onErrorRef.current?.(err);
180
227
  },
228
+ debug,
181
229
  });
182
230
  // Handle pending tool calls from model interface
183
231
  const handlePendingToolCalls = useCallback(async (data) => {
@@ -247,17 +295,17 @@ function useDevicChat(options) {
247
295
  ...(toolSchemas.length > 0 && { tools: toolSchemas }),
248
296
  };
249
297
  // Send message in async mode
250
- console.log('[useDevicChat] Sending message async...');
298
+ logRef.current.log('[useDevicChat] Sending message async...');
251
299
  const response = await clientRef.current.sendMessageAsync(assistantId, dto);
252
- console.log('[useDevicChat] sendMessageAsync response:', response);
300
+ logRef.current.log('[useDevicChat] sendMessageAsync response:', response);
253
301
  // Update chat UID if this is a new chat
254
302
  if (response.chatUid && response.chatUid !== chatUid) {
255
- console.log('[useDevicChat] Setting chatUid:', response.chatUid);
303
+ logRef.current.log('[useDevicChat] Setting chatUid:', response.chatUid);
256
304
  setChatUid(response.chatUid);
257
305
  onChatCreatedRef.current?.(response.chatUid);
258
306
  }
259
307
  // Start polling for results
260
- console.log('[useDevicChat] Setting shouldPoll to true');
308
+ logRef.current.log('[useDevicChat] Setting shouldPoll to true');
261
309
  setShouldPoll(true);
262
310
  }
263
311
  catch (err) {
@@ -280,11 +328,18 @@ function useDevicChat(options) {
280
328
  ]);
281
329
  // Clear chat
282
330
  const clearChat = useCallback(() => {
331
+ setShouldPoll(false);
332
+ setHandedOff(false);
333
+ setHandedOffSubThreadId(null);
334
+ if (handoffPollRef.current) {
335
+ clearInterval(handoffPollRef.current);
336
+ handoffPollRef.current = null;
337
+ }
283
338
  setMessages([]);
284
339
  setChatUid(null);
340
+ setIsLoading(false);
285
341
  setStatus('idle');
286
342
  setError(null);
287
- setShouldPoll(false);
288
343
  }, []);
289
344
  // Load existing chat
290
345
  const loadChat = useCallback(async (loadChatUid) => {
@@ -294,23 +349,30 @@ function useDevicChat(options) {
294
349
  onErrorRef.current?.(err);
295
350
  return;
296
351
  }
352
+ // Reset any active polling/handoff state from previous conversation
353
+ setShouldPoll(false);
354
+ setHandedOff(false);
355
+ setHandedOffSubThreadId(null);
356
+ if (handoffPollRef.current) {
357
+ clearInterval(handoffPollRef.current);
358
+ handoffPollRef.current = null;
359
+ }
297
360
  setIsLoading(true);
298
361
  setError(null);
299
362
  try {
300
363
  const history = await clientRef.current.getChatHistory(assistantId, loadChatUid, { tenantId: resolvedTenantId });
301
364
  setMessages(history.chatContent);
302
365
  setChatUid(loadChatUid);
303
- setStatus('completed');
366
+ // Check realtime status to resume in-progress conversations
367
+ await resumeFromRealtimeStatus(loadChatUid);
304
368
  }
305
369
  catch (err) {
306
370
  const error = err instanceof Error ? err : new Error(String(err));
307
371
  setError(error);
308
372
  onErrorRef.current?.(error);
309
- }
310
- finally {
311
373
  setIsLoading(false);
312
374
  }
313
- }, [assistantId, resolvedTenantId]);
375
+ }, [assistantId, resolvedTenantId, resumeFromRealtimeStatus]);
314
376
  // Handoff polling: while handedOff is true, poll the realtime endpoint every 5s
315
377
  // to detect when the parent thread is no longer in handed_off state.
316
378
  useEffect(() => {
@@ -319,7 +381,7 @@ function useDevicChat(options) {
319
381
  const pollHandoff = async () => {
320
382
  try {
321
383
  const realtime = await clientRef.current.getRealtimeHistory(assistantId, chatUid);
322
- console.log('[useDevicChat] Handoff poll - realtime status:', realtime.status);
384
+ logRef.current.log('[useDevicChat] Handoff poll - realtime status:', realtime.status);
323
385
  if (realtime.status !== 'handed_off') {
324
386
  // Handoff completed — clear handoff state and resume main polling
325
387
  if (handoffPollRef.current) {
@@ -344,7 +406,7 @@ function useDevicChat(options) {
344
406
  }, [handedOff, chatUid, assistantId]);
345
407
  // Called by HandoffSubagentWidget when the subthread reaches a terminal state
346
408
  const onHandoffCompleted = useCallback(() => {
347
- console.log('[useDevicChat] onHandoffCompleted called');
409
+ logRef.current.log('[useDevicChat] onHandoffCompleted called');
348
410
  // Clear the handoff polling
349
411
  if (handoffPollRef.current) {
350
412
  clearInterval(handoffPollRef.current);
@@ -359,14 +421,14 @@ function useDevicChat(options) {
359
421
  // then stops polling and resets loading state.
360
422
  const stopChat = useCallback(async () => {
361
423
  const uid = chatUidRef.current;
362
- console.log('[useDevicChat] stopChat called, chatUid:', uid);
424
+ logRef.current.log('[useDevicChat] stopChat called, chatUid:', uid);
363
425
  if (clientRef.current && uid) {
364
426
  try {
365
427
  await clientRef.current.stopChat(assistantId, uid);
366
- console.log('[useDevicChat] stopChat API call succeeded');
428
+ logRef.current.log('[useDevicChat] stopChat API call succeeded');
367
429
  }
368
430
  catch (err) {
369
- console.warn('[useDevicChat] stopChat API call failed:', err);
431
+ logRef.current.warn('[useDevicChat] stopChat API call failed:', err);
370
432
  }
371
433
  }
372
434
  setShouldPoll(false);
@@ -1 +1 @@
1
- {"version":3,"file":"useDevicChat.js","sources":["../../../src/hooks/useDevicChat.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useRef } from 'react';\nimport { useOptionalDevicContext } from '../provider';\nimport { DevicApiClient } from '../api/client';\nimport { usePolling } from './usePolling';\nimport { useModelInterface } from './useModelInterface';\n\nconsole.log('[devic-ui] Version: 0.6.1');\nimport type {\n ChatMessage,\n ChatFile,\n ModelInterfaceTool,\n RealtimeChatHistory,\n RealtimeStatus,\n} from '../api/types';\n\nexport interface UseDevicChatOptions {\n /**\n * Assistant identifier\n */\n assistantId: string;\n\n /**\n * Existing chat UID to continue a conversation\n */\n chatUid?: string;\n\n /**\n * API key (overrides provider context)\n */\n apiKey?: string;\n\n /**\n * Base URL (overrides provider context)\n */\n baseUrl?: string;\n\n /**\n * Tenant ID for multi-tenant environments\n */\n tenantId?: string;\n\n /**\n * Tenant metadata\n */\n tenantMetadata?: Record<string, any>;\n\n /**\n * Tools enabled from the assistant's configured tool groups\n */\n enabledTools?: string[];\n\n /**\n * Client-side tools for model interface protocol\n */\n modelInterfaceTools?: ModelInterfaceTool[];\n\n /**\n * Polling interval for async mode (ms)\n * @default 1000\n */\n pollingInterval?: number;\n\n /**\n * Callback when a message is sent\n */\n onMessageSent?: (message: ChatMessage) => void;\n\n /**\n * Callback when a message is received\n */\n onMessageReceived?: (message: ChatMessage) => void;\n\n /**\n * Callback when a tool is called\n */\n onToolCall?: (toolName: string, params: any) => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Callback when a new chat is created\n */\n onChatCreated?: (chatUid: string) => void;\n}\n\nexport interface UseDevicChatResult {\n /**\n * Current chat messages\n */\n messages: ChatMessage[];\n\n /**\n * Current chat UID\n */\n chatUid: string | null;\n\n /**\n * Whether a message is being processed\n */\n isLoading: boolean;\n\n /**\n * Current status\n */\n status: RealtimeStatus | 'idle';\n\n /**\n * Last error\n */\n error: Error | null;\n\n /**\n * Whether the assistant has handed off to a subagent\n */\n handedOff: boolean;\n\n /**\n * The subthread ID when a handoff is active\n */\n handedOffSubThreadId: string | null;\n\n /**\n * Send a message\n */\n sendMessage: (\n message: string,\n options?: {\n files?: ChatFile[];\n metadata?: Record<string, any>;\n }\n ) => Promise<void>;\n\n /**\n * Clear the chat and start a new conversation\n */\n clearChat: () => void;\n\n /**\n * Load an existing chat\n */\n loadChat: (chatUid: string) => Promise<void>;\n\n /**\n * Called when the handoff subagent completes.\n * Triggers reload of full chat content.\n */\n onHandoffCompleted: () => void;\n\n /**\n * Stop the current conversation processing (client-side only).\n * Stops polling and resets loading state.\n */\n stopChat: () => void;\n}\n\n/**\n * Main hook for managing chat with a Devic assistant\n *\n * @example\n * ```tsx\n * const {\n * messages,\n * isLoading,\n * sendMessage,\n * } = useDevicChat({\n * assistantId: 'my-assistant',\n * modelInterfaceTools: [\n * {\n * toolName: 'get_user_location',\n * schema: { ... },\n * callback: async () => ({ lat: 40.7, lng: -74.0 })\n * }\n * ],\n * onMessageReceived: (msg) => console.log('Received:', msg),\n * });\n * ```\n */\nexport function useDevicChat(options: UseDevicChatOptions): UseDevicChatResult {\n const {\n assistantId,\n chatUid: initialChatUid,\n apiKey: propsApiKey,\n baseUrl: propsBaseUrl,\n tenantId,\n tenantMetadata,\n enabledTools,\n modelInterfaceTools = [],\n pollingInterval = 1000,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n } = options;\n\n // Get context (may be null if not wrapped in provider)\n const context = useOptionalDevicContext();\n\n // Resolve configuration\n const apiKey = propsApiKey || context?.apiKey;\n const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };\n\n // State\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [chatUid, setChatUid] = useState<string | null>(initialChatUid || null);\n const [isLoading, setIsLoading] = useState(false);\n const [status, setStatus] = useState<RealtimeStatus | 'idle'>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Handoff state\n const [handedOff, setHandedOff] = useState(false);\n const [handedOffSubThreadId, setHandedOffSubThreadId] = useState<string | null>(null);\n const handoffPollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Polling state\n const [shouldPoll, setShouldPoll] = useState(false);\n\n // Keep a ref to chatUid so async callbacks always read the latest value\n const chatUidRef = useRef(chatUid);\n chatUidRef.current = chatUid;\n\n // Refs for callbacks\n const onMessageReceivedRef = useRef(onMessageReceived);\n const onErrorRef = useRef(onError);\n const onChatCreatedRef = useRef(onChatCreated);\n\n useEffect(() => {\n onMessageReceivedRef.current = onMessageReceived;\n onErrorRef.current = onError;\n onChatCreatedRef.current = onChatCreated;\n });\n\n // Create API client\n const clientRef = useRef<DevicApiClient | null>(null);\n if (!clientRef.current && apiKey) {\n clientRef.current = new DevicApiClient({ apiKey, baseUrl });\n }\n\n // Update client config if it changes\n useEffect(() => {\n if (clientRef.current && apiKey) {\n clientRef.current.setConfig({ apiKey, baseUrl });\n }\n }, [apiKey, baseUrl]);\n\n // Load initial chat history if chatUid prop is provided\n // This runs once on mount (or when initialChatUid changes) to fetch existing conversation\n const initialChatLoadedRef = useRef(false);\n useEffect(() => {\n if (initialChatUid && clientRef.current && !initialChatLoadedRef.current) {\n initialChatLoadedRef.current = true;\n\n const loadInitialChat = async () => {\n setIsLoading(true);\n setError(null);\n try {\n const history = await clientRef.current!.getChatHistory(\n assistantId,\n initialChatUid,\n { tenantId: resolvedTenantId }\n );\n setMessages(history.chatContent);\n setChatUid(initialChatUid);\n setStatus('completed');\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n } finally {\n setIsLoading(false);\n }\n };\n\n loadInitialChat();\n }\n }, [initialChatUid, assistantId, resolvedTenantId]);\n\n // Model interface hook\n const {\n toolSchemas,\n handleToolCalls,\n extractPendingToolCalls,\n } = useModelInterface({\n tools: modelInterfaceTools,\n onToolExecute: onToolCall,\n });\n\n // Polling hook - uses callbacks for side effects, return value not needed\n console.log('[useDevicChat] Render - shouldPoll:', shouldPoll, 'chatUid:', chatUid);\n usePolling(\n shouldPoll ? chatUid : null,\n async () => {\n console.log('[useDevicChat] fetchFn called, chatUid:', chatUid);\n if (!clientRef.current || !chatUid) {\n throw new Error('Cannot poll without client or chatUid');\n }\n const result = await clientRef.current.getRealtimeHistory(assistantId, chatUid);\n console.log('[useDevicChat] getRealtimeHistory result:', result);\n return result;\n },\n {\n interval: pollingInterval,\n enabled: shouldPoll,\n stopStatuses: ['completed', 'error', 'waiting_for_tool_response', 'handed_off'],\n onUpdate: async (data: RealtimeChatHistory) => {\n console.log('[useDevicChat] onUpdate called, status:', data.status);\n\n // Merge realtime data with optimistic messages\n setMessages((prev) => {\n const realtimeUIDs = new Set(data.chatHistory.map((m) => m.uid));\n const realtimeUserMessages = new Set(\n data.chatHistory\n .filter((m) => m.role === 'user')\n .map((m) => m.content?.message)\n );\n\n // Keep optimistic messages not yet in realtime data\n const optimistic = prev.filter((m) => {\n if (realtimeUIDs.has(m.uid)) return false;\n if (m.role === 'user' && realtimeUserMessages.has(m.content?.message)) return false;\n return true;\n });\n\n return [...data.chatHistory, ...optimistic];\n });\n setStatus(data.status);\n\n // Notify about new messages\n const lastMessage = data.chatHistory[data.chatHistory.length - 1];\n if (lastMessage && lastMessage.role === 'assistant') {\n onMessageReceivedRef.current?.(lastMessage);\n }\n\n // Handle model interface - check for pending tool calls\n if (data.status === 'waiting_for_tool_response' || data.pendingToolCalls?.length) {\n await handlePendingToolCalls(data);\n }\n },\n onStop: (data) => {\n console.log('[useDevicChat] onStop called, status:', data?.status);\n setShouldPoll(false);\n\n if (data?.status === 'error') {\n setIsLoading(false);\n const err = new Error('Chat processing failed');\n setError(err);\n onErrorRef.current?.(err);\n } else if (data?.status === 'completed') {\n setIsLoading(false);\n } else if (data?.status === 'handed_off') {\n // Subagent is working — keep isLoading true so the UI stays in loading state.\n // Set handoff state directly from the realtime status.\n setHandedOff(true);\n\n const subThreadId = data.handedOffSubThreadId || null;\n console.log('[useDevicChat] Handoff state set:', { handedOff: true, subThreadId });\n if (subThreadId) {\n setHandedOffSubThreadId(subThreadId);\n }\n }\n // Note: waiting_for_tool_response is handled in onUpdate to avoid double execution\n },\n onError: (err) => {\n console.error('[useDevicChat] onError called:', err);\n setError(err);\n setIsLoading(false);\n setShouldPoll(false);\n onErrorRef.current?.(err);\n },\n }\n );\n\n // Handle pending tool calls from model interface\n const handlePendingToolCalls = useCallback(\n async (data: RealtimeChatHistory) => {\n if (!clientRef.current || !chatUid) return;\n\n // Get pending tool calls\n const pendingCalls = data.pendingToolCalls || extractPendingToolCalls(data.chatHistory);\n\n if (pendingCalls.length === 0) return;\n\n try {\n // Execute client-side tools\n const responses = await handleToolCalls(pendingCalls);\n\n if (responses.length > 0) {\n // Send tool responses back to the API\n await clientRef.current.sendToolResponses(assistantId, chatUid, responses);\n\n // Resume polling\n setShouldPoll(true);\n setIsLoading(true);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n }\n },\n [chatUid, assistantId, handleToolCalls, extractPendingToolCalls]\n );\n\n // Send a message\n const sendMessage = useCallback(\n async (\n message: string,\n sendOptions?: {\n files?: ChatFile[];\n metadata?: Record<string, any>;\n }\n ) => {\n if (!clientRef.current) {\n const err = new Error(\n 'API client not configured. Please provide an API key.'\n );\n setError(err);\n onErrorRef.current?.(err);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n setStatus('processing');\n\n // Add user message optimistically\n const userMessage: ChatMessage = {\n uid: `temp-${Date.now()}`,\n role: 'user',\n content: {\n message,\n files: sendOptions?.files?.map((f) => ({\n name: f.name,\n url: f.downloadUrl || '',\n type: f.fileType || 'other',\n })),\n },\n timestamp: Date.now(),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n onMessageSent?.(userMessage);\n\n try {\n // Build request DTO\n const dto = {\n message,\n chatUid: chatUid || undefined,\n files: sendOptions?.files,\n metadata: {\n ...resolvedTenantMetadata,\n ...sendOptions?.metadata,\n },\n tenantId: resolvedTenantId,\n enabledTools,\n // Include model interface tools if any\n ...(toolSchemas.length > 0 && { tools: toolSchemas }),\n };\n\n // Send message in async mode\n console.log('[useDevicChat] Sending message async...');\n const response = await clientRef.current.sendMessageAsync(assistantId, dto);\n console.log('[useDevicChat] sendMessageAsync response:', response);\n\n // Update chat UID if this is a new chat\n if (response.chatUid && response.chatUid !== chatUid) {\n console.log('[useDevicChat] Setting chatUid:', response.chatUid);\n setChatUid(response.chatUid);\n onChatCreatedRef.current?.(response.chatUid);\n }\n\n // Start polling for results\n console.log('[useDevicChat] Setting shouldPoll to true');\n setShouldPoll(true);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setIsLoading(false);\n setStatus('error');\n onErrorRef.current?.(error);\n\n // Remove optimistic user message on error\n setMessages((prev) => prev.filter((m) => m.uid !== userMessage.uid));\n }\n },\n [\n chatUid,\n assistantId,\n enabledTools,\n resolvedTenantId,\n resolvedTenantMetadata,\n toolSchemas,\n onMessageSent,\n ]\n );\n\n // Clear chat\n const clearChat = useCallback(() => {\n setMessages([]);\n setChatUid(null);\n setStatus('idle');\n setError(null);\n setShouldPoll(false);\n }, []);\n\n // Load existing chat\n const loadChat = useCallback(\n async (loadChatUid: string) => {\n if (!clientRef.current) {\n const err = new Error('API client not configured');\n setError(err);\n onErrorRef.current?.(err);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const history = await clientRef.current.getChatHistory(\n assistantId,\n loadChatUid,\n { tenantId: resolvedTenantId }\n );\n\n setMessages(history.chatContent);\n setChatUid(loadChatUid);\n setStatus('completed');\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n } finally {\n setIsLoading(false);\n }\n },\n [assistantId, resolvedTenantId]\n );\n\n // Handoff polling: while handedOff is true, poll the realtime endpoint every 5s\n // to detect when the parent thread is no longer in handed_off state.\n useEffect(() => {\n if (!handedOff || !chatUid || !clientRef.current) return;\n\n const pollHandoff = async () => {\n try {\n const realtime = await clientRef.current!.getRealtimeHistory(assistantId, chatUid!);\n console.log('[useDevicChat] Handoff poll - realtime status:', realtime.status);\n if (realtime.status !== 'handed_off') {\n // Handoff completed — clear handoff state and resume main polling\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n // Resume main polling to pick up the parent thread's continuation\n setShouldPoll(true);\n }\n } catch {}\n };\n\n handoffPollRef.current = setInterval(pollHandoff, 5000);\n return () => {\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n };\n }, [handedOff, chatUid, assistantId]);\n\n // Called by HandoffSubagentWidget when the subthread reaches a terminal state\n const onHandoffCompleted = useCallback(() => {\n console.log('[useDevicChat] onHandoffCompleted called');\n // Clear the handoff polling\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n // Clear handoff state and resume main polling\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n setShouldPoll(true);\n }, []);\n\n // Stop current conversation — calls the server-side stop endpoint\n // then stops polling and resets loading state.\n const stopChat = useCallback(async () => {\n const uid = chatUidRef.current;\n console.log('[useDevicChat] stopChat called, chatUid:', uid);\n if (clientRef.current && uid) {\n try {\n await clientRef.current.stopChat(assistantId, uid);\n console.log('[useDevicChat] stopChat API call succeeded');\n } catch (err) {\n console.warn('[useDevicChat] stopChat API call failed:', err);\n }\n }\n setShouldPoll(false);\n setIsLoading(false);\n setStatus('idle');\n }, [assistantId]);\n\n return {\n messages,\n chatUid,\n isLoading,\n status,\n error,\n handedOff,\n handedOffSubThreadId,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useDevicChat.js","sources":["../../../src/hooks/useDevicChat.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useRef, useMemo } from 'react';\nimport { useOptionalDevicContext } from '../provider';\nimport { DevicApiClient } from '../api/client';\nimport { usePolling } from './usePolling';\nimport { useModelInterface } from './useModelInterface';\nimport { createLogger } from '../utils/logger';\nimport type {\n ChatMessage,\n ChatFile,\n ModelInterfaceTool,\n RealtimeChatHistory,\n RealtimeStatus,\n} from '../api/types';\n\nexport interface UseDevicChatOptions {\n /**\n * Assistant identifier\n */\n assistantId: string;\n\n /**\n * Existing chat UID to continue a conversation\n */\n chatUid?: string;\n\n /**\n * API key (overrides provider context)\n */\n apiKey?: string;\n\n /**\n * Base URL (overrides provider context)\n */\n baseUrl?: string;\n\n /**\n * Tenant ID for multi-tenant environments\n */\n tenantId?: string;\n\n /**\n * Tenant metadata\n */\n tenantMetadata?: Record<string, any>;\n\n /**\n * Tools enabled from the assistant's configured tool groups\n */\n enabledTools?: string[];\n\n /**\n * Client-side tools for model interface protocol\n */\n modelInterfaceTools?: ModelInterfaceTool[];\n\n /**\n * Polling interval for async mode (ms)\n * @default 1000\n */\n pollingInterval?: number;\n\n /**\n * Callback when a message is sent\n */\n onMessageSent?: (message: ChatMessage) => void;\n\n /**\n * Callback when a message is received\n */\n onMessageReceived?: (message: ChatMessage) => void;\n\n /**\n * Callback when a tool is called\n */\n onToolCall?: (toolName: string, params: any) => void;\n\n /**\n * Callback when an error occurs\n */\n onError?: (error: Error) => void;\n\n /**\n * Callback when a new chat is created\n */\n onChatCreated?: (chatUid: string) => void;\n\n /**\n * Enable debug logging to the browser console.\n * Overrides the provider-level debug setting when provided.\n * @default false\n */\n debug?: boolean;\n}\n\nexport interface UseDevicChatResult {\n /**\n * Current chat messages\n */\n messages: ChatMessage[];\n\n /**\n * Current chat UID\n */\n chatUid: string | null;\n\n /**\n * Whether a message is being processed\n */\n isLoading: boolean;\n\n /**\n * Current status\n */\n status: RealtimeStatus | 'idle';\n\n /**\n * Last error\n */\n error: Error | null;\n\n /**\n * Whether the assistant has handed off to a subagent\n */\n handedOff: boolean;\n\n /**\n * The subthread ID when a handoff is active\n */\n handedOffSubThreadId: string | null;\n\n /**\n * Send a message\n */\n sendMessage: (\n message: string,\n options?: {\n files?: ChatFile[];\n metadata?: Record<string, any>;\n }\n ) => Promise<void>;\n\n /**\n * Clear the chat and start a new conversation\n */\n clearChat: () => void;\n\n /**\n * Load an existing chat\n */\n loadChat: (chatUid: string) => Promise<void>;\n\n /**\n * Called when the handoff subagent completes.\n * Triggers reload of full chat content.\n */\n onHandoffCompleted: () => void;\n\n /**\n * Stop the current conversation processing (client-side only).\n * Stops polling and resets loading state.\n */\n stopChat: () => void;\n}\n\n/**\n * Main hook for managing chat with a Devic assistant\n *\n * @example\n * ```tsx\n * const {\n * messages,\n * isLoading,\n * sendMessage,\n * } = useDevicChat({\n * assistantId: 'my-assistant',\n * modelInterfaceTools: [\n * {\n * toolName: 'get_user_location',\n * schema: { ... },\n * callback: async () => ({ lat: 40.7, lng: -74.0 })\n * }\n * ],\n * onMessageReceived: (msg) => console.log('Received:', msg),\n * });\n * ```\n */\nexport function useDevicChat(options: UseDevicChatOptions): UseDevicChatResult {\n const {\n assistantId,\n chatUid: initialChatUid,\n apiKey: propsApiKey,\n baseUrl: propsBaseUrl,\n tenantId,\n tenantMetadata,\n enabledTools,\n modelInterfaceTools = [],\n pollingInterval = 1000,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n debug: propsDebug,\n } = options;\n\n // Get context (may be null if not wrapped in provider)\n const context = useOptionalDevicContext();\n\n // Resolve configuration\n const apiKey = propsApiKey || context?.apiKey;\n const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };\n const debug = propsDebug ?? context?.debug ?? false;\n const log = useMemo(() => createLogger(debug), [debug]);\n const logRef = useRef(log);\n logRef.current = log;\n\n // State\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [chatUid, setChatUid] = useState<string | null>(initialChatUid || null);\n const [isLoading, setIsLoading] = useState(false);\n const [status, setStatus] = useState<RealtimeStatus | 'idle'>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Handoff state\n const [handedOff, setHandedOff] = useState(false);\n const [handedOffSubThreadId, setHandedOffSubThreadId] = useState<string | null>(null);\n const handoffPollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Polling state\n const [shouldPoll, setShouldPoll] = useState(false);\n\n // Keep a ref to chatUid so async callbacks always read the latest value\n const chatUidRef = useRef(chatUid);\n chatUidRef.current = chatUid;\n\n // Refs for callbacks\n const onMessageReceivedRef = useRef(onMessageReceived);\n const onErrorRef = useRef(onError);\n const onChatCreatedRef = useRef(onChatCreated);\n\n useEffect(() => {\n onMessageReceivedRef.current = onMessageReceived;\n onErrorRef.current = onError;\n onChatCreatedRef.current = onChatCreated;\n });\n\n // Create API client\n const clientRef = useRef<DevicApiClient | null>(null);\n if (!clientRef.current && apiKey) {\n clientRef.current = new DevicApiClient({ apiKey, baseUrl });\n }\n\n // Update client config if it changes\n useEffect(() => {\n if (clientRef.current && apiKey) {\n clientRef.current.setConfig({ apiKey, baseUrl });\n }\n }, [apiKey, baseUrl]);\n\n // Resume chat state based on realtime status.\n // Called after loading chat history to detect in-progress conversations.\n const resumeFromRealtimeStatus = useCallback(\n async (targetChatUid: string) => {\n if (!clientRef.current) return;\n try {\n const realtime = await clientRef.current.getRealtimeHistory(assistantId, targetChatUid);\n logRef.current.log('[useDevicChat] resumeFromRealtimeStatus:', realtime.status);\n\n // Update messages with realtime data (may be fresher than static history)\n if (realtime.chatHistory?.length) {\n setMessages(realtime.chatHistory);\n }\n setStatus(realtime.status);\n\n if (realtime.status === 'processing') {\n // Chat is still processing — resume polling\n setIsLoading(true);\n setShouldPoll(true);\n } else if (realtime.status === 'waiting_for_tool_response') {\n // Chat is waiting for tool response — resume polling to trigger tool handling\n setIsLoading(true);\n setShouldPoll(true);\n } else if (realtime.status === 'handed_off') {\n // Chat has an active handoff\n setIsLoading(true);\n setHandedOff(true);\n const subThreadId = realtime.handedOffSubThreadId || null;\n logRef.current.log('[useDevicChat] Resuming handoff state:', { subThreadId });\n if (subThreadId) {\n setHandedOffSubThreadId(subThreadId);\n }\n } else {\n // completed or error — just stop\n setIsLoading(false);\n }\n } catch (err) {\n // If realtime fetch fails (e.g. chat has no realtime entry), just stay idle\n logRef.current.warn('[useDevicChat] resumeFromRealtimeStatus failed:', err);\n setIsLoading(false);\n }\n },\n [assistantId]\n );\n\n // Load initial chat history if chatUid prop is provided\n // This runs once on mount (or when initialChatUid changes) to fetch existing conversation\n const initialChatLoadedRef = useRef(false);\n useEffect(() => {\n if (initialChatUid && clientRef.current && !initialChatLoadedRef.current) {\n initialChatLoadedRef.current = true;\n\n const loadInitialChat = async () => {\n setIsLoading(true);\n setError(null);\n try {\n const history = await clientRef.current!.getChatHistory(\n assistantId,\n initialChatUid,\n { tenantId: resolvedTenantId }\n );\n setMessages(history.chatContent);\n setChatUid(initialChatUid);\n\n // Check realtime status to resume in-progress conversations\n await resumeFromRealtimeStatus(initialChatUid);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n setIsLoading(false);\n }\n };\n\n loadInitialChat();\n }\n }, [initialChatUid, assistantId, resolvedTenantId, resumeFromRealtimeStatus]);\n\n // Model interface hook\n const {\n toolSchemas,\n handleToolCalls,\n extractPendingToolCalls,\n } = useModelInterface({\n tools: modelInterfaceTools,\n onToolExecute: onToolCall,\n });\n\n // Polling hook - uses callbacks for side effects, return value not needed\n logRef.current.log('[useDevicChat] Render - shouldPoll:', shouldPoll, 'chatUid:', chatUid);\n usePolling(\n shouldPoll ? chatUid : null,\n async () => {\n logRef.current.log('[useDevicChat] fetchFn called, chatUid:', chatUid);\n if (!clientRef.current || !chatUid) {\n throw new Error('Cannot poll without client or chatUid');\n }\n const result = await clientRef.current.getRealtimeHistory(assistantId, chatUid);\n logRef.current.log('[useDevicChat] getRealtimeHistory result:', result);\n return result;\n },\n {\n interval: pollingInterval,\n enabled: shouldPoll,\n stopStatuses: ['completed', 'error', 'waiting_for_tool_response', 'handed_off'],\n onUpdate: async (data: RealtimeChatHistory) => {\n logRef.current.log('[useDevicChat] onUpdate called, status:', data.status);\n\n // Merge realtime data with optimistic messages\n setMessages((prev) => {\n const realtimeUIDs = new Set(data.chatHistory.map((m) => m.uid));\n const realtimeUserMessages = new Set(\n data.chatHistory\n .filter((m) => m.role === 'user')\n .map((m) => m.content?.message)\n );\n\n // Keep optimistic messages not yet in realtime data\n const optimistic = prev.filter((m) => {\n if (realtimeUIDs.has(m.uid)) return false;\n if (m.role === 'user' && realtimeUserMessages.has(m.content?.message)) return false;\n return true;\n });\n\n return [...data.chatHistory, ...optimistic];\n });\n setStatus(data.status);\n\n // Notify about new messages\n const lastMessage = data.chatHistory[data.chatHistory.length - 1];\n if (lastMessage && lastMessage.role === 'assistant') {\n onMessageReceivedRef.current?.(lastMessage);\n }\n\n // Handle model interface - check for pending tool calls\n if (data.status === 'waiting_for_tool_response' || data.pendingToolCalls?.length) {\n await handlePendingToolCalls(data);\n }\n },\n onStop: (data) => {\n logRef.current.log('[useDevicChat] onStop called, status:', data?.status);\n setShouldPoll(false);\n\n if (data?.status === 'error') {\n setIsLoading(false);\n const err = new Error('Chat processing failed');\n setError(err);\n onErrorRef.current?.(err);\n } else if (data?.status === 'completed') {\n setIsLoading(false);\n } else if (data?.status === 'handed_off') {\n // Subagent is working — keep isLoading true so the UI stays in loading state.\n // Set handoff state directly from the realtime status.\n setHandedOff(true);\n\n const subThreadId = data.handedOffSubThreadId || null;\n logRef.current.log('[useDevicChat] Handoff state set:', { handedOff: true, subThreadId });\n if (subThreadId) {\n setHandedOffSubThreadId(subThreadId);\n }\n }\n // Note: waiting_for_tool_response is handled in onUpdate to avoid double execution\n },\n onError: (err) => {\n logRef.current.error('[useDevicChat] onError called:', err);\n setError(err);\n setIsLoading(false);\n setShouldPoll(false);\n onErrorRef.current?.(err);\n },\n debug,\n }\n );\n\n // Handle pending tool calls from model interface\n const handlePendingToolCalls = useCallback(\n async (data: RealtimeChatHistory) => {\n if (!clientRef.current || !chatUid) return;\n\n // Get pending tool calls\n const pendingCalls = data.pendingToolCalls || extractPendingToolCalls(data.chatHistory);\n\n if (pendingCalls.length === 0) return;\n\n try {\n // Execute client-side tools\n const responses = await handleToolCalls(pendingCalls);\n\n if (responses.length > 0) {\n // Send tool responses back to the API\n await clientRef.current.sendToolResponses(assistantId, chatUid, responses);\n\n // Resume polling\n setShouldPoll(true);\n setIsLoading(true);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n }\n },\n [chatUid, assistantId, handleToolCalls, extractPendingToolCalls]\n );\n\n // Send a message\n const sendMessage = useCallback(\n async (\n message: string,\n sendOptions?: {\n files?: ChatFile[];\n metadata?: Record<string, any>;\n }\n ) => {\n if (!clientRef.current) {\n const err = new Error(\n 'API client not configured. Please provide an API key.'\n );\n setError(err);\n onErrorRef.current?.(err);\n return;\n }\n\n setIsLoading(true);\n setError(null);\n setStatus('processing');\n\n // Add user message optimistically\n const userMessage: ChatMessage = {\n uid: `temp-${Date.now()}`,\n role: 'user',\n content: {\n message,\n files: sendOptions?.files?.map((f) => ({\n name: f.name,\n url: f.downloadUrl || '',\n type: f.fileType || 'other',\n })),\n },\n timestamp: Date.now(),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n onMessageSent?.(userMessage);\n\n try {\n // Build request DTO\n const dto = {\n message,\n chatUid: chatUid || undefined,\n files: sendOptions?.files,\n metadata: {\n ...resolvedTenantMetadata,\n ...sendOptions?.metadata,\n },\n tenantId: resolvedTenantId,\n enabledTools,\n // Include model interface tools if any\n ...(toolSchemas.length > 0 && { tools: toolSchemas }),\n };\n\n // Send message in async mode\n logRef.current.log('[useDevicChat] Sending message async...');\n const response = await clientRef.current.sendMessageAsync(assistantId, dto);\n logRef.current.log('[useDevicChat] sendMessageAsync response:', response);\n\n // Update chat UID if this is a new chat\n if (response.chatUid && response.chatUid !== chatUid) {\n logRef.current.log('[useDevicChat] Setting chatUid:', response.chatUid);\n setChatUid(response.chatUid);\n onChatCreatedRef.current?.(response.chatUid);\n }\n\n // Start polling for results\n logRef.current.log('[useDevicChat] Setting shouldPoll to true');\n setShouldPoll(true);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n setIsLoading(false);\n setStatus('error');\n onErrorRef.current?.(error);\n\n // Remove optimistic user message on error\n setMessages((prev) => prev.filter((m) => m.uid !== userMessage.uid));\n }\n },\n [\n chatUid,\n assistantId,\n enabledTools,\n resolvedTenantId,\n resolvedTenantMetadata,\n toolSchemas,\n onMessageSent,\n ]\n );\n\n // Clear chat\n const clearChat = useCallback(() => {\n setShouldPoll(false);\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n setMessages([]);\n setChatUid(null);\n setIsLoading(false);\n setStatus('idle');\n setError(null);\n }, []);\n\n // Load existing chat\n const loadChat = useCallback(\n async (loadChatUid: string) => {\n if (!clientRef.current) {\n const err = new Error('API client not configured');\n setError(err);\n onErrorRef.current?.(err);\n return;\n }\n\n // Reset any active polling/handoff state from previous conversation\n setShouldPoll(false);\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n\n setIsLoading(true);\n setError(null);\n\n try {\n const history = await clientRef.current.getChatHistory(\n assistantId,\n loadChatUid,\n { tenantId: resolvedTenantId }\n );\n\n setMessages(history.chatContent);\n setChatUid(loadChatUid);\n\n // Check realtime status to resume in-progress conversations\n await resumeFromRealtimeStatus(loadChatUid);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n setIsLoading(false);\n }\n },\n [assistantId, resolvedTenantId, resumeFromRealtimeStatus]\n );\n\n // Handoff polling: while handedOff is true, poll the realtime endpoint every 5s\n // to detect when the parent thread is no longer in handed_off state.\n useEffect(() => {\n if (!handedOff || !chatUid || !clientRef.current) return;\n\n const pollHandoff = async () => {\n try {\n const realtime = await clientRef.current!.getRealtimeHistory(assistantId, chatUid!);\n logRef.current.log('[useDevicChat] Handoff poll - realtime status:', realtime.status);\n if (realtime.status !== 'handed_off') {\n // Handoff completed — clear handoff state and resume main polling\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n // Resume main polling to pick up the parent thread's continuation\n setShouldPoll(true);\n }\n } catch {}\n };\n\n handoffPollRef.current = setInterval(pollHandoff, 5000);\n return () => {\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n };\n }, [handedOff, chatUid, assistantId]);\n\n // Called by HandoffSubagentWidget when the subthread reaches a terminal state\n const onHandoffCompleted = useCallback(() => {\n logRef.current.log('[useDevicChat] onHandoffCompleted called');\n // Clear the handoff polling\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n // Clear handoff state and resume main polling\n setHandedOff(false);\n setHandedOffSubThreadId(null);\n setShouldPoll(true);\n }, []);\n\n // Stop current conversation — calls the server-side stop endpoint\n // then stops polling and resets loading state.\n const stopChat = useCallback(async () => {\n const uid = chatUidRef.current;\n logRef.current.log('[useDevicChat] stopChat called, chatUid:', uid);\n if (clientRef.current && uid) {\n try {\n await clientRef.current.stopChat(assistantId, uid);\n logRef.current.log('[useDevicChat] stopChat API call succeeded');\n } catch (err) {\n logRef.current.warn('[useDevicChat] stopChat API call failed:', err);\n }\n }\n setShouldPoll(false);\n setIsLoading(false);\n setStatus('idle');\n }, [assistantId]);\n\n return {\n messages,\n chatUid,\n isLoading,\n status,\n error,\n handedOff,\n handedOffSubThreadId,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -27,6 +27,11 @@ export interface UsePollingOptions {
27
27
  * Callback on poll error
28
28
  */
29
29
  onError?: (error: Error) => void;
30
+ /**
31
+ * Enable debug logging
32
+ * @default false
33
+ */
34
+ debug?: boolean;
30
35
  }
31
36
  export interface UsePollingResult {
32
37
  /**
@@ -1,4 +1,5 @@
1
- import { useState, useRef, useEffect, useCallback } from 'react';
1
+ import { useMemo, useRef, useState, useEffect, useCallback } from 'react';
2
+ import { createLogger } from '../utils/logger.js';
2
3
 
3
4
  /**
4
5
  * Hook for polling real-time chat history
@@ -8,7 +9,10 @@ import { useState, useRef, useEffect, useCallback } from 'react';
8
9
  * @param options - Polling options
9
10
  */
10
11
  function usePolling(chatUid, fetchFn, options = {}) {
11
- const { interval = 1000, enabled = true, stopStatuses = ['completed', 'error'], onStop, onUpdate, onError, } = options;
12
+ const { interval = 1000, enabled = true, stopStatuses = ['completed', 'error'], onStop, onUpdate, onError, debug = false, } = options;
13
+ const log = useMemo(() => createLogger(debug), [debug]);
14
+ const logRef = useRef(log);
15
+ logRef.current = log;
12
16
  const [data, setData] = useState(null);
13
17
  const [isPolling, setIsPolling] = useState(false);
14
18
  const [error, setError] = useState(null);
@@ -38,13 +42,13 @@ function usePolling(chatUid, fetchFn, options = {}) {
38
42
  isPollingRef.current = false;
39
43
  }, []);
40
44
  const fetchData = useCallback(async () => {
41
- console.log('[usePolling] fetchData called, isMounted:', isMountedRef.current);
45
+ logRef.current.log('[usePolling] fetchData called, isMounted:', isMountedRef.current);
42
46
  if (!isMountedRef.current)
43
47
  return;
44
48
  try {
45
- console.log('[usePolling] Fetching...');
49
+ logRef.current.log('[usePolling] Fetching...');
46
50
  const result = await fetchFnRef.current();
47
- console.log('[usePolling] Fetch result:', { status: result.status, messageCount: result.chatHistory?.length });
51
+ logRef.current.log('[usePolling] Fetch result:', { status: result.status, messageCount: result.chatHistory?.length });
48
52
  if (!isMountedRef.current)
49
53
  return;
50
54
  setData(result);
@@ -52,16 +56,16 @@ function usePolling(chatUid, fetchFn, options = {}) {
52
56
  onUpdateRef.current?.(result);
53
57
  // Check if we should stop polling
54
58
  const shouldStop = stopStatusesRef.current.includes(result.status);
55
- console.log('[usePolling] Should stop?', shouldStop, 'stopStatuses:', stopStatusesRef.current, 'current status:', result.status);
59
+ logRef.current.log('[usePolling] Should stop?', shouldStop, 'stopStatuses:', stopStatusesRef.current, 'current status:', result.status);
56
60
  if (shouldStop) {
57
- console.log('[usePolling] Stopping polling due to status:', result.status);
61
+ logRef.current.log('[usePolling] Stopping polling due to status:', result.status);
58
62
  clearPolling();
59
63
  setIsPolling(false);
60
64
  onStopRef.current?.(result);
61
65
  }
62
66
  }
63
67
  catch (err) {
64
- console.error('[usePolling] Fetch error:', err);
68
+ logRef.current.error('[usePolling] Fetch error:', err);
65
69
  if (!isMountedRef.current)
66
70
  return;
67
71
  const error = err instanceof Error ? err : new Error(String(err));
@@ -92,9 +96,9 @@ function usePolling(chatUid, fetchFn, options = {}) {
92
96
  }, [fetchData]);
93
97
  // Auto-start polling when enabled and chatUid is set
94
98
  useEffect(() => {
95
- console.log('[usePolling] Auto-start effect triggered:', { enabled, chatUid, isPollingRef: isPollingRef.current, intervalRef: !!intervalRef.current });
99
+ logRef.current.log('[usePolling] Auto-start effect triggered:', { enabled, chatUid, isPollingRef: isPollingRef.current, intervalRef: !!intervalRef.current });
96
100
  if (!enabled || !chatUid) {
97
- console.log('[usePolling] Not enabled or no chatUid, stopping if active');
101
+ logRef.current.log('[usePolling] Not enabled or no chatUid, stopping if active');
98
102
  // Stop polling if disabled or no chatUid
99
103
  if (isPollingRef.current) {
100
104
  clearPolling();
@@ -104,7 +108,7 @@ function usePolling(chatUid, fetchFn, options = {}) {
104
108
  }
105
109
  // Start polling if not already polling
106
110
  if (!isPollingRef.current) {
107
- console.log('[usePolling] Starting polling, interval:', intervalValueRef.current);
111
+ logRef.current.log('[usePolling] Starting polling, interval:', intervalValueRef.current);
108
112
  isPollingRef.current = true;
109
113
  setIsPolling(true);
110
114
  setError(null);
@@ -112,10 +116,10 @@ function usePolling(chatUid, fetchFn, options = {}) {
112
116
  fetchData();
113
117
  // Set up interval
114
118
  intervalRef.current = setInterval(fetchData, intervalValueRef.current);
115
- console.log('[usePolling] Interval set:', intervalRef.current);
119
+ logRef.current.log('[usePolling] Interval set:', intervalRef.current);
116
120
  }
117
121
  else {
118
- console.log('[usePolling] Already polling, skipping start');
122
+ logRef.current.log('[usePolling] Already polling, skipping start');
119
123
  }
120
124
  // Only cleanup on unmount, not on every dependency change
121
125
  }, [enabled, chatUid, fetchData, clearPolling]);
@@ -1 +1 @@
1
- {"version":3,"file":"usePolling.js","sources":["../../../src/hooks/usePolling.ts"],"sourcesContent":["import { useState, useEffect, useRef, useCallback } from 'react';\nimport type { RealtimeChatHistory, RealtimeStatus } from '../api/types';\n\nexport interface UsePollingOptions {\n /**\n * Polling interval in milliseconds\n * @default 1000\n */\n interval?: number;\n\n /**\n * Whether polling is enabled\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Statuses that should stop polling\n * @default ['completed', 'error']\n */\n stopStatuses?: RealtimeStatus[];\n\n /**\n * Callback when polling stops\n */\n onStop?: (data: RealtimeChatHistory | null) => void;\n\n /**\n * Callback on each poll update\n */\n onUpdate?: (data: RealtimeChatHistory) => void;\n\n /**\n * Callback on poll error\n */\n onError?: (error: Error) => void;\n}\n\nexport interface UsePollingResult {\n /**\n * Current polling data\n */\n data: RealtimeChatHistory | null;\n\n /**\n * Whether polling is currently active\n */\n isPolling: boolean;\n\n /**\n * Last error that occurred\n */\n error: Error | null;\n\n /**\n * Start polling\n */\n start: () => void;\n\n /**\n * Stop polling\n */\n stop: () => void;\n\n /**\n * Manually trigger a fetch\n */\n refetch: () => Promise<void>;\n}\n\n/**\n * Hook for polling real-time chat history\n *\n * @param chatUid - The chat UID to poll for\n * @param fetchFn - Function that fetches the realtime history\n * @param options - Polling options\n */\nexport function usePolling(\n chatUid: string | null,\n fetchFn: () => Promise<RealtimeChatHistory>,\n options: UsePollingOptions = {}\n): UsePollingResult {\n const {\n interval = 1000,\n enabled = true,\n stopStatuses = ['completed', 'error'],\n onStop,\n onUpdate,\n onError,\n } = options;\n\n const [data, setData] = useState<RealtimeChatHistory | null>(null);\n const [isPolling, setIsPolling] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const isMountedRef = useRef(true);\n\n // Refs for callbacks and options to avoid stale closures and unnecessary re-renders\n const onStopRef = useRef(onStop);\n const onUpdateRef = useRef(onUpdate);\n const onErrorRef = useRef(onError);\n const fetchFnRef = useRef(fetchFn);\n const stopStatusesRef = useRef(stopStatuses);\n const intervalValueRef = useRef(interval);\n const isPollingRef = useRef(false);\n\n useEffect(() => {\n onStopRef.current = onStop;\n onUpdateRef.current = onUpdate;\n onErrorRef.current = onError;\n fetchFnRef.current = fetchFn;\n stopStatusesRef.current = stopStatuses;\n intervalValueRef.current = interval;\n });\n\n const clearPolling = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n isPollingRef.current = false;\n }, []);\n\n const fetchData = useCallback(async () => {\n console.log('[usePolling] fetchData called, isMounted:', isMountedRef.current);\n if (!isMountedRef.current) return;\n\n try {\n console.log('[usePolling] Fetching...');\n const result = await fetchFnRef.current();\n console.log('[usePolling] Fetch result:', { status: result.status, messageCount: result.chatHistory?.length });\n\n if (!isMountedRef.current) return;\n\n setData(result);\n setError(null);\n onUpdateRef.current?.(result);\n\n // Check if we should stop polling\n const shouldStop = stopStatusesRef.current.includes(result.status);\n console.log('[usePolling] Should stop?', shouldStop, 'stopStatuses:', stopStatusesRef.current, 'current status:', result.status);\n if (shouldStop) {\n console.log('[usePolling] Stopping polling due to status:', result.status);\n clearPolling();\n setIsPolling(false);\n onStopRef.current?.(result);\n }\n } catch (err) {\n console.error('[usePolling] Fetch error:', err);\n if (!isMountedRef.current) return;\n\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n\n // Stop polling on error\n clearPolling();\n setIsPolling(false);\n }\n }, [clearPolling]);\n\n const start = useCallback(() => {\n if (intervalRef.current) return;\n\n isPollingRef.current = true;\n setIsPolling(true);\n setError(null);\n\n // Immediate first fetch\n fetchData();\n\n // Set up interval\n intervalRef.current = setInterval(fetchData, intervalValueRef.current);\n }, [fetchData]);\n\n const stop = useCallback(() => {\n clearPolling();\n setIsPolling(false);\n }, [clearPolling]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n // Auto-start polling when enabled and chatUid is set\n useEffect(() => {\n console.log('[usePolling] Auto-start effect triggered:', { enabled, chatUid, isPollingRef: isPollingRef.current, intervalRef: !!intervalRef.current });\n\n if (!enabled || !chatUid) {\n console.log('[usePolling] Not enabled or no chatUid, stopping if active');\n // Stop polling if disabled or no chatUid\n if (isPollingRef.current) {\n clearPolling();\n setIsPolling(false);\n }\n return;\n }\n\n // Start polling if not already polling\n if (!isPollingRef.current) {\n console.log('[usePolling] Starting polling, interval:', intervalValueRef.current);\n isPollingRef.current = true;\n setIsPolling(true);\n setError(null);\n\n // Immediate first fetch\n fetchData();\n\n // Set up interval\n intervalRef.current = setInterval(fetchData, intervalValueRef.current);\n console.log('[usePolling] Interval set:', intervalRef.current);\n } else {\n console.log('[usePolling] Already polling, skipping start');\n }\n\n // Only cleanup on unmount, not on every dependency change\n }, [enabled, chatUid, fetchData, clearPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n clearPolling();\n };\n }, [clearPolling]);\n\n return {\n data,\n isPolling,\n error,\n start,\n stop,\n refetch,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"usePolling.js","sources":["../../../src/hooks/usePolling.ts"],"sourcesContent":["import { useState, useEffect, useRef, useCallback, useMemo } from 'react';\nimport type { RealtimeChatHistory, RealtimeStatus } from '../api/types';\nimport { createLogger } from '../utils/logger';\n\nexport interface UsePollingOptions {\n /**\n * Polling interval in milliseconds\n * @default 1000\n */\n interval?: number;\n\n /**\n * Whether polling is enabled\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Statuses that should stop polling\n * @default ['completed', 'error']\n */\n stopStatuses?: RealtimeStatus[];\n\n /**\n * Callback when polling stops\n */\n onStop?: (data: RealtimeChatHistory | null) => void;\n\n /**\n * Callback on each poll update\n */\n onUpdate?: (data: RealtimeChatHistory) => void;\n\n /**\n * Callback on poll error\n */\n onError?: (error: Error) => void;\n\n /**\n * Enable debug logging\n * @default false\n */\n debug?: boolean;\n}\n\nexport interface UsePollingResult {\n /**\n * Current polling data\n */\n data: RealtimeChatHistory | null;\n\n /**\n * Whether polling is currently active\n */\n isPolling: boolean;\n\n /**\n * Last error that occurred\n */\n error: Error | null;\n\n /**\n * Start polling\n */\n start: () => void;\n\n /**\n * Stop polling\n */\n stop: () => void;\n\n /**\n * Manually trigger a fetch\n */\n refetch: () => Promise<void>;\n}\n\n/**\n * Hook for polling real-time chat history\n *\n * @param chatUid - The chat UID to poll for\n * @param fetchFn - Function that fetches the realtime history\n * @param options - Polling options\n */\nexport function usePolling(\n chatUid: string | null,\n fetchFn: () => Promise<RealtimeChatHistory>,\n options: UsePollingOptions = {}\n): UsePollingResult {\n const {\n interval = 1000,\n enabled = true,\n stopStatuses = ['completed', 'error'],\n onStop,\n onUpdate,\n onError,\n debug = false,\n } = options;\n\n const log = useMemo(() => createLogger(debug), [debug]);\n const logRef = useRef(log);\n logRef.current = log;\n\n const [data, setData] = useState<RealtimeChatHistory | null>(null);\n const [isPolling, setIsPolling] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);\n const isMountedRef = useRef(true);\n\n // Refs for callbacks and options to avoid stale closures and unnecessary re-renders\n const onStopRef = useRef(onStop);\n const onUpdateRef = useRef(onUpdate);\n const onErrorRef = useRef(onError);\n const fetchFnRef = useRef(fetchFn);\n const stopStatusesRef = useRef(stopStatuses);\n const intervalValueRef = useRef(interval);\n const isPollingRef = useRef(false);\n\n useEffect(() => {\n onStopRef.current = onStop;\n onUpdateRef.current = onUpdate;\n onErrorRef.current = onError;\n fetchFnRef.current = fetchFn;\n stopStatusesRef.current = stopStatuses;\n intervalValueRef.current = interval;\n });\n\n const clearPolling = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current);\n intervalRef.current = null;\n }\n isPollingRef.current = false;\n }, []);\n\n const fetchData = useCallback(async () => {\n logRef.current.log('[usePolling] fetchData called, isMounted:', isMountedRef.current);\n if (!isMountedRef.current) return;\n\n try {\n logRef.current.log('[usePolling] Fetching...');\n const result = await fetchFnRef.current();\n logRef.current.log('[usePolling] Fetch result:', { status: result.status, messageCount: result.chatHistory?.length });\n\n if (!isMountedRef.current) return;\n\n setData(result);\n setError(null);\n onUpdateRef.current?.(result);\n\n // Check if we should stop polling\n const shouldStop = stopStatusesRef.current.includes(result.status);\n logRef.current.log('[usePolling] Should stop?', shouldStop, 'stopStatuses:', stopStatusesRef.current, 'current status:', result.status);\n if (shouldStop) {\n logRef.current.log('[usePolling] Stopping polling due to status:', result.status);\n clearPolling();\n setIsPolling(false);\n onStopRef.current?.(result);\n }\n } catch (err) {\n logRef.current.error('[usePolling] Fetch error:', err);\n if (!isMountedRef.current) return;\n\n const error = err instanceof Error ? err : new Error(String(err));\n setError(error);\n onErrorRef.current?.(error);\n\n // Stop polling on error\n clearPolling();\n setIsPolling(false);\n }\n }, [clearPolling]);\n\n const start = useCallback(() => {\n if (intervalRef.current) return;\n\n isPollingRef.current = true;\n setIsPolling(true);\n setError(null);\n\n // Immediate first fetch\n fetchData();\n\n // Set up interval\n intervalRef.current = setInterval(fetchData, intervalValueRef.current);\n }, [fetchData]);\n\n const stop = useCallback(() => {\n clearPolling();\n setIsPolling(false);\n }, [clearPolling]);\n\n const refetch = useCallback(async () => {\n await fetchData();\n }, [fetchData]);\n\n // Auto-start polling when enabled and chatUid is set\n useEffect(() => {\n logRef.current.log('[usePolling] Auto-start effect triggered:', { enabled, chatUid, isPollingRef: isPollingRef.current, intervalRef: !!intervalRef.current });\n\n if (!enabled || !chatUid) {\n logRef.current.log('[usePolling] Not enabled or no chatUid, stopping if active');\n // Stop polling if disabled or no chatUid\n if (isPollingRef.current) {\n clearPolling();\n setIsPolling(false);\n }\n return;\n }\n\n // Start polling if not already polling\n if (!isPollingRef.current) {\n logRef.current.log('[usePolling] Starting polling, interval:', intervalValueRef.current);\n isPollingRef.current = true;\n setIsPolling(true);\n setError(null);\n\n // Immediate first fetch\n fetchData();\n\n // Set up interval\n intervalRef.current = setInterval(fetchData, intervalValueRef.current);\n logRef.current.log('[usePolling] Interval set:', intervalRef.current);\n } else {\n logRef.current.log('[usePolling] Already polling, skipping start');\n }\n\n // Only cleanup on unmount, not on every dependency change\n }, [enabled, chatUid, fetchData, clearPolling]);\n\n // Cleanup on unmount\n useEffect(() => {\n isMountedRef.current = true;\n return () => {\n isMountedRef.current = false;\n clearPolling();\n };\n }, [clearPolling]);\n\n return {\n data,\n isPolling,\n error,\n start,\n stop,\n refetch,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -14,4 +14,4 @@ import type { DevicProviderProps } from './types';
14
14
  * </DevicProvider>
15
15
  * ```
16
16
  */
17
- export declare function DevicProvider({ apiKey, baseUrl, tenantId, tenantMetadata, children, }: DevicProviderProps): JSX.Element;
17
+ export declare function DevicProvider({ apiKey, baseUrl, tenantId, tenantMetadata, debug, children, }: DevicProviderProps): JSX.Element;
@@ -19,7 +19,7 @@ const DEFAULT_BASE_URL = 'https://api.devic.ai';
19
19
  * </DevicProvider>
20
20
  * ```
21
21
  */
22
- function DevicProvider({ apiKey, baseUrl = DEFAULT_BASE_URL, tenantId, tenantMetadata, children, }) {
22
+ function DevicProvider({ apiKey, baseUrl = DEFAULT_BASE_URL, tenantId, tenantMetadata, debug, children, }) {
23
23
  const contextValue = useMemo(() => {
24
24
  const client = new DevicApiClient({
25
25
  apiKey,
@@ -32,8 +32,9 @@ function DevicProvider({ apiKey, baseUrl = DEFAULT_BASE_URL, tenantId, tenantMet
32
32
  tenantId,
33
33
  tenantMetadata,
34
34
  isConfigured: !!apiKey,
35
+ debug,
35
36
  };
36
- }, [apiKey, baseUrl, tenantId, tenantMetadata]);
37
+ }, [apiKey, baseUrl, tenantId, tenantMetadata, debug]);
37
38
  return (jsx(DevicContext.Provider, { value: contextValue, children: children }));
38
39
  }
39
40