{"version":3,"file":"CopilotChat.cjs","names":["useCopilotChatConfiguration","DEFAULT_AGENT_ID","useAgent","useCopilotKit","useSuggestions","HttpAgent","transcribeAudio","TranscriptionError","TranscriptionErrorCode","renderSlot","CopilotChatView","CopilotChatConfigurationProvider"],"sources":["../../../src/components/chat/CopilotChat.tsx"],"sourcesContent":["import { useAgent } from \"@/hooks/use-agent\";\nimport { useSuggestions } from \"@/hooks/use-suggestions\";\nimport { CopilotChatView, CopilotChatViewProps } from \"./CopilotChatView\";\nimport { CopilotChatInputMode } from \"./CopilotChatInput\";\nimport {\n  CopilotChatConfigurationProvider,\n  CopilotChatLabels,\n  useCopilotChatConfiguration,\n} from \"@/providers/CopilotChatConfigurationProvider\";\nimport {\n  DEFAULT_AGENT_ID,\n  randomUUID,\n  TranscriptionErrorCode,\n} from \"@copilotkitnext/shared\";\nimport { Suggestion, CopilotKitCoreErrorCode } from \"@copilotkitnext/core\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { merge } from \"ts-deepmerge\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { AbstractAgent, HttpAgent } from \"@ag-ui/client\";\nimport { renderSlot, SlotValue } from \"@/lib/slots\";\nimport {\n  transcribeAudio,\n  TranscriptionError,\n} from \"@/lib/transcription-client\";\n\nexport type CopilotChatProps = Omit<\n  CopilotChatViewProps,\n  | \"messages\"\n  | \"isRunning\"\n  | \"suggestions\"\n  | \"suggestionLoadingIndexes\"\n  | \"onSelectSuggestion\"\n> & {\n  agentId?: string;\n  threadId?: string;\n  labels?: Partial<CopilotChatLabels>;\n  chatView?: SlotValue<typeof CopilotChatView>;\n  isModalDefaultOpen?: boolean;\n  /**\n   * Error handler scoped to this chat's agent. Fires in addition to the\n   * provider-level onError (does not suppress it). Receives only errors\n   * whose context.agentId matches this chat's agent.\n   */\n  onError?: (event: {\n    error: Error;\n    code: CopilotKitCoreErrorCode;\n    context: Record<string, any>;\n  }) => void | Promise<void>;\n};\nexport function CopilotChat({\n  agentId,\n  threadId,\n  labels,\n  chatView,\n  isModalDefaultOpen,\n  onError,\n  ...props\n}: CopilotChatProps) {\n  // Check for existing configuration provider\n  const existingConfig = useCopilotChatConfiguration();\n\n  // Apply priority: props > existing config > defaults\n  const resolvedAgentId =\n    agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;\n  const resolvedThreadId = useMemo(\n    () => threadId ?? existingConfig?.threadId ?? randomUUID(),\n    [threadId, existingConfig?.threadId],\n  );\n\n  const { agent } = useAgent({ agentId: resolvedAgentId });\n  const { copilotkit } = useCopilotKit();\n  const { suggestions: autoSuggestions } = useSuggestions({\n    agentId: resolvedAgentId,\n  });\n\n  // onError subscription — forward core errors scoped to this chat's agent\n  const onErrorRef = useRef(onError);\n  useEffect(() => {\n    onErrorRef.current = onError;\n  }, [onError]);\n\n  useEffect(() => {\n    if (!onErrorRef.current) return;\n\n    const subscription = copilotkit.subscribe({\n      onError: (event) => {\n        // Only forward errors that match this chat's agent\n        if (\n          event.context?.agentId === resolvedAgentId ||\n          !event.context?.agentId\n        ) {\n          onErrorRef.current?.({\n            error: event.error,\n            code: event.code,\n            context: event.context,\n          });\n        }\n      },\n    });\n\n    return () => {\n      subscription.unsubscribe();\n    };\n  }, [copilotkit, resolvedAgentId]);\n\n  // Transcription state\n  const [transcribeMode, setTranscribeMode] =\n    useState<CopilotChatInputMode>(\"input\");\n  const [inputValue, setInputValue] = useState(\"\");\n  const [transcriptionError, setTranscriptionError] = useState<string | null>(\n    null,\n  );\n  const [isTranscribing, setIsTranscribing] = useState(false);\n\n  // Check if transcription is enabled\n  const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;\n\n  // Check if browser supports MediaRecorder\n  const isMediaRecorderSupported =\n    typeof window !== \"undefined\" && typeof MediaRecorder !== \"undefined\";\n\n  const {\n    messageView: providedMessageView,\n    suggestionView: providedSuggestionView,\n    onStop: providedStopHandler,\n    ...restProps\n  } = props;\n\n  useEffect(() => {\n    let detached = false;\n\n    // Create a fresh AbortController so we can cancel the HTTP request on cleanup.\n    // HttpAgent (parent of ProxiedCopilotRuntimeAgent) uses this.abortController.signal\n    // in its fetch config. Unlike runAgent(), connectAgent() does NOT create a new\n    // AbortController automatically, so we must set one before connecting.\n    const connectAbortController = new AbortController();\n    if (agent instanceof HttpAgent) {\n      agent.abortController = connectAbortController;\n    }\n\n    const connect = async (agent: AbstractAgent) => {\n      try {\n        await copilotkit.connectAgent({ agent });\n      } catch (error) {\n        // Ignore errors from aborted connections (e.g., React StrictMode cleanup)\n        if (detached) return;\n        // connectAgent already emits via the subscriber system, but catch\n        // here to prevent unhandled rejections from unexpected errors.\n        console.error(\"CopilotChat: connectAgent failed\", error);\n      }\n    };\n    agent.threadId = resolvedThreadId;\n    connect(agent);\n    return () => {\n      // Abort the HTTP request and detach the active run.\n      // This is critical for React StrictMode which unmounts+remounts in dev,\n      // preventing duplicate /connect requests from reaching the server.\n      detached = true;\n      connectAbortController.abort();\n      // The .catch() is required to prevent a false-positive \"Uncaught (in promise)\n      // AbortError\" in browser devtools. detachActiveRun() itself does not reject,\n      // but without an attached handler V8 flags the promise chain as unhandled\n      // when the abort signal propagates through connected promises internally.\n      void agent.detachActiveRun().catch(() => {});\n    };\n    // copilotkit is intentionally excluded — it is a stable ref that never changes.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [resolvedThreadId, agent, resolvedAgentId]);\n\n  const onSubmitInput = useCallback(\n    async (value: string) => {\n      agent.addMessage({\n        id: randomUUID(),\n        role: \"user\",\n        content: value,\n      });\n      // Clear input after submitting\n      setInputValue(\"\");\n      try {\n        await copilotkit.runAgent({ agent });\n      } catch (error) {\n        console.error(\"CopilotChat: runAgent failed\", error);\n      }\n    },\n    // copilotkit is intentionally excluded — it is a stable ref that never changes.\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [agent],\n  );\n\n  const handleSelectSuggestion = useCallback(\n    async (suggestion: Suggestion) => {\n      agent.addMessage({\n        id: randomUUID(),\n        role: \"user\",\n        content: suggestion.message,\n      });\n\n      try {\n        await copilotkit.runAgent({ agent });\n      } catch (error) {\n        console.error(\n          \"CopilotChat: runAgent failed after selecting suggestion\",\n          error,\n        );\n      }\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [agent],\n  );\n\n  const stopCurrentRun = useCallback(() => {\n    try {\n      copilotkit.stopAgent({ agent });\n    } catch (error) {\n      console.error(\"CopilotChat: stopAgent failed\", error);\n      try {\n        agent.abortRun();\n      } catch (abortError) {\n        console.error(\"CopilotChat: abortRun fallback failed\", abortError);\n      }\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [agent]);\n\n  // Transcription handlers\n  const handleStartTranscribe = useCallback(() => {\n    setTranscriptionError(null);\n    setTranscribeMode(\"transcribe\");\n  }, []);\n\n  const handleCancelTranscribe = useCallback(() => {\n    setTranscriptionError(null);\n    setTranscribeMode(\"input\");\n  }, []);\n\n  const handleFinishTranscribe = useCallback(() => {\n    setTranscribeMode(\"input\");\n  }, []);\n\n  // Handle audio blob from CopilotChatInput and transcribe it\n  const handleFinishTranscribeWithAudio = useCallback(\n    async (audioBlob: Blob) => {\n      setIsTranscribing(true);\n      try {\n        setTranscriptionError(null);\n\n        // Send to transcription endpoint\n        const result = await transcribeAudio(copilotkit, audioBlob);\n\n        // Insert transcribed text into input\n        setInputValue((prev) => {\n          const trimmedPrev = prev.trim();\n          if (trimmedPrev) {\n            return `${trimmedPrev} ${result.text}`;\n          }\n          return result.text;\n        });\n      } catch (error) {\n        console.error(\"CopilotChat: Transcription failed\", error);\n\n        // Show contextual error message based on error type\n        if (error instanceof TranscriptionError) {\n          const { code, retryable, message } = error.info;\n          switch (code) {\n            case TranscriptionErrorCode.RATE_LIMITED:\n              setTranscriptionError(\"Too many requests. Please wait a moment.\");\n              break;\n            case TranscriptionErrorCode.AUTH_FAILED:\n              setTranscriptionError(\n                \"Authentication error. Please check your configuration.\",\n              );\n              break;\n            case TranscriptionErrorCode.AUDIO_TOO_LONG:\n              setTranscriptionError(\n                \"Recording is too long. Please try a shorter recording.\",\n              );\n              break;\n            case TranscriptionErrorCode.AUDIO_TOO_SHORT:\n              setTranscriptionError(\n                \"Recording is too short. Please try again.\",\n              );\n              break;\n            case TranscriptionErrorCode.INVALID_AUDIO_FORMAT:\n              setTranscriptionError(\"Audio format not supported.\");\n              break;\n            case TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:\n              setTranscriptionError(\"Transcription service is not available.\");\n              break;\n            case TranscriptionErrorCode.NETWORK_ERROR:\n              setTranscriptionError(\n                \"Network error. Please check your connection.\",\n              );\n              break;\n            default:\n              // For retryable errors, show more helpful message\n              setTranscriptionError(\n                retryable ? \"Transcription failed. Please try again.\" : message,\n              );\n          }\n        } else {\n          // Fallback for unexpected errors\n          setTranscriptionError(\"Transcription failed. Please try again.\");\n        }\n      } finally {\n        setIsTranscribing(false);\n      }\n    },\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [],\n  );\n\n  // Clear transcription error after a delay\n  useEffect(() => {\n    if (transcriptionError) {\n      const timer = setTimeout(() => {\n        setTranscriptionError(null);\n      }, 5000);\n      return () => clearTimeout(timer);\n    }\n  }, [transcriptionError]);\n\n  const mergedProps = merge(\n    {\n      isRunning: agent.isRunning,\n      suggestions: autoSuggestions,\n      onSelectSuggestion: handleSelectSuggestion,\n      suggestionView: providedSuggestionView,\n    },\n    {\n      ...restProps,\n      ...(typeof providedMessageView === \"string\"\n        ? { messageView: { className: providedMessageView } }\n        : providedMessageView !== undefined\n          ? { messageView: providedMessageView }\n          : {}),\n    },\n  );\n\n  const hasMessages = agent.messages.length > 0;\n  const shouldAllowStop = agent.isRunning && hasMessages;\n  const effectiveStopHandler = shouldAllowStop\n    ? (providedStopHandler ?? stopCurrentRun)\n    : providedStopHandler;\n\n  // Determine if transcription feature should be available\n  const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;\n\n  // Determine mode: transcribing takes priority, then transcribe mode, then default to input\n  const effectiveMode: CopilotChatInputMode = isTranscribing\n    ? \"processing\"\n    : transcribeMode;\n\n  // Memoize messages array - only create new reference when content actually changes\n  // (agent.messages is mutated in place, so we need a new reference for React to detect changes)\n\n  const messages = useMemo(\n    () => [...agent.messages],\n    [JSON.stringify(agent.messages)],\n  );\n\n  const finalProps = merge(mergedProps, {\n    messages,\n    // Input behavior props\n    onSubmitMessage: onSubmitInput,\n    onStop: effectiveStopHandler,\n    inputMode: effectiveMode,\n    inputValue,\n    onInputChange: setInputValue,\n    // Only provide transcription handlers if feature is available\n    onStartTranscribe: showTranscription ? handleStartTranscribe : undefined,\n    onCancelTranscribe: showTranscription ? handleCancelTranscribe : undefined,\n    onFinishTranscribe: showTranscription ? handleFinishTranscribe : undefined,\n    onFinishTranscribeWithAudio: showTranscription\n      ? handleFinishTranscribeWithAudio\n      : undefined,\n  }) as CopilotChatViewProps;\n\n  // Always create a provider with merged values\n  // This ensures priority: props > existing config > defaults\n  const RenderedChatView = renderSlot(chatView, CopilotChatView, finalProps);\n\n  return (\n    <CopilotChatConfigurationProvider\n      agentId={resolvedAgentId}\n      threadId={resolvedThreadId}\n      labels={labels}\n      isModalDefaultOpen={isModalDefaultOpen}\n    >\n      {transcriptionError && (\n        <div\n          style={{\n            position: \"absolute\",\n            bottom: \"100px\",\n            left: \"50%\",\n            transform: \"translateX(-50%)\",\n            backgroundColor: \"#ef4444\",\n            color: \"white\",\n            padding: \"8px 16px\",\n            borderRadius: \"8px\",\n            fontSize: \"14px\",\n            zIndex: 50,\n          }}\n        >\n          {transcriptionError}\n        </div>\n      )}\n      {RenderedChatView}\n    </CopilotChatConfigurationProvider>\n  );\n}\n\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace CopilotChat {\n  export const View = CopilotChatView;\n}\n"],"mappings":"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"}