@devicai/ui 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/api/assistantInfo.js.map +1 -1
- package/dist/cjs/api/client.js.map +1 -1
- package/dist/cjs/api/types.js.map +1 -1
- package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js +42 -3
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ConversationSelector.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ErrorBoundary.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/LimitBanner.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ReferenceChip.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ToolTimeline.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/UsageBar.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/pastedText.js.map +1 -1
- package/dist/cjs/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
- package/dist/cjs/components/Feedback/FeedbackModal.js.map +1 -1
- package/dist/cjs/components/Feedback/MessageActions.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js +22 -10
- package/dist/cjs/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js +43 -18
- package/dist/cjs/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/McpConnectForm.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/McpServersSection.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/useIntegrations.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/useTenantMcp.js.map +1 -1
- package/dist/cjs/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
- package/dist/cjs/components/theme.js.map +1 -1
- package/dist/cjs/hooks/useDevicChat.js.map +1 -1
- package/dist/cjs/hooks/useModelInterface.js.map +1 -1
- package/dist/cjs/hooks/usePolling.js.map +1 -1
- package/dist/cjs/hooks/useSpeechRecording.js.map +1 -1
- package/dist/cjs/provider/DevicContext.js.map +1 -1
- package/dist/cjs/provider/DevicProvider.js.map +1 -1
- package/dist/cjs/provider/sharedSession.js.map +1 -1
- package/dist/cjs/styles.css +1 -1
- package/dist/cjs/utils/avatar.js.map +1 -1
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/logger.js.map +1 -1
- package/dist/cjs/utils/toolGroups.js.map +1 -1
- package/dist/esm/api/assistantInfo.js.map +1 -1
- package/dist/esm/api/client.js.map +1 -1
- package/dist/esm/api/types.d.ts +16 -0
- package/dist/esm/api/types.js.map +1 -1
- package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.js +42 -3
- package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatInput.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatMessages.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ConversationSelector.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ErrorBoundary.js.map +1 -1
- package/dist/esm/components/ChatDrawer/HandoffSubagentWidget.js.map +1 -1
- package/dist/esm/components/ChatDrawer/LimitBanner.js.map +1 -1
- package/dist/esm/components/ChatDrawer/RecalledMemoriesWidget.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ReferenceChip.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ToolTimeline.js.map +1 -1
- package/dist/esm/components/ChatDrawer/UsageBar.js.map +1 -1
- package/dist/esm/components/ChatDrawer/pastedText.js.map +1 -1
- package/dist/esm/components/CoreMemoryModal/CoreMemoryModal.js.map +1 -1
- package/dist/esm/components/Feedback/FeedbackModal.js.map +1 -1
- package/dist/esm/components/Feedback/MessageActions.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/ConnectFieldsForm.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationLogo.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsHint.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.d.ts +17 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js +22 -10
- package/dist/esm/components/IntegrationsModal/IntegrationsLauncher.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.d.ts +22 -3
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js +44 -19
- package/dist/esm/components/IntegrationsModal/IntegrationsToggle.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/McpConnectForm.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/McpServersSection.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/useIntegrations.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/useTenantMcp.js.map +1 -1
- package/dist/esm/components/ThreadStateTag/ThreadStateTag.js.map +1 -1
- package/dist/esm/components/theme.js.map +1 -1
- package/dist/esm/hooks/useDevicChat.js.map +1 -1
- package/dist/esm/hooks/useModelInterface.js.map +1 -1
- package/dist/esm/hooks/usePolling.js.map +1 -1
- package/dist/esm/hooks/useSpeechRecording.js.map +1 -1
- package/dist/esm/provider/DevicContext.js.map +1 -1
- package/dist/esm/provider/DevicProvider.js.map +1 -1
- package/dist/esm/provider/sharedSession.js.map +1 -1
- package/dist/esm/styles.css +1 -1
- package/dist/esm/utils/avatar.js.map +1 -1
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/logger.js.map +1 -1
- package/dist/esm/utils/toolGroups.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
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 type { TenantMetadata, SubtenantMetadata } from '../provider';\nimport { DevicApiClient, DevicApiError } from '../api/client';\nimport { usePolling, resolvePollingInterval } from './usePolling';\nimport { useModelInterface, type PendingWidgetCall } from './useModelInterface';\nimport { createLogger } from '../utils/logger';\nimport type {\n ChatMessage,\n ChatFile,\n ModelInterfaceTool,\n RealtimeChatHistory,\n RealtimeStatus,\n RecalledMemoryRecord,\n TenantLimitExceeded,\n} from '../api/types';\n\n/**\n * Cadence for the handoff watch, which only waits for the parent thread to\n * leave `handed_off` and so does not need the main conversation's rhythm.\n * A configured `pollingInterval` still wins over it.\n */\nconst DEFAULT_HANDOFF_POLL_INTERVAL_MS = 5000;\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 (e.g. { name, email, imageUrl })\n */\n tenantMetadata?: TenantMetadata;\n\n /**\n * Subtenant ID identifying a user/entity inside the tenant\n */\n subtenantId?: string;\n\n /**\n * Subtenant metadata (e.g. { id, name, email, imageUrl })\n */\n subtenantMetadata?: SubtenantMetadata;\n\n /**\n * Tags applied to the conversation. Sent as the top-level `tags` of each\n * message (distinct from metadata). Falls back to the DevicProvider's `tags`\n * and is merged (deduped) with any per-message tags passed to `sendMessage`.\n */\n tags?: string[];\n\n /**\n * Tools enabled from the assistant's configured tool groups\n */\n enabledTools?: string[];\n\n /**\n * Apps the end user connected that should sit out the messages sent from\n * here, by slug (`[\"gmail\"]`).\n *\n * A deny list over their own connected apps, not over the assistant's tools:\n * what is not named stays available. Sent with every message while it is set,\n * so it lasts as long as the caller keeps it set — the server keeps nothing\n * and the account is never disconnected.\n */\n disabledIntegrations?: string[];\n\n /**\n * Client-side tools for model interface protocol\n */\n modelInterfaceTools?: ModelInterfaceTool[];\n\n /**\n * How often (ms) the conversation in progress is polled for new content.\n * Overrides the DevicProvider's `pollingInterval`. Values below 250 ms are\n * clamped.\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 * Custom file upload handler. When provided, replaces the default\n * upload to Devic API. Receives the raw File objects and must return\n * an array of ChatFile with downloadUrl set.\n */\n onFileUpload?: (files: File[]) => Promise<ChatFile[]>;\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 * Set when the last message was blocked by a tenant/subtenant usage limit\n * (HTTP 429 / realtime status `limit_exceeded`). Carries the blocking rule,\n * current vs. limit and when the window resets. `null` while not limited.\n * Cleared when a new message is sent or the chat is cleared.\n */\n limitExceeded: TenantLimitExceeded | null;\n\n /**\n * Structured long-term-memory recall events of the conversation, streamed\n * in while the assistant is still processing (via the realtime poll) and\n * hydrated from the persisted history on load. Empty for assistants\n * without memory.\n */\n recalledMemories: RecalledMemoryRecord[];\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?: File[];\n metadata?: Record<string, any>;\n /** Id of a speech-to-text transcript that seeded this message. */\n transcriptId?: string;\n /** Per-message tags, merged (deduped) with the resolved conversation tags. */\n tags?: string[];\n /**\n * Connected apps to leave out of this one message, by slug. Replaces the\n * hook's own `disabledIntegrations` rather than adding to it, so a caller\n * can send one message with everything on by passing `[]`.\n */\n disabledIntegrations?: string[];\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 * Pending tool calls that require user interaction via a response widget\n */\n pendingWidgetCalls: PendingWidgetCall[];\n\n /**\n * Submit a response for a pending widget tool call.\n * Sends the response to the API and resumes the conversation.\n */\n submitWidgetResponse: (toolCallId: string, response: any) => Promise<void>;\n\n /**\n * Cancel a pending widget tool call.\n * Sends an error response so the model can continue.\n */\n cancelWidgetCall: (toolCallId: string, reason?: string) => Promise<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 subtenantId,\n subtenantMetadata,\n tags,\n enabledTools,\n disabledIntegrations,\n modelInterfaceTools = [],\n pollingInterval: propsPollingInterval,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n onFileUpload,\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 getTenantSession = context?.getTenantSession;\n const onSessionExpired = context?.onSessionExpired;\n const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };\n const resolvedSubtenantId = subtenantId || context?.subtenantId;\n const resolvedSubtenantMetadata = {\n ...context?.subtenantMetadata,\n ...subtenantMetadata,\n };\n // Provider tags + hook tags, deduped. Per-message tags are merged at send time.\n const resolvedTags = useMemo(\n () => Array.from(new Set([...(context?.tags ?? []), ...(tags ?? [])])),\n [context?.tags, tags]\n );\n const pollingInterval = resolvePollingInterval(\n propsPollingInterval,\n context?.pollingInterval\n );\n const handoffPollingInterval = resolvePollingInterval(\n propsPollingInterval,\n context?.pollingInterval,\n DEFAULT_HANDOFF_POLL_INTERVAL_MS\n );\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 const [limitExceeded, setLimitExceeded] = useState<TenantLimitExceeded | null>(\n null\n );\n\n // Long-term-memory recall events of the conversation. The realtime blob\n // only carries the in-flight run's records, so incoming batches merge (by\n // uid) instead of replacing — earlier runs' recalls stay visible.\n const [recalledMemories, setRecalledMemories] = useState<RecalledMemoryRecord[]>([]);\n const mergeRecalledMemories = useCallback(\n (incoming?: RecalledMemoryRecord[]) => {\n if (!incoming?.length) return;\n setRecalledMemories((prev) => {\n const known = new Set(prev.map((r) => r.uid));\n const fresh = incoming.filter((r) => !known.has(r.uid));\n return fresh.length ? [...prev, ...fresh] : prev;\n });\n },\n []\n );\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 || getTenantSession)) {\n clientRef.current = new DevicApiClient({ apiKey, baseUrl, getTenantSession, onSessionExpired });\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 mergeRecalledMemories(realtime.recalledMemories);\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, mergeRecalledMemories]\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 mergeRecalledMemories(history.recalledMemories);\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 // Pending widget calls awaiting user interaction\n const [pendingWidgetCalls, setPendingWidgetCalls] = useState<PendingWidgetCall[]>([]);\n const pendingWidgetCallsRef = useRef<PendingWidgetCall[]>([]);\n useEffect(() => {\n pendingWidgetCallsRef.current = pendingWidgetCalls;\n }, [pendingWidgetCalls]);\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: [\n 'completed',\n 'error',\n 'waiting_for_tool_response',\n 'handed_off',\n 'limit_exceeded',\n ],\n onUpdate: async (data: RealtimeChatHistory) => {\n logRef.current.log('[useDevicChat] onUpdate called, status:', data.status);\n\n // Merge realtime data with optimistic messages.\n // When a server user message matches an optimistic one by text, adopt the\n // optimistic uid so React's key stays stable (avoids unmount/remount flicker).\n setMessages((prev) => {\n const normalize = (t?: string) => (t ?? '').trim();\n const optimisticUserByText = new Map(\n prev\n .filter((m) => m.role === 'user' && m.uid.startsWith('temp-'))\n .map((m) => [normalize(m.content?.message), m.uid])\n );\n\n const adoptedTempUids = new Set<string>();\n const merged = data.chatHistory.map((m) => {\n if (m.role === 'user') {\n const tempUid = optimisticUserByText.get(normalize(m.content?.message));\n if (tempUid) {\n adoptedTempUids.add(tempUid);\n // Keep the server uid around: recall anchors reference it.\n return { ...m, uid: tempUid, serverUid: m.uid };\n }\n }\n return m;\n });\n\n const mergedUIDs = new Set(merged.map((m) => m.uid));\n const optimistic = prev.filter(\n (m) => !mergedUIDs.has(m.uid) && !adoptedTempUids.has(m.uid)\n );\n\n return [...merged, ...optimistic];\n });\n // Surface recall events while the run is still processing, so the\n // \"recalled memories\" strip shows before the first response lands.\n mergeRecalledMemories(data.recalledMemories);\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 === 'limit_exceeded') {\n // The message was blocked by a tenant/subtenant usage limit before\n // reaching the LLM. Surface the details so the UI can show a banner.\n setIsLoading(false);\n const details: TenantLimitExceeded = data.limitExceeded || {\n message: 'Usage limit reached.',\n };\n setLimitExceeded(details);\n const err = new Error(\n details.message || 'Usage limit reached.'\n ) as Error & { errorType?: string; details?: TenantLimitExceeded };\n err.errorType = 'TENANT_LIMIT_EXCEEDED';\n err.details = details;\n setError(err);\n onErrorRef.current?.(err);\n } else 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 (partitioned into immediate responses and widget-driven)\n const { responses, widgetCalls } = await handleToolCalls(pendingCalls);\n\n // Queue widget-driven tool calls for user interaction\n if (widgetCalls.length > 0) {\n const existingIds = new Set(pendingWidgetCallsRef.current.map((c) => c.toolCall.id));\n const deduped = widgetCalls.filter((c) => !existingIds.has(c.toolCall.id));\n const next = [...pendingWidgetCallsRef.current, ...deduped];\n pendingWidgetCallsRef.current = next;\n setPendingWidgetCalls(next);\n // Stop polling while we wait for the user to submit the widget response.\n // The widget submission will re-trigger polling via submitWidgetResponse.\n setShouldPoll(false);\n setIsLoading(false);\n }\n\n if (responses.length > 0) {\n // Send tool responses back to the API, restating the tools so the\n // continuation keeps them on offer\n await clientRef.current.sendToolResponses(\n assistantId,\n chatUid,\n responses,\n toolSchemas\n );\n\n // Only resume polling if no widgets are blocking\n if (widgetCalls.length === 0) {\n setShouldPoll(true);\n setIsLoading(true);\n }\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, toolSchemas]\n );\n\n // Send a message\n const sendMessage = useCallback(\n async (\n message: string,\n sendOptions?: {\n files?: File[];\n metadata?: Record<string, any>;\n transcriptId?: string;\n tags?: string[];\n disabledIntegrations?: string[];\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 setLimitExceeded(null);\n setStatus('processing');\n\n // Add user message optimistically (show file names before upload)\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: '',\n type: f.type.split('/')[0] || 'other',\n })),\n },\n timestamp: Date.now(),\n ...(sendOptions?.transcriptId && {\n transcriptId: sendOptions.transcriptId,\n }),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n onMessageSent?.(userMessage);\n\n try {\n // Upload files if provided\n let uploadedFiles: ChatFile[] | undefined;\n if (sendOptions?.files && sendOptions.files.length > 0) {\n logRef.current.log('[useDevicChat] Uploading files...');\n if (onFileUpload) {\n // Use custom upload handler\n uploadedFiles = await onFileUpload(sendOptions.files);\n } else {\n // Default: upload via Devic API\n const uploadResults = await Promise.all(\n sendOptions.files.map((file) => clientRef.current!.uploadFile(file))\n );\n uploadedFiles = uploadResults.map((r) => ({\n name: r.name,\n downloadUrl: r.downloadUrl,\n fileType: r.fileType as ChatFile['fileType'],\n }));\n }\n logRef.current.log('[useDevicChat] Files uploaded:', uploadedFiles);\n\n // Update optimistic message with download URLs\n setMessages((prev) =>\n prev.map((m) =>\n m.uid === userMessage.uid\n ? {\n ...m,\n content: {\n ...m.content,\n files: uploadedFiles!.map((f) => ({\n name: f.name,\n url: f.downloadUrl || '',\n type: f.fileType || 'other',\n })),\n },\n }\n : m\n )\n );\n }\n\n // Build request DTO\n const hasSubtenantMetadata =\n resolvedSubtenantMetadata &&\n Object.keys(resolvedSubtenantMetadata).length > 0;\n const dto = {\n message,\n chatUid: chatUid || undefined,\n files: uploadedFiles,\n metadata: {\n ...resolvedTenantMetadata,\n ...(hasSubtenantMetadata && {\n subtenantMetadata: resolvedSubtenantMetadata,\n }),\n ...sendOptions?.metadata,\n },\n tenantId: resolvedTenantId,\n ...(resolvedSubtenantId && { subtenantId: resolvedSubtenantId }),\n // Conversation tags: provider + hook + per-message, deduped\n ...(() => {\n const merged = Array.from(\n new Set([...resolvedTags, ...(sendOptions?.tags ?? [])])\n );\n return merged.length > 0 ? { tags: merged } : {};\n })(),\n enabledTools,\n // The end user's own apps that sit this message out. Sent only when\n // some are switched off: an older API ignores the field, and there is\n // no reason to put an empty array in every request.\n ...(() => {\n const off = sendOptions?.disabledIntegrations ?? disabledIntegrations;\n return off?.length ? { disabledIntegrations: off } : {};\n })(),\n // Include model interface tools if any\n ...(toolSchemas.length > 0 && { tools: toolSchemas }),\n // Link to the speech-to-text transcript that seeded this message, if any\n ...(sendOptions?.transcriptId && { transcriptId: sendOptions.transcriptId }),\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\n // A synchronous usage-limit block surfaces as HTTP 429 /\n // TENANT_LIMIT_EXCEEDED (sync send path). Async sends surface it via the\n // realtime `limit_exceeded` status instead — both are handled.\n if (\n err instanceof DevicApiError &&\n (err.statusCode === 429 || err.errorType === 'TENANT_LIMIT_EXCEEDED')\n ) {\n const details: TenantLimitExceeded =\n (err.details as TenantLimitExceeded) || { message: err.message };\n if (!details.message) details.message = err.message;\n setLimitExceeded(details);\n }\n\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 // Without this, the callback keeps the list from the render that created\n // it: switching an app off would not take effect until something else\n // happened to rebuild it.\n disabledIntegrations,\n resolvedTenantId,\n resolvedTenantMetadata,\n resolvedSubtenantId,\n resolvedSubtenantMetadata,\n resolvedTags,\n toolSchemas,\n onMessageSent,\n onFileUpload,\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 setLimitExceeded(null);\n setRecalledMemories([]);\n pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\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 pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n\n setIsLoading(true);\n setError(null);\n setRecalledMemories([]);\n\n try {\n const history = await clientRef.current.getChatHistory(\n assistantId,\n loadChatUid,\n { tenantId: resolvedTenantId }\n );\n\n setMessages(history.chatContent);\n mergeRecalledMemories(history.recalledMemories);\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, mergeRecalledMemories]\n );\n\n // Handoff polling: while handedOff is true, poll the realtime endpoint every\n // 5s (or the configured cadence) to detect when the parent thread is no\n // 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, handoffPollingInterval);\n return () => {\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n };\n }, [handedOff, chatUid, assistantId, handoffPollingInterval]);\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 // Submit a response for a pending widget tool call\n const submitWidgetResponse = useCallback(\n async (toolCallId: string, response: any) => {\n const uid = chatUidRef.current;\n if (!clientRef.current || !uid) return;\n\n // Check pending widgets via ref (state updates are async)\n const current = pendingWidgetCallsRef.current;\n const found = current.some((c) => c.toolCall.id === toolCallId);\n if (!found) return;\n\n // Remove the widget call from pending list\n const remaining = current.filter((c) => c.toolCall.id !== toolCallId);\n pendingWidgetCallsRef.current = remaining;\n setPendingWidgetCalls(remaining);\n\n try {\n logRef.current.log('[useDevicChat] sending widget tool response', toolCallId);\n await clientRef.current.sendToolResponses(\n assistantId,\n uid,\n [{ tool_call_id: toolCallId, content: response, role: 'tool' }],\n toolSchemas\n );\n // Resume polling only if no more widget calls are blocking\n if (remaining.length === 0) {\n setShouldPoll(true);\n setIsLoading(true);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n logRef.current.error('[useDevicChat] submitWidgetResponse failed', error);\n setError(error);\n onErrorRef.current?.(error);\n }\n },\n [assistantId, toolSchemas]\n );\n\n // Cancel a pending widget tool call (sends an error response to the model)\n const cancelWidgetCall = useCallback(\n async (toolCallId: string, reason?: string) => {\n await submitWidgetResponse(toolCallId, {\n error: reason || 'User cancelled the tool call',\n });\n },\n [submitWidgetResponse]\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 limitExceeded,\n recalledMemories,\n handedOff,\n handedOffSubThreadId,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n pendingWidgetCalls,\n submitWidgetResponse,\n cancelWidgetCall,\n };\n}\n"],"names":["useOptionalDevicContext","useMemo","resolvePollingInterval","createLogger","useRef","useState","useCallback","useEffect","DevicApiClient","useModelInterface","usePolling","DevicApiError"],"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 type { TenantMetadata, SubtenantMetadata } from '../provider';\nimport { DevicApiClient, DevicApiError } from '../api/client';\nimport { usePolling, resolvePollingInterval } from './usePolling';\nimport { useModelInterface, type PendingWidgetCall } from './useModelInterface';\nimport { createLogger } from '../utils/logger';\nimport type {\n ChatMessage,\n ChatFile,\n ModelInterfaceTool,\n RealtimeChatHistory,\n RealtimeStatus,\n RecalledMemoryRecord,\n TenantLimitExceeded,\n} from '../api/types';\n\n/**\n * Cadence for the handoff watch, which only waits for the parent thread to\n * leave `handed_off` and so does not need the main conversation's rhythm.\n * A configured `pollingInterval` still wins over it.\n */\nconst DEFAULT_HANDOFF_POLL_INTERVAL_MS = 5000;\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 (e.g. { name, email, imageUrl })\n */\n tenantMetadata?: TenantMetadata;\n\n /**\n * Subtenant ID identifying a user/entity inside the tenant\n */\n subtenantId?: string;\n\n /**\n * Subtenant metadata (e.g. { id, name, email, imageUrl })\n */\n subtenantMetadata?: SubtenantMetadata;\n\n /**\n * Tags applied to the conversation. Sent as the top-level `tags` of each\n * message (distinct from metadata). Falls back to the DevicProvider's `tags`\n * and is merged (deduped) with any per-message tags passed to `sendMessage`.\n */\n tags?: string[];\n\n /**\n * Tools enabled from the assistant's configured tool groups\n */\n enabledTools?: string[];\n\n /**\n * Apps the end user connected that should sit out the messages sent from\n * here, by slug (`[\"gmail\"]`).\n *\n * A deny list over their own connected apps, not over the assistant's tools:\n * what is not named stays available. Sent with every message while it is set,\n * so it lasts as long as the caller keeps it set — the server keeps nothing\n * and the account is never disconnected.\n */\n disabledIntegrations?: string[];\n\n /**\n * Client-side tools for model interface protocol\n */\n modelInterfaceTools?: ModelInterfaceTool[];\n\n /**\n * How often (ms) the conversation in progress is polled for new content.\n * Overrides the DevicProvider's `pollingInterval`. Values below 250 ms are\n * clamped.\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 * Custom file upload handler. When provided, replaces the default\n * upload to Devic API. Receives the raw File objects and must return\n * an array of ChatFile with downloadUrl set.\n */\n onFileUpload?: (files: File[]) => Promise<ChatFile[]>;\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 * Set when the last message was blocked by a tenant/subtenant usage limit\n * (HTTP 429 / realtime status `limit_exceeded`). Carries the blocking rule,\n * current vs. limit and when the window resets. `null` while not limited.\n * Cleared when a new message is sent or the chat is cleared.\n */\n limitExceeded: TenantLimitExceeded | null;\n\n /**\n * Structured long-term-memory recall events of the conversation, streamed\n * in while the assistant is still processing (via the realtime poll) and\n * hydrated from the persisted history on load. Empty for assistants\n * without memory.\n */\n recalledMemories: RecalledMemoryRecord[];\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?: File[];\n metadata?: Record<string, any>;\n /** Id of a speech-to-text transcript that seeded this message. */\n transcriptId?: string;\n /** Per-message tags, merged (deduped) with the resolved conversation tags. */\n tags?: string[];\n /**\n * Connected apps to leave out of this one message, by slug. Replaces the\n * hook's own `disabledIntegrations` rather than adding to it, so a caller\n * can send one message with everything on by passing `[]`.\n */\n disabledIntegrations?: string[];\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 * Pending tool calls that require user interaction via a response widget\n */\n pendingWidgetCalls: PendingWidgetCall[];\n\n /**\n * Submit a response for a pending widget tool call.\n * Sends the response to the API and resumes the conversation.\n */\n submitWidgetResponse: (toolCallId: string, response: any) => Promise<void>;\n\n /**\n * Cancel a pending widget tool call.\n * Sends an error response so the model can continue.\n */\n cancelWidgetCall: (toolCallId: string, reason?: string) => Promise<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 subtenantId,\n subtenantMetadata,\n tags,\n enabledTools,\n disabledIntegrations,\n modelInterfaceTools = [],\n pollingInterval: propsPollingInterval,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n onFileUpload,\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 getTenantSession = context?.getTenantSession;\n const onSessionExpired = context?.onSessionExpired;\n const baseUrl = propsBaseUrl || context?.baseUrl || 'https://api.devic.ai';\n const resolvedTenantId = tenantId || context?.tenantId;\n const resolvedTenantMetadata = { ...context?.tenantMetadata, ...tenantMetadata };\n const resolvedSubtenantId = subtenantId || context?.subtenantId;\n const resolvedSubtenantMetadata = {\n ...context?.subtenantMetadata,\n ...subtenantMetadata,\n };\n // Provider tags + hook tags, deduped. Per-message tags are merged at send time.\n const resolvedTags = useMemo(\n () => Array.from(new Set([...(context?.tags ?? []), ...(tags ?? [])])),\n [context?.tags, tags]\n );\n const pollingInterval = resolvePollingInterval(\n propsPollingInterval,\n context?.pollingInterval\n );\n const handoffPollingInterval = resolvePollingInterval(\n propsPollingInterval,\n context?.pollingInterval,\n DEFAULT_HANDOFF_POLL_INTERVAL_MS\n );\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 const [limitExceeded, setLimitExceeded] = useState<TenantLimitExceeded | null>(\n null\n );\n\n // Long-term-memory recall events of the conversation. The realtime blob\n // only carries the in-flight run's records, so incoming batches merge (by\n // uid) instead of replacing — earlier runs' recalls stay visible.\n const [recalledMemories, setRecalledMemories] = useState<RecalledMemoryRecord[]>([]);\n const mergeRecalledMemories = useCallback(\n (incoming?: RecalledMemoryRecord[]) => {\n if (!incoming?.length) return;\n setRecalledMemories((prev) => {\n const known = new Set(prev.map((r) => r.uid));\n const fresh = incoming.filter((r) => !known.has(r.uid));\n return fresh.length ? [...prev, ...fresh] : prev;\n });\n },\n []\n );\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 || getTenantSession)) {\n clientRef.current = new DevicApiClient({ apiKey, baseUrl, getTenantSession, onSessionExpired });\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 mergeRecalledMemories(realtime.recalledMemories);\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, mergeRecalledMemories]\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 mergeRecalledMemories(history.recalledMemories);\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 // Pending widget calls awaiting user interaction\n const [pendingWidgetCalls, setPendingWidgetCalls] = useState<PendingWidgetCall[]>([]);\n const pendingWidgetCallsRef = useRef<PendingWidgetCall[]>([]);\n useEffect(() => {\n pendingWidgetCallsRef.current = pendingWidgetCalls;\n }, [pendingWidgetCalls]);\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: [\n 'completed',\n 'error',\n 'waiting_for_tool_response',\n 'handed_off',\n 'limit_exceeded',\n ],\n onUpdate: async (data: RealtimeChatHistory) => {\n logRef.current.log('[useDevicChat] onUpdate called, status:', data.status);\n\n // Merge realtime data with optimistic messages.\n // When a server user message matches an optimistic one by text, adopt the\n // optimistic uid so React's key stays stable (avoids unmount/remount flicker).\n setMessages((prev) => {\n const normalize = (t?: string) => (t ?? '').trim();\n const optimisticUserByText = new Map(\n prev\n .filter((m) => m.role === 'user' && m.uid.startsWith('temp-'))\n .map((m) => [normalize(m.content?.message), m.uid])\n );\n\n const adoptedTempUids = new Set<string>();\n const merged = data.chatHistory.map((m) => {\n if (m.role === 'user') {\n const tempUid = optimisticUserByText.get(normalize(m.content?.message));\n if (tempUid) {\n adoptedTempUids.add(tempUid);\n // Keep the server uid around: recall anchors reference it.\n return { ...m, uid: tempUid, serverUid: m.uid };\n }\n }\n return m;\n });\n\n const mergedUIDs = new Set(merged.map((m) => m.uid));\n const optimistic = prev.filter(\n (m) => !mergedUIDs.has(m.uid) && !adoptedTempUids.has(m.uid)\n );\n\n return [...merged, ...optimistic];\n });\n // Surface recall events while the run is still processing, so the\n // \"recalled memories\" strip shows before the first response lands.\n mergeRecalledMemories(data.recalledMemories);\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 === 'limit_exceeded') {\n // The message was blocked by a tenant/subtenant usage limit before\n // reaching the LLM. Surface the details so the UI can show a banner.\n setIsLoading(false);\n const details: TenantLimitExceeded = data.limitExceeded || {\n message: 'Usage limit reached.',\n };\n setLimitExceeded(details);\n const err = new Error(\n details.message || 'Usage limit reached.'\n ) as Error & { errorType?: string; details?: TenantLimitExceeded };\n err.errorType = 'TENANT_LIMIT_EXCEEDED';\n err.details = details;\n setError(err);\n onErrorRef.current?.(err);\n } else 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 (partitioned into immediate responses and widget-driven)\n const { responses, widgetCalls } = await handleToolCalls(pendingCalls);\n\n // Queue widget-driven tool calls for user interaction\n if (widgetCalls.length > 0) {\n const existingIds = new Set(pendingWidgetCallsRef.current.map((c) => c.toolCall.id));\n const deduped = widgetCalls.filter((c) => !existingIds.has(c.toolCall.id));\n const next = [...pendingWidgetCallsRef.current, ...deduped];\n pendingWidgetCallsRef.current = next;\n setPendingWidgetCalls(next);\n // Stop polling while we wait for the user to submit the widget response.\n // The widget submission will re-trigger polling via submitWidgetResponse.\n setShouldPoll(false);\n setIsLoading(false);\n }\n\n if (responses.length > 0) {\n // Send tool responses back to the API, restating the tools so the\n // continuation keeps them on offer\n await clientRef.current.sendToolResponses(\n assistantId,\n chatUid,\n responses,\n toolSchemas\n );\n\n // Only resume polling if no widgets are blocking\n if (widgetCalls.length === 0) {\n setShouldPoll(true);\n setIsLoading(true);\n }\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, toolSchemas]\n );\n\n // Send a message\n const sendMessage = useCallback(\n async (\n message: string,\n sendOptions?: {\n files?: File[];\n metadata?: Record<string, any>;\n transcriptId?: string;\n tags?: string[];\n disabledIntegrations?: string[];\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 setLimitExceeded(null);\n setStatus('processing');\n\n // Add user message optimistically (show file names before upload)\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: '',\n type: f.type.split('/')[0] || 'other',\n })),\n },\n timestamp: Date.now(),\n ...(sendOptions?.transcriptId && {\n transcriptId: sendOptions.transcriptId,\n }),\n };\n\n setMessages((prev) => [...prev, userMessage]);\n onMessageSent?.(userMessage);\n\n try {\n // Upload files if provided\n let uploadedFiles: ChatFile[] | undefined;\n if (sendOptions?.files && sendOptions.files.length > 0) {\n logRef.current.log('[useDevicChat] Uploading files...');\n if (onFileUpload) {\n // Use custom upload handler\n uploadedFiles = await onFileUpload(sendOptions.files);\n } else {\n // Default: upload via Devic API\n const uploadResults = await Promise.all(\n sendOptions.files.map((file) => clientRef.current!.uploadFile(file))\n );\n uploadedFiles = uploadResults.map((r) => ({\n name: r.name,\n downloadUrl: r.downloadUrl,\n fileType: r.fileType as ChatFile['fileType'],\n }));\n }\n logRef.current.log('[useDevicChat] Files uploaded:', uploadedFiles);\n\n // Update optimistic message with download URLs\n setMessages((prev) =>\n prev.map((m) =>\n m.uid === userMessage.uid\n ? {\n ...m,\n content: {\n ...m.content,\n files: uploadedFiles!.map((f) => ({\n name: f.name,\n url: f.downloadUrl || '',\n type: f.fileType || 'other',\n })),\n },\n }\n : m\n )\n );\n }\n\n // Build request DTO\n const hasSubtenantMetadata =\n resolvedSubtenantMetadata &&\n Object.keys(resolvedSubtenantMetadata).length > 0;\n const dto = {\n message,\n chatUid: chatUid || undefined,\n files: uploadedFiles,\n metadata: {\n ...resolvedTenantMetadata,\n ...(hasSubtenantMetadata && {\n subtenantMetadata: resolvedSubtenantMetadata,\n }),\n ...sendOptions?.metadata,\n },\n tenantId: resolvedTenantId,\n ...(resolvedSubtenantId && { subtenantId: resolvedSubtenantId }),\n // Conversation tags: provider + hook + per-message, deduped\n ...(() => {\n const merged = Array.from(\n new Set([...resolvedTags, ...(sendOptions?.tags ?? [])])\n );\n return merged.length > 0 ? { tags: merged } : {};\n })(),\n enabledTools,\n // The end user's own apps that sit this message out. Sent only when\n // some are switched off: an older API ignores the field, and there is\n // no reason to put an empty array in every request.\n ...(() => {\n const off = sendOptions?.disabledIntegrations ?? disabledIntegrations;\n return off?.length ? { disabledIntegrations: off } : {};\n })(),\n // Include model interface tools if any\n ...(toolSchemas.length > 0 && { tools: toolSchemas }),\n // Link to the speech-to-text transcript that seeded this message, if any\n ...(sendOptions?.transcriptId && { transcriptId: sendOptions.transcriptId }),\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\n // A synchronous usage-limit block surfaces as HTTP 429 /\n // TENANT_LIMIT_EXCEEDED (sync send path). Async sends surface it via the\n // realtime `limit_exceeded` status instead — both are handled.\n if (\n err instanceof DevicApiError &&\n (err.statusCode === 429 || err.errorType === 'TENANT_LIMIT_EXCEEDED')\n ) {\n const details: TenantLimitExceeded =\n (err.details as TenantLimitExceeded) || { message: err.message };\n if (!details.message) details.message = err.message;\n setLimitExceeded(details);\n }\n\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 // Without this, the callback keeps the list from the render that created\n // it: switching an app off would not take effect until something else\n // happened to rebuild it.\n disabledIntegrations,\n resolvedTenantId,\n resolvedTenantMetadata,\n resolvedSubtenantId,\n resolvedSubtenantMetadata,\n resolvedTags,\n toolSchemas,\n onMessageSent,\n onFileUpload,\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 setLimitExceeded(null);\n setRecalledMemories([]);\n pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\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 pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n\n setIsLoading(true);\n setError(null);\n setRecalledMemories([]);\n\n try {\n const history = await clientRef.current.getChatHistory(\n assistantId,\n loadChatUid,\n { tenantId: resolvedTenantId }\n );\n\n setMessages(history.chatContent);\n mergeRecalledMemories(history.recalledMemories);\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, mergeRecalledMemories]\n );\n\n // Handoff polling: while handedOff is true, poll the realtime endpoint every\n // 5s (or the configured cadence) to detect when the parent thread is no\n // 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, handoffPollingInterval);\n return () => {\n if (handoffPollRef.current) {\n clearInterval(handoffPollRef.current);\n handoffPollRef.current = null;\n }\n };\n }, [handedOff, chatUid, assistantId, handoffPollingInterval]);\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 // Submit a response for a pending widget tool call\n const submitWidgetResponse = useCallback(\n async (toolCallId: string, response: any) => {\n const uid = chatUidRef.current;\n if (!clientRef.current || !uid) return;\n\n // Check pending widgets via ref (state updates are async)\n const current = pendingWidgetCallsRef.current;\n const found = current.some((c) => c.toolCall.id === toolCallId);\n if (!found) return;\n\n // Remove the widget call from pending list\n const remaining = current.filter((c) => c.toolCall.id !== toolCallId);\n pendingWidgetCallsRef.current = remaining;\n setPendingWidgetCalls(remaining);\n\n try {\n logRef.current.log('[useDevicChat] sending widget tool response', toolCallId);\n await clientRef.current.sendToolResponses(\n assistantId,\n uid,\n [{ tool_call_id: toolCallId, content: response, role: 'tool' }],\n toolSchemas\n );\n // Resume polling only if no more widget calls are blocking\n if (remaining.length === 0) {\n setShouldPoll(true);\n setIsLoading(true);\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n logRef.current.error('[useDevicChat] submitWidgetResponse failed', error);\n setError(error);\n onErrorRef.current?.(error);\n }\n },\n [assistantId, toolSchemas]\n );\n\n // Cancel a pending widget tool call (sends an error response to the model)\n const cancelWidgetCall = useCallback(\n async (toolCallId: string, reason?: string) => {\n await submitWidgetResponse(toolCallId, {\n error: reason || 'User cancelled the tool call',\n });\n },\n [submitWidgetResponse]\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 limitExceeded,\n recalledMemories,\n handedOff,\n handedOffSubThreadId,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n pendingWidgetCalls,\n submitWidgetResponse,\n cancelWidgetCall,\n };\n}\n"],"names":["useOptionalDevicContext","useMemo","resolvePollingInterval","createLogger","useRef","useState","useCallback","useEffect","DevicApiClient","useModelInterface","usePolling","DevicApiError"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useModelInterface.js","sources":["../../../../src/hooks/useModelInterface.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport type {\n ModelInterfaceTool,\n ModelInterfaceToolSchema,\n ToolCall,\n ToolCallResponse,\n ChatMessage,\n ResponseWidgetConfig,\n} from '../api/types';\n\nexport interface PendingWidgetCall {\n toolCall: ToolCall;\n params: any;\n widget: ResponseWidgetConfig;\n toolName: string;\n}\n\nexport interface HandleToolCallsResult {\n /** Responses ready to send back to the API (from callback-based tools) */\n responses: ToolCallResponse[];\n /** Tool calls that require user interaction via a response widget */\n widgetCalls: PendingWidgetCall[];\n}\n\nexport interface UseModelInterfaceOptions {\n /**\n * Client-side tools available for model interface protocol\n */\n tools: ModelInterfaceTool[];\n\n /**\n * Callback when a tool is being executed\n */\n onToolExecute?: (toolName: string, params: any) => void;\n\n /**\n * Callback when a tool execution completes\n */\n onToolComplete?: (toolName: string, result: any) => void;\n\n /**\n * Callback when a tool execution fails\n */\n onToolError?: (toolName: string, error: Error) => void;\n}\n\nexport interface UseModelInterfaceResult {\n /**\n * Tool schemas to send to the API\n */\n toolSchemas: ModelInterfaceToolSchema[];\n\n /**\n * Check if a tool call should be handled client-side\n */\n isClientTool: (toolName: string) => boolean;\n\n /**\n * Check if a tool has a response widget (user-driven) instead of a callback\n */\n hasResponseWidget: (toolName: string) => boolean;\n\n /**\n * Get the tool definition by name\n */\n getTool: (toolName: string) => ModelInterfaceTool | undefined;\n\n /**\n * Handle tool calls from the model.\n * Callback-based tools are executed immediately and their responses returned.\n * Widget-based tools are returned as pending widget calls for user interaction.\n */\n handleToolCalls: (toolCalls: ToolCall[]) => Promise<HandleToolCallsResult>;\n\n /**\n * Process messages and extract pending tool calls that need client handling\n */\n extractPendingToolCalls: (messages: ChatMessage[]) => ToolCall[];\n}\n\n/**\n * Hook for implementing the Model Interface Protocol\n *\n * The Model Interface Protocol allows client-side tools to be executed\n * during an assistant conversation. When the model calls a client-side tool,\n * this hook handles executing the tool and preparing the response.\n *\n * @example\n * ```tsx\n * const { toolSchemas, handleToolCalls } = useModelInterface({\n * tools: [\n * {\n * toolName: 'get_user_location',\n * schema: {\n * type: 'function',\n * function: {\n * name: 'get_user_location',\n * description: 'Get user current location',\n * parameters: { type: 'object', properties: {} }\n * }\n * },\n * callback: async () => {\n * const pos = await getCurrentPosition();\n * return { lat: pos.coords.latitude, lng: pos.coords.longitude };\n * }\n * }\n * ]\n * });\n * ```\n */\nexport function useModelInterface(\n options: UseModelInterfaceOptions\n): UseModelInterfaceResult {\n const { tools, onToolExecute, onToolComplete, onToolError } = options;\n\n // Extract tool schemas for API\n const toolSchemas = useMemo(() => {\n return tools.map((tool) => tool.schema);\n }, [tools]);\n\n // Map of tool name to tool definition\n const toolMap = useMemo(() => {\n return new Map(tools.map((tool) => [tool.toolName, tool]));\n }, [tools]);\n\n // Check if a tool is a client-side tool\n const isClientTool = useCallback(\n (toolName: string): boolean => {\n return toolMap.has(toolName);\n },\n [toolMap]\n );\n\n const hasResponseWidget = useCallback(\n (toolName: string): boolean => Boolean(toolMap.get(toolName)?.responseWidget),\n [toolMap]\n );\n\n const getTool = useCallback(\n (toolName: string): ModelInterfaceTool | undefined => toolMap.get(toolName),\n [toolMap]\n );\n\n // Handle tool calls: execute callback tools, queue widget tools for user input\n const handleToolCalls = useCallback(\n async (toolCalls: ToolCall[]): Promise<HandleToolCallsResult> => {\n const responses: ToolCallResponse[] = [];\n const widgetCalls: PendingWidgetCall[] = [];\n\n for (const toolCall of toolCalls) {\n const toolName = toolCall.function.name;\n const tool = toolMap.get(toolName);\n\n if (!tool) continue;\n\n let params: any = {};\n try {\n params = JSON.parse(toolCall.function.arguments || '{}');\n } catch {\n // Keep empty params if parsing fails\n }\n\n onToolExecute?.(toolName, params);\n\n if (tool.responseWidget) {\n widgetCalls.push({\n toolCall,\n params,\n widget: tool.responseWidget,\n toolName,\n });\n continue;\n }\n\n if (!tool.callback) {\n // Neither callback nor widget — respond with error so the model can continue\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: `Tool \"${toolName}\" has no callback or responseWidget` },\n role: 'tool',\n });\n continue;\n }\n\n try {\n const result = await tool.callback(params);\n onToolComplete?.(toolName, result);\n responses.push({\n tool_call_id: toolCall.id,\n content: result,\n role: 'tool',\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onToolError?.(toolName, error);\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: error.message },\n role: 'tool',\n });\n }\n }\n\n return { responses, widgetCalls };\n },\n [toolMap, onToolExecute, onToolComplete, onToolError]\n );\n\n // Extract pending tool calls from messages that need client handling\n const extractPendingToolCalls = useCallback(\n (messages: ChatMessage[]): ToolCall[] => {\n const pendingCalls: ToolCall[] = [];\n\n // Look at the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n\n if (message.role === 'assistant' && message.tool_calls?.length) {\n // Filter for client-side tools only\n const clientToolCalls = message.tool_calls.filter((tc) =>\n isClientTool(tc.function.name)\n );\n\n // Check if these tool calls have been responded to\n const respondedToolIds = new Set(\n messages\n .slice(i + 1)\n .filter((m) => m.role === 'tool')\n .map((m) => m.tool_call_id)\n );\n\n // Get unresponded tool calls\n for (const tc of clientToolCalls) {\n if (!respondedToolIds.has(tc.id)) {\n pendingCalls.push(tc);\n }\n }\n\n // Only check the most recent assistant message with tool calls\n break;\n }\n }\n\n return pendingCalls;\n },\n [isClientTool]\n );\n\n return {\n toolSchemas,\n isClientTool,\n hasResponseWidget,\n getTool,\n handleToolCalls,\n extractPendingToolCalls,\n };\n}\n"],"names":["useMemo","useCallback"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useModelInterface.js","sources":["../../../../src/hooks/useModelInterface.ts"],"sourcesContent":["import { useCallback, useMemo } from 'react';\nimport type {\n ModelInterfaceTool,\n ModelInterfaceToolSchema,\n ToolCall,\n ToolCallResponse,\n ChatMessage,\n ResponseWidgetConfig,\n} from '../api/types';\n\nexport interface PendingWidgetCall {\n toolCall: ToolCall;\n params: any;\n widget: ResponseWidgetConfig;\n toolName: string;\n}\n\nexport interface HandleToolCallsResult {\n /** Responses ready to send back to the API (from callback-based tools) */\n responses: ToolCallResponse[];\n /** Tool calls that require user interaction via a response widget */\n widgetCalls: PendingWidgetCall[];\n}\n\nexport interface UseModelInterfaceOptions {\n /**\n * Client-side tools available for model interface protocol\n */\n tools: ModelInterfaceTool[];\n\n /**\n * Callback when a tool is being executed\n */\n onToolExecute?: (toolName: string, params: any) => void;\n\n /**\n * Callback when a tool execution completes\n */\n onToolComplete?: (toolName: string, result: any) => void;\n\n /**\n * Callback when a tool execution fails\n */\n onToolError?: (toolName: string, error: Error) => void;\n}\n\nexport interface UseModelInterfaceResult {\n /**\n * Tool schemas to send to the API\n */\n toolSchemas: ModelInterfaceToolSchema[];\n\n /**\n * Check if a tool call should be handled client-side\n */\n isClientTool: (toolName: string) => boolean;\n\n /**\n * Check if a tool has a response widget (user-driven) instead of a callback\n */\n hasResponseWidget: (toolName: string) => boolean;\n\n /**\n * Get the tool definition by name\n */\n getTool: (toolName: string) => ModelInterfaceTool | undefined;\n\n /**\n * Handle tool calls from the model.\n * Callback-based tools are executed immediately and their responses returned.\n * Widget-based tools are returned as pending widget calls for user interaction.\n */\n handleToolCalls: (toolCalls: ToolCall[]) => Promise<HandleToolCallsResult>;\n\n /**\n * Process messages and extract pending tool calls that need client handling\n */\n extractPendingToolCalls: (messages: ChatMessage[]) => ToolCall[];\n}\n\n/**\n * Hook for implementing the Model Interface Protocol\n *\n * The Model Interface Protocol allows client-side tools to be executed\n * during an assistant conversation. When the model calls a client-side tool,\n * this hook handles executing the tool and preparing the response.\n *\n * @example\n * ```tsx\n * const { toolSchemas, handleToolCalls } = useModelInterface({\n * tools: [\n * {\n * toolName: 'get_user_location',\n * schema: {\n * type: 'function',\n * function: {\n * name: 'get_user_location',\n * description: 'Get user current location',\n * parameters: { type: 'object', properties: {} }\n * }\n * },\n * callback: async () => {\n * const pos = await getCurrentPosition();\n * return { lat: pos.coords.latitude, lng: pos.coords.longitude };\n * }\n * }\n * ]\n * });\n * ```\n */\nexport function useModelInterface(\n options: UseModelInterfaceOptions\n): UseModelInterfaceResult {\n const { tools, onToolExecute, onToolComplete, onToolError } = options;\n\n // Extract tool schemas for API\n const toolSchemas = useMemo(() => {\n return tools.map((tool) => tool.schema);\n }, [tools]);\n\n // Map of tool name to tool definition\n const toolMap = useMemo(() => {\n return new Map(tools.map((tool) => [tool.toolName, tool]));\n }, [tools]);\n\n // Check if a tool is a client-side tool\n const isClientTool = useCallback(\n (toolName: string): boolean => {\n return toolMap.has(toolName);\n },\n [toolMap]\n );\n\n const hasResponseWidget = useCallback(\n (toolName: string): boolean => Boolean(toolMap.get(toolName)?.responseWidget),\n [toolMap]\n );\n\n const getTool = useCallback(\n (toolName: string): ModelInterfaceTool | undefined => toolMap.get(toolName),\n [toolMap]\n );\n\n // Handle tool calls: execute callback tools, queue widget tools for user input\n const handleToolCalls = useCallback(\n async (toolCalls: ToolCall[]): Promise<HandleToolCallsResult> => {\n const responses: ToolCallResponse[] = [];\n const widgetCalls: PendingWidgetCall[] = [];\n\n for (const toolCall of toolCalls) {\n const toolName = toolCall.function.name;\n const tool = toolMap.get(toolName);\n\n if (!tool) continue;\n\n let params: any = {};\n try {\n params = JSON.parse(toolCall.function.arguments || '{}');\n } catch {\n // Keep empty params if parsing fails\n }\n\n onToolExecute?.(toolName, params);\n\n if (tool.responseWidget) {\n widgetCalls.push({\n toolCall,\n params,\n widget: tool.responseWidget,\n toolName,\n });\n continue;\n }\n\n if (!tool.callback) {\n // Neither callback nor widget — respond with error so the model can continue\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: `Tool \"${toolName}\" has no callback or responseWidget` },\n role: 'tool',\n });\n continue;\n }\n\n try {\n const result = await tool.callback(params);\n onToolComplete?.(toolName, result);\n responses.push({\n tool_call_id: toolCall.id,\n content: result,\n role: 'tool',\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onToolError?.(toolName, error);\n responses.push({\n tool_call_id: toolCall.id,\n content: { error: error.message },\n role: 'tool',\n });\n }\n }\n\n return { responses, widgetCalls };\n },\n [toolMap, onToolExecute, onToolComplete, onToolError]\n );\n\n // Extract pending tool calls from messages that need client handling\n const extractPendingToolCalls = useCallback(\n (messages: ChatMessage[]): ToolCall[] => {\n const pendingCalls: ToolCall[] = [];\n\n // Look at the last assistant message\n for (let i = messages.length - 1; i >= 0; i--) {\n const message = messages[i];\n\n if (message.role === 'assistant' && message.tool_calls?.length) {\n // Filter for client-side tools only\n const clientToolCalls = message.tool_calls.filter((tc) =>\n isClientTool(tc.function.name)\n );\n\n // Check if these tool calls have been responded to\n const respondedToolIds = new Set(\n messages\n .slice(i + 1)\n .filter((m) => m.role === 'tool')\n .map((m) => m.tool_call_id)\n );\n\n // Get unresponded tool calls\n for (const tc of clientToolCalls) {\n if (!respondedToolIds.has(tc.id)) {\n pendingCalls.push(tc);\n }\n }\n\n // Only check the most recent assistant message with tool calls\n break;\n }\n }\n\n return pendingCalls;\n },\n [isClientTool]\n );\n\n return {\n toolSchemas,\n isClientTool,\n hasResponseWidget,\n getTool,\n handleToolCalls,\n extractPendingToolCalls,\n };\n}\n"],"names":["useMemo","useCallback"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
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\n/**\n * Cadence used when nobody configures one.\n */\nexport const DEFAULT_POLLING_INTERVAL_MS = 1000;\n\n/**\n * Floor applied to any configured cadence. A conversation in flight is polled\n * from every mounted component, so a value below this turns the widget into a\n * request loop against the API rather than a faster chat.\n */\nexport const MIN_POLLING_INTERVAL_MS = 250;\n\n/**\n * Picks the first usable cadence out of the candidates, in priority order\n * (component prop, then provider, then the caller's own default), ignoring\n * anything that is not a positive finite number and clamping the winner to\n * `MIN_POLLING_INTERVAL_MS`.\n */\nexport function resolvePollingInterval(\n ...candidates: Array<number | undefined | null>\n): number {\n for (const value of candidates) {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n return Math.max(value, MIN_POLLING_INTERVAL_MS);\n }\n }\n return DEFAULT_POLLING_INTERVAL_MS;\n}\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 = DEFAULT_POLLING_INTERVAL_MS,\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 // Cadence the running timer was armed with, so a change can be detected.\n const armedIntervalRef = useRef<number | null>(null);\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 armedIntervalRef.current = null;\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 armedIntervalRef.current = intervalValueRef.current;\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 armedIntervalRef.current = intervalValueRef.current;\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 // Re-arm the timer when the cadence changes while a conversation is already\n // in flight. Without this a new `interval` would only take effect on the next\n // conversation, which is not what changing a provider setting looks like.\n useEffect(() => {\n if (!isPollingRef.current || armedIntervalRef.current === null) return;\n if (armedIntervalRef.current === interval) return;\n\n logRef.current.log('[usePolling] Interval changed mid-flight:', {\n from: armedIntervalRef.current,\n to: interval,\n });\n if (intervalRef.current) clearInterval(intervalRef.current);\n armedIntervalRef.current = interval;\n intervalRef.current = setInterval(fetchData, interval);\n }, [interval, fetchData]);\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":["useMemo","createLogger","useRef","useState","useEffect","useCallback"],"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\n/**\n * Cadence used when nobody configures one.\n */\nexport const DEFAULT_POLLING_INTERVAL_MS = 1000;\n\n/**\n * Floor applied to any configured cadence. A conversation in flight is polled\n * from every mounted component, so a value below this turns the widget into a\n * request loop against the API rather than a faster chat.\n */\nexport const MIN_POLLING_INTERVAL_MS = 250;\n\n/**\n * Picks the first usable cadence out of the candidates, in priority order\n * (component prop, then provider, then the caller's own default), ignoring\n * anything that is not a positive finite number and clamping the winner to\n * `MIN_POLLING_INTERVAL_MS`.\n */\nexport function resolvePollingInterval(\n ...candidates: Array<number | undefined | null>\n): number {\n for (const value of candidates) {\n if (typeof value === 'number' && Number.isFinite(value) && value > 0) {\n return Math.max(value, MIN_POLLING_INTERVAL_MS);\n }\n }\n return DEFAULT_POLLING_INTERVAL_MS;\n}\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 = DEFAULT_POLLING_INTERVAL_MS,\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 // Cadence the running timer was armed with, so a change can be detected.\n const armedIntervalRef = useRef<number | null>(null);\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 armedIntervalRef.current = null;\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 armedIntervalRef.current = intervalValueRef.current;\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 armedIntervalRef.current = intervalValueRef.current;\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 // Re-arm the timer when the cadence changes while a conversation is already\n // in flight. Without this a new `interval` would only take effect on the next\n // conversation, which is not what changing a provider setting looks like.\n useEffect(() => {\n if (!isPollingRef.current || armedIntervalRef.current === null) return;\n if (armedIntervalRef.current === interval) return;\n\n logRef.current.log('[usePolling] Interval changed mid-flight:', {\n from: armedIntervalRef.current,\n to: interval,\n });\n if (intervalRef.current) clearInterval(intervalRef.current);\n armedIntervalRef.current = interval;\n intervalRef.current = setInterval(fetchData, interval);\n }, [interval, fetchData]);\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":["useMemo","createLogger","useRef","useState","useEffect","useCallback"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSpeechRecording.js","sources":["../../../../src/hooks/useSpeechRecording.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Recording lifecycle state.\n * - `idle`: not recording\n * - `recording`: actively capturing audio\n * - `paused`: capture paused, can be resumed\n */\nexport type SpeechRecordingStatus = 'idle' | 'recording' | 'paused';\n\nexport interface UseSpeechRecordingOptions {\n /** Number of equalizer bars to expose in `levels`. @default 5 */\n bars?: number;\n /** Preferred MediaRecorder mime type. Falls back to a supported one. */\n mimeType?: string;\n /**\n * Auto-stop the recording after a short silence, but only once speech has\n * actually been detected. When it triggers, the countdown runs and then\n * `onAutoStop` is fired. Disable to require a manual confirm. @default true\n */\n autoStop?: boolean;\n /**\n * Continuous silence (ms) that must elapse — after speech was detected —\n * before the auto-stop countdown begins. @default 1000\n */\n autoStopSilenceMs?: number;\n /** Duration (ms) of the auto-stop countdown shown before firing. @default 1000 */\n autoStopCountdownMs?: number;\n /**\n * Silence is **adaptive**: the threshold is this fraction of the loudest\n * speech observed in the recording (e.g. 0.1 = 10% of peak voice). This makes\n * it robust to ambient noise — a quiet room and a loud one calibrate\n * differently. @default 0.1\n */\n autoStopSilenceRatio?: number;\n /**\n * Absolute floor (0..1) for the adaptive silence threshold, so it never drops\n * so low that background hiss reads as \"sound\". @default 0.02\n */\n autoStopSilenceLevel?: number;\n /**\n * Absolute floor (0..1) a peak must clear to first count as speech (arms the\n * detector / calibrates the reference loudness). @default 0.12\n */\n autoStopSpeechLevel?: number;\n /**\n * Fired when the silence countdown completes. The consumer decides what\n * \"stop\" means (e.g. confirm + transcribe). Read fresh on every frame, so it\n * does not need to be memoized.\n */\n onAutoStop?: () => void;\n}\n\nexport interface UseSpeechRecordingResult {\n status: SpeechRecordingStatus;\n isRecording: boolean;\n isPaused: boolean;\n /** Whether the browser supports audio recording. */\n isSupported: boolean;\n /** Normalized amplitude per bar (0..1), updated in real time for the equalizer. */\n levels: number[];\n /** Elapsed recording time in milliseconds (excludes paused time). */\n durationMs: number;\n /** Last error message, if any (e.g. permission denied). */\n error: string | null;\n /** True while the silence-triggered auto-stop countdown is running. */\n isAutoStopping: boolean;\n /** Auto-stop countdown progress, 1 (full) → 0 (empty), for an inverted ring. */\n autoStopProgress: number;\n /** True once a real voice peak was detected in the current recording. */\n speechDetected: boolean;\n /** Request mic access and start recording. */\n start: () => Promise<void>;\n /** Pause an active recording. */\n pause: () => void;\n /** Resume a paused recording. */\n resume: () => void;\n /** Stop and return the recorded audio as a Blob (null if nothing captured). */\n stop: () => Promise<Blob | null>;\n /** Stop and discard the recording without producing a Blob. */\n cancel: () => void;\n}\n\nconst PREFERRED_MIME_TYPES = [\n 'audio/webm;codecs=opus',\n 'audio/webm',\n 'audio/ogg;codecs=opus',\n 'audio/mp4',\n];\n\nfunction pickMimeType(preferred?: string): string | undefined {\n if (typeof MediaRecorder === 'undefined') return undefined;\n const candidates = preferred\n ? [preferred, ...PREFERRED_MIME_TYPES]\n : PREFERRED_MIME_TYPES;\n for (const type of candidates) {\n if (MediaRecorder.isTypeSupported(type)) return type;\n }\n return undefined;\n}\n\n/**\n * Encapsulates microphone capture via MediaRecorder plus a Web Audio analyser\n * that exposes live amplitude levels for an equalizer-style animation.\n *\n * The hook owns all teardown: stopping tracks, closing the AudioContext and\n * cancelling the animation frame on stop/cancel/unmount.\n */\nexport function useSpeechRecording(\n options: UseSpeechRecordingOptions = {},\n): UseSpeechRecordingResult {\n const {\n bars = 5,\n mimeType,\n autoStop = true,\n autoStopSilenceMs = 1000,\n autoStopCountdownMs = 1000,\n autoStopSilenceRatio = 0.1,\n autoStopSilenceLevel = 0.02,\n autoStopSpeechLevel = 0.12,\n onAutoStop,\n } = options;\n\n const isSupported =\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices?.getUserMedia &&\n typeof MediaRecorder !== 'undefined';\n\n const [status, setStatus] = useState<SpeechRecordingStatus>('idle');\n const [levels, setLevels] = useState<number[]>(() => new Array(bars).fill(0));\n const [durationMs, setDurationMs] = useState(0);\n const [error, setError] = useState<string | null>(null);\n const [isAutoStopping, setIsAutoStopping] = useState(false);\n const [autoStopProgress, setAutoStopProgress] = useState(1);\n const [speechDetected, setSpeechDetected] = useState(false);\n\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n const audioContextRef = useRef<AudioContext | null>(null);\n const analyserRef = useRef<AnalyserNode | null>(null);\n const rafRef = useRef<number | null>(null);\n const startedAtRef = useRef<number>(0);\n const accumulatedRef = useRef<number>(0);\n const stopResolveRef = useRef<((blob: Blob | null) => void) | null>(null);\n const discardRef = useRef<boolean>(false);\n\n // --- Auto-stop (silence detection) state, all kept in refs so the rAF loop\n // reads it without forcing `tick` to re-subscribe. ---\n const hasSpeechRef = useRef(false); // user has spoken at least once\n const loudestRef = useRef(0); // loudest peak seen (reference for adaptive silence)\n const silenceStartRef = useRef<number | null>(null); // when current silence began\n const autoStopStartRef = useRef<number | null>(null); // when the countdown started\n const autoStopFiredRef = useRef(false); // guard against re-firing before stop\n // Latest-config ref so the loop always sees fresh thresholds and callback.\n const autoStopCfgRef = useRef({\n autoStop,\n autoStopSilenceMs,\n autoStopCountdownMs,\n autoStopSilenceRatio,\n autoStopSilenceLevel,\n autoStopSpeechLevel,\n onAutoStop,\n });\n autoStopCfgRef.current = {\n autoStop,\n autoStopSilenceMs,\n autoStopCountdownMs,\n autoStopSilenceRatio,\n autoStopSilenceLevel,\n autoStopSpeechLevel,\n onAutoStop,\n };\n\n // Reset all auto-stop tracking. `keepSpeech` preserves the \"user has spoken\"\n // flag across a pause/resume so it doesn't re-arm from scratch.\n const resetAutoStop = useCallback((keepSpeech = false) => {\n if (!keepSpeech) {\n hasSpeechRef.current = false;\n loudestRef.current = 0;\n setSpeechDetected(false);\n }\n silenceStartRef.current = null;\n autoStopStartRef.current = null;\n autoStopFiredRef.current = false;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n }, []);\n\n // Evaluate the current peak amplitude against the silence/speech thresholds\n // and drive the auto-stop countdown. Called once per animation frame.\n const evaluateAutoStop = useCallback((peak: number) => {\n const cfg = autoStopCfgRef.current;\n if (!cfg.autoStop || autoStopFiredRef.current) return;\n const now = Date.now();\n\n // 1) Arm only once a real voice peak (absolute floor) has been heard. This\n // also seeds the reference loudness so ambient noise alone can't arm it.\n if (!hasSpeechRef.current) {\n if (peak >= cfg.autoStopSpeechLevel) {\n hasSpeechRef.current = true;\n loudestRef.current = peak;\n setSpeechDetected(true);\n }\n return;\n }\n\n // 2) Track the loudest voice so the silence threshold scales with how loud\n // the user actually speaks (robust to ambient noise).\n if (peak > loudestRef.current) loudestRef.current = peak;\n\n // 3) Adaptive thresholds: silence = ratio of the loudest voice (with an\n // absolute floor); activity sits above it for hysteresis, so background\n // noise between the two doesn't keep cancelling the countdown.\n const silenceThreshold = Math.max(\n cfg.autoStopSilenceLevel,\n cfg.autoStopSilenceRatio * loudestRef.current,\n );\n const activityThreshold = silenceThreshold * 1.8;\n\n if (peak >= activityThreshold) {\n // Talking again: reset silence and cancel any pending countdown.\n silenceStartRef.current = null;\n if (autoStopStartRef.current !== null) {\n autoStopStartRef.current = null;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n }\n return;\n }\n\n if (peak <= silenceThreshold) {\n if (silenceStartRef.current === null) silenceStartRef.current = now;\n\n if (autoStopStartRef.current === null) {\n // Waiting out the silence window before the countdown begins.\n if (now - silenceStartRef.current >= cfg.autoStopSilenceMs) {\n autoStopStartRef.current = now;\n setIsAutoStopping(true);\n setAutoStopProgress(1);\n }\n } else {\n // Countdown running: drain the ring 1 → 0, then fire.\n const elapsed = now - autoStopStartRef.current;\n const progress = Math.max(0, 1 - elapsed / cfg.autoStopCountdownMs);\n setAutoStopProgress(progress);\n if (elapsed >= cfg.autoStopCountdownMs) {\n autoStopFiredRef.current = true;\n autoStopStartRef.current = null;\n silenceStartRef.current = null;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n cfg.onAutoStop?.();\n }\n }\n }\n // Hysteresis deadzone (silence < peak < activity): keep timers running.\n }, []);\n\n const cleanupAudioGraph = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n if (audioContextRef.current) {\n audioContextRef.current.close().catch(() => undefined);\n audioContextRef.current = null;\n }\n analyserRef.current = null;\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((t) => t.stop());\n streamRef.current = null;\n }\n }, []);\n\n // Drives both the equalizer levels and the duration counter.\n const tick = useCallback(() => {\n const analyser = analyserRef.current;\n if (analyser) {\n const data = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(data);\n const bucketSize = Math.floor(data.length / bars) || 1;\n const next: number[] = [];\n let peak = 0;\n for (let i = 0; i < bars; i++) {\n let sum = 0;\n for (let j = 0; j < bucketSize; j++) {\n sum += data[i * bucketSize + j] ?? 0;\n }\n // Normalize 0..255 -> 0..1 with a small floor so bars stay visible.\n const level = Math.min(1, sum / bucketSize / 255);\n next.push(level);\n if (level > peak) peak = level;\n }\n setLevels(next);\n evaluateAutoStop(peak);\n }\n setDurationMs(accumulatedRef.current + (Date.now() - startedAtRef.current));\n rafRef.current = requestAnimationFrame(tick);\n }, [bars, evaluateAutoStop]);\n\n const start = useCallback(async () => {\n if (!isSupported) {\n setError('Audio recording is not supported in this browser');\n return;\n }\n if (mediaRecorderRef.current) return;\n\n setError(null);\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n streamRef.current = stream;\n\n const selectedType = pickMimeType(mimeType);\n const recorder = selectedType\n ? new MediaRecorder(stream, { mimeType: selectedType })\n : new MediaRecorder(stream);\n mediaRecorderRef.current = recorder;\n chunksRef.current = [];\n discardRef.current = false;\n resetAutoStop();\n\n recorder.ondataavailable = (e: BlobEvent) => {\n if (e.data && e.data.size > 0) chunksRef.current.push(e.data);\n };\n recorder.onstop = () => {\n const type = recorder.mimeType || selectedType || 'audio/webm';\n const blob = discardRef.current\n ? null\n : new Blob(chunksRef.current, { type });\n chunksRef.current = [];\n cleanupAudioGraph();\n mediaRecorderRef.current = null;\n setStatus('idle');\n setLevels(new Array(bars).fill(0));\n setDurationMs(0);\n accumulatedRef.current = 0;\n resetAutoStop();\n const resolve = stopResolveRef.current;\n stopResolveRef.current = null;\n resolve?.(blob);\n };\n\n // Web Audio analyser for the equalizer animation.\n const AudioCtx =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext: typeof AudioContext })\n .webkitAudioContext;\n const audioContext = new AudioCtx();\n const source = audioContext.createMediaStreamSource(stream);\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = 64;\n source.connect(analyser);\n audioContextRef.current = audioContext;\n analyserRef.current = analyser;\n\n accumulatedRef.current = 0;\n startedAtRef.current = Date.now();\n recorder.start(100);\n setStatus('recording');\n rafRef.current = requestAnimationFrame(tick);\n } catch (e) {\n cleanupAudioGraph();\n mediaRecorderRef.current = null;\n const message =\n (e as Error)?.name === 'NotAllowedError'\n ? 'Microphone permission denied'\n : `Could not start recording: ${(e as Error)?.message || 'unknown error'}`;\n setError(message);\n setStatus('idle');\n }\n }, [isSupported, mimeType, bars, tick, cleanupAudioGraph, resetAutoStop]);\n\n const pause = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state === 'recording') {\n recorder.pause();\n accumulatedRef.current += Date.now() - startedAtRef.current;\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n setLevels(new Array(bars).fill(0));\n // Drop any in-flight silence/countdown but remember that speech happened.\n resetAutoStop(true);\n setStatus('paused');\n }\n }, [bars, resetAutoStop]);\n\n const resume = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state === 'paused') {\n recorder.resume();\n startedAtRef.current = Date.now();\n // Fresh silence timing on resume so it doesn't fire immediately.\n resetAutoStop(true);\n rafRef.current = requestAnimationFrame(tick);\n setStatus('recording');\n }\n }, [tick, resetAutoStop]);\n\n const stop = useCallback((): Promise<Blob | null> => {\n const recorder = mediaRecorderRef.current;\n if (!recorder) return Promise.resolve(null);\n discardRef.current = false;\n return new Promise<Blob | null>((resolve) => {\n stopResolveRef.current = resolve;\n recorder.stop();\n });\n }, []);\n\n const cancel = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (!recorder) {\n cleanupAudioGraph();\n setStatus('idle');\n setLevels(new Array(bars).fill(0));\n setDurationMs(0);\n resetAutoStop();\n return;\n }\n discardRef.current = true;\n recorder.stop();\n }, [bars, cleanupAudioGraph, resetAutoStop]);\n\n // Teardown on unmount.\n useEffect(() => {\n return () => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state !== 'inactive') {\n discardRef.current = true;\n try {\n recorder.stop();\n } catch {\n // ignore\n }\n }\n cleanupAudioGraph();\n };\n }, [cleanupAudioGraph]);\n\n return {\n status,\n isRecording: status === 'recording',\n isPaused: status === 'paused',\n isSupported,\n levels,\n durationMs,\n error,\n isAutoStopping,\n autoStopProgress,\n speechDetected,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useSpeechRecording.js","sources":["../../../../src/hooks/useSpeechRecording.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\n/**\n * Recording lifecycle state.\n * - `idle`: not recording\n * - `recording`: actively capturing audio\n * - `paused`: capture paused, can be resumed\n */\nexport type SpeechRecordingStatus = 'idle' | 'recording' | 'paused';\n\nexport interface UseSpeechRecordingOptions {\n /** Number of equalizer bars to expose in `levels`. @default 5 */\n bars?: number;\n /** Preferred MediaRecorder mime type. Falls back to a supported one. */\n mimeType?: string;\n /**\n * Auto-stop the recording after a short silence, but only once speech has\n * actually been detected. When it triggers, the countdown runs and then\n * `onAutoStop` is fired. Disable to require a manual confirm. @default true\n */\n autoStop?: boolean;\n /**\n * Continuous silence (ms) that must elapse — after speech was detected —\n * before the auto-stop countdown begins. @default 1000\n */\n autoStopSilenceMs?: number;\n /** Duration (ms) of the auto-stop countdown shown before firing. @default 1000 */\n autoStopCountdownMs?: number;\n /**\n * Silence is **adaptive**: the threshold is this fraction of the loudest\n * speech observed in the recording (e.g. 0.1 = 10% of peak voice). This makes\n * it robust to ambient noise — a quiet room and a loud one calibrate\n * differently. @default 0.1\n */\n autoStopSilenceRatio?: number;\n /**\n * Absolute floor (0..1) for the adaptive silence threshold, so it never drops\n * so low that background hiss reads as \"sound\". @default 0.02\n */\n autoStopSilenceLevel?: number;\n /**\n * Absolute floor (0..1) a peak must clear to first count as speech (arms the\n * detector / calibrates the reference loudness). @default 0.12\n */\n autoStopSpeechLevel?: number;\n /**\n * Fired when the silence countdown completes. The consumer decides what\n * \"stop\" means (e.g. confirm + transcribe). Read fresh on every frame, so it\n * does not need to be memoized.\n */\n onAutoStop?: () => void;\n}\n\nexport interface UseSpeechRecordingResult {\n status: SpeechRecordingStatus;\n isRecording: boolean;\n isPaused: boolean;\n /** Whether the browser supports audio recording. */\n isSupported: boolean;\n /** Normalized amplitude per bar (0..1), updated in real time for the equalizer. */\n levels: number[];\n /** Elapsed recording time in milliseconds (excludes paused time). */\n durationMs: number;\n /** Last error message, if any (e.g. permission denied). */\n error: string | null;\n /** True while the silence-triggered auto-stop countdown is running. */\n isAutoStopping: boolean;\n /** Auto-stop countdown progress, 1 (full) → 0 (empty), for an inverted ring. */\n autoStopProgress: number;\n /** True once a real voice peak was detected in the current recording. */\n speechDetected: boolean;\n /** Request mic access and start recording. */\n start: () => Promise<void>;\n /** Pause an active recording. */\n pause: () => void;\n /** Resume a paused recording. */\n resume: () => void;\n /** Stop and return the recorded audio as a Blob (null if nothing captured). */\n stop: () => Promise<Blob | null>;\n /** Stop and discard the recording without producing a Blob. */\n cancel: () => void;\n}\n\nconst PREFERRED_MIME_TYPES = [\n 'audio/webm;codecs=opus',\n 'audio/webm',\n 'audio/ogg;codecs=opus',\n 'audio/mp4',\n];\n\nfunction pickMimeType(preferred?: string): string | undefined {\n if (typeof MediaRecorder === 'undefined') return undefined;\n const candidates = preferred\n ? [preferred, ...PREFERRED_MIME_TYPES]\n : PREFERRED_MIME_TYPES;\n for (const type of candidates) {\n if (MediaRecorder.isTypeSupported(type)) return type;\n }\n return undefined;\n}\n\n/**\n * Encapsulates microphone capture via MediaRecorder plus a Web Audio analyser\n * that exposes live amplitude levels for an equalizer-style animation.\n *\n * The hook owns all teardown: stopping tracks, closing the AudioContext and\n * cancelling the animation frame on stop/cancel/unmount.\n */\nexport function useSpeechRecording(\n options: UseSpeechRecordingOptions = {},\n): UseSpeechRecordingResult {\n const {\n bars = 5,\n mimeType,\n autoStop = true,\n autoStopSilenceMs = 1000,\n autoStopCountdownMs = 1000,\n autoStopSilenceRatio = 0.1,\n autoStopSilenceLevel = 0.02,\n autoStopSpeechLevel = 0.12,\n onAutoStop,\n } = options;\n\n const isSupported =\n typeof navigator !== 'undefined' &&\n !!navigator.mediaDevices?.getUserMedia &&\n typeof MediaRecorder !== 'undefined';\n\n const [status, setStatus] = useState<SpeechRecordingStatus>('idle');\n const [levels, setLevels] = useState<number[]>(() => new Array(bars).fill(0));\n const [durationMs, setDurationMs] = useState(0);\n const [error, setError] = useState<string | null>(null);\n const [isAutoStopping, setIsAutoStopping] = useState(false);\n const [autoStopProgress, setAutoStopProgress] = useState(1);\n const [speechDetected, setSpeechDetected] = useState(false);\n\n const mediaRecorderRef = useRef<MediaRecorder | null>(null);\n const streamRef = useRef<MediaStream | null>(null);\n const chunksRef = useRef<Blob[]>([]);\n const audioContextRef = useRef<AudioContext | null>(null);\n const analyserRef = useRef<AnalyserNode | null>(null);\n const rafRef = useRef<number | null>(null);\n const startedAtRef = useRef<number>(0);\n const accumulatedRef = useRef<number>(0);\n const stopResolveRef = useRef<((blob: Blob | null) => void) | null>(null);\n const discardRef = useRef<boolean>(false);\n\n // --- Auto-stop (silence detection) state, all kept in refs so the rAF loop\n // reads it without forcing `tick` to re-subscribe. ---\n const hasSpeechRef = useRef(false); // user has spoken at least once\n const loudestRef = useRef(0); // loudest peak seen (reference for adaptive silence)\n const silenceStartRef = useRef<number | null>(null); // when current silence began\n const autoStopStartRef = useRef<number | null>(null); // when the countdown started\n const autoStopFiredRef = useRef(false); // guard against re-firing before stop\n // Latest-config ref so the loop always sees fresh thresholds and callback.\n const autoStopCfgRef = useRef({\n autoStop,\n autoStopSilenceMs,\n autoStopCountdownMs,\n autoStopSilenceRatio,\n autoStopSilenceLevel,\n autoStopSpeechLevel,\n onAutoStop,\n });\n autoStopCfgRef.current = {\n autoStop,\n autoStopSilenceMs,\n autoStopCountdownMs,\n autoStopSilenceRatio,\n autoStopSilenceLevel,\n autoStopSpeechLevel,\n onAutoStop,\n };\n\n // Reset all auto-stop tracking. `keepSpeech` preserves the \"user has spoken\"\n // flag across a pause/resume so it doesn't re-arm from scratch.\n const resetAutoStop = useCallback((keepSpeech = false) => {\n if (!keepSpeech) {\n hasSpeechRef.current = false;\n loudestRef.current = 0;\n setSpeechDetected(false);\n }\n silenceStartRef.current = null;\n autoStopStartRef.current = null;\n autoStopFiredRef.current = false;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n }, []);\n\n // Evaluate the current peak amplitude against the silence/speech thresholds\n // and drive the auto-stop countdown. Called once per animation frame.\n const evaluateAutoStop = useCallback((peak: number) => {\n const cfg = autoStopCfgRef.current;\n if (!cfg.autoStop || autoStopFiredRef.current) return;\n const now = Date.now();\n\n // 1) Arm only once a real voice peak (absolute floor) has been heard. This\n // also seeds the reference loudness so ambient noise alone can't arm it.\n if (!hasSpeechRef.current) {\n if (peak >= cfg.autoStopSpeechLevel) {\n hasSpeechRef.current = true;\n loudestRef.current = peak;\n setSpeechDetected(true);\n }\n return;\n }\n\n // 2) Track the loudest voice so the silence threshold scales with how loud\n // the user actually speaks (robust to ambient noise).\n if (peak > loudestRef.current) loudestRef.current = peak;\n\n // 3) Adaptive thresholds: silence = ratio of the loudest voice (with an\n // absolute floor); activity sits above it for hysteresis, so background\n // noise between the two doesn't keep cancelling the countdown.\n const silenceThreshold = Math.max(\n cfg.autoStopSilenceLevel,\n cfg.autoStopSilenceRatio * loudestRef.current,\n );\n const activityThreshold = silenceThreshold * 1.8;\n\n if (peak >= activityThreshold) {\n // Talking again: reset silence and cancel any pending countdown.\n silenceStartRef.current = null;\n if (autoStopStartRef.current !== null) {\n autoStopStartRef.current = null;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n }\n return;\n }\n\n if (peak <= silenceThreshold) {\n if (silenceStartRef.current === null) silenceStartRef.current = now;\n\n if (autoStopStartRef.current === null) {\n // Waiting out the silence window before the countdown begins.\n if (now - silenceStartRef.current >= cfg.autoStopSilenceMs) {\n autoStopStartRef.current = now;\n setIsAutoStopping(true);\n setAutoStopProgress(1);\n }\n } else {\n // Countdown running: drain the ring 1 → 0, then fire.\n const elapsed = now - autoStopStartRef.current;\n const progress = Math.max(0, 1 - elapsed / cfg.autoStopCountdownMs);\n setAutoStopProgress(progress);\n if (elapsed >= cfg.autoStopCountdownMs) {\n autoStopFiredRef.current = true;\n autoStopStartRef.current = null;\n silenceStartRef.current = null;\n setIsAutoStopping(false);\n setAutoStopProgress(1);\n cfg.onAutoStop?.();\n }\n }\n }\n // Hysteresis deadzone (silence < peak < activity): keep timers running.\n }, []);\n\n const cleanupAudioGraph = useCallback(() => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n if (audioContextRef.current) {\n audioContextRef.current.close().catch(() => undefined);\n audioContextRef.current = null;\n }\n analyserRef.current = null;\n if (streamRef.current) {\n streamRef.current.getTracks().forEach((t) => t.stop());\n streamRef.current = null;\n }\n }, []);\n\n // Drives both the equalizer levels and the duration counter.\n const tick = useCallback(() => {\n const analyser = analyserRef.current;\n if (analyser) {\n const data = new Uint8Array(analyser.frequencyBinCount);\n analyser.getByteFrequencyData(data);\n const bucketSize = Math.floor(data.length / bars) || 1;\n const next: number[] = [];\n let peak = 0;\n for (let i = 0; i < bars; i++) {\n let sum = 0;\n for (let j = 0; j < bucketSize; j++) {\n sum += data[i * bucketSize + j] ?? 0;\n }\n // Normalize 0..255 -> 0..1 with a small floor so bars stay visible.\n const level = Math.min(1, sum / bucketSize / 255);\n next.push(level);\n if (level > peak) peak = level;\n }\n setLevels(next);\n evaluateAutoStop(peak);\n }\n setDurationMs(accumulatedRef.current + (Date.now() - startedAtRef.current));\n rafRef.current = requestAnimationFrame(tick);\n }, [bars, evaluateAutoStop]);\n\n const start = useCallback(async () => {\n if (!isSupported) {\n setError('Audio recording is not supported in this browser');\n return;\n }\n if (mediaRecorderRef.current) return;\n\n setError(null);\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n streamRef.current = stream;\n\n const selectedType = pickMimeType(mimeType);\n const recorder = selectedType\n ? new MediaRecorder(stream, { mimeType: selectedType })\n : new MediaRecorder(stream);\n mediaRecorderRef.current = recorder;\n chunksRef.current = [];\n discardRef.current = false;\n resetAutoStop();\n\n recorder.ondataavailable = (e: BlobEvent) => {\n if (e.data && e.data.size > 0) chunksRef.current.push(e.data);\n };\n recorder.onstop = () => {\n const type = recorder.mimeType || selectedType || 'audio/webm';\n const blob = discardRef.current\n ? null\n : new Blob(chunksRef.current, { type });\n chunksRef.current = [];\n cleanupAudioGraph();\n mediaRecorderRef.current = null;\n setStatus('idle');\n setLevels(new Array(bars).fill(0));\n setDurationMs(0);\n accumulatedRef.current = 0;\n resetAutoStop();\n const resolve = stopResolveRef.current;\n stopResolveRef.current = null;\n resolve?.(blob);\n };\n\n // Web Audio analyser for the equalizer animation.\n const AudioCtx =\n window.AudioContext ||\n (window as unknown as { webkitAudioContext: typeof AudioContext })\n .webkitAudioContext;\n const audioContext = new AudioCtx();\n const source = audioContext.createMediaStreamSource(stream);\n const analyser = audioContext.createAnalyser();\n analyser.fftSize = 64;\n source.connect(analyser);\n audioContextRef.current = audioContext;\n analyserRef.current = analyser;\n\n accumulatedRef.current = 0;\n startedAtRef.current = Date.now();\n recorder.start(100);\n setStatus('recording');\n rafRef.current = requestAnimationFrame(tick);\n } catch (e) {\n cleanupAudioGraph();\n mediaRecorderRef.current = null;\n const message =\n (e as Error)?.name === 'NotAllowedError'\n ? 'Microphone permission denied'\n : `Could not start recording: ${(e as Error)?.message || 'unknown error'}`;\n setError(message);\n setStatus('idle');\n }\n }, [isSupported, mimeType, bars, tick, cleanupAudioGraph, resetAutoStop]);\n\n const pause = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state === 'recording') {\n recorder.pause();\n accumulatedRef.current += Date.now() - startedAtRef.current;\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n setLevels(new Array(bars).fill(0));\n // Drop any in-flight silence/countdown but remember that speech happened.\n resetAutoStop(true);\n setStatus('paused');\n }\n }, [bars, resetAutoStop]);\n\n const resume = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state === 'paused') {\n recorder.resume();\n startedAtRef.current = Date.now();\n // Fresh silence timing on resume so it doesn't fire immediately.\n resetAutoStop(true);\n rafRef.current = requestAnimationFrame(tick);\n setStatus('recording');\n }\n }, [tick, resetAutoStop]);\n\n const stop = useCallback((): Promise<Blob | null> => {\n const recorder = mediaRecorderRef.current;\n if (!recorder) return Promise.resolve(null);\n discardRef.current = false;\n return new Promise<Blob | null>((resolve) => {\n stopResolveRef.current = resolve;\n recorder.stop();\n });\n }, []);\n\n const cancel = useCallback(() => {\n const recorder = mediaRecorderRef.current;\n if (!recorder) {\n cleanupAudioGraph();\n setStatus('idle');\n setLevels(new Array(bars).fill(0));\n setDurationMs(0);\n resetAutoStop();\n return;\n }\n discardRef.current = true;\n recorder.stop();\n }, [bars, cleanupAudioGraph, resetAutoStop]);\n\n // Teardown on unmount.\n useEffect(() => {\n return () => {\n const recorder = mediaRecorderRef.current;\n if (recorder && recorder.state !== 'inactive') {\n discardRef.current = true;\n try {\n recorder.stop();\n } catch {\n // ignore\n }\n }\n cleanupAudioGraph();\n };\n }, [cleanupAudioGraph]);\n\n return {\n status,\n isRecording: status === 'recording',\n isPaused: status === 'paused',\n isSupported,\n levels,\n durationMs,\n error,\n isAutoStopping,\n autoStopProgress,\n speechDetected,\n start,\n pause,\n resume,\n stop,\n cancel,\n };\n}\n"],"names":["useState","useRef","useCallback","useEffect"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevicContext.js","sources":["../../../../src/provider/DevicContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\nimport type { DevicContextValue } from './types';\n\n/**\n * Context for Devic configuration\n */\nexport const DevicContext = createContext<DevicContextValue | null>(null);\n\n/**\n * Hook to access the Devic context\n * @throws Error if used outside of DevicProvider\n */\nexport function useDevicContext(): DevicContextValue {\n const context = useContext(DevicContext);\n if (!context) {\n throw new Error(\n 'useDevicContext must be used within a DevicProvider. ' +\n 'Make sure to wrap your component tree with <DevicProvider>.'\n );\n }\n return context;\n}\n\n/**\n * Hook to optionally access the Devic context\n * Returns null if not within a provider (for components that can work standalone)\n */\nexport function useOptionalDevicContext(): DevicContextValue | null {\n return useContext(DevicContext);\n}\n"],"names":["createContext","useContext"],"mappings":";;;;AAGA;;AAEG;MACU,YAAY,GAAGA,mBAAa,CAA2B,IAAI;AAExE;;;AAGG;SACa,eAAe,GAAA;AAC7B,IAAA,MAAM,OAAO,GAAGC,gBAAU,CAAC,YAAY,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE;QACZ,MAAM,IAAI,KAAK,CACb,uDAAuD;AACvD,YAAA,6DAA6D,CAC9D;IACH;AACA,IAAA,OAAO,OAAO;AAChB;AAEA;;;AAGG;SACa,uBAAuB,GAAA;AACrC,IAAA,OAAOA,gBAAU,CAAC,YAAY,CAAC;AACjC
|
|
1
|
+
{"version":3,"file":"DevicContext.js","sources":["../../../../src/provider/DevicContext.ts"],"sourcesContent":["import { createContext, useContext } from 'react';\nimport type { DevicContextValue } from './types';\n\n/**\n * Context for Devic configuration\n */\nexport const DevicContext = createContext<DevicContextValue | null>(null);\n\n/**\n * Hook to access the Devic context\n * @throws Error if used outside of DevicProvider\n */\nexport function useDevicContext(): DevicContextValue {\n const context = useContext(DevicContext);\n if (!context) {\n throw new Error(\n 'useDevicContext must be used within a DevicProvider. ' +\n 'Make sure to wrap your component tree with <DevicProvider>.'\n );\n }\n return context;\n}\n\n/**\n * Hook to optionally access the Devic context\n * Returns null if not within a provider (for components that can work standalone)\n */\nexport function useOptionalDevicContext(): DevicContextValue | null {\n return useContext(DevicContext);\n}\n"],"names":["createContext","useContext"],"mappings":";;;;AAGA;;AAEG;MACU,YAAY,GAAGA,mBAAa,CAA2B,IAAI;AAExE;;;AAGG;SACa,eAAe,GAAA;AAC7B,IAAA,MAAM,OAAO,GAAGC,gBAAU,CAAC,YAAY,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE;QACZ,MAAM,IAAI,KAAK,CACb,uDAAuD;AACvD,YAAA,6DAA6D,CAC9D;IACH;AACA,IAAA,OAAO,OAAO;AAChB;AAEA;;;AAGG;SACa,uBAAuB,GAAA;AACrC,IAAA,OAAOA,gBAAU,CAAC,YAAY,CAAC;AACjC;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DevicProvider.js","sources":["../../../../src/provider/DevicProvider.tsx"],"sourcesContent":["import React, { useMemo, useState, useRef, useCallback } from 'react';\nimport { DevicContext } from './DevicContext';\nimport { DevicApiClient } from '../api/client';\nimport { createSharedSession } from './sharedSession';\nimport { generateId } from '../utils';\nimport type {\n DevicProviderProps,\n DevicContextValue,\n AIReference,\n DrawerRegistration,\n} from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.devic.ai';\n\n/**\n * Provider component for Devic UI configuration\n *\n * @example\n * ```tsx\n * <DevicProvider\n * apiKey=\"devic-xxx\"\n * baseUrl=\"https://api.devic.ai\"\n * tenantId=\"tenant-123\"\n * tenantMetadata={{ userId: '456' }}\n * >\n * <App />\n * </DevicProvider>\n * ```\n */\nexport function DevicProvider({\n apiKey,\n getTenantSession,\n onSessionExpired,\n baseUrl = DEFAULT_BASE_URL,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n debug,\n children,\n}: DevicProviderProps): JSX.Element {\n const [references, setReferences] = useState<AIReference[]>([]);\n const drawerRef = useRef<DrawerRegistration | null>(null);\n\n const addReference = useCallback((ref: Omit<AIReference, 'id'>): string => {\n const id = generateId();\n setReferences((prev) => [...prev, { ...ref, id }]);\n return id;\n }, []);\n\n const removeReference = useCallback((id: string) => {\n setReferences((prev) => prev.filter((r) => r.id !== id));\n }, []);\n\n const clearReferences = useCallback(() => {\n setReferences([]);\n }, []);\n\n const registerDrawer = useCallback((handle: DrawerRegistration) => {\n drawerRef.current = handle;\n return () => {\n if (drawerRef.current === handle) {\n drawerRef.current = null;\n }\n };\n }, []);\n\n const openDrawer = useCallback(() => {\n drawerRef.current?.open();\n }, []);\n\n // Both callbacks are read through a ref and handed on as stable wrappers.\n // An inline `getTenantSession={() => …}` is a new function on every render,\n // and passing it straight through would rebuild the client — throwing away\n // the session it holds, so the page fetches a token per render — and rebuild\n // the context, so every component below it would do the same.\n const sessionSourceRef = useRef(getTenantSession);\n sessionSourceRef.current = getTenantSession;\n const expiredRef = useRef(onSessionExpired);\n expiredRef.current = onSessionExpired;\n const usesSessions = !!getTenantSession;\n\n // One session for the whole tree: every component below builds its own API\n // client, and each dedupes only its own renewals — so without this, N clients\n // ask for N tokens. See `createSharedSession`.\n const tenantSession = useMemo(\n () =>\n usesSessions\n ? createSharedSession(() => sessionSourceRef.current!())\n : undefined,\n [usesSessions]\n );\n const sessionExpired = useMemo(() => () => expiredRef.current?.(), []);\n\n const client = useMemo(\n () =>\n new DevicApiClient({\n apiKey,\n baseUrl,\n getTenantSession: tenantSession,\n onSessionExpired: sessionExpired,\n }),\n [apiKey, baseUrl, tenantSession, sessionExpired]\n );\n\n const contextValue = useMemo<DevicContextValue>(\n () => ({\n client,\n apiKey,\n getTenantSession: tenantSession,\n onSessionExpired: sessionExpired,\n baseUrl,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n isConfigured: !!apiKey || usesSessions,\n debug,\n references,\n addReference,\n removeReference,\n clearReferences,\n registerDrawer,\n openDrawer,\n }),\n [\n client,\n apiKey,\n tenantSession,\n sessionExpired,\n usesSessions,\n baseUrl,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n debug,\n references,\n addReference,\n removeReference,\n clearReferences,\n registerDrawer,\n openDrawer,\n ]\n );\n\n return (\n <DevicContext.Provider value={contextValue}>\n {children}\n </DevicContext.Provider>\n );\n}\n"],"names":["useState","useRef","useCallback","generateId","useMemo","createSharedSession","client","DevicApiClient","_jsx","DevicContext"],"mappings":"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
|
|
1
|
+
{"version":3,"file":"DevicProvider.js","sources":["../../../../src/provider/DevicProvider.tsx"],"sourcesContent":["import React, { useMemo, useState, useRef, useCallback } from 'react';\nimport { DevicContext } from './DevicContext';\nimport { DevicApiClient } from '../api/client';\nimport { createSharedSession } from './sharedSession';\nimport { generateId } from '../utils';\nimport type {\n DevicProviderProps,\n DevicContextValue,\n AIReference,\n DrawerRegistration,\n} from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.devic.ai';\n\n/**\n * Provider component for Devic UI configuration\n *\n * @example\n * ```tsx\n * <DevicProvider\n * apiKey=\"devic-xxx\"\n * baseUrl=\"https://api.devic.ai\"\n * tenantId=\"tenant-123\"\n * tenantMetadata={{ userId: '456' }}\n * >\n * <App />\n * </DevicProvider>\n * ```\n */\nexport function DevicProvider({\n apiKey,\n getTenantSession,\n onSessionExpired,\n baseUrl = DEFAULT_BASE_URL,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n debug,\n children,\n}: DevicProviderProps): JSX.Element {\n const [references, setReferences] = useState<AIReference[]>([]);\n const drawerRef = useRef<DrawerRegistration | null>(null);\n\n const addReference = useCallback((ref: Omit<AIReference, 'id'>): string => {\n const id = generateId();\n setReferences((prev) => [...prev, { ...ref, id }]);\n return id;\n }, []);\n\n const removeReference = useCallback((id: string) => {\n setReferences((prev) => prev.filter((r) => r.id !== id));\n }, []);\n\n const clearReferences = useCallback(() => {\n setReferences([]);\n }, []);\n\n const registerDrawer = useCallback((handle: DrawerRegistration) => {\n drawerRef.current = handle;\n return () => {\n if (drawerRef.current === handle) {\n drawerRef.current = null;\n }\n };\n }, []);\n\n const openDrawer = useCallback(() => {\n drawerRef.current?.open();\n }, []);\n\n // Both callbacks are read through a ref and handed on as stable wrappers.\n // An inline `getTenantSession={() => …}` is a new function on every render,\n // and passing it straight through would rebuild the client — throwing away\n // the session it holds, so the page fetches a token per render — and rebuild\n // the context, so every component below it would do the same.\n const sessionSourceRef = useRef(getTenantSession);\n sessionSourceRef.current = getTenantSession;\n const expiredRef = useRef(onSessionExpired);\n expiredRef.current = onSessionExpired;\n const usesSessions = !!getTenantSession;\n\n // One session for the whole tree: every component below builds its own API\n // client, and each dedupes only its own renewals — so without this, N clients\n // ask for N tokens. See `createSharedSession`.\n const tenantSession = useMemo(\n () =>\n usesSessions\n ? createSharedSession(() => sessionSourceRef.current!())\n : undefined,\n [usesSessions]\n );\n const sessionExpired = useMemo(() => () => expiredRef.current?.(), []);\n\n const client = useMemo(\n () =>\n new DevicApiClient({\n apiKey,\n baseUrl,\n getTenantSession: tenantSession,\n onSessionExpired: sessionExpired,\n }),\n [apiKey, baseUrl, tenantSession, sessionExpired]\n );\n\n const contextValue = useMemo<DevicContextValue>(\n () => ({\n client,\n apiKey,\n getTenantSession: tenantSession,\n onSessionExpired: sessionExpired,\n baseUrl,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n isConfigured: !!apiKey || usesSessions,\n debug,\n references,\n addReference,\n removeReference,\n clearReferences,\n registerDrawer,\n openDrawer,\n }),\n [\n client,\n apiKey,\n tenantSession,\n sessionExpired,\n usesSessions,\n baseUrl,\n tenantId,\n tenantMetadata,\n subtenantId,\n subtenantMetadata,\n tags,\n pollingInterval,\n debug,\n references,\n addReference,\n removeReference,\n clearReferences,\n registerDrawer,\n openDrawer,\n ]\n );\n\n return (\n <DevicContext.Provider value={contextValue}>\n {children}\n </DevicContext.Provider>\n );\n}\n"],"names":["useState","useRef","useCallback","generateId","useMemo","createSharedSession","client","DevicApiClient","_jsx","DevicContext"],"mappings":"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"}
|