@origonai/web-chat-sdk 0.1.0 → 0.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n BOT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n AGENT: 'agent', // this is human agent (dock side)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getMessages, authenticate } from './http.js'\nimport { getDeviceId, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(messages: Array) => void} [onMessagesUpdate] - Called when messages array should be updated\n * @property {(isTyping: boolean) => void} [onTyping] - Called when typing status changes\n * @property {(isLiveAgent: boolean) => void} [onLiveAgentMode] - Called when live agent mode status changes\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {boolean} liveAgent\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n liveAgent: false,\n lastStreamId: undefined,\n messages: [],\n callbacks\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n\n if (payload.sessionId) {\n const messagesRes = await getMessages(payload.sessionId)\n messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.BOT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n channel: msg.channel,\n done: true\n }))\n }\n\n const searchParams = new URLSearchParams({\n externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n\n await sleep(200)\n\n // Add loading message for bot if not in live agent mode\n if (!currentSession.liveAgent) {\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n } else {\n resolve(currentSession.sessionId)\n }\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({\n message: text,\n html\n }),\n signal: currentSession.abortController.signal,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n if (currentSession.liveAgent) {\n resolve(currentSession.sessionId)\n }\n } else if (data.agent) {\n currentSession.liveAgent = true\n const { type, data: payload } = data.agent\n\n switch (type) {\n case 'typing':\n currentSession.callbacks.onTyping?.(true)\n currentSession.callbacks.onLiveAgentMode?.(true)\n break\n case 'typingOff':\n currentSession.callbacks.onTyping?.(false)\n currentSession.callbacks.onLiveAgentMode?.(true)\n break\n case 'message':\n if (payload.role !== MESSAGE_ROLES.USER) {\n // Remove loading messages and add new message\n currentSession.messages = currentSession.messages.filter((msg) => !msg.loading)\n currentSession.messages = [...currentSession.messages, payload]\n\n const isEnded = payload.action === 'ended' || payload.action === 'left'\n if (isEnded) {\n currentSession.liveAgent = false\n }\n\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n currentSession.callbacks.onLiveAgentMode?.(!isEnded)\n }\n if (payload.action === 'ended' || payload.action === 'left') {\n currentSession.liveAgent = false\n }\n break\n default:\n break\n }\n } else if (data.message !== undefined) {\n let messages = currentSession.messages\n\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n messages = [\n ...messages,\n {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n ]\n }\n }\n\n // Update the last message with new content\n currentSession.messages = messages.map((msg, index) => {\n if (index === messages.length - 1) {\n return {\n ...msg,\n loading: false,\n text: (msg.text || '') + data.message,\n done: data.done ?? msg.done\n }\n }\n return msg\n })\n\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n\n if (data.done) {\n resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n } else if (data.error) {\n const errorMessage = 'Failed to connect to the system'\n currentSession.messages = currentSession.messages.map((msg, index) => {\n if (index === currentSession.messages.length - 1) {\n return {\n ...msg,\n loading: false,\n errorText: errorMessage\n }\n }\n return msg\n })\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n reject(new Error(errorMessage))\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n currentSession.messages = currentSession.messages.map((msg, index) => {\n if (index === currentSession.messages.length - 1) {\n return {\n ...msg,\n loading: false,\n errorText: errorMessage,\n done: true\n }\n }\n return msg\n })\n currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n// Module-level configuration\nlet _config = {\n endpoint: null\n}\n\n/**\n * Configure the API service with endpoint\n * @param {{ endpoint: string }} credentials\n */\nexport function configure(credentials) {\n _config = {\n endpoint: credentials.endpoint\n }\n}\n\n/**\n * Get current configuration\n * @returns {{ endpoint: string | null }}\n */\nexport function getConfig() {\n return { ..._config }\n}\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n // Store endpoint for subsequent requests\n configure({ endpoint })\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const { endpoint } = _config\n\n if (!endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","type","isEnded","index","_o","_p","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","_config","configure","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","handleCallServerEvent","action","handleAnswer","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
1
+ {"version":3,"file":"origon-chat-sdk.js","sources":["../src/utils.js","../src/constants.js","../src/chat.js","../src/http.js","../src/call.js"],"sourcesContent":["/**\n * Utility functions for the Chat SDK\n */\n\nexport function uuidv7() {\n const timestamp = Date.now()\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n\n // Set timestamp (48 bits)\n bytes[0] = (timestamp >> 40) & 0xff\n bytes[1] = (timestamp >> 32) & 0xff\n bytes[2] = (timestamp >> 24) & 0xff\n bytes[3] = (timestamp >> 16) & 0xff\n bytes[4] = (timestamp >> 8) & 0xff\n bytes[5] = timestamp & 0xff\n\n // Set version 7 (0111)\n bytes[6] = (bytes[6] & 0x0f) | 0x70\n\n // Set variant (10xx)\n bytes[8] = (bytes[8] & 0x3f) | 0x80\n\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('')\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(\n 16,\n 20\n )}-${hex.slice(20)}`\n}\n\nexport function getDeviceId() {\n if (localStorage.getItem('chatDeviceId')) {\n return localStorage.getItem('chatDeviceId')\n }\n\n const deviceId = uuidv7()\n localStorage.setItem('chatDeviceId', deviceId)\n return deviceId\n}\n\nexport function parseJwt(token) {\n try {\n const base64Url = token.split('.')[1]\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map(function (c) {\n return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)\n })\n .join('')\n )\n\n return JSON.parse(jsonPayload)\n } catch {\n return null\n }\n}\n\nexport async function sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nexport function getSocketEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/wss`\n } catch {\n console.error('Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n\nexport function getCallServerEndpoint(baseUrl) {\n let socketEndpoint\n try {\n const url = new URL(baseUrl)\n socketEndpoint = `wss://${url.hostname}${url.pathname}/audio`\n } catch {\n console.error('getCallServerEndpoint: Invalid base URL: ', baseUrl)\n }\n return socketEndpoint\n}\n","/**\n * Constants for the Chat SDK\n */\n\nexport const MESSAGE_ROLES = {\n BOT: 'assistant', // this can be automated or LLM AI Agent response\n USER: 'user', // this is widget user\n AGENT: 'agent', // this is human agent (dock side)\n SYSTEM: 'system' // this is system message, for ex \"Agent joined\" / \"Agent left\"\n}\n","/**\n * Chat Service for Chat SDK\n * Handles real-time chat functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { fetchEventSource } from '@microsoft/fetch-event-source'\nimport { getMessages, authenticate } from './http.js'\nimport { getDeviceId, sleep } from './utils.js'\nimport { MESSAGE_ROLES } from './constants.js'\n\n/**\n * @typedef {Object} ChatCallbacks\n * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added\n * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated\n * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated\n */\n\n/**\n * @typedef {Object} ChatSession\n * @property {string} sessionId\n * @property {string} sseUrl\n * @property {string} [requestId]\n * @property {AbortController} [abortController]\n * @property {string} [lastStreamId]\n * @property {Array} messages\n * @property {ChatCallbacks} callbacks\n */\n\n/**\n * Create a new chat session\n * @param {ChatCallbacks} [callbacks={}]\n * @returns {ChatSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n credentials: undefined,\n authenticated: false,\n configData: undefined,\n sessionId: undefined,\n requestId: undefined,\n sseUrl: undefined,\n abortController: undefined,\n lastStreamId: undefined,\n messages: [],\n callbacks\n }\n}\n\n/** @type {ChatSession} */\nlet currentSession = createSession()\n\n/**\n * Set callbacks for the current session\n * @param {ChatCallbacks} callbacks\n */\nexport function setCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Initialize the chat session\n * @param {Object} credentials - Credentials for the chat\n */\nexport function initialize(credentials) {\n console.log('Initializing chat...', credentials)\n currentSession.credentials = credentials\n if (credentials.token) {\n currentSession.authenticated = true\n }\n}\n\n/**\n * Get current chat session credentials\n * @returns {{ endpoint: string, apiKey: string } | undefined}\n */\nexport function getCredentials() {\n return currentSession.credentials\n}\n\n/**\n * Update the session ID and notify via callback\n * @param {string} sessionId - The new session ID\n */\nexport function updateSessionId(sessionId) {\n if (sessionId && sessionId !== currentSession.sessionId) {\n currentSession.sessionId = sessionId\n currentSession.callbacks.onSessionUpdate?.(sessionId)\n }\n}\n\n/**\n * Initiate a new chat session or resume an existing one\n * @param {Object} credentials - Credentials for the chat\n * @param {Object} payload - Payload for the chat. It contains sessionId (optional)\n * @param {string} [payload.sessionId] - Optional session ID to resume\n * @returns {Promise<{ sessionId: string, messages: Array }>}\n */\nexport async function startChat(payload = {}) {\n try {\n console.log('startChat: ', payload, currentSession)\n\n let configData = null\n if (!currentSession.authenticated) {\n configData = await authenticate(currentSession.credentials)\n currentSession.authenticated = true\n currentSession.configData = configData\n } else {\n configData = currentSession.configData\n }\n\n let messages = []\n\n if (payload.sessionId) {\n const messagesRes = await getMessages(payload.sessionId)\n messages = (messagesRes?.sessionHistory ?? []).map((msg) => ({\n id: msg.id,\n text: msg.text,\n role: msg.youtubeVideo\n ? MESSAGE_ROLES.BOT // for youtube video messages, role is \"system\" from backend, we need to make it \"assistant\"\n : msg.role,\n timestamp: msg.timestamp,\n video: msg.youtubeVideo,\n channel: msg.channel,\n done: true\n }))\n }\n\n const searchParams = new URLSearchParams({\n externalId: getExternalId()\n })\n currentSession.sseUrl = `${currentSession.credentials.endpoint}?${searchParams.toString()}`\n currentSession.sessionId = payload.sessionId\n currentSession.messages = messages\n\n console.log('Chat initiated successfully')\n\n return {\n sessionId: currentSession.sessionId,\n messages,\n configData\n }\n } catch (error) {\n console.error(`Failed to start chat: ${error.message}`)\n cleanup()\n throw error\n }\n}\n\n/**\n * Disconnect from the current chat session\n */\nexport function disconnect() {\n cleanup()\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.abortController) {\n currentSession.abortController.abort()\n }\n\n const { callbacks, credentials } = currentSession\n currentSession = createSession(callbacks)\n currentSession.credentials = credentials\n\n console.log('Chat session cleaned up')\n}\n\nexport function getExternalId() {\n if (currentSession.credentials?.externalId) {\n return currentSession.credentials.externalId\n }\n return getDeviceId()\n}\n\n/**\n * Send a message in the current chat session\n * @param {{ text: string, html?: string }} message\n * @returns {Promise<string>}\n */\nexport function sendMessage({ text, html }) {\n return new Promise((resolve, reject) => {\n ;(async () => {\n try {\n // Add user message\n const userMessage = {\n role: MESSAGE_ROLES.USER,\n text,\n html,\n timestamp: new Date().toISOString()\n }\n currentSession.messages = [...currentSession.messages, userMessage]\n currentSession.callbacks.onMessageAdd?.(userMessage)\n\n await sleep(200)\n\n const loadingMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, loadingMessage]\n currentSession.callbacks.onMessageAdd?.(loadingMessage)\n\n const url = new URL(currentSession.sseUrl)\n if (currentSession.sessionId) {\n url.searchParams.set('sessionId', currentSession.sessionId)\n }\n if (currentSession.requestId) {\n url.searchParams.set('requestId', currentSession.requestId)\n }\n\n currentSession.lastStreamId = undefined\n\n // Create a new abort controller for this request\n currentSession.abortController = new AbortController()\n\n const headers = {\n 'Content-Type': 'application/json'\n }\n if (currentSession.credentials?.token) {\n headers.Authorization = `Bearer ${currentSession.credentials.token}`\n }\n\n await fetchEventSource(url.toString(), {\n method: 'POST',\n headers,\n body: JSON.stringify({\n message: text,\n html\n }),\n signal: currentSession.abortController.signal,\n onopen: async (response) => {\n if (!response.ok) {\n console.error('Failed to send message bad response: ', response)\n throw new Error('Failed to send message')\n }\n },\n onmessage: (event) => {\n console.log('Event: ', event)\n const data = JSON.parse(event.data)\n\n if (data.status === 'connected') {\n currentSession.sessionId = data.sessionId\n currentSession.requestId = data.requestId\n } else if (data.message !== undefined) {\n // If streamId changes, start a new assistant message\n if (data.streamId !== undefined) {\n if (currentSession.lastStreamId === undefined) {\n currentSession.lastStreamId = data.streamId\n } else if (data.streamId !== currentSession.lastStreamId) {\n currentSession.lastStreamId = data.streamId\n const newBotMessage = {\n role: MESSAGE_ROLES.BOT,\n text: '',\n loading: true\n }\n currentSession.messages = [...currentSession.messages, newBotMessage]\n currentSession.callbacks.onMessageAdd?.(newBotMessage)\n }\n }\n\n // Update the last message with new content\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n text: (lastMsg.text || '') + data.message,\n done: data.done ?? lastMsg.done\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n\n if (data.done) {\n resolve(currentSession.sessionId)\n }\n\n // Store session info for reuse\n currentSession.sessionId = data.session_id ?? currentSession.sessionId\n currentSession.requestId = data.requestId ?? currentSession.requestId\n } else if (data.error) {\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: errorMessage\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(new Error(errorMessage))\n }\n },\n onerror: (error) => {\n throw error // Rethrow to stop retries\n },\n openWhenHidden: true\n })\n } catch (error) {\n console.error('Failed to send message: ', error)\n const errorMessage = 'Failed to connect to the system'\n const lastIndex = currentSession.messages.length - 1\n const lastMsg = currentSession.messages[lastIndex]\n const updatedMsg = {\n ...lastMsg,\n loading: false,\n errorText: lastMsg.done ? undefined : error.message || errorMessage,\n done: true\n }\n currentSession.messages = currentSession.messages.map((msg, index) =>\n index === lastIndex ? updatedMsg : msg\n )\n currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)\n reject(error)\n }\n })()\n })\n}\n","/**\n * API Service for Chat SDK\n * Handles all HTTP requests without depending on external state\n */\n\nimport { getCredentials, getExternalId } from './chat.js'\n\nconst AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'\nconst INITIALIZATION_ERROR = 'Chat SDK not initialized'\n\n/**\n * Authenticate with the chat service\n * @param {{ endpoint: string }} credentials\n * @returns {Promise<object>} Authentication response data\n */\nexport async function authenticate(payload) {\n const { endpoint } = payload\n const url = `${endpoint}/config`\n\n const response = await fetch(url, {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\n }\n })\n\n if (!response.ok) {\n const errorPayload = await response.json()\n throw new Error(errorPayload?.error || AUTHENTICATION_ERROR)\n }\n\n const res = await response.json()\n const data = res.data\n\n return data\n}\n\n/**\n * Get chat history for the current device\n * @returns {Promise<{ sessions: Array }>}\n */\nexport async function getHistory() {\n const queryParams = new URLSearchParams({\n externalId: getExternalId()\n })\n const response = await fetchRequest(`/sessions?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load history, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Get messages for a specific session\n * @param {string} sessionId\n * @returns {Promise<{ sessionHistory: Array }>}\n */\nexport async function getMessages(sessionId) {\n const queryParams = new URLSearchParams({\n sessionId\n })\n const response = await fetchRequest(`/session?${queryParams.toString()}`, 'GET')\n\n if (!response.ok) {\n throw new Error('Unable to load messages, please try again later')\n }\n\n return response.json()\n}\n\n/**\n * Internal fetch request helper\n * @param {string} pathname\n * @param {string} method\n * @param {object|null} body\n * @returns {Promise<Response>}\n */\nasync function fetchRequest(pathname, method = 'GET', body = null) {\n const credentials = getCredentials()\n\n if (!credentials?.endpoint) {\n throw new Error(INITIALIZATION_ERROR)\n }\n\n const url = `${endpoint}${pathname}`\n\n return fetch(url, {\n headers: {\n 'Content-Type': 'application/json'\n },\n method,\n body: body ? JSON.stringify(body) : null\n })\n}\n","/**\n * Socket Service for Call SDK\n * Handles WebRTC call functionality without depending on external state\n * Uses callbacks to communicate state changes to the consumer\n */\n\nimport { getCallServerEndpoint } from './utils.js'\nimport { getCredentials, getExternalId, updateSessionId } from './chat.js'\n\n/**\n * @typedef {Object} CallCallbacks\n * @property {(status: string) => void} [onCallStatus] - Called when call status changes\n * @property {(error: string | null) => void} [onCallError] - Called when call error occurs\n */\n\n/**\n * @typedef {Object} CallSession\n * @property {string} [sessionId]\n * @property {WebSocket} [socket]\n * @property {RTCPeerConnection} [peerConnection]\n * @property {MediaStream} [localStream]\n * @property {MediaStream} [remoteStream]\n * @property {HTMLAudioElement} [remoteAudio]\n * @property {boolean} isMuted\n * @property {string} callStatus\n * @property {NodeJS.Timeout} [pingInterval]\n * @property {number} pingCount\n * @property {number | null} lastPongTime\n * @property {CallCallbacks} callbacks\n * @property {string[]} localIceCandidates - Queued local ICE candidates to send after remote description is set\n * @property {string[]} pendingRemoteIceCandidates - Queued remote ICE candidates to add after remote description is set\n */\n\n/**\n * Create a new call session\n * @param {CallCallbacks} [callbacks={}]\n * @returns {CallSession}\n */\nfunction createSession(callbacks = {}) {\n return {\n sessionId: undefined,\n socket: null,\n peerConnection: null,\n localStream: null,\n remoteStream: null,\n remoteAudio: null,\n isMuted: false,\n callStatus: 'disconnected',\n pingInterval: null,\n pingCount: 0,\n lastPongTime: null,\n callbacks,\n localIceCandidates: [],\n pendingRemoteIceCandidates: []\n }\n}\n\n/** @type {CallSession} */\nlet currentSession = createSession()\n\nconst rtcConfig = {\n iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:stun1.l.google.com:19302' }]\n}\n\n/**\n * Set callbacks for the current session\n * @param {CallCallbacks} callbacks\n */\nexport function setCallCallbacks(callbacks) {\n currentSession.callbacks = { ...currentSession.callbacks, ...callbacks }\n}\n\n/**\n * Clean up the current session\n */\nfunction cleanup() {\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n\n if (currentSession.remoteStream) {\n currentSession.remoteStream = null\n }\n\n if (currentSession.remoteAudio) {\n currentSession.remoteAudio.srcObject = null\n if (currentSession.remoteAudio.parentNode) {\n currentSession.remoteAudio.parentNode.removeChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio = null\n }\n\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n\n stopPingInterval()\n\n const callbacks = currentSession.callbacks\n currentSession = createSession(callbacks)\n\n console.log('Call session cleaned up')\n}\n\n/**\n * Update call status and notify callback\n * @param {string} status\n */\nfunction setCallStatus(status) {\n currentSession.callStatus = status\n currentSession.callbacks.onCallStatus?.(status)\n}\n\n/**\n * Update call error and notify callback\n * @param {string | null} error\n */\nfunction setCallError(error) {\n currentSession.callbacks.onCallError?.(error)\n}\n\n/**\n * Stop ping interval\n */\nfunction stopPingInterval() {\n if (currentSession.pingInterval) {\n clearInterval(currentSession.pingInterval)\n currentSession.pingInterval = null\n }\n}\n\n/**\n * Start ping interval\n */\nfunction startPingInterval() {\n stopPingInterval()\n\n currentSession.pingInterval = setInterval(() => {\n if (currentSession.socket && currentSession.socket.readyState === WebSocket.OPEN) {\n currentSession.pingCount++\n const pingMessage = {\n type: 'ping',\n timestamp: Date.now(),\n count: currentSession.pingCount\n }\n sendEvent(pingMessage)\n console.log(`Sending keep-alive ping #${currentSession.pingCount}`)\n } else {\n console.log('Socket not open, stopping ping interval')\n stopPingInterval()\n }\n }, 10000)\n}\n\n/**\n * Handle pong response\n */\nfunction handlePong() {\n currentSession.lastPongTime = Date.now()\n console.log(`Received pong #${currentSession.pingCount}`)\n}\n\n/**\n * Send event through socket\n * @param {Object} payload\n */\nfunction sendEvent(payload) {\n if (!currentSession.socket) {\n console.error('Failed to send event: no socket instance')\n return\n }\n if (currentSession.socket.readyState !== WebSocket.OPEN) {\n console.error('Failed to send event: socket state not open ', payload)\n return\n }\n\n currentSession.socket.send(JSON.stringify(payload))\n}\n\n/**\n * Get user media\n */\nasync function getUserMedia() {\n try {\n currentSession.localStream = await navigator.mediaDevices.getUserMedia({\n audio: true,\n video: false\n })\n console.log('Got audio media')\n } catch (error) {\n console.error(`Failed to get audio media: ${error.message}`)\n throw error\n }\n}\n\n/**\n * Create peer connection\n */\nfunction createPeerConnection() {\n currentSession.peerConnection = new RTCPeerConnection(rtcConfig)\n\n currentSession.peerConnection.onicecandidate = (event) => {\n if (event.candidate) {\n const candidateJson = JSON.stringify(event.candidate)\n // Queue local ICE candidates until remote description is set\n if (currentSession.peerConnection && currentSession.peerConnection.remoteDescription) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent ICE candidate immediately')\n } else {\n currentSession.localIceCandidates.push(candidateJson)\n console.log('Queued local ICE candidate')\n }\n }\n }\n\n currentSession.peerConnection.ontrack = (event) => {\n console.log('Received remote audio stream')\n currentSession.remoteStream = event.streams[0]\n\n if (!currentSession.remoteAudio) {\n currentSession.remoteAudio = document.createElement('audio')\n currentSession.remoteAudio.autoplay = true\n currentSession.remoteAudio.controls = false\n document.body.appendChild(currentSession.remoteAudio)\n }\n currentSession.remoteAudio.srcObject = currentSession.remoteStream\n // explicitly kick off playback and catch any policy/gesture errors\n currentSession.remoteAudio\n .play()\n .then(() => console.log('🔊 remote audio playing'))\n .catch((err) => console.error('❌ playback error:', err))\n }\n\n currentSession.peerConnection.onconnectionstatechange = () => {\n const newState = currentSession.peerConnection.connectionState\n console.log(`Connection state: ${newState}`)\n\n if (newState === 'connected') {\n setCallStatus('connected')\n } else if (newState === 'disconnected' || newState === 'closed') {\n setCallStatus('disconnected')\n disconnectCall()\n }\n }\n\n currentSession.peerConnection.oniceconnectionstatechange = () => {\n console.log(`ICE connection state: ${currentSession.peerConnection.iceConnectionState}`)\n }\n}\n\n/**\n * Connect socket\n * @param {{ sessionId?: string }} payload\n */\nfunction connectSocket(payload) {\n return new Promise((fulfill, reject) => {\n if (\n currentSession.socket &&\n (currentSession.socket.readyState === WebSocket.CONNECTING ||\n currentSession.socket.readyState === WebSocket.OPEN)\n ) {\n console.log('Socket in connecting/open state, returning.')\n fulfill(currentSession.socket.readyState === WebSocket.OPEN)\n return\n }\n\n console.log('Initializing socket connection..')\n const credentials = getCredentials()\n if (!credentials || !credentials.endpoint) {\n reject(new Error('SDK not initialized. Please initialize SDK first.'))\n return\n }\n\n // Extract hostname from endpoint\n const socketEndpoint = getCallServerEndpoint(credentials.endpoint)\n if (!socketEndpoint) {\n reject(\n new Error(\n 'Invalid endpoint while initializing SDK. Please check the endpoint and try again.'\n )\n )\n return\n }\n\n const externalId = getExternalId()\n const queryParams = new URLSearchParams({\n externalId\n })\n if (payload.sessionId) {\n queryParams.set('sessionId', payload.sessionId)\n }\n if (credentials.token) {\n queryParams.set('token', credentials.token)\n }\n\n const socketUrl = `${socketEndpoint}?${queryParams.toString()}`\n currentSession.socket = new WebSocket(socketUrl)\n\n currentSession.socket.onopen = (event) => {\n console.log('Socket connection established: ', event)\n startPingInterval()\n fulfill(true)\n }\n\n currentSession.socket.onmessage = (event) => {\n const data = JSON.parse(event.data)\n handleCallServerEvent(data)\n }\n\n currentSession.socket.onerror = (error) => {\n console.error('Socket error: ', error)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n reject(error)\n }\n\n currentSession.socket.onclose = (event) => {\n console.log('Socket connection closed: ', event)\n stopPingInterval()\n }\n })\n}\n\n/**\n * Handle call server event\n * @param {Object} action\n */\nfunction handleCallServerEvent(action) {\n console.log('Handling socket server event: ', action)\n\n switch (action.type) {\n case 'pong':\n handlePong()\n break\n\n case 'answer':\n handleAnswer(action.data)\n break\n\n case 'ice':\n handleIceCandidate(action.data)\n break\n\n case 'renegotiationOffer':\n handleRenegotiationOffer(action.data)\n break\n case 'end':\n disconnectCall()\n break\n case 'error':\n setCallStatus('error')\n setCallError(action.error || 'Unable to connect voice')\n break\n\n default:\n console.log('Unknown call event type: ', action.type)\n break\n }\n}\n\n/**\n * Handle answer\n * @param {Object} data\n */\nasync function handleAnswer(data) {\n try {\n console.log('Received answer')\n\n currentSession.sessionId = data.sessionId\n // Update chat session with the new sessionId and notify controller\n updateSessionId(data.sessionId)\n\n if (currentSession.peerConnection) {\n const answer = new RTCSessionDescription({\n type: 'answer',\n sdp: data.sdp\n })\n console.log('Setting remote description answer: ', answer)\n await currentSession.peerConnection.setRemoteDescription(answer)\n console.log('Remote description set')\n\n // Send all queued local ICE candidates\n for (const candidateJson of currentSession.localIceCandidates) {\n sendEvent({\n type: 'ice',\n data: {\n candidate: candidateJson\n }\n })\n console.log('Sent queued local ICE candidate')\n }\n currentSession.localIceCandidates = []\n\n // Process any pending remote ICE candidates\n for (const candidateJson of currentSession.pendingRemoteIceCandidates) {\n try {\n const candidate = new RTCIceCandidate(JSON.parse(candidateJson))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added pending remote ICE candidate')\n } catch (err) {\n console.error(`Failed to add pending ICE candidate: ${err.message}`)\n }\n }\n currentSession.pendingRemoteIceCandidates = []\n }\n } catch (error) {\n console.error(`Failed to handle answer: ${error.message}`)\n }\n}\n\n/**\n * Handle ICE candidate\n * @param {Object} data\n */\nasync function handleIceCandidate(data) {\n try {\n if (currentSession.peerConnection) {\n // Check if remote description is set\n if (!currentSession.peerConnection.remoteDescription) {\n // Queue the candidate until remote description is set\n currentSession.pendingRemoteIceCandidates.push(data.candidate)\n console.log('Queued remote ICE candidate - remote description not set')\n return\n }\n const candidate = new RTCIceCandidate(JSON.parse(data.candidate))\n await currentSession.peerConnection.addIceCandidate(candidate)\n console.log('Added ICE candidate')\n }\n } catch (error) {\n console.error(`Failed to add ICE candidate: ${error.message}`)\n }\n}\n\n/**\n * Handle renegotiation offer\n * @param {Object} data\n */\nasync function handleRenegotiationOffer(data) {\n try {\n console.log('Received renegotiation offer')\n\n if (currentSession.peerConnection) {\n const offer = new RTCSessionDescription({\n type: 'offer',\n sdp: data.sdp\n })\n console.log('Setting remote description offer: ', offer)\n await currentSession.peerConnection.setRemoteDescription(offer)\n console.log('Remote description set')\n\n const answer = await currentSession.peerConnection.createAnswer()\n await currentSession.peerConnection.setLocalDescription(answer)\n\n sendEvent({\n type: 'renegotiationAnswer',\n data: {\n sdp: answer.sdp\n }\n })\n }\n } catch (error) {\n console.error(`Failed to handle renegotiation offer: ${error.message}`)\n }\n}\n\n/**\n * Start a call\n * @param {{ sessionId?: string }} payload\n */\nexport async function startCall(payload = {}) {\n try {\n if (currentSession.callStatus === 'connecting' || currentSession.callStatus === 'connected') {\n console.log(`Call already in ${currentSession.callStatus} state`)\n return\n }\n\n console.log('Starting audio call...')\n setCallStatus('connecting')\n setCallError(null)\n\n currentSession.sessionId = payload.sessionId\n\n await getUserMedia()\n\n createPeerConnection()\n\n currentSession.localStream.getTracks().forEach((track) => {\n currentSession.peerConnection.addTrack(track, currentSession.localStream)\n console.log(`Added ${track.kind} track`)\n })\n await connectSocket(payload)\n const offer = await currentSession.peerConnection.createOffer()\n await currentSession.peerConnection.setLocalDescription(offer)\n\n sendEvent({\n type: 'offer',\n data: {\n sdp: offer.sdp\n }\n })\n\n console.log('Call initiated successfully')\n } catch (error) {\n console.log('error: ', error)\n console.error(`Failed to start call: ${error.message}`)\n setCallStatus('error')\n setCallError(error.message || 'Unable to connect voice')\n cleanup()\n }\n}\n\n/**\n * Disconnect call\n */\nexport function disconnectCall() {\n sendEvent({\n type: 'end'\n })\n if (currentSession.socket) {\n currentSession.socket.close()\n currentSession.socket = null\n }\n setCallStatus('disconnected')\n if (currentSession.peerConnection) {\n currentSession.peerConnection.close()\n currentSession.peerConnection = null\n }\n if (currentSession.localStream) {\n currentSession.localStream.getTracks().forEach((track) => track.stop())\n currentSession.localStream = null\n }\n cleanup()\n}\n\n/**\n * Toggle mute\n * @returns {boolean}\n */\nexport function toggleMute() {\n if (currentSession.localStream) {\n const audioTrack = currentSession.localStream.getAudioTracks()[0]\n if (audioTrack) {\n audioTrack.enabled = !audioTrack.enabled\n currentSession.isMuted = !audioTrack.enabled\n console.log(`Audio ${currentSession.isMuted ? 'muted' : 'unmuted'}`)\n return currentSession.isMuted\n }\n }\n return false\n}\n\n/**\n * Get local stream\n * @returns {MediaStream | null}\n */\nexport function getLocalStream() {\n return currentSession.localStream\n}\n\n/**\n * Get inbound audio energy\n * @returns {Promise<number>}\n */\nexport function getInboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'inbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no inbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n\n/**\n * Get outbound audio energy (not implemented in original, but may be needed)\n * @returns {Promise<number>}\n */\nexport function getOutboundAudioEnergy() {\n return new Promise((resolve, reject) => {\n if (!currentSession.peerConnection) {\n reject(new Error('no peer connection'))\n return\n }\n currentSession.peerConnection\n .getStats()\n .then((stats) => {\n stats.forEach((report) => {\n if (report.type == 'outbound-rtp') {\n resolve(report.totalAudioEnergy)\n }\n })\n reject(new Error('no outbound-rtp stats found'))\n })\n .catch((err) => {\n reject(err)\n })\n })\n}\n"],"names":["uuidv7","timestamp","bytes","hex","b","getDeviceId","deviceId","sleep","ms","resolve","getCallServerEndpoint","baseUrl","socketEndpoint","url","e","MESSAGE_ROLES","createSession","callbacks","currentSession","setCallbacks","initialize","credentials","getCredentials","updateSessionId","sessionId","startChat","payload","configData","authenticate","messages","messagesRes","getMessages","_a","msg","searchParams","getExternalId","error","cleanup","disconnect","sendMessage","text","html","reject","userMessage","loadingMessage","headers","fetchEventSource","response","event","data","newBotMessage","lastIndex","lastMsg","updatedMsg","_c","index","_f","_g","errorMessage","AUTHENTICATION_ERROR","INITIALIZATION_ERROR","endpoint","errorPayload","getHistory","queryParams","fetchRequest","pathname","method","body","rtcConfig","setCallCallbacks","track","stopPingInterval","setCallStatus","status","_b","setCallError","startPingInterval","pingMessage","sendEvent","handlePong","getUserMedia","createPeerConnection","candidateJson","err","newState","disconnectCall","connectSocket","fulfill","externalId","socketUrl","handleCallServerEvent","action","handleAnswer","handleIceCandidate","handleRenegotiationOffer","answer","candidate","offer","startCall","toggleMute","audioTrack","getLocalStream","getInboundAudioEnergy","stats","report","getOutboundAudioEnergy"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@origonai/web-chat-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Origon Web Chat SDK - Core chat functionality without UI dependencies",
5
5
  "type": "module",
6
6
  "main": "./dist/origon-chat-sdk.js",
package/src/chat.js CHANGED
@@ -11,9 +11,8 @@ import { MESSAGE_ROLES } from './constants.js'
11
11
 
12
12
  /**
13
13
  * @typedef {Object} ChatCallbacks
14
- * @property {(messages: Array) => void} [onMessagesUpdate] - Called when messages array should be updated
15
- * @property {(isTyping: boolean) => void} [onTyping] - Called when typing status changes
16
- * @property {(isLiveAgent: boolean) => void} [onLiveAgentMode] - Called when live agent mode status changes
14
+ * @property {(message: Object) => void} [onMessageAdd] - Called when a new message is added
15
+ * @property {(index: number, updatedMsg: Object) => void} [onMessageUpdate] - Called when an existing message is updated
17
16
  * @property {(sessionId: string) => void} [onSessionUpdate] - Called when session ID is updated
18
17
  */
19
18
 
@@ -22,7 +21,6 @@ import { MESSAGE_ROLES } from './constants.js'
22
21
  * @property {string} sessionId
23
22
  * @property {string} sseUrl
24
23
  * @property {string} [requestId]
25
- * @property {boolean} liveAgent
26
24
  * @property {AbortController} [abortController]
27
25
  * @property {string} [lastStreamId]
28
26
  * @property {Array} messages
@@ -43,7 +41,6 @@ function createSession(callbacks = {}) {
43
41
  requestId: undefined,
44
42
  sseUrl: undefined,
45
43
  abortController: undefined,
46
- liveAgent: false,
47
44
  lastStreamId: undefined,
48
45
  messages: [],
49
46
  callbacks
@@ -196,22 +193,17 @@ export function sendMessage({ text, html }) {
196
193
  timestamp: new Date().toISOString()
197
194
  }
198
195
  currentSession.messages = [...currentSession.messages, userMessage]
199
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
196
+ currentSession.callbacks.onMessageAdd?.(userMessage)
200
197
 
201
198
  await sleep(200)
202
199
 
203
- // Add loading message for bot if not in live agent mode
204
- if (!currentSession.liveAgent) {
205
- const loadingMessage = {
206
- role: MESSAGE_ROLES.BOT,
207
- text: '',
208
- loading: true
209
- }
210
- currentSession.messages = [...currentSession.messages, loadingMessage]
211
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
212
- } else {
213
- resolve(currentSession.sessionId)
200
+ const loadingMessage = {
201
+ role: MESSAGE_ROLES.BOT,
202
+ text: '',
203
+ loading: true
214
204
  }
205
+ currentSession.messages = [...currentSession.messages, loadingMessage]
206
+ currentSession.callbacks.onMessageAdd?.(loadingMessage)
215
207
 
216
208
  const url = new URL(currentSession.sseUrl)
217
209
  if (currentSession.sessionId) {
@@ -254,77 +246,37 @@ export function sendMessage({ text, html }) {
254
246
  if (data.status === 'connected') {
255
247
  currentSession.sessionId = data.sessionId
256
248
  currentSession.requestId = data.requestId
257
- if (currentSession.liveAgent) {
258
- resolve(currentSession.sessionId)
259
- }
260
- } else if (data.agent) {
261
- currentSession.liveAgent = true
262
- const { type, data: payload } = data.agent
263
-
264
- switch (type) {
265
- case 'typing':
266
- currentSession.callbacks.onTyping?.(true)
267
- currentSession.callbacks.onLiveAgentMode?.(true)
268
- break
269
- case 'typingOff':
270
- currentSession.callbacks.onTyping?.(false)
271
- currentSession.callbacks.onLiveAgentMode?.(true)
272
- break
273
- case 'message':
274
- if (payload.role !== MESSAGE_ROLES.USER) {
275
- // Remove loading messages and add new message
276
- currentSession.messages = currentSession.messages.filter((msg) => !msg.loading)
277
- currentSession.messages = [...currentSession.messages, payload]
278
-
279
- const isEnded = payload.action === 'ended' || payload.action === 'left'
280
- if (isEnded) {
281
- currentSession.liveAgent = false
282
- }
283
-
284
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
285
- currentSession.callbacks.onLiveAgentMode?.(!isEnded)
286
- }
287
- if (payload.action === 'ended' || payload.action === 'left') {
288
- currentSession.liveAgent = false
289
- }
290
- break
291
- default:
292
- break
293
- }
294
249
  } else if (data.message !== undefined) {
295
- let messages = currentSession.messages
296
-
297
250
  // If streamId changes, start a new assistant message
298
251
  if (data.streamId !== undefined) {
299
252
  if (currentSession.lastStreamId === undefined) {
300
253
  currentSession.lastStreamId = data.streamId
301
254
  } else if (data.streamId !== currentSession.lastStreamId) {
302
255
  currentSession.lastStreamId = data.streamId
303
- messages = [
304
- ...messages,
305
- {
306
- role: MESSAGE_ROLES.BOT,
307
- text: '',
308
- loading: true
309
- }
310
- ]
256
+ const newBotMessage = {
257
+ role: MESSAGE_ROLES.BOT,
258
+ text: '',
259
+ loading: true
260
+ }
261
+ currentSession.messages = [...currentSession.messages, newBotMessage]
262
+ currentSession.callbacks.onMessageAdd?.(newBotMessage)
311
263
  }
312
264
  }
313
265
 
314
266
  // Update the last message with new content
315
- currentSession.messages = messages.map((msg, index) => {
316
- if (index === messages.length - 1) {
317
- return {
318
- ...msg,
319
- loading: false,
320
- text: (msg.text || '') + data.message,
321
- done: data.done ?? msg.done
322
- }
323
- }
324
- return msg
325
- })
267
+ const lastIndex = currentSession.messages.length - 1
268
+ const lastMsg = currentSession.messages[lastIndex]
269
+ const updatedMsg = {
270
+ ...lastMsg,
271
+ loading: false,
272
+ text: (lastMsg.text || '') + data.message,
273
+ done: data.done ?? lastMsg.done
274
+ }
275
+ currentSession.messages = currentSession.messages.map((msg, index) =>
276
+ index === lastIndex ? updatedMsg : msg
277
+ )
326
278
 
327
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
279
+ currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
328
280
 
329
281
  if (data.done) {
330
282
  resolve(currentSession.sessionId)
@@ -335,17 +287,17 @@ export function sendMessage({ text, html }) {
335
287
  currentSession.requestId = data.requestId ?? currentSession.requestId
336
288
  } else if (data.error) {
337
289
  const errorMessage = 'Failed to connect to the system'
338
- currentSession.messages = currentSession.messages.map((msg, index) => {
339
- if (index === currentSession.messages.length - 1) {
340
- return {
341
- ...msg,
342
- loading: false,
343
- errorText: errorMessage
344
- }
345
- }
346
- return msg
347
- })
348
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
290
+ const lastIndex = currentSession.messages.length - 1
291
+ const lastMsg = currentSession.messages[lastIndex]
292
+ const updatedMsg = {
293
+ ...lastMsg,
294
+ loading: false,
295
+ errorText: errorMessage
296
+ }
297
+ currentSession.messages = currentSession.messages.map((msg, index) =>
298
+ index === lastIndex ? updatedMsg : msg
299
+ )
300
+ currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
349
301
  reject(new Error(errorMessage))
350
302
  }
351
303
  },
@@ -357,18 +309,18 @@ export function sendMessage({ text, html }) {
357
309
  } catch (error) {
358
310
  console.error('Failed to send message: ', error)
359
311
  const errorMessage = 'Failed to connect to the system'
360
- currentSession.messages = currentSession.messages.map((msg, index) => {
361
- if (index === currentSession.messages.length - 1) {
362
- return {
363
- ...msg,
364
- loading: false,
365
- errorText: errorMessage,
366
- done: true
367
- }
368
- }
369
- return msg
370
- })
371
- currentSession.callbacks.onMessagesUpdate?.([...currentSession.messages])
312
+ const lastIndex = currentSession.messages.length - 1
313
+ const lastMsg = currentSession.messages[lastIndex]
314
+ const updatedMsg = {
315
+ ...lastMsg,
316
+ loading: false,
317
+ errorText: lastMsg.done ? undefined : error.message || errorMessage,
318
+ done: true
319
+ }
320
+ currentSession.messages = currentSession.messages.map((msg, index) =>
321
+ index === lastIndex ? updatedMsg : msg
322
+ )
323
+ currentSession.callbacks.onMessageUpdate?.(lastIndex, updatedMsg)
372
324
  reject(error)
373
325
  }
374
326
  })()
package/src/http.js CHANGED
@@ -3,34 +3,11 @@
3
3
  * Handles all HTTP requests without depending on external state
4
4
  */
5
5
 
6
- import { getExternalId } from './chat.js'
6
+ import { getCredentials, getExternalId } from './chat.js'
7
7
 
8
8
  const AUTHENTICATION_ERROR = 'Something went wrong initializing the chat'
9
9
  const INITIALIZATION_ERROR = 'Chat SDK not initialized'
10
10
 
11
- // Module-level configuration
12
- let _config = {
13
- endpoint: null
14
- }
15
-
16
- /**
17
- * Configure the API service with endpoint
18
- * @param {{ endpoint: string }} credentials
19
- */
20
- export function configure(credentials) {
21
- _config = {
22
- endpoint: credentials.endpoint
23
- }
24
- }
25
-
26
- /**
27
- * Get current configuration
28
- * @returns {{ endpoint: string | null }}
29
- */
30
- export function getConfig() {
31
- return { ..._config }
32
- }
33
-
34
11
  /**
35
12
  * Authenticate with the chat service
36
13
  * @param {{ endpoint: string }} credentials
@@ -55,9 +32,6 @@ export async function authenticate(payload) {
55
32
  const res = await response.json()
56
33
  const data = res.data
57
34
 
58
- // Store endpoint for subsequent requests
59
- configure({ endpoint })
60
-
61
35
  return data
62
36
  }
63
37
 
@@ -104,9 +78,9 @@ export async function getMessages(sessionId) {
104
78
  * @returns {Promise<Response>}
105
79
  */
106
80
  async function fetchRequest(pathname, method = 'GET', body = null) {
107
- const { endpoint } = _config
81
+ const credentials = getCredentials()
108
82
 
109
- if (!endpoint) {
83
+ if (!credentials?.endpoint) {
110
84
  throw new Error(INITIALIZATION_ERROR)
111
85
  }
112
86
 
package/src/index.js CHANGED
@@ -21,7 +21,8 @@
21
21
  * // Set up callbacks for state updates
22
22
  * setCallbacks({
23
23
  * onMessage: (msg) => console.log('New message:', msg),
24
- * onMessagesUpdate: (messages) => updateUI(messages),
24
+ * onMessageAdd: (message) => updateUI(message),
25
+ * onMessageUpdate: (index, updatedMsg) => updateUI(updatedMsg),
25
26
  * onTyping: (isTyping) => showTypingIndicator(isTyping),
26
27
  * onError: (error) => console.error(error)
27
28
  * })
@@ -38,7 +39,7 @@
38
39
  */
39
40
 
40
41
  // HTTP API functions
41
- export { authenticate, getHistory, getMessages, configure } from './http.js'
42
+ export { authenticate, getHistory, getMessages } from './http.js'
42
43
 
43
44
  // Chat functions
44
45
  export { initialize, startChat, sendMessage, disconnect, setCallbacks } from './chat.js'