@devicai/ui 0.58.0 → 0.59.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/README.md +24 -0
- package/dist/cjs/api/assistantInfo.js.map +1 -1
- package/dist/cjs/api/client.js +17 -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 +2 -1
- package/dist/cjs/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/cjs/components/AICommandBar/useAICommandBar.js +5 -1
- package/dist/cjs/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/cjs/components/AIElementWrapper/AIElementWrapper.js +2 -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 +5 -1
- package/dist/cjs/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js +2 -1
- package/dist/cjs/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js +5 -1
- package/dist/cjs/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/cjs/components/ChatDrawer/ChatDrawer.js +2 -1
- 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/CompactionWidget.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/GuardrailNotice.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/QueueNotice.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.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/cjs/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
- 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/integrationChoice.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 +10 -2
- package/dist/cjs/hooks/useDevicChat.js.map +1 -1
- package/dist/cjs/hooks/useModelInterface.js.map +1 -1
- package/dist/cjs/hooks/usePolling.js +117 -7
- package/dist/cjs/hooks/usePolling.js.map +1 -1
- package/dist/cjs/hooks/useSpeechRecording.js.map +1 -1
- package/dist/cjs/i18n/translate.js.map +1 -1
- package/dist/cjs/i18n/useTranslations.js.map +1 -1
- package/dist/cjs/provider/DevicContext.js.map +1 -1
- package/dist/cjs/provider/DevicProvider.js +3 -1
- package/dist/cjs/provider/DevicProvider.js.map +1 -1
- package/dist/cjs/provider/sharedSession.js.map +1 -1
- package/dist/cjs/utils/avatar.js.map +1 -1
- package/dist/cjs/utils/consumeChatStream.js +44 -0
- package/dist/cjs/utils/consumeChatStream.js.map +1 -0
- 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.d.ts +5 -1
- package/dist/esm/api/client.js +17 -1
- package/dist/esm/api/client.js.map +1 -1
- package/dist/esm/api/types.d.ts +2 -0
- package/dist/esm/api/types.js.map +1 -1
- package/dist/esm/components/AICommandBar/AICommandBar.js +2 -1
- package/dist/esm/components/AICommandBar/AICommandBar.js.map +1 -1
- package/dist/esm/components/AICommandBar/AICommandBar.types.d.ts +6 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.d.ts +2 -0
- package/dist/esm/components/AICommandBar/useAICommandBar.js +6 -2
- package/dist/esm/components/AICommandBar/useAICommandBar.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.js +2 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/AIElementWrapper.types.d.ts +6 -0
- package/dist/esm/components/AIElementWrapper/activeWrapperRegistry.js.map +1 -1
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.d.ts +2 -0
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js +6 -2
- package/dist/esm/components/AIElementWrapper/useAIElementWrapper.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.js +2 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.js.map +1 -1
- package/dist/esm/components/AIGenerationButton/AIGenerationButton.types.d.ts +6 -0
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.d.ts +2 -0
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js +6 -2
- package/dist/esm/components/AIGenerationButton/useAIGenerationButton.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.js +2 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.js.map +1 -1
- package/dist/esm/components/ChatDrawer/ChatDrawer.types.d.ts +8 -0
- 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/CompactionWidget.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/GuardrailNotice.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/QueueNotice.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.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsModal.js.map +1 -1
- package/dist/esm/components/IntegrationsModal/IntegrationsPanel.js.map +1 -1
- 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/integrationChoice.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/index.d.ts +1 -1
- package/dist/esm/hooks/useDevicChat.d.ts +6 -0
- package/dist/esm/hooks/useDevicChat.js +12 -4
- package/dist/esm/hooks/useDevicChat.js.map +1 -1
- package/dist/esm/hooks/useModelInterface.js.map +1 -1
- package/dist/esm/hooks/usePolling.d.ts +35 -0
- package/dist/esm/hooks/usePolling.js +114 -8
- package/dist/esm/hooks/usePolling.js.map +1 -1
- package/dist/esm/hooks/useSpeechRecording.js.map +1 -1
- package/dist/esm/i18n/translate.js.map +1 -1
- package/dist/esm/i18n/useTranslations.js.map +1 -1
- package/dist/esm/provider/DevicContext.js.map +1 -1
- package/dist/esm/provider/DevicProvider.d.ts +1 -1
- package/dist/esm/provider/DevicProvider.js +3 -1
- package/dist/esm/provider/DevicProvider.js.map +1 -1
- package/dist/esm/provider/sharedSession.js.map +1 -1
- package/dist/esm/provider/types.d.ts +18 -0
- package/dist/esm/utils/avatar.js.map +1 -1
- package/dist/esm/utils/consumeChatStream.d.ts +6 -0
- package/dist/esm/utils/consumeChatStream.js +42 -0
- package/dist/esm/utils/consumeChatStream.js.map +1 -0
- 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 +3 -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 { useAssistantInfo } from '../api/assistantInfo';\nimport { useTranslations } from '../i18n';\nimport type {\n ChatMessage,\n ChatFile,\n CompactionActivity,\n CompactionCheckpoint,\n ModelInterfaceTool,\n QueueDisposition,\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\n/**\n * How many polls the conversation may read as finished while it still owes an\n * answer to something queued, before the wait is given up.\n *\n * The window is real: a run liquidates, and the follow-up run that drains the\n * queue has not marked itself as processing yet. Bounded so a message that\n * never comes back cannot poll forever.\n */\nconst QUEUE_HANDOVER_GRACE_TICKS = 60;\n\n/** Messages are matched to their optimistic copies by text, so it is normalized. */\nconst normalizeText = (text?: string): string => (text ?? '').trim();\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 * Whether this assistant takes messages while the conversation is busy.\n *\n * Left unset it is resolved from the assistant itself (`messageQueueEnabled`),\n * through the shared per-assistant lookup — one request, shared with the\n * avatar and the connected-apps control, whichever asks first. Pass a boolean\n * to skip the lookup entirely or to override what it says.\n *\n * With it off the input closes while the assistant answers, as it always has.\n * With it on, a message written mid-answer is accepted and joins the\n * assistant's next turn.\n */\n messageQueue?: boolean;\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\n/**\n * What became of a send.\n *\n * `queued` is an acceptance, not a failure: the message is on the conversation\n * and will be answered later. `rejected` is the opposite — nothing was\n * accepted, and the caller still holds the only copy of what the user wrote.\n */\nexport type SendMessageResult =\n | { queued: true; queuePosition: number; willProcess: QueueDisposition }\n | { queued: false }\n | {\n rejected: true;\n /**\n * `chat_busy`: this assistant does not queue and is working.\n * `queue_full`: it does, and the queue is at its configured ceiling.\n * `error`: anything else, already reported through `error`/`onError`.\n */\n reason: 'chat_busy' | 'queue_full' | 'error';\n message: string;\n /** What the user wrote, so it can be restored. */\n restoredText: string;\n };\n\nexport interface StopResult {\n /** Text of the queued messages the stop threw away, joined by newlines. */\n restoredText?: string;\n /** How many were discarded. */\n discarded: number;\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 * Compaction checkpoints of the conversation, oldest first. Each one folds\n * the messages before its boundary into a summary the model reads instead\n * of them; the messages themselves stay in `messages`.\n */\n compactions: CompactionCheckpoint[];\n\n /**\n * The compaction being written right now, or null. A compaction is a model\n * call of its own between two assistant messages, so this is what tells a\n * UI why the conversation has gone quiet.\n */\n compaction: CompactionActivity | null;\n\n /**\n * Whether the assistant has handed off to a subagent\n */\n handedOff: boolean;\n\n /**\n * The subthread ID when a handoff is active\n */\n handedOffSubThreadId: string | null;\n\n /**\n * Send a message\n */\n sendMessage: (\n message: string,\n options?: {\n files?: 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<SendMessageResult>;\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 * How many messages this conversation has accepted but not yet shown to the\n * model. Includes anything queued through another channel, so it is the\n * conversation's queue and not this client's.\n */\n queuedCount: number;\n\n /**\n * Whether this assistant takes messages while it is busy — resolved from the\n * assistant, or from the `messageQueue` option when given. False until the\n * lookup settles: an input that opens and then closes is worse than one that\n * opens a beat late.\n */\n queueEnabled: boolean;\n\n /**\n * Stop the current conversation processing.\n *\n * Returns the text of anything the stop discarded, so the caller can put it\n * back where the user wrote it — a stop should not be the way someone's\n * typing disappears.\n */\n stopChat: () => Promise<StopResult>;\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 messageQueue,\n debug: propsDebug,\n } = options;\n\n // Get context (may be null if not wrapped in provider)\n const t = useTranslations();\n // Read through a ref inside the polling callbacks: they are built once and\n // must not be rebuilt (restarting the poll) just because a dictionary changed.\n const tRef = useRef(t);\n tRef.current = t;\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 // Compaction checkpoints of the conversation, and the one being written\n // right now. Same merge rule as the recalls: the realtime blob only carries\n // the in-flight run's checkpoints, so incoming batches merge by uid.\n const [compactions, setCompactions] = useState<CompactionCheckpoint[]>([]);\n const mergeCompactions = useCallback((incoming?: CompactionCheckpoint[]) => {\n if (!incoming?.length) return;\n setCompactions((prev) => {\n const known = new Set(prev.map((c) => c.uid));\n const fresh = incoming.filter((c) => !known.has(c.uid));\n return fresh.length ? [...prev, ...fresh] : prev;\n });\n }, []);\n // Absent on every ordinary realtime write, which is how a finished\n // compaction stops being reported.\n const [compaction, setCompaction] = useState<CompactionActivity | null>(null);\n\n // Handoff state\n const [handedOff, setHandedOff] = useState(false);\n const [handedOffSubThreadId, setHandedOffSubThreadId] = useState<string | null>(null);\n const handoffPollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Polling state\n const [shouldPoll, setShouldPoll] = useState(false);\n\n // Keep a ref to chatUid so async callbacks always read the latest value\n const chatUidRef = useRef(chatUid);\n chatUidRef.current = chatUid;\n\n // Read by `sendMessage`, which must not restate the state of a run already in\n // flight — its own callback identity would otherwise carry a stale value.\n const isLoadingRef = useRef(isLoading);\n isLoadingRef.current = isLoading;\n\n // Two messages sent inside the same millisecond would otherwise share a uid,\n // which the queue makes easy to do.\n const optimisticSeqRef = useRef(0);\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 // --- Message queue --------------------------------------------------------\n\n /**\n * Messages accepted by this conversation that the model has not seen yet.\n * Taken from the poll, so it counts whatever else queued on the conversation\n * too — another tab, or the same person writing from another channel.\n */\n const [queuedCount, setQueuedCount] = useState(0);\n\n /**\n * When something was queued from here that has not been answered yet.\n *\n * Deliberately not a list of the texts sent: a drain can merge several queued\n * messages into a single user turn, so what comes back is not what went out\n * and matching them by text would wait forever. What is being waited for is an\n * answer, and an assistant message written after the message was accepted is\n * that answer.\n */\n const awaitingAnswerSinceRef = useRef<number | null>(null);\n const queueGraceTicksRef = useRef(0);\n\n const rememberAwaiting = useCallback(() => {\n awaitingAnswerSinceRef.current = Date.now();\n }, []);\n\n const resetQueueState = useCallback(() => {\n awaitingAnswerSinceRef.current = null;\n queueGraceTicksRef.current = 0;\n setQueuedCount(0);\n }, []);\n\n /**\n * Asked for as soon as the hook is alive, rather than when the first run\n * starts: the answer decides whether the input stays open while the assistant\n * works, and resolving it late means the box visibly closes and reopens on the\n * first message of every session. One request, shared with everything else\n * that asks about this assistant. Passing `messageQueue` skips it entirely.\n */\n const queueLookup = useAssistantInfo({\n assistantId,\n client: clientRef.current,\n baseUrl,\n credential: apiKey || 'session',\n enabled: messageQueue === undefined,\n });\n\n /**\n * Absent means no. An assistant that has not answered yet, or an API too old\n * to carry the field, leaves the input closed while it works — the same thing\n * it did before this existed. Opening it on a maybe would promise a queue the\n * conversation then refuses.\n */\n const queueEnabled =\n messageQueue ??\n (queueLookup.settled &&\n queueLookup.assistant?.messageQueueEnabled === true);\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 const queuedOnServer = (realtime.pendingUserMessages ?? []).map((m) => ({\n ...m,\n queued: true,\n }));\n if (realtime.chatHistory?.length || queuedOnServer.length) {\n setMessages([...(realtime.chatHistory ?? []), ...queuedOnServer]);\n }\n mergeRecalledMemories(realtime.recalledMemories);\n mergeCompactions(realtime.compactions);\n setCompaction(realtime.compaction ?? null);\n setStatus(realtime.status);\n setQueuedCount(realtime.queuedMessages ?? 0);\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 if ((realtime.queuedMessages ?? 0) > 0) {\n // The run settled, but the conversation still owes an answer to\n // something queued — reopened on a conversation whose follow-up run\n // has not started yet. Watch it until the queue is served.\n setIsLoading(true);\n setShouldPoll(true);\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, mergeCompactions]\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 mergeCompactions(history.compactions);\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(t('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 // An assistant message written after something was queued from here is\n // the answer that was being waited for.\n const awaitingSince = awaitingAnswerSinceRef.current;\n if (\n awaitingSince !== null &&\n (data.queuedMessages ?? 0) === 0 &&\n (data.chatHistory ?? []).some(\n (m) => m.role === 'assistant' && (m.timestamp ?? 0) > awaitingSince\n )\n ) {\n awaitingAnswerSinceRef.current = null;\n }\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 // A queue of uids per text rather than one: two identical messages are\n // two messages, and pairing both with the same optimistic copy would\n // drop one of them from the conversation.\n const optimisticUserByText = new Map<string, string[]>();\n prev\n .filter((m) => m.role === 'user' && m.uid.startsWith('temp-'))\n .forEach((m) => {\n const key = normalizeText(m.content?.message);\n const bucket = optimisticUserByText.get(key);\n if (bucket) bucket.push(m.uid);\n else optimisticUserByText.set(key, [m.uid]);\n });\n\n const adoptedTempUids = new Set<string>();\n const adopt = (m: ChatMessage): ChatMessage => {\n if (m.role !== 'user') return m;\n const tempUid = optimisticUserByText\n .get(normalizeText(m.content?.message))\n ?.shift();\n if (!tempUid) return m;\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 const merged = data.chatHistory.map(adopt);\n // Accepted, but not part of the conversation yet. Drawn between the\n // history and the optimistic ones, which is where they land once a\n // turn takes them. The copy is the server's, so it survives a reload —\n // where the API does not return them, this is empty and the optimistic\n // ones kept below stand in.\n const queued = (data.pendingUserMessages ?? []).map((m) => ({\n ...adopt(m),\n queued: true,\n }));\n\n // Which of this client's own queued bubbles to keep drawing.\n //\n // Matching them to the history by text does not work: a drain can\n // merge several queued messages into one user turn, and an optimistic\n // copy that never finds its pair would sit there marked as queued for\n // the rest of the conversation. The server's queue is the authority,\n // and these are kept only where it cannot speak for them:\n // - too recently accepted for this poll to have seen them;\n // - counted by `queuedMessages` but not itemised, which is what an\n // API without `pendingUserMessages` reports.\n const optimisticQueued = prev.filter(\n (m) => m.queued && m.uid.startsWith('temp-')\n );\n const tooRecent = Date.now() - pollingInterval;\n const keptQueued = new Set(\n optimisticQueued\n .filter((m) => (m.queuedAt ?? 0) > tooRecent)\n .map((m) => m.uid)\n );\n let unitemised =\n (data.queuedMessages ?? queued.length) - queued.length - keptQueued.size;\n for (let i = optimisticQueued.length - 1; i >= 0 && unitemised > 0; i--) {\n if (keptQueued.has(optimisticQueued[i].uid)) continue;\n keptQueued.add(optimisticQueued[i].uid);\n unitemised -= 1;\n }\n\n const mergedUIDs = new Set(\n [...merged, ...queued].map((m) => m.uid)\n );\n const optimistic = prev.filter(\n (m) =>\n !mergedUIDs.has(m.uid) &&\n !adoptedTempUids.has(m.uid) &&\n (!m.queued || keptQueued.has(m.uid))\n );\n\n return [...merged, ...queued, ...optimistic];\n });\n setQueuedCount(data.queuedMessages ?? 0);\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 // A compaction runs between two assistant messages: without this the\n // conversation looks stalled while it is being written.\n mergeCompactions(data.compactions);\n setCompaction(data.compaction ?? null);\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 holdOpen: (data) => {\n // Only `completed` is worth waiting on. An error, a usage limit or a\n // gate mean something else is going on, and holding the poll open would\n // just be watching a conversation that is not coming back.\n const owed =\n (data.queuedMessages ?? 0) > 0 ||\n awaitingAnswerSinceRef.current !== null;\n if (!owed || data.status !== 'completed') {\n queueGraceTicksRef.current = 0;\n return false;\n }\n queueGraceTicksRef.current += 1;\n if (queueGraceTicksRef.current <= QUEUE_HANDOVER_GRACE_TICKS) return true;\n // Nothing came back in time: stop pretending it will.\n logRef.current.warn(\n '[useDevicChat] queue handover window expired, stopping the watch'\n );\n awaitingAnswerSinceRef.current = null;\n return false;\n },\n onStop: (data) => {\n logRef.current.log('[useDevicChat] onStop called, status:', data?.status);\n setShouldPoll(false);\n queueGraceTicksRef.current = 0;\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: tRef.current('Usage limit reached.'),\n };\n setLimitExceeded(details);\n const err = new Error(\n details.message || tRef.current('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(t('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 ): Promise<SendMessageResult> => {\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 { rejected: true, reason: 'error', message: err.message, restoredText: message };\n }\n\n // Whether something was already running when this was written. If it was,\n // this send must not restate it: turning the indicator on and off around a\n // message that merely joined a queue would report on a run it has nothing\n // to do with — and a refusal would then stop an indicator for a run that\n // is still perfectly alive.\n const wasBusy = isLoadingRef.current;\n\n if (!wasBusy) {\n setIsLoading(true);\n setStatus('processing');\n }\n setError(null);\n setLimitExceeded(null);\n\n // Add user message optimistically (show file names before upload)\n const userMessage: ChatMessage = {\n uid: `temp-${Date.now()}-${optimisticSeqRef.current++}`,\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\n if (response.queued) {\n // Accepted, but not on its way to the model yet. Draw it as such, and\n // remember it: the poll has to keep running until it comes back inside\n // the conversation, however long the run in flight takes.\n rememberAwaiting();\n const queuedAt = Date.now();\n setMessages((prev) =>\n prev.map((m) =>\n m.uid === userMessage.uid ? { ...m, queued: true, queuedAt } : m\n )\n );\n setQueuedCount(response.queuePosition || 1);\n return {\n queued: true,\n queuePosition: response.queuePosition || 1,\n willProcess: response.willProcess || 'next_turn',\n };\n }\n\n return { queued: false };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // Nothing was accepted either way, so the bubble goes.\n setMessages((prev) => prev.filter((m) => m.uid !== userMessage.uid));\n\n // The conversation turning a message down is not the conversation\n // breaking. Two shapes of the same 409: an assistant that does not queue\n // says so by name, and a full queue comes back as a plain conflict.\n const refusal =\n err instanceof DevicApiError\n ? err.errorType === 'CHAT_BUSY'\n ? 'chat_busy'\n : err.statusCode === 409\n ? 'queue_full'\n : null\n : null;\n\n if (refusal) {\n logRef.current.log('[useDevicChat] send refused:', refusal, error.message);\n // Deliberately leaves `isLoading`, `status` and `error` alone: the run\n // this message was written into is still going, and reporting the\n // refusal by stopping its indicator — or by painting the conversation\n // as failed — would be a lie about the run, not about the send.\n if (!wasBusy) {\n setIsLoading(false);\n setStatus('idle');\n }\n return {\n rejected: true,\n reason: refusal,\n message: error.message,\n restoredText: message,\n };\n }\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 // Same reasoning as the refusal above: a send that failed while a run\n // was already going says nothing about that run, which the poll is still\n // watching. The error is reported either way.\n if (!wasBusy) {\n setIsLoading(false);\n setStatus('error');\n }\n onErrorRef.current?.(error);\n\n return {\n rejected: true,\n reason: 'error',\n message: error.message,\n restoredText: message,\n };\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 rememberAwaiting,\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 setCompactions([]);\n setCompaction(null);\n resetQueueState();\n pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\n }, [resetQueueState]);\n\n // Load existing chat\n const loadChat = useCallback(\n async (loadChatUid: string) => {\n if (!clientRef.current) {\n const err = new Error(t('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 setCompactions([]);\n setCompaction(null);\n // The queue belongs to the conversation being left behind.\n resetQueueState();\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 mergeCompactions(history.compactions);\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 [\n assistantId,\n resolvedTenantId,\n resumeFromRealtimeStatus,\n mergeRecalledMemories,\n mergeCompactions,\n resetQueueState,\n ]\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 (): Promise<StopResult> => {\n const uid = chatUidRef.current;\n logRef.current.log('[useDevicChat] stopChat called, chatUid:', uid);\n let discarded: ChatMessage[] = [];\n if (clientRef.current && uid) {\n try {\n const result = await clientRef.current.stopChat(assistantId, uid);\n discarded = result?.discardedMessages ?? [];\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\n // Stopping throws away whatever was queued behind the run — answering it\n // would be the opposite of what was just asked for. The bubbles go with it,\n // and the text is handed back so it can return to the box instead of\n // disappearing. An API that does not report what it discarded leaves the\n // bubbles alone, and the next poll has the last word.\n if (discarded.length) {\n setMessages((prev) => prev.filter((m) => !m.queued));\n }\n resetQueueState();\n\n setShouldPoll(false);\n setIsLoading(false);\n setStatus('idle');\n\n const restoredText = discarded\n .map((m) => m.content?.message)\n .filter(Boolean)\n .join('\\n');\n\n return {\n discarded: discarded.length,\n ...(restoredText ? { restoredText } : {}),\n };\n }, [assistantId, resetQueueState]);\n\n return {\n messages,\n chatUid,\n isLoading,\n status,\n error,\n limitExceeded,\n recalledMemories,\n compactions,\n compaction,\n handedOff,\n handedOffSubThreadId,\n queuedCount,\n queueEnabled,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n pendingWidgetCalls,\n submitWidgetResponse,\n cancelWidgetCall,\n };\n}\n"],"names":["useTranslations","useRef","useOptionalDevicContext","useMemo","resolvePollingInterval","createLogger","useState","useCallback","useEffect","DevicApiClient","useAssistantInfo","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, resolveStreaming } from './usePolling';\nimport { useModelInterface, type PendingWidgetCall } from './useModelInterface';\nimport { createLogger } from '../utils/logger';\nimport { useAssistantInfo } from '../api/assistantInfo';\nimport { useTranslations } from '../i18n';\nimport type {\n ChatMessage,\n ChatFile,\n CompactionActivity,\n CompactionCheckpoint,\n ModelInterfaceTool,\n QueueDisposition,\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\n/**\n * How many polls the conversation may read as finished while it still owes an\n * answer to something queued, before the wait is given up.\n *\n * The window is real: a run liquidates, and the follow-up run that drains the\n * queue has not marked itself as processing yet. Bounded so a message that\n * never comes back cannot poll forever.\n */\nconst QUEUE_HANDOVER_GRACE_TICKS = 60;\n\n/** Messages are matched to their optimistic copies by text, so it is normalized. */\nconst normalizeText = (text?: string): string => (text ?? '').trim();\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 * Follow the conversation over a server-sent event stream instead of\n * polling it. Overrides the DevicProvider's `streaming`.\n * @default false\n */\n streaming?: boolean;\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 * Whether this assistant takes messages while the conversation is busy.\n *\n * Left unset it is resolved from the assistant itself (`messageQueueEnabled`),\n * through the shared per-assistant lookup — one request, shared with the\n * avatar and the connected-apps control, whichever asks first. Pass a boolean\n * to skip the lookup entirely or to override what it says.\n *\n * With it off the input closes while the assistant answers, as it always has.\n * With it on, a message written mid-answer is accepted and joins the\n * assistant's next turn.\n */\n messageQueue?: boolean;\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\n/**\n * What became of a send.\n *\n * `queued` is an acceptance, not a failure: the message is on the conversation\n * and will be answered later. `rejected` is the opposite — nothing was\n * accepted, and the caller still holds the only copy of what the user wrote.\n */\nexport type SendMessageResult =\n | { queued: true; queuePosition: number; willProcess: QueueDisposition }\n | { queued: false }\n | {\n rejected: true;\n /**\n * `chat_busy`: this assistant does not queue and is working.\n * `queue_full`: it does, and the queue is at its configured ceiling.\n * `error`: anything else, already reported through `error`/`onError`.\n */\n reason: 'chat_busy' | 'queue_full' | 'error';\n message: string;\n /** What the user wrote, so it can be restored. */\n restoredText: string;\n };\n\nexport interface StopResult {\n /** Text of the queued messages the stop threw away, joined by newlines. */\n restoredText?: string;\n /** How many were discarded. */\n discarded: number;\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 * Compaction checkpoints of the conversation, oldest first. Each one folds\n * the messages before its boundary into a summary the model reads instead\n * of them; the messages themselves stay in `messages`.\n */\n compactions: CompactionCheckpoint[];\n\n /**\n * The compaction being written right now, or null. A compaction is a model\n * call of its own between two assistant messages, so this is what tells a\n * UI why the conversation has gone quiet.\n */\n compaction: CompactionActivity | null;\n\n /**\n * Whether the assistant has handed off to a subagent\n */\n handedOff: boolean;\n\n /**\n * The subthread ID when a handoff is active\n */\n handedOffSubThreadId: string | null;\n\n /**\n * Send a message\n */\n sendMessage: (\n message: string,\n options?: {\n files?: 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<SendMessageResult>;\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 * How many messages this conversation has accepted but not yet shown to the\n * model. Includes anything queued through another channel, so it is the\n * conversation's queue and not this client's.\n */\n queuedCount: number;\n\n /**\n * Whether this assistant takes messages while it is busy — resolved from the\n * assistant, or from the `messageQueue` option when given. False until the\n * lookup settles: an input that opens and then closes is worse than one that\n * opens a beat late.\n */\n queueEnabled: boolean;\n\n /**\n * Stop the current conversation processing.\n *\n * Returns the text of anything the stop discarded, so the caller can put it\n * back where the user wrote it — a stop should not be the way someone's\n * typing disappears.\n */\n stopChat: () => Promise<StopResult>;\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 streaming: propsStreaming,\n onMessageSent,\n onMessageReceived,\n onToolCall,\n onError,\n onChatCreated,\n onFileUpload,\n messageQueue,\n debug: propsDebug,\n } = options;\n\n // Get context (may be null if not wrapped in provider)\n const t = useTranslations();\n // Read through a ref inside the polling callbacks: they are built once and\n // must not be rebuilt (restarting the poll) just because a dictionary changed.\n const tRef = useRef(t);\n tRef.current = t;\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 streaming = resolveStreaming(propsStreaming, context?.streaming);\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 [streamingMessage, setStreamingMessage] = useState<ChatMessage | null>(null);\n const [chatUid, setChatUid] = useState<string | null>(initialChatUid || null);\n useEffect(() => { setStreamingMessage(null); }, [chatUid]);\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 // Compaction checkpoints of the conversation, and the one being written\n // right now. Same merge rule as the recalls: the realtime blob only carries\n // the in-flight run's checkpoints, so incoming batches merge by uid.\n const [compactions, setCompactions] = useState<CompactionCheckpoint[]>([]);\n const mergeCompactions = useCallback((incoming?: CompactionCheckpoint[]) => {\n if (!incoming?.length) return;\n setCompactions((prev) => {\n const known = new Set(prev.map((c) => c.uid));\n const fresh = incoming.filter((c) => !known.has(c.uid));\n return fresh.length ? [...prev, ...fresh] : prev;\n });\n }, []);\n // Absent on every ordinary realtime write, which is how a finished\n // compaction stops being reported.\n const [compaction, setCompaction] = useState<CompactionActivity | null>(null);\n\n // Handoff state\n const [handedOff, setHandedOff] = useState(false);\n const [handedOffSubThreadId, setHandedOffSubThreadId] = useState<string | null>(null);\n const handoffPollRef = useRef<ReturnType<typeof setInterval> | null>(null);\n\n // Polling state\n const [shouldPoll, setShouldPoll] = useState(false);\n\n // Keep a ref to chatUid so async callbacks always read the latest value\n const chatUidRef = useRef(chatUid);\n chatUidRef.current = chatUid;\n\n // Read by `sendMessage`, which must not restate the state of a run already in\n // flight — its own callback identity would otherwise carry a stale value.\n const isLoadingRef = useRef(isLoading);\n isLoadingRef.current = isLoading;\n\n // Two messages sent inside the same millisecond would otherwise share a uid,\n // which the queue makes easy to do.\n const optimisticSeqRef = useRef(0);\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 // --- Message queue --------------------------------------------------------\n\n /**\n * Messages accepted by this conversation that the model has not seen yet.\n * Taken from the poll, so it counts whatever else queued on the conversation\n * too — another tab, or the same person writing from another channel.\n */\n const [queuedCount, setQueuedCount] = useState(0);\n\n /**\n * When something was queued from here that has not been answered yet.\n *\n * Deliberately not a list of the texts sent: a drain can merge several queued\n * messages into a single user turn, so what comes back is not what went out\n * and matching them by text would wait forever. What is being waited for is an\n * answer, and an assistant message written after the message was accepted is\n * that answer.\n */\n const awaitingAnswerSinceRef = useRef<number | null>(null);\n const queueGraceTicksRef = useRef(0);\n\n const rememberAwaiting = useCallback(() => {\n awaitingAnswerSinceRef.current = Date.now();\n }, []);\n\n const resetQueueState = useCallback(() => {\n awaitingAnswerSinceRef.current = null;\n queueGraceTicksRef.current = 0;\n setQueuedCount(0);\n }, []);\n\n /**\n * Asked for as soon as the hook is alive, rather than when the first run\n * starts: the answer decides whether the input stays open while the assistant\n * works, and resolving it late means the box visibly closes and reopens on the\n * first message of every session. One request, shared with everything else\n * that asks about this assistant. Passing `messageQueue` skips it entirely.\n */\n const queueLookup = useAssistantInfo({\n assistantId,\n client: clientRef.current,\n baseUrl,\n credential: apiKey || 'session',\n enabled: messageQueue === undefined,\n });\n\n /**\n * Absent means no. An assistant that has not answered yet, or an API too old\n * to carry the field, leaves the input closed while it works — the same thing\n * it did before this existed. Opening it on a maybe would promise a queue the\n * conversation then refuses.\n */\n const queueEnabled =\n messageQueue ??\n (queueLookup.settled &&\n queueLookup.assistant?.messageQueueEnabled === true);\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 const queuedOnServer = (realtime.pendingUserMessages ?? []).map((m) => ({\n ...m,\n queued: true,\n }));\n if (realtime.chatHistory?.length || queuedOnServer.length) {\n setMessages([...(realtime.chatHistory ?? []), ...queuedOnServer]);\n }\n mergeRecalledMemories(realtime.recalledMemories);\n mergeCompactions(realtime.compactions);\n setCompaction(realtime.compaction ?? null);\n setStatus(realtime.status);\n setQueuedCount(realtime.queuedMessages ?? 0);\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 if ((realtime.queuedMessages ?? 0) > 0) {\n // The run settled, but the conversation still owes an answer to\n // something queued — reopened on a conversation whose follow-up run\n // has not started yet. Watch it until the queue is served.\n setIsLoading(true);\n setShouldPoll(true);\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, mergeCompactions]\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 mergeCompactions(history.compactions);\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(t('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 // Only when asked for: the poll is the default until the flag flips.\n streamFn: streaming\n ? (onSnapshot, signal, onActivity) => clientRef.current!.streamRealtimeHistory(assistantId, chatUid!, onSnapshot, signal, onActivity)\n : undefined,\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 setStreamingMessage(data.status === 'processing' ? data.streamingMessage || null : null);\n logRef.current.log('[useDevicChat] onUpdate called, status:', data.status);\n\n // An assistant message written after something was queued from here is\n // the answer that was being waited for.\n const awaitingSince = awaitingAnswerSinceRef.current;\n if (\n awaitingSince !== null &&\n (data.queuedMessages ?? 0) === 0 &&\n (data.chatHistory ?? []).some(\n (m) => m.role === 'assistant' && (m.timestamp ?? 0) > awaitingSince\n )\n ) {\n awaitingAnswerSinceRef.current = null;\n }\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 // A queue of uids per text rather than one: two identical messages are\n // two messages, and pairing both with the same optimistic copy would\n // drop one of them from the conversation.\n const optimisticUserByText = new Map<string, string[]>();\n prev\n .filter((m) => m.role === 'user' && m.uid.startsWith('temp-'))\n .forEach((m) => {\n const key = normalizeText(m.content?.message);\n const bucket = optimisticUserByText.get(key);\n if (bucket) bucket.push(m.uid);\n else optimisticUserByText.set(key, [m.uid]);\n });\n\n const adoptedTempUids = new Set<string>();\n const adopt = (m: ChatMessage): ChatMessage => {\n if (m.role !== 'user') return m;\n const tempUid = optimisticUserByText\n .get(normalizeText(m.content?.message))\n ?.shift();\n if (!tempUid) return m;\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 const merged = data.chatHistory.map(adopt);\n // Accepted, but not part of the conversation yet. Drawn between the\n // history and the optimistic ones, which is where they land once a\n // turn takes them. The copy is the server's, so it survives a reload —\n // where the API does not return them, this is empty and the optimistic\n // ones kept below stand in.\n const queued = (data.pendingUserMessages ?? []).map((m) => ({\n ...adopt(m),\n queued: true,\n }));\n\n // Which of this client's own queued bubbles to keep drawing.\n //\n // Matching them to the history by text does not work: a drain can\n // merge several queued messages into one user turn, and an optimistic\n // copy that never finds its pair would sit there marked as queued for\n // the rest of the conversation. The server's queue is the authority,\n // and these are kept only where it cannot speak for them:\n // - too recently accepted for this poll to have seen them;\n // - counted by `queuedMessages` but not itemised, which is what an\n // API without `pendingUserMessages` reports.\n const optimisticQueued = prev.filter(\n (m) => m.queued && m.uid.startsWith('temp-')\n );\n const tooRecent = Date.now() - pollingInterval;\n const keptQueued = new Set(\n optimisticQueued\n .filter((m) => (m.queuedAt ?? 0) > tooRecent)\n .map((m) => m.uid)\n );\n let unitemised =\n (data.queuedMessages ?? queued.length) - queued.length - keptQueued.size;\n for (let i = optimisticQueued.length - 1; i >= 0 && unitemised > 0; i--) {\n if (keptQueued.has(optimisticQueued[i].uid)) continue;\n keptQueued.add(optimisticQueued[i].uid);\n unitemised -= 1;\n }\n\n const mergedUIDs = new Set(\n [...merged, ...queued].map((m) => m.uid)\n );\n const optimistic = prev.filter(\n (m) =>\n !mergedUIDs.has(m.uid) &&\n !adoptedTempUids.has(m.uid) &&\n (!m.queued || keptQueued.has(m.uid))\n );\n\n return [...merged, ...queued, ...optimistic];\n });\n setQueuedCount(data.queuedMessages ?? 0);\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 // A compaction runs between two assistant messages: without this the\n // conversation looks stalled while it is being written.\n mergeCompactions(data.compactions);\n setCompaction(data.compaction ?? null);\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 holdOpen: (data) => {\n // Only `completed` is worth waiting on. An error, a usage limit or a\n // gate mean something else is going on, and holding the poll open would\n // just be watching a conversation that is not coming back.\n const owed =\n (data.queuedMessages ?? 0) > 0 ||\n awaitingAnswerSinceRef.current !== null;\n if (!owed || data.status !== 'completed') {\n queueGraceTicksRef.current = 0;\n return false;\n }\n queueGraceTicksRef.current += 1;\n if (queueGraceTicksRef.current <= QUEUE_HANDOVER_GRACE_TICKS) return true;\n // Nothing came back in time: stop pretending it will.\n logRef.current.warn(\n '[useDevicChat] queue handover window expired, stopping the watch'\n );\n awaitingAnswerSinceRef.current = null;\n return false;\n },\n onStop: (data) => {\n logRef.current.log('[useDevicChat] onStop called, status:', data?.status);\n setShouldPoll(false);\n queueGraceTicksRef.current = 0;\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: tRef.current('Usage limit reached.'),\n };\n setLimitExceeded(details);\n const err = new Error(\n details.message || tRef.current('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(t('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 ): Promise<SendMessageResult> => {\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 { rejected: true, reason: 'error', message: err.message, restoredText: message };\n }\n\n // Whether something was already running when this was written. If it was,\n // this send must not restate it: turning the indicator on and off around a\n // message that merely joined a queue would report on a run it has nothing\n // to do with — and a refusal would then stop an indicator for a run that\n // is still perfectly alive.\n const wasBusy = isLoadingRef.current;\n\n if (!wasBusy) {\n setIsLoading(true);\n setStatus('processing');\n }\n setError(null);\n setLimitExceeded(null);\n\n // Add user message optimistically (show file names before upload)\n const userMessage: ChatMessage = {\n uid: `temp-${Date.now()}-${optimisticSeqRef.current++}`,\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\n if (response.queued) {\n // Accepted, but not on its way to the model yet. Draw it as such, and\n // remember it: the poll has to keep running until it comes back inside\n // the conversation, however long the run in flight takes.\n rememberAwaiting();\n const queuedAt = Date.now();\n setMessages((prev) =>\n prev.map((m) =>\n m.uid === userMessage.uid ? { ...m, queued: true, queuedAt } : m\n )\n );\n setQueuedCount(response.queuePosition || 1);\n return {\n queued: true,\n queuePosition: response.queuePosition || 1,\n willProcess: response.willProcess || 'next_turn',\n };\n }\n\n return { queued: false };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n\n // Nothing was accepted either way, so the bubble goes.\n setMessages((prev) => prev.filter((m) => m.uid !== userMessage.uid));\n\n // The conversation turning a message down is not the conversation\n // breaking. Two shapes of the same 409: an assistant that does not queue\n // says so by name, and a full queue comes back as a plain conflict.\n const refusal =\n err instanceof DevicApiError\n ? err.errorType === 'CHAT_BUSY'\n ? 'chat_busy'\n : err.statusCode === 409\n ? 'queue_full'\n : null\n : null;\n\n if (refusal) {\n logRef.current.log('[useDevicChat] send refused:', refusal, error.message);\n // Deliberately leaves `isLoading`, `status` and `error` alone: the run\n // this message was written into is still going, and reporting the\n // refusal by stopping its indicator — or by painting the conversation\n // as failed — would be a lie about the run, not about the send.\n if (!wasBusy) {\n setIsLoading(false);\n setStatus('idle');\n }\n return {\n rejected: true,\n reason: refusal,\n message: error.message,\n restoredText: message,\n };\n }\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 // Same reasoning as the refusal above: a send that failed while a run\n // was already going says nothing about that run, which the poll is still\n // watching. The error is reported either way.\n if (!wasBusy) {\n setIsLoading(false);\n setStatus('error');\n }\n onErrorRef.current?.(error);\n\n return {\n rejected: true,\n reason: 'error',\n message: error.message,\n restoredText: message,\n };\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 rememberAwaiting,\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 setCompactions([]);\n setCompaction(null);\n resetQueueState();\n pendingWidgetCallsRef.current = [];\n setPendingWidgetCalls([]);\n }, [resetQueueState]);\n\n // Load existing chat\n const loadChat = useCallback(\n async (loadChatUid: string) => {\n if (!clientRef.current) {\n const err = new Error(t('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 setCompactions([]);\n setCompaction(null);\n // The queue belongs to the conversation being left behind.\n resetQueueState();\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 mergeCompactions(history.compactions);\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 [\n assistantId,\n resolvedTenantId,\n resumeFromRealtimeStatus,\n mergeRecalledMemories,\n mergeCompactions,\n resetQueueState,\n ]\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 (): Promise<StopResult> => {\n const uid = chatUidRef.current;\n logRef.current.log('[useDevicChat] stopChat called, chatUid:', uid);\n let discarded: ChatMessage[] = [];\n if (clientRef.current && uid) {\n try {\n const result = await clientRef.current.stopChat(assistantId, uid);\n discarded = result?.discardedMessages ?? [];\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\n // Stopping throws away whatever was queued behind the run — answering it\n // would be the opposite of what was just asked for. The bubbles go with it,\n // and the text is handed back so it can return to the box instead of\n // disappearing. An API that does not report what it discarded leaves the\n // bubbles alone, and the next poll has the last word.\n if (discarded.length) {\n setMessages((prev) => prev.filter((m) => !m.queued));\n }\n resetQueueState();\n\n setShouldPoll(false);\n setIsLoading(false);\n setStatus('idle');\n\n const restoredText = discarded\n .map((m) => m.content?.message)\n .filter(Boolean)\n .join('\\n');\n\n return {\n discarded: discarded.length,\n ...(restoredText ? { restoredText } : {}),\n };\n }, [assistantId, resetQueueState]);\n\n return {\n messages: streamingMessage && isLoading ? [...messages, streamingMessage] : messages,\n chatUid,\n isLoading,\n status,\n error,\n limitExceeded,\n recalledMemories,\n compactions,\n compaction,\n handedOff,\n handedOffSubThreadId,\n queuedCount,\n queueEnabled,\n sendMessage,\n clearChat,\n loadChat,\n onHandoffCompleted,\n stopChat,\n pendingWidgetCalls,\n submitWidgetResponse,\n cancelWidgetCall,\n };\n}\n"],"names":["useTranslations","useRef","useOptionalDevicContext","useMemo","resolvePollingInterval","resolveStreaming","createLogger","useState","useEffect","useCallback","DevicApiClient","useAssistantInfo","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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"}
|