@elevenlabs/client 0.15.1 → 0.16.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.
@@ -1,2 +1,2 @@
1
- import{Room as e,RoomEvent as t,ConnectionState as n,Track as o,createLocalAudioTrack as s}from"livekit-client";function i(){return i=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},i.apply(null,arguments)}const a=new Uint8Array(0);class r{static getFullOptions(e){var t;const n=function(e){var t,n;const{textOnly:o}=null!=(t=null==(n=e.overrides)?void 0:n.conversation)?t:{},{textOnly:s}=e;return"boolean"==typeof s?("boolean"==typeof o&&s!==o&&console.warn(`Conflicting textOnly options provided: ${s} via options.textOnly (will be used) and ${o} via options.overrides.conversation.textOnly (will be ignored)`),s):"boolean"==typeof o?o:void 0}(e);return i({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{},onInterruption:()=>{}},e,{textOnly:n,overrides:i({},e.overrides,{conversation:i({},null==(t=e.overrides)?void 0:t.conversation,{textOnly:n})})})}constructor(e,t){var n=this;this.options=void 0,this.connection=void 0,this.lastInterruptTimestamp=0,this.mode="listening",this.status="connecting",this.volume=1,this.currentEventId=1,this.lastFeedbackEventId=0,this.canSendFeedback=!1,this.endSessionWithDetails=async function(e){"connected"!==n.status&&"connecting"!==n.status||(n.updateStatus("disconnecting"),await n.handleEndSession(),n.updateStatus("disconnected"),n.options.onDisconnect&&n.options.onDisconnect(e))},this.onMessage=async function(e){switch(e.type){case"interruption":return void n.handleInterruption(e);case"agent_response":return void n.handleAgentResponse(e);case"user_transcript":return void n.handleUserTranscript(e);case"internal_tentative_agent_response":return void n.handleTentativeAgentResponse(e);case"client_tool_call":try{await n.handleClientToolCall(e)}catch(t){n.onError(`Unexpected error in client tool call handling: ${t instanceof Error?t.message:String(t)}`,{clientToolName:e.client_tool_call.tool_name,toolCallId:e.client_tool_call.tool_call_id})}return;case"audio":return void n.handleAudio(e);case"vad_score":return void n.handleVadScore(e);case"ping":return void n.connection.sendMessage({type:"pong",event_id:e.ping_event.event_id});case"mcp_tool_call":return void n.handleMCPToolCall(e);case"mcp_connection_status":return void n.handleMCPConnectionStatus(e);case"agent_tool_request":return void n.handleAgentToolRequest(e);case"agent_tool_response":return void n.handleAgentToolResponse(e);case"conversation_initiation_metadata":return void n.handleConversationMetadata(e);case"asr_initiation_metadata":return void n.handleAsrInitiationMetadata(e);case"agent_chat_response_part":return void n.handleAgentChatResponsePart(e);case"error":return void n.handleErrorEvent(e);default:return void(n.options.onDebug&&n.options.onDebug(e))}},this.setVolume=({volume:e})=>{this.volume=e},this.options=e,this.connection=t,this.options.onConnect&&this.options.onConnect({conversationId:t.conversationId}),this.connection.onMessage(this.onMessage),this.connection.onDisconnect(this.endSessionWithDetails),this.connection.onModeChange(e=>this.updateMode(e)),this.updateStatus("connected")}endSession(){return this.endSessionWithDetails({reason:"user"})}async handleEndSession(){this.connection.close()}updateMode(e){e!==this.mode&&(this.mode=e,this.options.onModeChange&&this.options.onModeChange({mode:e}))}updateStatus(e){e!==this.status&&(this.status=e,this.options.onStatusChange&&this.options.onStatusChange({status:e}))}updateCanSendFeedback(){const e=this.currentEventId!==this.lastFeedbackEventId;this.canSendFeedback!==e&&(this.canSendFeedback=e,this.options.onCanSendFeedbackChange&&this.options.onCanSendFeedbackChange({canSendFeedback:e}))}handleInterruption(e){e.interruption_event&&(this.lastInterruptTimestamp=e.interruption_event.event_id,this.options.onInterruption&&this.options.onInterruption({event_id:e.interruption_event.event_id}))}handleAgentResponse(e){this.options.onMessage&&this.options.onMessage({source:"ai",role:"agent",message:e.agent_response_event.agent_response,event_id:e.agent_response_event.event_id})}handleUserTranscript(e){this.options.onMessage&&this.options.onMessage({source:"user",role:"user",message:e.user_transcription_event.user_transcript,event_id:e.user_transcription_event.event_id})}handleTentativeAgentResponse(e){this.options.onDebug&&this.options.onDebug({type:"tentative_agent_response",response:e.tentative_agent_response_internal_event.tentative_agent_response})}handleVadScore(e){this.options.onVadScore&&this.options.onVadScore({vadScore:e.vad_score_event.vad_score})}async handleClientToolCall(e){if(Object.prototype.hasOwnProperty.call(this.options.clientTools,e.client_tool_call.tool_name))try{var t;const n=null!=(t=await this.options.clientTools[e.client_tool_call.tool_name](e.client_tool_call.parameters))?t:"Client tool execution successful.",o="object"==typeof n?JSON.stringify(n):String(n);this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:o,is_error:!1})}catch(t){this.onError(`Client tool execution failed with following error: ${null==t?void 0:t.message}`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool execution failed: ${null==t?void 0:t.message}`,is_error:!0})}else{if(this.options.onUnhandledClientToolCall)return void this.options.onUnhandledClientToolCall(e.client_tool_call);this.onError(`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,is_error:!0})}}handleAudio(e){}handleMCPToolCall(e){this.options.onMCPToolCall&&this.options.onMCPToolCall(e.mcp_tool_call)}handleMCPConnectionStatus(e){this.options.onMCPConnectionStatus&&this.options.onMCPConnectionStatus(e.mcp_connection_status)}handleAgentToolRequest(e){this.options.onAgentToolRequest&&this.options.onAgentToolRequest(e.agent_tool_request)}handleAgentToolResponse(e){"end_call"===e.agent_tool_response.tool_name&&this.endSessionWithDetails({reason:"agent",context:new CloseEvent("end_call",{reason:"Agent ended the call"})}),this.options.onAgentToolResponse&&this.options.onAgentToolResponse(e.agent_tool_response)}handleConversationMetadata(e){this.options.onConversationMetadata&&this.options.onConversationMetadata(e.conversation_initiation_metadata_event)}handleAsrInitiationMetadata(e){this.options.onAsrInitiationMetadata&&this.options.onAsrInitiationMetadata(e.asr_initiation_metadata_event)}handleAgentChatResponsePart(e){this.options.onAgentChatResponsePart&&this.options.onAgentChatResponsePart(e.text_response_part)}handleErrorEvent(e){const t=e.error_event.error_type,n=e.error_event.message||e.error_event.reason||"Unknown error";"max_duration_exceeded"!==t?this.onError(`Server error: ${n}`,{errorType:t,code:e.error_event.code,debugMessage:e.error_event.debug_message,details:e.error_event.details}):this.endSessionWithDetails({reason:"error",message:n,context:new Event("max_duration_exceeded")})}onError(e,t){console.error(e,t),this.options.onError&&this.options.onError(e,t)}getId(){return this.connection.conversationId}isOpen(){return"connected"===this.status}setMicMuted(e){this.connection.setMicMuted(e)}getInputByteFrequencyData(){return a}getOutputByteFrequencyData(){return a}getInputVolume(){return 0}getOutputVolume(){return 0}sendFeedback(e){this.canSendFeedback?(this.connection.sendMessage({type:"feedback",score:e?"like":"dislike",event_id:this.currentEventId}),this.lastFeedbackEventId=this.currentEventId,this.updateCanSendFeedback()):console.warn(0===this.lastFeedbackEventId?"Cannot send feedback: the conversation has not started yet.":"Cannot send feedback: feedback has already been sent for the current response.")}sendContextualUpdate(e){this.connection.sendMessage({type:"contextual_update",text:e})}sendUserMessage(e){this.connection.sendMessage({type:"user_message",text:e})}sendUserActivity(){this.connection.sendMessage({type:"user_activity"})}sendMCPToolApprovalResult(e,t){this.connection.sendMessage({type:"mcp_tool_approval_result",tool_call_id:e,is_approved:t})}}class c{constructor(e={}){this.queue=[],this.disconnectionDetails=null,this.onDisconnectCallback=null,this.onMessageCallback=null,this.onModeChangeCallback=null,this.onDebug=void 0,this.onDebug=e.onDebug}debug(e){this.onDebug&&this.onDebug(e)}onMessage(e){this.onMessageCallback=e;const t=this.queue;this.queue=[],t.length>0&&queueMicrotask(()=>{t.forEach(e)})}onDisconnect(e){this.onDisconnectCallback=e;const t=this.disconnectionDetails;t&&queueMicrotask(()=>{e(t)})}onModeChange(e){this.onModeChangeCallback=e}updateMode(e){var t;null==(t=this.onModeChangeCallback)||t.call(this,e)}disconnect(e){var t;this.disconnectionDetails||(this.disconnectionDetails=e,null==(t=this.onDisconnectCallback)||t.call(this,e))}handleMessage(e){this.onMessageCallback?this.onMessageCallback(e):this.queue.push(e)}}function l(e){const[t,n]=e.split("_");if(!["pcm","ulaw"].includes(t))throw new Error(`Invalid format: ${e}`);const o=Number.parseInt(n);if(Number.isNaN(o))throw new Error(`Invalid sample rate: ${n}`);return{format:t,sampleRate:o}}const u="0.15.1";function d(e){return!!e.type}const h="conversation_initiation_client_data";function p(e){var t;const n={type:h};var o,s,i,a,r,c,l,u;return e.overrides&&(n.conversation_config_override={agent:{prompt:null==(o=e.overrides.agent)?void 0:o.prompt,first_message:null==(s=e.overrides.agent)?void 0:s.firstMessage,language:null==(i=e.overrides.agent)?void 0:i.language},tts:{voice_id:null==(a=e.overrides.tts)?void 0:a.voiceId,speed:null==(r=e.overrides.tts)?void 0:r.speed,stability:null==(c=e.overrides.tts)?void 0:c.stability,similarity_boost:null==(l=e.overrides.tts)?void 0:l.similarityBoost},conversation:{text_only:null==(u=e.overrides.conversation)?void 0:u.textOnly}}),e.customLlmExtraBody&&(n.custom_llm_extra_body=e.customLlmExtraBody),e.dynamicVariables&&(n.dynamic_variables=e.dynamicVariables),e.userId&&(n.user_id=e.userId),null!=(t=e.overrides)&&t.client&&(n.source_info={source:e.overrides.client.source,version:e.overrides.client.version}),n}class m extends Error{constructor(e,t){super(e),this.closeCode=void 0,this.closeReason=void 0,this.name="SessionConnectionError",this.closeCode=null==t?void 0:t.closeCode,this.closeReason=null==t?void 0:t.closeReason}}class v extends c{constructor(e,t,n,o){super(),this.socket=void 0,this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.socket=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.socket.addEventListener("error",e=>{setTimeout(()=>this.disconnect({reason:"error",message:"The connection was closed due to a socket error.",context:e}),0)}),this.socket.addEventListener("close",e=>{this.disconnect(1e3===e.code?{reason:"agent",context:e,closeCode:e.code,closeReason:e.reason||void 0}:{reason:"error",message:e.reason||"The connection was closed by the server.",context:e,closeCode:e.code,closeReason:e.reason||void 0})}),this.socket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);if(!d(t))return void this.debug({type:"invalid_event",message:"Received invalid socket event",data:e.data});this.handleMessage(t)}catch(t){this.debug({type:"parsing_error",message:"Failed to parse socket message",error:t instanceof Error?t.message:String(t),data:e.data})}})}static async create(e){let t=null;try{var n,o,s;const i=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let a;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||u,c=(null==(s=e.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";a=`${e.signedUrl}${t}source=${c}&version=${r}`}else a=`${i}/v1/convai/conversation?agent_id=${e.agentId}&source=${c}&version=${r}`;const h=["convai"];e.authorization&&h.push(`bearer.${e.authorization}`),t=new WebSocket(a,h);const g=await new Promise((n,o)=>{t.addEventListener("open",()=>{var n;const o=p(e);null==(n=t)||n.send(JSON.stringify(o))},{once:!0}),t.addEventListener("error",e=>{setTimeout(()=>o(new m("The connection was closed due to a socket error.")),0)}),t.addEventListener("close",e=>{o(new m(e.reason||(1e3===e.code?"Connection closed normally before session could be established.":"Connection closed unexpectedly before session could be established."),{closeCode:e.code,closeReason:e.reason||void 0}))}),t.addEventListener("message",e=>{const t=JSON.parse(e.data);d(t)&&("conversation_initiation_metadata"===t.type?n(t.conversation_initiation_metadata_event):console.warn("First received message is not conversation metadata."))},{once:!0})}),{conversation_id:f,agent_output_audio_format:_,user_input_audio_format:w}=g,b=l(null!=w?w:"pcm_16000"),y=l(_);return new v(t,f,b,y)}catch(e){var i;throw null==(i=t)||i.close(),e}}close(){this.socket.close(1e3,"User ended conversation")}sendMessage(e){this.socket.send(JSON.stringify(e))}async setMicMuted(e){console.warn(`WebSocket connection setMicMuted called with ${e}, but this is handled by VoiceConversation`)}}function g(e){const t=new Uint8Array(e);return window.btoa(String.fromCharCode(...t))}function f(e){const t=window.atob(e),n=t.length,o=new Uint8Array(n);for(let e=0;e<n;e++)o[e]=t.charCodeAt(e);return o.buffer}const _=new Map;function w(e,t){return async(n,o)=>{const s=_.get(e);if(s)return n.addModule(s);if(o)try{return await n.addModule(o),void _.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module from path: ${o}. Error: ${t}`)}const i=new Blob([t],{type:"application/javascript"}),a=URL.createObjectURL(i);try{return await n.addModule(a),void _.set(e,a)}catch(e){URL.revokeObjectURL(a)}try{const o=`data:application/javascript;base64,${btoa(t)}`;await n.addModule(o),_.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module. Make sure the browser supports AudioWorklets. If you are using a strict CSP, you may need to self-host the worklet files.`)}}}const b=w("rawAudioProcessor",'/*\n * ulaw encoding logic taken from the wavefile library\n * https://github.com/rochars/wavefile/blob/master/lib/codecs/mulaw.js\n * USED BY @elevenlabs/client\n */\n\nconst BIAS = 0x84;\nconst CLIP = 32635;\nconst encodeTable = [\n 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,\n 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7\n];\n\nfunction encodeSample(sample) {\n let sign;\n let exponent;\n let mantissa;\n let muLawSample;\n sign = (sample >> 8) & 0x80;\n if (sign !== 0) sample = -sample;\n sample = sample + BIAS;\n if (sample > CLIP) sample = CLIP;\n exponent = encodeTable[(sample>>7) & 0xFF];\n mantissa = (sample >> (exponent+3)) & 0x0F;\n muLawSample = ~(sign | (exponent << 4) | mantissa);\n \n return muLawSample;\n}\n\nclass RawAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.isMuted = false;\n this.buffer = []; // Initialize an empty buffer\n this.bufferSize = data.sampleRate / 10;\n this.format = data.format;\n\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(1, sampleRate, data.sampleRate).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "setMuted":\n this.isMuted = data.isMuted;\n break;\n }\n };\n }\n process(inputs) {\n if (!this.buffer) {\n return true;\n }\n \n const input = inputs[0]; // Get the first input node\n if (input.length > 0) {\n let channelData = input[0]; // Get the first channel\'s data\n\n // Resample the audio if necessary\n if (this.resampler) {\n channelData = this.resampler.full(channelData);\n }\n\n // Add channel data to the buffer\n this.buffer.push(...channelData);\n // Get max volume \n let sum = 0.0;\n for (let i = 0; i < channelData.length; i++) {\n sum += channelData[i] * channelData[i];\n }\n const maxVolume = Math.sqrt(sum / channelData.length);\n // Check if buffer size has reached or exceeded the threshold\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = this.isMuted \n ? new Float32Array(this.buffer.length)\n : new Float32Array(this.buffer);\n\n let encodedArray = this.format === "ulaw"\n ? new Uint8Array(float32Array.length)\n : new Int16Array(float32Array.length);\n\n // Iterate through the Float32Array and convert each sample to PCM16\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to the range [-1, 1]\n let sample = Math.max(-1, Math.min(1, float32Array[i]));\n\n // Scale the sample to the range [-32768, 32767]\n let value = sample < 0 ? sample * 32768 : sample * 32767;\n if (this.format === "ulaw") {\n value = encodeSample(Math.round(value));\n }\n\n encodedArray[i] = value;\n }\n\n // Send the buffered data to the main script\n this.port.postMessage([encodedArray, maxVolume]);\n\n // Clear the buffer after sending\n this.buffer = [];\n }\n }\n return true; // Continue processing\n }\n}\nregisterProcessor("rawAudioProcessor", RawAudioProcessor);\n');class y extends c{constructor(e,t,n,o,s={}){super(s),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=!1,this.audioEventId=1,this.audioCaptureContext=null,this.audioElements=[],this.outputDeviceId=null,this.outputAnalyser=null,this.outputFrequencyData=null,this.room=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.setupRoomEventListeners()}static async create(n){let o;if("conversationToken"in n&&n.conversationToken)o=n.conversationToken;else{if(!("agentId"in n)||!n.agentId)throw new Error("Either conversationToken or agentId is required for WebRTC connection");try{var s,i,a;const e=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.version)||u,t=(null==(i=n.overrides)||null==(i=i.client)?void 0:i.source)||"js_sdk",c=`${r=null!=(a=n.origin)?a:"https://api.elevenlabs.io",r.replace(/^wss:\/\//,"https://")}/v1/convai/conversation/token?agent_id=${n.agentId}&source=${t}&version=${e}`,l=await fetch(c);if(!l.ok)throw new Error(`ElevenLabs API returned ${l.status} ${l.statusText}`);if(o=(await l.json()).token,!o)throw new Error("No conversation token received from API")}catch(e){let t=e instanceof Error?e.message:String(e);throw e instanceof Error&&e.message.includes("401")&&(t="Your agent has authentication enabled, but no signed URL or conversation token was provided."),new Error(`Failed to fetch conversation token for agent ${n.agentId}: ${t}`)}}var r;const c=new e;try{const e=`room_${Date.now()}`,s=l("pcm_48000"),i=l("pcm_48000"),a=new y(c,e,s,i,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var d;await c.connect(r,o),await new Promise(e=>{if(a.isConnected)e();else{const n=()=>{c.off(t.Connected,n),e()};c.on(t.Connected,n)}}),c.name&&(a.conversationId=(null==(d=c.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:d[0])||c.name),n.textOnly||await c.localParticipant.setMicrophoneEnabled(!0);const u=p(n);return a.debug({type:h,message:u}),await a.sendMessage(u),a}catch(e){throw await c.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(t.Connected,async function(){e.isConnected=!0,console.info("WebRTC room connected")}),this.room.on(t.Disconnected,e=>{this.isConnected=!1,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})})}),this.room.on(t.ConnectionStateChanged,e=>{e===n.Disconnected&&(this.isConnected=!1,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}))}),this.room.on(t.DataReceived,(e,t)=>{try{const t=JSON.parse((new TextDecoder).decode(e));if("audio"===t.type)return;d(t)?this.handleMessage(t):console.warn("Invalid socket event received:",t)}catch(t){console.warn("Failed to parse incoming data message:",t),console.warn("Raw payload:",(new TextDecoder).decode(e))}}),this.room.on(t.TrackSubscribed,async function(t,n,s){if(t.kind===o.Kind.Audio&&s.identity.includes("agent")){const n=t,o=n.attach();if(o.autoplay=!0,o.controls=!1,e.outputDeviceId&&o.setSinkId)try{await o.setSinkId(e.outputDeviceId)}catch(e){console.warn("Failed to set output device for new audio element:",e)}o.style.display="none",document.body.appendChild(o),e.audioElements.push(o),1===e.audioElements.length&&(null==e.onDebug||e.onDebug({type:"audio_element_ready"})),await e.setupAudioCapture(n)}}),this.room.on(t.ActiveSpeakersChanged,async function(t){e.updateMode(t.length>0&&t[0].identity.startsWith("agent")?"speaking":"listening")}),this.room.on(t.ParticipantDisconnected,e=>{var t;null!=(t=e.identity)&&t.startsWith("agent")&&this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:"agent disconnected"})})})}close(){if(this.isConnected){try{this.room.localParticipant.audioTrackPublications.forEach(e=>{e.track&&e.track.stop()})}catch(e){console.warn("Error stopping local tracks:",e)}this.audioCaptureContext&&(this.audioCaptureContext.close().catch(e=>{console.warn("Error closing audio capture context:",e)}),this.audioCaptureContext=null),this.audioElements.forEach(e=>{e.parentNode&&e.parentNode.removeChild(e)}),this.audioElements=[],this.room.disconnect()}}async sendMessage(e){if(this.isConnected&&this.room.localParticipant){if(!("user_audio_chunk"in e))try{const t=(new TextEncoder).encode(JSON.stringify(e));await this.room.localParticipant.publishData(t,{reliable:!0})}catch(t){this.debug({type:"send_message_error",message:{message:e,error:t}}),console.error("Failed to send message via WebRTC:",t)}}else console.warn("Cannot send message: room not connected or no local participant")}getRoom(){return this.room}async setMicMuted(e){if(!this.isConnected||!this.room.localParticipant)return void console.warn("Cannot set microphone muted: room not connected or no local participant");const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);if(null!=t&&t.track)try{e?await t.track.mute():await t.track.unmute()}catch(t){await this.room.localParticipant.setMicrophoneEnabled(!e)}else await this.room.localParticipant.setMicrophoneEnabled(!e)}async setupAudioCapture(e){try{const t=new AudioContext;this.audioCaptureContext=t,this.outputAnalyser=t.createAnalyser(),this.outputAnalyser.fftSize=2048,this.outputAnalyser.smoothingTimeConstant=.8;const n=new MediaStream([e.mediaStreamTrack]),o=t.createMediaStreamSource(n);o.connect(this.outputAnalyser),await b(t.audioWorklet);const s=new AudioWorkletNode(t,"rawAudioProcessor");this.outputAnalyser.connect(s),s.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),s.port.onmessage=e=>{const[t,n]=e.data;if(n>.01){const e=g(t.buffer),n=this.audioEventId++;this.handleMessage({type:"audio",audio_event:{audio_base_64:e,event_id:n}})}},o.connect(s)}catch(e){console.warn("Failed to set up audio capture:",e)}}setAudioVolume(e){this.audioElements.forEach(t=>{t.volume=e})}async setAudioOutputDevice(e){if(!("setSinkId"in HTMLAudioElement.prototype))throw new Error("setSinkId is not supported in this browser");const t=this.audioElements.map(async function(t){try{await t.setSinkId(e)}catch(e){throw console.error("Failed to set sink ID for audio element:",e),e}});await Promise.all(t),this.outputDeviceId=e}async setAudioInputDevice(e){if(!this.isConnected||!this.room.localParticipant)throw new Error("Cannot change input device: room not connected or no local participant");try{const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);null!=t&&t.track&&(await t.track.stop(),await this.room.localParticipant.unpublishTrack(t.track));const n={deviceId:{exact:e},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:{ideal:1}},i=await s(n);await this.room.localParticipant.publishTrack(i,{name:"microphone",source:o.Source.Microphone})}catch(e){console.error("Failed to change input device:",e);try{await this.room.localParticipant.setMicrophoneEnabled(!0)}catch(e){console.error("Failed to recover microphone after device switch error:",e)}throw e}}getOutputByteFrequencyData(){return this.outputAnalyser?(null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.outputAnalyser.frequencyBinCount)),this.outputAnalyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData):null}}async function S(e){const t=function(e){return e.connectionType?e.connectionType:"conversationToken"in e&&e.conversationToken?"webrtc":"websocket"}(e);switch(t){case"websocket":return v.create(e);case"webrtc":return y.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function E(){return["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function C(e={default:0,android:3e3}){let t=e.default;var n;if(/android/i.test(navigator.userAgent))t=null!=(n=e.android)?n:t;else if(E()){var o;t=null!=(o=e.ios)?o:t}t>0&&await new Promise(e=>setTimeout(e,t))}class k extends r{static async startSession(e){const t=r.getFullOptions(e);t.onStatusChange&&t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1}),t.onModeChange&&t.onModeChange({mode:"listening"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1});let n=null;try{return await C(t.connectionDelay),n=await S(e),new k(t,n)}catch(e){var o;throw t.onStatusChange&&t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}async function R(e,t){const n=t||"https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js";await e.audioWorklet.addModule(n)}const I={echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:{ideal:1}};class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:s,libsampleratePath:a,onError:r}){let c=null,l=null;try{const u=i({sampleRate:{ideal:e}},I);if(E()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(u.deviceId={ideal:e.deviceId})}o&&(u.deviceId=M.getDeviceIdConstraint(o));const d=navigator.mediaDevices.getSupportedConstraints().sampleRate;c=new window.AudioContext(d?{sampleRate:e}:{});const h=c.createAnalyser();d||await R(c,a),await b(c.audioWorklet,null==s?void 0:s.rawAudioProcessor);const p=i({voiceIsolation:!0},u);l=await navigator.mediaDevices.getUserMedia({audio:p});const m=c.createMediaStreamSource(l),v=new AudioWorkletNode(c,"rawAudioProcessor");v.port.postMessage({type:"setFormat",format:t,sampleRate:e}),m.connect(h),h.connect(v),await c.resume();const g=await navigator.permissions.query({name:"microphone"});return new M(c,h,v,l,m,g,r)}catch(e){var u,d;throw null==(u=l)||u.getTracks().forEach(e=>{e.stop()}),null==(d=c)||d.close(),e}}static getDeviceIdConstraint(e){if(e)return E()?{ideal:e}:{exact:e}}constructor(e,t,n,o,s,i,a=console.error){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.mediaStreamSource=void 0,this.permissions=void 0,this.onError=void 0,this.settingInput=!1,this.handlePermissionsChange=()=>{if("denied"===this.permissions.state)this.onError("Microphone permission denied");else if(!this.settingInput){var e;const[t]=this.inputStream.getAudioTracks(),{deviceId:n}=null!=(e=null==t?void 0:t.getSettings())?e:{};this.setInputDevice(n).catch(e=>{this.onError("Failed to reset input device after permission change:",e)})}},this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o,this.mediaStreamSource=s,this.permissions=i,this.onError=a,this.permissions.addEventListener("change",this.handlePermissionsChange)}forgetInputStreamAndSource(){for(const e of this.inputStream.getTracks())e.stop();this.mediaStreamSource.disconnect()}async close(){this.forgetInputStreamAndSource(),this.permissions.removeEventListener("change",this.handlePermissionsChange),await this.context.close()}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e})}async setInputDevice(e){try{if(this.settingInput)throw new Error("Input device is already being set");this.settingInput=!0;const t=i({},I);e&&(t.deviceId=M.getDeviceIdConstraint(e));const n=i({voiceIsolation:!0},t),o=await navigator.mediaDevices.getUserMedia({audio:n});this.forgetInputStreamAndSource(),this.inputStream=o,this.mediaStreamSource=this.context.createMediaStreamSource(o),this.mediaStreamSource.connect(this.analyser)}catch(e){throw this.onError("Failed to switch input device:",e),e}finally{this.settingInput=!1}}}const A=w("audioConcatProcessor",'/*\n * ulaw decoding logic taken from the wavefile library\n * https://github.com/rochars/wavefile/blob/master/lib/codecs/mulaw.js\n * USED BY @elevenlabs/client\n */\n\nconst decodeTable = [0, 132, 396, 924, 1980, 4092, 8316, 16764];\n\nfunction decodeSample(muLawSample) {\n let sign;\n let exponent;\n let mantissa;\n let sample;\n muLawSample = ~muLawSample;\n sign = muLawSample & 0x80;\n exponent = (muLawSample >> 4) & 0x07;\n mantissa = muLawSample & 0x0f;\n sample = decodeTable[exponent] + (mantissa << (exponent + 3));\n if (sign !== 0) sample = -sample;\n\n return sample;\n}\n\nclass AudioConcatProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffers = []; // Initialize an empty buffer\n this.cursor = 0;\n this.currentBuffer = null;\n this.wasInterrupted = false;\n this.finished = false;\n\n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.format = data.format;\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(\n 1,\n data.sampleRate,\n sampleRate\n ).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "buffer":\n this.wasInterrupted = false;\n this.buffers.push(\n this.format === "ulaw"\n ? new Uint8Array(data.buffer)\n : new Int16Array(data.buffer)\n );\n break;\n case "interrupt":\n this.wasInterrupted = true;\n break;\n case "clearInterrupted":\n if (this.wasInterrupted) {\n this.wasInterrupted = false;\n this.buffers = [];\n this.currentBuffer = null;\n }\n }\n };\n }\n process(_, outputs) {\n let finished = false;\n const output = outputs[0][0];\n for (let i = 0; i < output.length; i++) {\n if (!this.currentBuffer) {\n if (this.buffers.length === 0) {\n finished = true;\n break;\n }\n this.currentBuffer = this.buffers.shift();\n if (this.resampler) {\n this.currentBuffer = this.resampler.full(this.currentBuffer);\n }\n this.cursor = 0;\n }\n\n let value = this.currentBuffer[this.cursor];\n if (this.format === "ulaw") {\n value = decodeSample(value);\n }\n output[i] = value / 32768;\n this.cursor++;\n\n if (this.cursor >= this.currentBuffer.length) {\n this.currentBuffer = null;\n }\n }\n\n if (this.finished !== finished) {\n this.finished = finished;\n this.port.postMessage({ type: "process", finished });\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("audioConcatProcessor", AudioConcatProcessor);\n');class D{static async create({sampleRate:e,format:t,outputDeviceId:n,workletPaths:o,libsampleratePath:s}){let i=null,a=null;try{const r=navigator.mediaDevices.getSupportedConstraints().sampleRate;i=new AudioContext(r?{sampleRate:e}:{});const c=i.createAnalyser(),l=i.createGain();a=new Audio,a.src="",a.load(),a.autoplay=!0,a.style.display="none",document.body.appendChild(a);const u=i.createMediaStreamDestination();a.srcObject=u.stream,l.connect(c),c.connect(u),r&&i.sampleRate===e||(i.sampleRate!==e&&console.warn(`[ConversationalAI] Sample rate ${e} not available, resampling to ${i.sampleRate}`),await R(i,s)),await A(i.audioWorklet,null==o?void 0:o.audioConcatProcessor);const d=new AudioWorkletNode(i,"audioConcatProcessor");return d.port.postMessage({type:"setFormat",format:t,sampleRate:e}),d.connect(l),await i.resume(),n&&a.setSinkId&&await a.setSinkId(n),new D(i,c,l,d,a)}catch(e){var r,c;throw null!=(r=a)&&r.parentNode&&a.parentNode.removeChild(a),null==(c=a)||c.pause(),i&&"closed"!==i.state&&await i.close(),e}}constructor(e,t,n,o,s){this.context=void 0,this.analyser=void 0,this.gain=void 0,this.worklet=void 0,this.audioElement=void 0,this.context=e,this.analyser=t,this.gain=n,this.worklet=o,this.audioElement=s}async setOutputDevice(e){if(!("setSinkId"in HTMLAudioElement.prototype))throw new Error("setSinkId is not supported in this browser");await this.audioElement.setSinkId(e||"")}async close(){this.audioElement.parentNode&&this.audioElement.parentNode.removeChild(this.audioElement),this.audioElement.pause(),await this.context.close()}}class T extends r{static async requestWakeLock(){if("wakeLock"in navigator)try{return await navigator.wakeLock.request("screen")}catch(e){}return null}static async startSession(e){var t;const n=r.getFullOptions(e);n.onStatusChange&&n.onStatusChange({status:"connecting"}),n.onCanSendFeedbackChange&&n.onCanSendFeedbackChange({canSendFeedback:!1});let o=null,s=null,a=null,c=null,l=null;(null==(t=e.useWakeLock)||t)&&(l=await T.requestWakeLock());try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:!0}),await C(n.connectionDelay),s=await S(e),[o,a]=await Promise.all([M.create(i({},s.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices,inputDeviceId:e.inputDeviceId,workletPaths:e.workletPaths,libsampleratePath:e.libsampleratePath})),D.create(i({},s.outputFormat,{outputDeviceId:e.outputDeviceId,workletPaths:e.workletPaths}))]),null==(u=c)||u.getTracks().forEach(e=>{e.stop()}),c=null,new T(n,s,o,a,l)}catch(e){var d,h,p,m;n.onStatusChange&&n.onStatusChange({status:"disconnected"}),null==(d=c)||d.getTracks().forEach(e=>{e.stop()}),null==(h=s)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=a)?void 0:m.close());try{var v;await(null==(v=l)?void 0:v.release()),l=null}catch(e){}throw e}}constructor(e,t,n,o,s){super(e,t),this.input=void 0,this.output=void 0,this.wakeLock=void 0,this.inputFrequencyData=void 0,this.outputFrequencyData=void 0,this.visibilityChangeHandler=null,this.onInputWorkletMessage=e=>{"connected"===this.status&&this.connection.sendMessage({user_audio_chunk:g(e.data[0].buffer)})},this.onOutputWorkletMessage=({data:e})=>{"process"===e.type&&this.updateMode(e.finished?"listening":"speaking")},this.addAudioBase64Chunk=e=>{this.output.gain.gain.cancelScheduledValues(this.output.context.currentTime),this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"}),this.output.worklet.port.postMessage({type:"buffer",buffer:f(e)})},this.fadeOutAudio=()=>{this.updateMode("listening"),this.output.worklet.port.postMessage({type:"interrupt"}),this.output.gain.gain.exponentialRampToValueAtTime(1e-4,this.output.context.currentTime+2),setTimeout(()=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"})},2e3)},this.calculateVolume=e=>{if(0===e.length)return 0;let t=0;for(let n=0;n<e.length;n++)t+=e[n]/255;return t/=e.length,t<0?0:t>1?1:t},this.setVolume=({volume:e})=>{const t=Number.isFinite(e)?Math.min(1,Math.max(0,e)):1;this.volume=t,this.connection instanceof y?this.connection.setAudioVolume(t):this.output.gain.gain.value=t},this.input=n,this.output=o,this.wakeLock=s,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage,s&&(this.visibilityChangeHandler=()=>{var e;"visible"===document.visibilityState&&null!=(e=this.wakeLock)&&e.released&&T.requestWakeLock().then(e=>{this.wakeLock=e})},document.addEventListener("visibilitychange",this.visibilityChangeHandler))}async handleEndSession(){await super.handleEndSession(),this.visibilityChangeHandler&&document.removeEventListener("visibilitychange",this.visibilityChangeHandler);try{var e;await(null==(e=this.wakeLock)?void 0:e.release()),this.wakeLock=null}catch(e){}await this.input.close(),await this.output.close()}handleInterruption(e){super.handleInterruption(e),this.fadeOutAudio()}handleAudio(e){var t,n;super.handleAudio(e),e.audio_event.alignment&&this.options.onAudioAlignment&&this.options.onAudioAlignment(e.audio_event.alignment),this.lastInterruptTimestamp<=e.audio_event.event_id&&(e.audio_event.audio_base_64&&(null==(t=(n=this.options).onAudio)||t.call(n,e.audio_event.audio_base_64),this.connection instanceof y||this.addAudioBase64Chunk(e.audio_event.audio_base_64)),this.currentEventId=e.audio_event.event_id,this.updateCanSendFeedback(),this.updateMode("speaking"))}setMicMuted(e){this.connection instanceof y?this.connection.setMicMuted(e):this.input.setMuted(e)}getInputByteFrequencyData(){return null!=this.inputFrequencyData||(this.inputFrequencyData=new Uint8Array(this.input.analyser.frequencyBinCount)),this.input.analyser.getByteFrequencyData(this.inputFrequencyData),this.inputFrequencyData}getOutputByteFrequencyData(){return this.connection instanceof y?this.connection.getOutputByteFrequencyData()||new Uint8Array(1024):(null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.output.analyser.frequencyBinCount)),this.output.analyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData)}getInputVolume(){return this.calculateVolume(this.getInputByteFrequencyData())}getOutputVolume(){return this.calculateVolume(this.getOutputByteFrequencyData())}async changeInputDevice({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o}){try{if(this.connection instanceof v)try{return await this.input.setInputDevice(o),this.input}catch(e){console.warn("Failed to change device on existing input, recreating:",e)}this.connection instanceof y&&await this.connection.setAudioInputDevice(o||""),await this.input.close();const s=await M.create({sampleRate:null!=e?e:this.connection.inputFormat.sampleRate,format:null!=t?t:this.connection.inputFormat.format,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:this.options.workletPaths,libsampleratePath:this.options.libsampleratePath,onError:this.options.onError});return this.input=s,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.input}catch(e){throw console.error("Error changing input device",e),e}}async changeOutputDevice({sampleRate:e,format:t,outputDeviceId:n}){try{if(this.connection instanceof v)try{return await this.output.setOutputDevice(n),this.output}catch(e){console.warn("Failed to change device on existing output, recreating:",e)}this.connection instanceof y&&await this.connection.setAudioOutputDevice(n||""),await this.output.close();const o=await D.create({sampleRate:null!=e?e:this.connection.outputFormat.sampleRate,format:null!=t?t:this.connection.outputFormat.format,outputDeviceId:n,workletPaths:this.options.workletPaths});return this.output=o,this.output}catch(e){throw console.error("Error changing output device",e),e}}}function F(e,t,n="https://api.elevenlabs.io"){const o={};return"boolean"==typeof t?o.feedback=t?"like":"dislike":(o.rating=t.rating,o.comment=t.comment),fetch(`${n}/v1/convai/conversations/${e}/feedback`,{method:"POST",body:JSON.stringify(o),headers:{"Content-Type":"application/json"}})}class P{constructor(){this.listeners=new Map}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set);const n=this.listeners.get(e);n&&n.add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,...t){const n=this.listeners.get(e);n&&n.forEach(e=>{e(...t)})}}var x;!function(e){e.SESSION_STARTED="session_started",e.PARTIAL_TRANSCRIPT="partial_transcript",e.COMMITTED_TRANSCRIPT="committed_transcript",e.COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS="committed_transcript_with_timestamps",e.AUTH_ERROR="auth_error",e.ERROR="error",e.OPEN="open",e.CLOSE="close",e.QUOTA_EXCEEDED="quota_exceeded",e.COMMIT_THROTTLED="commit_throttled",e.TRANSCRIBER_ERROR="transcriber_error",e.UNACCEPTED_TERMS="unaccepted_terms",e.RATE_LIMITED="rate_limited",e.INPUT_ERROR="input_error",e.QUEUE_OVERFLOW="queue_overflow",e.RESOURCE_EXHAUSTED="resource_exhausted",e.SESSION_TIME_LIMIT_EXCEEDED="session_time_limit_exceeded",e.CHUNK_SIZE_EXCEEDED="chunk_size_exceeded",e.INSUFFICIENT_AUDIO_ACTIVITY="insufficient_audio_activity"}(x||(x={}));class O{constructor(e){this.websocket=null,this.eventEmitter=new P,this.currentSampleRate=16e3,this._audioCleanup=void 0,this.currentSampleRate=e}setWebSocket(e){this.websocket=e,this.websocket.readyState===WebSocket.OPEN?this.eventEmitter.emit(x.OPEN):this.websocket.addEventListener("open",()=>{this.eventEmitter.emit(x.OPEN)}),this.websocket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);switch(t.message_type){case"session_started":this.eventEmitter.emit(x.SESSION_STARTED,t);break;case"partial_transcript":this.eventEmitter.emit(x.PARTIAL_TRANSCRIPT,t);break;case"committed_transcript":this.eventEmitter.emit(x.COMMITTED_TRANSCRIPT,t);break;case"committed_transcript_with_timestamps":this.eventEmitter.emit(x.COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS,t);break;case"auth_error":this.eventEmitter.emit(x.AUTH_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"quota_exceeded":this.eventEmitter.emit(x.QUOTA_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"commit_throttled":this.eventEmitter.emit(x.COMMIT_THROTTLED,t),this.eventEmitter.emit(x.ERROR,t);break;case"transcriber_error":this.eventEmitter.emit(x.TRANSCRIBER_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"unaccepted_terms":this.eventEmitter.emit(x.UNACCEPTED_TERMS,t),this.eventEmitter.emit(x.ERROR,t);break;case"rate_limited":this.eventEmitter.emit(x.RATE_LIMITED,t),this.eventEmitter.emit(x.ERROR,t);break;case"input_error":this.eventEmitter.emit(x.INPUT_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"queue_overflow":this.eventEmitter.emit(x.QUEUE_OVERFLOW,t),this.eventEmitter.emit(x.ERROR,t);break;case"resource_exhausted":this.eventEmitter.emit(x.RESOURCE_EXHAUSTED,t),this.eventEmitter.emit(x.ERROR,t);break;case"session_time_limit_exceeded":this.eventEmitter.emit(x.SESSION_TIME_LIMIT_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"chunk_size_exceeded":this.eventEmitter.emit(x.CHUNK_SIZE_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"insufficient_audio_activity":this.eventEmitter.emit(x.INSUFFICIENT_AUDIO_ACTIVITY,t),this.eventEmitter.emit(x.ERROR,t);break;case"error":this.eventEmitter.emit(x.ERROR,t);break;default:console.warn("Unknown message type:",t)}}catch(t){console.error("Failed to parse WebSocket message:",t,e.data),this.eventEmitter.emit(x.ERROR,new Error(`Failed to parse message: ${t}`))}}),this.websocket.addEventListener("error",e=>{console.error("WebSocket error:",e),this.eventEmitter.emit(x.ERROR,e)}),this.websocket.addEventListener("close",e=>{if(console.log(`WebSocket closed: code=${e.code}, reason="${e.reason}", wasClean=${e.wasClean}`),!e.wasClean||1e3!==e.code&&1005!==e.code){const t=`WebSocket closed unexpectedly: ${e.code} - ${e.reason||"No reason provided"}`;console.error(t),this.eventEmitter.emit(x.ERROR,new Error(t))}this.eventEmitter.emit(x.CLOSE,e)})}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}send(e){var t,n;if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");const o={message_type:"input_audio_chunk",audio_base_64:e.audioBase64,commit:null!=(t=e.commit)&&t,sample_rate:null!=(n=e.sampleRate)?n:this.currentSampleRate,previous_text:e.previousText};this.websocket.send(JSON.stringify(o))}commit(){if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.websocket.send(JSON.stringify({message_type:"input_audio_chunk",audio_base_64:"",commit:!0,sample_rate:this.currentSampleRate}))}close(){this._audioCleanup&&this._audioCleanup(),this.websocket&&this.websocket.close(1e3,"User ended session")}}const L=w("scribeAudioProcessor",'/*\n * Scribe Audio Processor for converting microphone audio to PCM16 format\n * Supports resampling for browsers like Firefox that don\'t support\n * AudioContext sample rate constraints.\n * USED BY @elevenlabs/client\n */\n\nclass ScribeAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffer = [];\n this.bufferSize = 4096; // Buffer size for optimal chunk transmission\n\n // Resampling state\n this.inputSampleRate = null;\n this.outputSampleRate = null;\n this.resampleRatio = 1;\n this.lastSample = 0;\n this.resampleAccumulator = 0;\n\n this.port.onmessage = ({ data }) => {\n if (data.type === "configure") {\n this.inputSampleRate = data.inputSampleRate;\n this.outputSampleRate = data.outputSampleRate;\n if (this.inputSampleRate && this.outputSampleRate) {\n this.resampleRatio = this.inputSampleRate / this.outputSampleRate;\n }\n }\n };\n }\n\n // Linear interpolation resampling\n resample(inputData) {\n if (this.resampleRatio === 1 || !this.inputSampleRate) {\n return inputData;\n }\n\n const outputSamples = [];\n\n for (let i = 0; i < inputData.length; i++) {\n const currentSample = inputData[i];\n\n // Generate output samples using linear interpolation\n while (this.resampleAccumulator < 1) {\n const interpolated =\n this.lastSample +\n (currentSample - this.lastSample) * this.resampleAccumulator;\n outputSamples.push(interpolated);\n this.resampleAccumulator += this.resampleRatio;\n }\n\n this.resampleAccumulator -= 1;\n this.lastSample = currentSample;\n }\n\n return new Float32Array(outputSamples);\n }\n\n process(inputs) {\n const input = inputs[0];\n if (input.length > 0) {\n let channelData = input[0]; // Get first channel (mono)\n\n // Resample if needed (for Firefox and other browsers that don\'t\n // support AudioContext sample rate constraints)\n if (this.resampleRatio !== 1) {\n channelData = this.resample(channelData);\n }\n\n // Add incoming audio to buffer\n for (let i = 0; i < channelData.length; i++) {\n this.buffer.push(channelData[i]);\n }\n\n // When buffer reaches threshold, convert and send\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = new Float32Array(this.buffer);\n const int16Array = new Int16Array(float32Array.length);\n\n // Convert Float32 [-1, 1] to Int16 [-32768, 32767]\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to prevent overflow\n const sample = Math.max(-1, Math.min(1, float32Array[i]));\n // Scale to PCM16 range\n int16Array[i] = sample < 0 ? sample * 32768 : sample * 32767;\n }\n\n // Send to main thread as transferable ArrayBuffer\n this.port.postMessage(\n {\n audioData: int16Array.buffer\n },\n [int16Array.buffer]\n );\n\n // Clear buffer\n this.buffer = [];\n }\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("scribeAudioProcessor", ScribeAudioProcessor);\n\n');var U,N;!function(e){e.PCM_8000="pcm_8000",e.PCM_16000="pcm_16000",e.PCM_22050="pcm_22050",e.PCM_24000="pcm_24000",e.PCM_44100="pcm_44100",e.PCM_48000="pcm_48000",e.ULAW_8000="ulaw_8000"}(U||(U={})),function(e){e.MANUAL="manual",e.VAD="vad"}(N||(N={}));class W{static getWebSocketUri(e=W.DEFAULT_BASE_URI){return`${e}/v1/speech-to-text/realtime`}static buildWebSocketUri(e){const t=W.getWebSocketUri(e.baseUri),n=new URLSearchParams;if(n.append("model_id",e.modelId),n.append("token",e.token),void 0!==e.commitStrategy&&n.append("commit_strategy",e.commitStrategy),void 0!==e.audioFormat&&n.append("audio_format",e.audioFormat),void 0!==e.vadSilenceThresholdSecs){if(e.vadSilenceThresholdSecs<=.3||e.vadSilenceThresholdSecs>3)throw new Error("vadSilenceThresholdSecs must be between 0.3 and 3.0");n.append("vad_silence_threshold_secs",e.vadSilenceThresholdSecs.toString())}if(void 0!==e.vadThreshold){if(e.vadThreshold<.1||e.vadThreshold>.9)throw new Error("vadThreshold must be between 0.1 and 0.9");n.append("vad_threshold",e.vadThreshold.toString())}if(void 0!==e.minSpeechDurationMs){if(e.minSpeechDurationMs<=50||e.minSpeechDurationMs>2e3)throw new Error("minSpeechDurationMs must be between 50 and 2000");n.append("min_speech_duration_ms",e.minSpeechDurationMs.toString())}if(void 0!==e.minSilenceDurationMs){if(e.minSilenceDurationMs<=50||e.minSilenceDurationMs>2e3)throw new Error("minSilenceDurationMs must be between 50 and 2000");n.append("min_silence_duration_ms",e.minSilenceDurationMs.toString())}void 0!==e.languageCode&&n.append("language_code",e.languageCode),void 0!==e.includeTimestamps&&n.append("include_timestamps",e.includeTimestamps?"true":"false");const o=n.toString();return o?`${t}?${o}`:t}static connect(e){if(!e.modelId)throw new Error("modelId is required");const t=new O("microphone"in e&&e.microphone?16e3:e.sampleRate),n=W.buildWebSocketUri(e),o=new WebSocket(n);return"microphone"in e&&e.microphone&&o.addEventListener("open",()=>{W.streamFromMicrophone(e,t)}),t.setWebSocket(o),t}static async streamFromMicrophone(e,t){const n=16e3;try{var o,s,i,a,r,c,l,u,d,h;const p=await navigator.mediaDevices.getUserMedia({audio:{deviceId:null==(o=e.microphone)?void 0:o.deviceId,echoCancellation:null==(s=null==(i=e.microphone)?void 0:i.echoCancellation)||s,noiseSuppression:null==(a=null==(r=e.microphone)?void 0:r.noiseSuppression)||a,autoGainControl:null==(c=null==(l=e.microphone)?void 0:l.autoGainControl)||c,channelCount:null!=(u=null==(d=e.microphone)?void 0:d.channelCount)?u:1,sampleRate:{ideal:n}}}),m=null==(h=p.getAudioTracks()[0])?void 0:h.getSettings(),v=null==m?void 0:m.sampleRate,g=new AudioContext(v?{sampleRate:v}:{});await L(g.audioWorklet);const f=g.createMediaStreamSource(p),_=new AudioWorkletNode(g,"scribeAudioProcessor");g.sampleRate!==n&&_.port.postMessage({type:"configure",inputSampleRate:g.sampleRate,outputSampleRate:n}),_.port.onmessage=e=>{const{audioData:n}=e.data,o=new Uint8Array(n);let s="";for(let e=0;e<o.length;e++)s+=String.fromCharCode(o[e]);const i=btoa(s);t.send({audioBase64:i})},f.connect(_),"suspended"===g.state&&await g.resume(),t._audioCleanup=()=>{p.getTracks().forEach(e=>{e.stop()}),f.disconnect(),_.disconnect(),g.close()}}catch(e){throw console.error("Failed to start microphone streaming:",e),e}}}W.DEFAULT_BASE_URI="wss://api.elevenlabs.io";class q extends r{static startSession(e){const t=q.getFullOptions(e);return t.textOnly?k.startSession(t):T.startSession(t)}}export{U as AudioFormat,N as CommitStrategy,q as Conversation,M as Input,D as Output,O as RealtimeConnection,x as RealtimeEvents,W as Scribe,m as SessionConnectionError,k as TextConversation,T as VoiceConversation,y as WebRTCConnection,v as WebSocketConnection,S as createConnection,F as postOverallFeedback};
1
+ import{Room as e,RoomEvent as t,ConnectionState as n,Track as o,createLocalAudioTrack as i}from"livekit-client";function s(){return s=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)({}).hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e},s.apply(null,arguments)}const a=new Uint8Array(0);class r{static getFullOptions(e){var t;const n=function(e){var t,n;const{textOnly:o}=null!=(t=null==(n=e.overrides)?void 0:n.conversation)?t:{},{textOnly:i}=e;return"boolean"==typeof i?("boolean"==typeof o&&i!==o&&console.warn(`Conflicting textOnly options provided: ${i} via options.textOnly (will be used) and ${o} via options.overrides.conversation.textOnly (will be ignored)`),i):"boolean"==typeof o?o:void 0}(e);return s({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{},onInterruption:()=>{}},e,{textOnly:n,overrides:s({},e.overrides,{conversation:s({},null==(t=e.overrides)?void 0:t.conversation,{textOnly:n})})})}constructor(e,t){var n=this;this.options=void 0,this.connection=void 0,this.lastInterruptTimestamp=0,this.mode="listening",this.status="connecting",this.volume=1,this.currentEventId=1,this.lastFeedbackEventId=0,this.canSendFeedback=!1,this.endSessionWithDetails=async function(e){"connected"!==n.status&&"connecting"!==n.status||(n.updateStatus("disconnecting"),await n.handleEndSession(),n.updateStatus("disconnected"),n.options.onDisconnect&&n.options.onDisconnect(e))},this.onMessage=async function(e){switch(e.type){case"interruption":return void n.handleInterruption(e);case"agent_response":return void n.handleAgentResponse(e);case"user_transcript":return void n.handleUserTranscript(e);case"internal_tentative_agent_response":return void n.handleTentativeAgentResponse(e);case"client_tool_call":try{await n.handleClientToolCall(e)}catch(t){n.onError(`Unexpected error in client tool call handling: ${t instanceof Error?t.message:String(t)}`,{clientToolName:e.client_tool_call.tool_name,toolCallId:e.client_tool_call.tool_call_id})}return;case"audio":return void n.handleAudio(e);case"vad_score":return void n.handleVadScore(e);case"ping":return void n.connection.sendMessage({type:"pong",event_id:e.ping_event.event_id});case"mcp_tool_call":return void n.handleMCPToolCall(e);case"mcp_connection_status":return void n.handleMCPConnectionStatus(e);case"agent_tool_request":return void n.handleAgentToolRequest(e);case"agent_tool_response":return void n.handleAgentToolResponse(e);case"conversation_initiation_metadata":return void n.handleConversationMetadata(e);case"asr_initiation_metadata":return void n.handleAsrInitiationMetadata(e);case"agent_chat_response_part":return void n.handleAgentChatResponsePart(e);case"guardrail_triggered":return void n.handleGuardrailTriggered(e);case"error":return void n.handleErrorEvent(e);default:return void(n.options.onDebug&&n.options.onDebug(e))}},this.setVolume=({volume:e})=>{this.volume=e},this.options=e,this.connection=t,this.options.onConnect&&this.options.onConnect({conversationId:t.conversationId}),this.connection.onMessage(this.onMessage),this.connection.onDisconnect(this.endSessionWithDetails),this.connection.onModeChange(e=>this.updateMode(e)),this.updateStatus("connected")}endSession(){return this.endSessionWithDetails({reason:"user"})}async handleEndSession(){this.connection.close()}updateMode(e){e!==this.mode&&(this.mode=e,this.options.onModeChange&&this.options.onModeChange({mode:e}))}updateStatus(e){e!==this.status&&(this.status=e,this.options.onStatusChange&&this.options.onStatusChange({status:e}))}updateCanSendFeedback(){const e=this.currentEventId!==this.lastFeedbackEventId;this.canSendFeedback!==e&&(this.canSendFeedback=e,this.options.onCanSendFeedbackChange&&this.options.onCanSendFeedbackChange({canSendFeedback:e}))}handleInterruption(e){e.interruption_event&&(this.lastInterruptTimestamp=e.interruption_event.event_id,this.options.onInterruption&&this.options.onInterruption({event_id:e.interruption_event.event_id}))}handleAgentResponse(e){this.options.onMessage&&this.options.onMessage({source:"ai",role:"agent",message:e.agent_response_event.agent_response,event_id:e.agent_response_event.event_id})}handleUserTranscript(e){this.options.onMessage&&this.options.onMessage({source:"user",role:"user",message:e.user_transcription_event.user_transcript,event_id:e.user_transcription_event.event_id})}handleTentativeAgentResponse(e){this.options.onDebug&&this.options.onDebug({type:"tentative_agent_response",response:e.tentative_agent_response_internal_event.tentative_agent_response})}handleVadScore(e){this.options.onVadScore&&this.options.onVadScore({vadScore:e.vad_score_event.vad_score})}async handleClientToolCall(e){if(Object.prototype.hasOwnProperty.call(this.options.clientTools,e.client_tool_call.tool_name))try{var t;const n=null!=(t=await this.options.clientTools[e.client_tool_call.tool_name](e.client_tool_call.parameters))?t:"Client tool execution successful.",o="object"==typeof n?JSON.stringify(n):String(n);this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:o,is_error:!1})}catch(t){this.onError(`Client tool execution failed with following error: ${null==t?void 0:t.message}`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool execution failed: ${null==t?void 0:t.message}`,is_error:!0})}else{if(this.options.onUnhandledClientToolCall)return void this.options.onUnhandledClientToolCall(e.client_tool_call);this.onError(`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,{clientToolName:e.client_tool_call.tool_name}),this.connection.sendMessage({type:"client_tool_result",tool_call_id:e.client_tool_call.tool_call_id,result:`Client tool with name ${e.client_tool_call.tool_name} is not defined on client`,is_error:!0})}}handleAudio(e){}handleMCPToolCall(e){this.options.onMCPToolCall&&this.options.onMCPToolCall(e.mcp_tool_call)}handleMCPConnectionStatus(e){this.options.onMCPConnectionStatus&&this.options.onMCPConnectionStatus(e.mcp_connection_status)}handleAgentToolRequest(e){this.options.onAgentToolRequest&&this.options.onAgentToolRequest(e.agent_tool_request)}handleAgentToolResponse(e){"end_call"===e.agent_tool_response.tool_name&&this.endSessionWithDetails({reason:"agent",context:new CloseEvent("end_call",{reason:"Agent ended the call"})}),this.options.onAgentToolResponse&&this.options.onAgentToolResponse(e.agent_tool_response)}handleConversationMetadata(e){this.options.onConversationMetadata&&this.options.onConversationMetadata(e.conversation_initiation_metadata_event)}handleAsrInitiationMetadata(e){this.options.onAsrInitiationMetadata&&this.options.onAsrInitiationMetadata(e.asr_initiation_metadata_event)}handleAgentChatResponsePart(e){this.options.onAgentChatResponsePart&&this.options.onAgentChatResponsePart(e.text_response_part)}handleGuardrailTriggered(e){this.options.onGuardrailTriggered&&this.options.onGuardrailTriggered()}handleErrorEvent(e){const t=e.error_event.error_type,n=e.error_event.message||e.error_event.reason||"Unknown error";"max_duration_exceeded"!==t?this.onError(`Server error: ${n}`,{errorType:t,code:e.error_event.code,debugMessage:e.error_event.debug_message,details:e.error_event.details}):this.endSessionWithDetails({reason:"error",message:n,context:new Event("max_duration_exceeded")})}onError(e,t){console.error(e,t),this.options.onError&&this.options.onError(e,t)}getId(){return this.connection.conversationId}isOpen(){return"connected"===this.status}setMicMuted(e){this.connection.setMicMuted(e)}getInputByteFrequencyData(){return a}getOutputByteFrequencyData(){return a}getInputVolume(){return 0}getOutputVolume(){return 0}sendFeedback(e){this.canSendFeedback?(this.connection.sendMessage({type:"feedback",score:e?"like":"dislike",event_id:this.currentEventId}),this.lastFeedbackEventId=this.currentEventId,this.updateCanSendFeedback()):console.warn(0===this.lastFeedbackEventId?"Cannot send feedback: the conversation has not started yet.":"Cannot send feedback: feedback has already been sent for the current response.")}sendContextualUpdate(e){this.connection.sendMessage({type:"contextual_update",text:e})}sendUserMessage(e){this.connection.sendMessage({type:"user_message",text:e})}sendUserActivity(){this.connection.sendMessage({type:"user_activity"})}sendMCPToolApprovalResult(e,t){this.connection.sendMessage({type:"mcp_tool_approval_result",tool_call_id:e,is_approved:t})}sendMultimodalMessage(e){this.connection.sendMessage({type:"multimodal_message",text:e.text?{type:"user_message",text:e.text}:void 0,file:e.fileId?{type:"file_input",file_id:e.fileId}:void 0})}}class c{constructor(e={}){this.queue=[],this.disconnectionDetails=null,this.onDisconnectCallback=null,this.onMessageCallback=null,this.onModeChangeCallback=null,this.onDebug=void 0,this.onDebug=e.onDebug}debug(e){this.onDebug&&this.onDebug(e)}onMessage(e){this.onMessageCallback=e;const t=this.queue;this.queue=[],t.length>0&&queueMicrotask(()=>{t.forEach(e)})}onDisconnect(e){this.onDisconnectCallback=e;const t=this.disconnectionDetails;t&&queueMicrotask(()=>{e(t)})}onModeChange(e){this.onModeChangeCallback=e}updateMode(e){var t;null==(t=this.onModeChangeCallback)||t.call(this,e)}disconnect(e){var t;this.disconnectionDetails||(this.disconnectionDetails=e,null==(t=this.onDisconnectCallback)||t.call(this,e))}handleMessage(e){this.onMessageCallback?this.onMessageCallback(e):this.queue.push(e)}}function l(e){const[t,n]=e.split("_");if(!["pcm","ulaw"].includes(t))throw new Error(`Invalid format: ${e}`);const o=Number.parseInt(n);if(Number.isNaN(o))throw new Error(`Invalid sample rate: ${n}`);return{format:t,sampleRate:o}}const u="0.16.0";function d(e){return!!e.type}const h="conversation_initiation_client_data";function p(e){var t;const n={type:h};var o,i,s,a,r,c,l,u;return e.overrides&&(n.conversation_config_override={agent:{prompt:null==(o=e.overrides.agent)?void 0:o.prompt,first_message:null==(i=e.overrides.agent)?void 0:i.firstMessage,language:null==(s=e.overrides.agent)?void 0:s.language},tts:{voice_id:null==(a=e.overrides.tts)?void 0:a.voiceId,speed:null==(r=e.overrides.tts)?void 0:r.speed,stability:null==(c=e.overrides.tts)?void 0:c.stability,similarity_boost:null==(l=e.overrides.tts)?void 0:l.similarityBoost},conversation:{text_only:null==(u=e.overrides.conversation)?void 0:u.textOnly}}),e.customLlmExtraBody&&(n.custom_llm_extra_body=e.customLlmExtraBody),e.dynamicVariables&&(n.dynamic_variables=e.dynamicVariables),e.userId&&(n.user_id=e.userId),null!=(t=e.overrides)&&t.client&&(n.source_info={source:e.overrides.client.source,version:e.overrides.client.version}),n}class m extends Error{constructor(e,t){super(e),this.closeCode=void 0,this.closeReason=void 0,this.name="SessionConnectionError",this.closeCode=null==t?void 0:t.closeCode,this.closeReason=null==t?void 0:t.closeReason}}class v extends c{constructor(e,t,n,o){super(),this.socket=void 0,this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.socket=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.socket.addEventListener("error",e=>{setTimeout(()=>this.disconnect({reason:"error",message:"The connection was closed due to a socket error.",context:e}),0)}),this.socket.addEventListener("close",e=>{this.disconnect(1e3===e.code?{reason:"agent",context:e,closeCode:e.code,closeReason:e.reason||void 0}:{reason:"error",message:e.reason||"The connection was closed by the server.",context:e,closeCode:e.code,closeReason:e.reason||void 0})}),this.socket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);if(!d(t))return void this.debug({type:"invalid_event",message:"Received invalid socket event",data:e.data});this.handleMessage(t)}catch(t){this.debug({type:"parsing_error",message:"Failed to parse socket message",error:t instanceof Error?t.message:String(t),data:e.data})}})}static async create(e){let t=null;try{var n,o,i;const s=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let a;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||u,c=(null==(i=e.overrides)||null==(i=i.client)?void 0:i.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";a=`${e.signedUrl}${t}source=${c}&version=${r}`}else a=`${s}/v1/convai/conversation?agent_id=${e.agentId}&source=${c}&version=${r}`;e.environment&&(a+=`&environment=${encodeURIComponent(e.environment)}`);const h=["convai"];e.authorization&&h.push(`bearer.${e.authorization}`),t=new WebSocket(a,h);const g=await new Promise((n,o)=>{t.addEventListener("open",()=>{var n;const o=p(e);null==(n=t)||n.send(JSON.stringify(o))},{once:!0}),t.addEventListener("error",e=>{setTimeout(()=>o(new m("The connection was closed due to a socket error.")),0)}),t.addEventListener("close",e=>{o(new m(e.reason||(1e3===e.code?"Connection closed normally before session could be established.":"Connection closed unexpectedly before session could be established."),{closeCode:e.code,closeReason:e.reason||void 0}))}),t.addEventListener("message",e=>{const t=JSON.parse(e.data);d(t)&&("conversation_initiation_metadata"===t.type?n(t.conversation_initiation_metadata_event):console.warn("First received message is not conversation metadata."))},{once:!0})}),{conversation_id:f,agent_output_audio_format:_,user_input_audio_format:w}=g,b=l(null!=w?w:"pcm_16000"),y=l(_);return new v(t,f,b,y)}catch(e){var s;throw null==(s=t)||s.close(),e}}close(){this.socket.close(1e3,"User ended conversation")}sendMessage(e){this.socket.send(JSON.stringify(e))}async setMicMuted(e){console.warn(`WebSocket connection setMicMuted called with ${e}, but this is handled by VoiceConversation`)}}function g(e){const t=new Uint8Array(e);return window.btoa(String.fromCharCode(...t))}function f(e){const t=window.atob(e),n=t.length,o=new Uint8Array(n);for(let e=0;e<n;e++)o[e]=t.charCodeAt(e);return o.buffer}const _=new Map;function w(e,t){return async(n,o)=>{const i=_.get(e);if(i)return n.addModule(i);if(o)try{return await n.addModule(o),void _.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module from path: ${o}. Error: ${t}`)}const s=new Blob([t],{type:"application/javascript"}),a=URL.createObjectURL(s);try{return await n.addModule(a),void _.set(e,a)}catch(e){URL.revokeObjectURL(a)}try{const o=`data:application/javascript;base64,${btoa(t)}`;await n.addModule(o),_.set(e,o)}catch(t){throw new Error(`Failed to load the ${e} worklet module. Make sure the browser supports AudioWorklets. If you are using a strict CSP, you may need to self-host the worklet files.`)}}}const b=w("rawAudioProcessor",'/*\n * ulaw encoding logic taken from the wavefile library\n * https://github.com/rochars/wavefile/blob/master/lib/codecs/mulaw.js\n * USED BY @elevenlabs/client\n */\n\nconst BIAS = 0x84;\nconst CLIP = 32635;\nconst encodeTable = [\n 0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,\n 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7\n];\n\nfunction encodeSample(sample) {\n let sign;\n let exponent;\n let mantissa;\n let muLawSample;\n sign = (sample >> 8) & 0x80;\n if (sign !== 0) sample = -sample;\n sample = sample + BIAS;\n if (sample > CLIP) sample = CLIP;\n exponent = encodeTable[(sample>>7) & 0xFF];\n mantissa = (sample >> (exponent+3)) & 0x0F;\n muLawSample = ~(sign | (exponent << 4) | mantissa);\n \n return muLawSample;\n}\n\nclass RawAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n \n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.isMuted = false;\n this.buffer = []; // Initialize an empty buffer\n this.bufferSize = data.sampleRate / 10;\n this.format = data.format;\n\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(1, sampleRate, data.sampleRate).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "setMuted":\n this.isMuted = data.isMuted;\n break;\n }\n };\n }\n process(inputs) {\n if (!this.buffer) {\n return true;\n }\n \n const input = inputs[0]; // Get the first input node\n if (input.length > 0) {\n let channelData = input[0]; // Get the first channel\'s data\n\n // Resample the audio if necessary\n if (this.resampler) {\n channelData = this.resampler.full(channelData);\n }\n\n // Add channel data to the buffer\n this.buffer.push(...channelData);\n // Get max volume \n let sum = 0.0;\n for (let i = 0; i < channelData.length; i++) {\n sum += channelData[i] * channelData[i];\n }\n const maxVolume = Math.sqrt(sum / channelData.length);\n // Check if buffer size has reached or exceeded the threshold\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = this.isMuted \n ? new Float32Array(this.buffer.length)\n : new Float32Array(this.buffer);\n\n let encodedArray = this.format === "ulaw"\n ? new Uint8Array(float32Array.length)\n : new Int16Array(float32Array.length);\n\n // Iterate through the Float32Array and convert each sample to PCM16\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to the range [-1, 1]\n let sample = Math.max(-1, Math.min(1, float32Array[i]));\n\n // Scale the sample to the range [-32768, 32767]\n let value = sample < 0 ? sample * 32768 : sample * 32767;\n if (this.format === "ulaw") {\n value = encodeSample(Math.round(value));\n }\n\n encodedArray[i] = value;\n }\n\n // Send the buffered data to the main script\n this.port.postMessage([encodedArray, maxVolume]);\n\n // Clear the buffer after sending\n this.buffer = [];\n }\n }\n return true; // Continue processing\n }\n}\nregisterProcessor("rawAudioProcessor", RawAudioProcessor);\n');class y extends c{constructor(e,t,n,o,i={}){super(i),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=!1,this.audioEventId=1,this.audioCaptureContext=null,this.audioElements=[],this.outputDeviceId=null,this.outputAnalyser=null,this.outputFrequencyData=null,this.room=e,this.conversationId=t,this.inputFormat=n,this.outputFormat=o,this.setupRoomEventListeners()}static async create(n){let o;if("conversationToken"in n&&n.conversationToken)o=n.conversationToken;else{if(!("agentId"in n)||!n.agentId)throw new Error("Either conversationToken or agentId is required for WebRTC connection");try{var i,s,a;const e=(null==(i=n.overrides)||null==(i=i.client)?void 0:i.version)||u,t=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk";let c=`${r=null!=(a=n.origin)?a:"https://api.elevenlabs.io",r.replace(/^wss:\/\//,"https://")}/v1/convai/conversation/token?agent_id=${n.agentId}&source=${t}&version=${e}`;n.environment&&(c+=`&environment=${encodeURIComponent(n.environment)}`);const l=await fetch(c);if(!l.ok)throw new Error(`ElevenLabs API returned ${l.status} ${l.statusText}`);if(o=(await l.json()).token,!o)throw new Error("No conversation token received from API")}catch(e){let t=e instanceof Error?e.message:String(e);throw e instanceof Error&&e.message.includes("401")&&(t="Your agent has authentication enabled, but no signed URL or conversation token was provided."),new Error(`Failed to fetch conversation token for agent ${n.agentId}: ${t}`)}}var r;const c=new e;try{const e=`room_${Date.now()}`,i=l("pcm_48000"),s=l("pcm_48000"),a=new y(c,e,i,s,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var d;await c.connect(r,o),await new Promise(e=>{if(a.isConnected)e();else{const n=()=>{c.off(t.Connected,n),e()};c.on(t.Connected,n)}}),c.name&&(a.conversationId=(null==(d=c.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:d[0])||c.name),n.textOnly||await c.localParticipant.setMicrophoneEnabled(!0);const u=p(n);return a.debug({type:h,message:u}),await a.sendMessage(u),a}catch(e){throw await c.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(t.Connected,async function(){e.isConnected=!0,console.info("WebRTC room connected")}),this.room.on(t.Disconnected,e=>{this.isConnected=!1,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})})}),this.room.on(t.ConnectionStateChanged,e=>{e===n.Disconnected&&(this.isConnected=!1,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}))}),this.room.on(t.DataReceived,(e,t)=>{try{const t=JSON.parse((new TextDecoder).decode(e));if("audio"===t.type)return;d(t)?this.handleMessage(t):console.warn("Invalid socket event received:",t)}catch(t){console.warn("Failed to parse incoming data message:",t),console.warn("Raw payload:",(new TextDecoder).decode(e))}}),this.room.on(t.TrackSubscribed,async function(t,n,i){if(t.kind===o.Kind.Audio&&i.identity.includes("agent")){const n=t,o=n.attach();if(o.autoplay=!0,o.controls=!1,e.outputDeviceId&&o.setSinkId)try{await o.setSinkId(e.outputDeviceId)}catch(e){console.warn("Failed to set output device for new audio element:",e)}o.style.display="none",document.body.appendChild(o),e.audioElements.push(o),1===e.audioElements.length&&(null==e.onDebug||e.onDebug({type:"audio_element_ready"})),await e.setupAudioCapture(n)}}),this.room.on(t.ActiveSpeakersChanged,async function(t){e.updateMode(t.length>0&&t[0].identity.startsWith("agent")?"speaking":"listening")}),this.room.on(t.ParticipantDisconnected,e=>{var t;null!=(t=e.identity)&&t.startsWith("agent")&&this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:"agent disconnected"})})})}close(){if(this.isConnected){try{this.room.localParticipant.audioTrackPublications.forEach(e=>{e.track&&e.track.stop()})}catch(e){console.warn("Error stopping local tracks:",e)}this.audioCaptureContext&&(this.audioCaptureContext.close().catch(e=>{console.warn("Error closing audio capture context:",e)}),this.audioCaptureContext=null),this.audioElements.forEach(e=>{e.parentNode&&e.parentNode.removeChild(e)}),this.audioElements=[],this.room.disconnect()}}async sendMessage(e){if(this.isConnected&&this.room.localParticipant){if(!("user_audio_chunk"in e))try{const t=(new TextEncoder).encode(JSON.stringify(e));await this.room.localParticipant.publishData(t,{reliable:!0})}catch(t){this.debug({type:"send_message_error",message:{message:e,error:t}}),console.error("Failed to send message via WebRTC:",t)}}else console.warn("Cannot send message: room not connected or no local participant")}getRoom(){return this.room}async setMicMuted(e){if(!this.isConnected||!this.room.localParticipant)return void console.warn("Cannot set microphone muted: room not connected or no local participant");const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);if(null!=t&&t.track)try{e?await t.track.mute():await t.track.unmute()}catch(t){await this.room.localParticipant.setMicrophoneEnabled(!e)}else await this.room.localParticipant.setMicrophoneEnabled(!e)}async setupAudioCapture(e){try{const t=new AudioContext;this.audioCaptureContext=t,this.outputAnalyser=t.createAnalyser(),this.outputAnalyser.fftSize=2048,this.outputAnalyser.smoothingTimeConstant=.8;const n=new MediaStream([e.mediaStreamTrack]),o=t.createMediaStreamSource(n);o.connect(this.outputAnalyser),await b(t.audioWorklet);const i=new AudioWorkletNode(t,"rawAudioProcessor");this.outputAnalyser.connect(i),i.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),i.port.onmessage=e=>{const[t,n]=e.data;if(n>.01){const e=g(t.buffer),n=this.audioEventId++;this.handleMessage({type:"audio",audio_event:{audio_base_64:e,event_id:n}})}},o.connect(i)}catch(e){console.warn("Failed to set up audio capture:",e)}}setAudioVolume(e){this.audioElements.forEach(t=>{t.volume=e})}async setAudioOutputDevice(e){if(!("setSinkId"in HTMLAudioElement.prototype))throw new Error("setSinkId is not supported in this browser");const t=this.audioElements.map(async function(t){try{await t.setSinkId(e)}catch(e){throw console.error("Failed to set sink ID for audio element:",e),e}});await Promise.all(t),this.outputDeviceId=e}async setAudioInputDevice(e){if(!this.isConnected||!this.room.localParticipant)throw new Error("Cannot change input device: room not connected or no local participant");try{const t=this.room.localParticipant.getTrackPublication(o.Source.Microphone);null!=t&&t.track&&(await t.track.stop(),await this.room.localParticipant.unpublishTrack(t.track));const n={deviceId:{exact:e},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:{ideal:1}},s=await i(n);await this.room.localParticipant.publishTrack(s,{name:"microphone",source:o.Source.Microphone})}catch(e){console.error("Failed to change input device:",e);try{await this.room.localParticipant.setMicrophoneEnabled(!0)}catch(e){console.error("Failed to recover microphone after device switch error:",e)}throw e}}getOutputByteFrequencyData(){return this.outputAnalyser?(null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.outputAnalyser.frequencyBinCount)),this.outputAnalyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData):null}}async function S(e){const t=function(e){return e.connectionType?e.connectionType:"conversationToken"in e&&e.conversationToken?"webrtc":"websocket"}(e);switch(t){case"websocket":return v.create(e);case"webrtc":return y.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function E(){return["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function C(e={default:0,android:3e3}){let t=e.default;var n;if(/android/i.test(navigator.userAgent))t=null!=(n=e.android)?n:t;else if(E()){var o;t=null!=(o=e.ios)?o:t}t>0&&await new Promise(e=>setTimeout(e,t))}class k extends r{constructor(...e){super(...e),this.type="text"}static async startSession(e){const t=r.getFullOptions(e);t.onStatusChange&&t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1}),t.onModeChange&&t.onModeChange({mode:"listening"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:!1});let n=null;try{return await C(t.connectionDelay),n=await S(e),new k(t,n)}catch(e){var o;throw t.onStatusChange&&t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}async function R(e,t){const n=t||"https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js";await e.audioWorklet.addModule(n)}const I={echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0,channelCount:{ideal:1}};class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:i,libsampleratePath:a,onError:r}){let c=null,l=null;try{const u=s({sampleRate:{ideal:e}},I);if(E()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(u.deviceId={ideal:e.deviceId})}o&&(u.deviceId=M.getDeviceIdConstraint(o));const d=navigator.mediaDevices.getSupportedConstraints().sampleRate;c=new window.AudioContext(d?{sampleRate:e}:{});const h=c.createAnalyser();d||await R(c,a),await b(c.audioWorklet,null==i?void 0:i.rawAudioProcessor);const p=s({voiceIsolation:!0},u);l=await navigator.mediaDevices.getUserMedia({audio:p});const m=c.createMediaStreamSource(l),v=new AudioWorkletNode(c,"rawAudioProcessor");v.port.postMessage({type:"setFormat",format:t,sampleRate:e}),m.connect(h),h.connect(v),await c.resume();const g=await navigator.permissions.query({name:"microphone"});return new M(c,h,v,l,m,g,r)}catch(e){var u,d;throw null==(u=l)||u.getTracks().forEach(e=>{e.stop()}),null==(d=c)||d.close(),e}}static getDeviceIdConstraint(e){if(e)return E()?{ideal:e}:{exact:e}}constructor(e,t,n,o,i,s,a=console.error){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.mediaStreamSource=void 0,this.permissions=void 0,this.onError=void 0,this.settingInput=!1,this.handlePermissionsChange=()=>{if("denied"===this.permissions.state)this.onError("Microphone permission denied");else if(!this.settingInput){var e;const[t]=this.inputStream.getAudioTracks(),{deviceId:n}=null!=(e=null==t?void 0:t.getSettings())?e:{};this.setInputDevice(n).catch(e=>{this.onError("Failed to reset input device after permission change:",e)})}},this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o,this.mediaStreamSource=i,this.permissions=s,this.onError=a,this.permissions.addEventListener("change",this.handlePermissionsChange)}forgetInputStreamAndSource(){for(const e of this.inputStream.getTracks())e.stop();this.mediaStreamSource.disconnect()}async close(){this.forgetInputStreamAndSource(),this.permissions.removeEventListener("change",this.handlePermissionsChange),await this.context.close()}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e})}async setInputDevice(e){try{if(this.settingInput)throw new Error("Input device is already being set");this.settingInput=!0;const t=s({},I);e&&(t.deviceId=M.getDeviceIdConstraint(e));const n=s({voiceIsolation:!0},t),o=await navigator.mediaDevices.getUserMedia({audio:n});this.forgetInputStreamAndSource(),this.inputStream=o,this.mediaStreamSource=this.context.createMediaStreamSource(o),this.mediaStreamSource.connect(this.analyser)}catch(e){throw this.onError("Failed to switch input device:",e),e}finally{this.settingInput=!1}}}const A=w("audioConcatProcessor",'/*\n * ulaw decoding logic taken from the wavefile library\n * https://github.com/rochars/wavefile/blob/master/lib/codecs/mulaw.js\n * USED BY @elevenlabs/client\n */\n\nconst decodeTable = [0, 132, 396, 924, 1980, 4092, 8316, 16764];\n\nfunction decodeSample(muLawSample) {\n let sign;\n let exponent;\n let mantissa;\n let sample;\n muLawSample = ~muLawSample;\n sign = muLawSample & 0x80;\n exponent = (muLawSample >> 4) & 0x07;\n mantissa = muLawSample & 0x0f;\n sample = decodeTable[exponent] + (mantissa << (exponent + 3));\n if (sign !== 0) sample = -sample;\n\n return sample;\n}\n\nclass AudioConcatProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffers = []; // Initialize an empty buffer\n this.cursor = 0;\n this.currentBuffer = null;\n this.wasInterrupted = false;\n this.finished = false;\n\n this.port.onmessage = ({ data }) => {\n switch (data.type) {\n case "setFormat":\n this.format = data.format;\n if (globalThis.LibSampleRate && sampleRate !== data.sampleRate) {\n globalThis.LibSampleRate.create(\n 1,\n data.sampleRate,\n sampleRate\n ).then(resampler => {\n this.resampler = resampler;\n });\n }\n break;\n case "buffer":\n this.wasInterrupted = false;\n this.buffers.push(\n this.format === "ulaw"\n ? new Uint8Array(data.buffer)\n : new Int16Array(data.buffer)\n );\n break;\n case "interrupt":\n this.wasInterrupted = true;\n break;\n case "clearInterrupted":\n if (this.wasInterrupted) {\n this.wasInterrupted = false;\n this.buffers = [];\n this.currentBuffer = null;\n }\n }\n };\n }\n process(_, outputs) {\n let finished = false;\n const output = outputs[0][0];\n for (let i = 0; i < output.length; i++) {\n if (!this.currentBuffer) {\n if (this.buffers.length === 0) {\n finished = true;\n break;\n }\n this.currentBuffer = this.buffers.shift();\n if (this.resampler) {\n this.currentBuffer = this.resampler.full(this.currentBuffer);\n }\n this.cursor = 0;\n }\n\n let value = this.currentBuffer[this.cursor];\n if (this.format === "ulaw") {\n value = decodeSample(value);\n }\n output[i] = value / 32768;\n this.cursor++;\n\n if (this.cursor >= this.currentBuffer.length) {\n this.currentBuffer = null;\n }\n }\n\n if (this.finished !== finished) {\n this.finished = finished;\n this.port.postMessage({ type: "process", finished });\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("audioConcatProcessor", AudioConcatProcessor);\n');class D{static async create({sampleRate:e,format:t,outputDeviceId:n,workletPaths:o,libsampleratePath:i}){let s=null,a=null;try{const r=navigator.mediaDevices.getSupportedConstraints().sampleRate;s=new AudioContext(r?{sampleRate:e}:{});const c=s.createAnalyser(),l=s.createGain();a=new Audio,a.src="",a.load(),a.autoplay=!0,a.style.display="none",document.body.appendChild(a);const u=s.createMediaStreamDestination();a.srcObject=u.stream,l.connect(c),c.connect(u),r&&s.sampleRate===e||(s.sampleRate!==e&&console.warn(`[ConversationalAI] Sample rate ${e} not available, resampling to ${s.sampleRate}`),await R(s,i)),await A(s.audioWorklet,null==o?void 0:o.audioConcatProcessor);const d=new AudioWorkletNode(s,"audioConcatProcessor");return d.port.postMessage({type:"setFormat",format:t,sampleRate:e}),d.connect(l),await s.resume(),n&&a.setSinkId&&await a.setSinkId(n),new D(s,c,l,d,a)}catch(e){var r,c;throw null!=(r=a)&&r.parentNode&&a.parentNode.removeChild(a),null==(c=a)||c.pause(),s&&"closed"!==s.state&&await s.close(),e}}constructor(e,t,n,o,i){this.context=void 0,this.analyser=void 0,this.gain=void 0,this.worklet=void 0,this.audioElement=void 0,this.context=e,this.analyser=t,this.gain=n,this.worklet=o,this.audioElement=i}async setOutputDevice(e){if(!("setSinkId"in HTMLAudioElement.prototype))throw new Error("setSinkId is not supported in this browser");await this.audioElement.setSinkId(e||"")}async close(){this.audioElement.parentNode&&this.audioElement.parentNode.removeChild(this.audioElement),this.audioElement.pause(),await this.context.close()}}class T extends r{static async requestWakeLock(){if("wakeLock"in navigator)try{return await navigator.wakeLock.request("screen")}catch(e){}return null}static async startSession(e){var t;const n=r.getFullOptions(e);n.onStatusChange&&n.onStatusChange({status:"connecting"}),n.onCanSendFeedbackChange&&n.onCanSendFeedbackChange({canSendFeedback:!1});let o=null,i=null,a=null,c=null,l=null;(null==(t=e.useWakeLock)||t)&&(l=await T.requestWakeLock());try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:!0}),await C(n.connectionDelay),i=await S(e),[o,a]=await Promise.all([M.create(s({},i.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices,inputDeviceId:e.inputDeviceId,workletPaths:e.workletPaths,libsampleratePath:e.libsampleratePath})),D.create(s({},i.outputFormat,{outputDeviceId:e.outputDeviceId,workletPaths:e.workletPaths}))]),null==(u=c)||u.getTracks().forEach(e=>{e.stop()}),c=null,new T(n,i,o,a,l)}catch(e){var d,h,p,m;n.onStatusChange&&n.onStatusChange({status:"disconnected"}),null==(d=c)||d.getTracks().forEach(e=>{e.stop()}),null==(h=i)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=a)?void 0:m.close());try{var v;await(null==(v=l)?void 0:v.release()),l=null}catch(e){}throw e}}constructor(e,t,n,o,i){super(e,t),this.input=void 0,this.output=void 0,this.wakeLock=void 0,this.type="voice",this.inputFrequencyData=void 0,this.outputFrequencyData=void 0,this.visibilityChangeHandler=null,this.onInputWorkletMessage=e=>{"connected"===this.status&&this.connection.sendMessage({user_audio_chunk:g(e.data[0].buffer)})},this.onOutputWorkletMessage=({data:e})=>{"process"===e.type&&this.updateMode(e.finished?"listening":"speaking")},this.addAudioBase64Chunk=e=>{this.output.gain.gain.cancelScheduledValues(this.output.context.currentTime),this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"}),this.output.worklet.port.postMessage({type:"buffer",buffer:f(e)})},this.fadeOutAudio=()=>{this.updateMode("listening"),this.output.worklet.port.postMessage({type:"interrupt"}),this.output.gain.gain.exponentialRampToValueAtTime(1e-4,this.output.context.currentTime+2),setTimeout(()=>{this.output.gain.gain.value=this.volume,this.output.worklet.port.postMessage({type:"clearInterrupted"})},2e3)},this.calculateVolume=e=>{if(0===e.length)return 0;let t=0;for(let n=0;n<e.length;n++)t+=e[n]/255;return t/=e.length,t<0?0:t>1?1:t},this.setVolume=({volume:e})=>{const t=Number.isFinite(e)?Math.min(1,Math.max(0,e)):1;this.volume=t,this.connection instanceof y?this.connection.setAudioVolume(t):this.output.gain.gain.value=t},this.input=n,this.output=o,this.wakeLock=i,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage,i&&(this.visibilityChangeHandler=()=>{var e;"visible"===document.visibilityState&&null!=(e=this.wakeLock)&&e.released&&T.requestWakeLock().then(e=>{this.wakeLock=e})},document.addEventListener("visibilitychange",this.visibilityChangeHandler))}async handleEndSession(){await super.handleEndSession(),this.visibilityChangeHandler&&document.removeEventListener("visibilitychange",this.visibilityChangeHandler);try{var e;await(null==(e=this.wakeLock)?void 0:e.release()),this.wakeLock=null}catch(e){}await this.input.close(),await this.output.close()}handleInterruption(e){super.handleInterruption(e),this.fadeOutAudio()}handleAudio(e){var t,n;super.handleAudio(e),e.audio_event.alignment&&this.options.onAudioAlignment&&this.options.onAudioAlignment(e.audio_event.alignment),this.lastInterruptTimestamp<=e.audio_event.event_id&&(e.audio_event.audio_base_64&&(null==(t=(n=this.options).onAudio)||t.call(n,e.audio_event.audio_base_64),this.connection instanceof y||this.addAudioBase64Chunk(e.audio_event.audio_base_64)),this.currentEventId=e.audio_event.event_id,this.updateCanSendFeedback(),this.updateMode("speaking"))}setMicMuted(e){this.connection instanceof y?this.connection.setMicMuted(e):this.input.setMuted(e)}getInputByteFrequencyData(){return null!=this.inputFrequencyData||(this.inputFrequencyData=new Uint8Array(this.input.analyser.frequencyBinCount)),this.input.analyser.getByteFrequencyData(this.inputFrequencyData),this.inputFrequencyData}getOutputByteFrequencyData(){return this.connection instanceof y?this.connection.getOutputByteFrequencyData()||new Uint8Array(1024):(null!=this.outputFrequencyData||(this.outputFrequencyData=new Uint8Array(this.output.analyser.frequencyBinCount)),this.output.analyser.getByteFrequencyData(this.outputFrequencyData),this.outputFrequencyData)}getInputVolume(){return this.calculateVolume(this.getInputByteFrequencyData())}getOutputVolume(){return this.calculateVolume(this.getOutputByteFrequencyData())}async changeInputDevice({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o}){try{if(this.connection instanceof v)try{return await this.input.setInputDevice(o),this.input}catch(e){console.warn("Failed to change device on existing input, recreating:",e)}this.connection instanceof y&&await this.connection.setAudioInputDevice(o||""),await this.input.close();const i=await M.create({sampleRate:null!=e?e:this.connection.inputFormat.sampleRate,format:null!=t?t:this.connection.inputFormat.format,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:this.options.workletPaths,libsampleratePath:this.options.libsampleratePath,onError:this.options.onError});return this.input=i,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.input}catch(e){throw console.error("Error changing input device",e),e}}async changeOutputDevice({sampleRate:e,format:t,outputDeviceId:n}){try{if(this.connection instanceof v)try{return await this.output.setOutputDevice(n),this.output}catch(e){console.warn("Failed to change device on existing output, recreating:",e)}this.connection instanceof y&&await this.connection.setAudioOutputDevice(n||""),await this.output.close();const o=await D.create({sampleRate:null!=e?e:this.connection.outputFormat.sampleRate,format:null!=t?t:this.connection.outputFormat.format,outputDeviceId:n,workletPaths:this.options.workletPaths});return this.output=o,this.output}catch(e){throw console.error("Error changing output device",e),e}}}function F(e,t,n="https://api.elevenlabs.io"){const o={};return"boolean"==typeof t?o.feedback=t?"like":"dislike":(o.rating=t.rating,o.comment=t.comment),fetch(`${n}/v1/convai/conversations/${e}/feedback`,{method:"POST",body:JSON.stringify(o),headers:{"Content-Type":"application/json"}})}class P{constructor(){this.listeners=new Map}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set);const n=this.listeners.get(e);n&&n.add(t)}off(e,t){const n=this.listeners.get(e);n&&n.delete(t)}emit(e,...t){const n=this.listeners.get(e);n&&n.forEach(e=>{e(...t)})}}var x;!function(e){e.SESSION_STARTED="session_started",e.PARTIAL_TRANSCRIPT="partial_transcript",e.COMMITTED_TRANSCRIPT="committed_transcript",e.COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS="committed_transcript_with_timestamps",e.AUTH_ERROR="auth_error",e.ERROR="error",e.OPEN="open",e.CLOSE="close",e.QUOTA_EXCEEDED="quota_exceeded",e.COMMIT_THROTTLED="commit_throttled",e.TRANSCRIBER_ERROR="transcriber_error",e.UNACCEPTED_TERMS="unaccepted_terms",e.RATE_LIMITED="rate_limited",e.INPUT_ERROR="input_error",e.QUEUE_OVERFLOW="queue_overflow",e.RESOURCE_EXHAUSTED="resource_exhausted",e.SESSION_TIME_LIMIT_EXCEEDED="session_time_limit_exceeded",e.CHUNK_SIZE_EXCEEDED="chunk_size_exceeded",e.INSUFFICIENT_AUDIO_ACTIVITY="insufficient_audio_activity"}(x||(x={}));class O{constructor(e){this.websocket=null,this.eventEmitter=new P,this.currentSampleRate=16e3,this._audioCleanup=void 0,this.currentSampleRate=e}setWebSocket(e){this.websocket=e,this.websocket.readyState===WebSocket.OPEN?this.eventEmitter.emit(x.OPEN):this.websocket.addEventListener("open",()=>{this.eventEmitter.emit(x.OPEN)}),this.websocket.addEventListener("message",e=>{try{const t=JSON.parse(e.data);switch(t.message_type){case"session_started":this.eventEmitter.emit(x.SESSION_STARTED,t);break;case"partial_transcript":this.eventEmitter.emit(x.PARTIAL_TRANSCRIPT,t);break;case"committed_transcript":this.eventEmitter.emit(x.COMMITTED_TRANSCRIPT,t);break;case"committed_transcript_with_timestamps":this.eventEmitter.emit(x.COMMITTED_TRANSCRIPT_WITH_TIMESTAMPS,t);break;case"auth_error":this.eventEmitter.emit(x.AUTH_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"quota_exceeded":this.eventEmitter.emit(x.QUOTA_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"commit_throttled":this.eventEmitter.emit(x.COMMIT_THROTTLED,t),this.eventEmitter.emit(x.ERROR,t);break;case"transcriber_error":this.eventEmitter.emit(x.TRANSCRIBER_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"unaccepted_terms":this.eventEmitter.emit(x.UNACCEPTED_TERMS,t),this.eventEmitter.emit(x.ERROR,t);break;case"rate_limited":this.eventEmitter.emit(x.RATE_LIMITED,t),this.eventEmitter.emit(x.ERROR,t);break;case"input_error":this.eventEmitter.emit(x.INPUT_ERROR,t),this.eventEmitter.emit(x.ERROR,t);break;case"queue_overflow":this.eventEmitter.emit(x.QUEUE_OVERFLOW,t),this.eventEmitter.emit(x.ERROR,t);break;case"resource_exhausted":this.eventEmitter.emit(x.RESOURCE_EXHAUSTED,t),this.eventEmitter.emit(x.ERROR,t);break;case"session_time_limit_exceeded":this.eventEmitter.emit(x.SESSION_TIME_LIMIT_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"chunk_size_exceeded":this.eventEmitter.emit(x.CHUNK_SIZE_EXCEEDED,t),this.eventEmitter.emit(x.ERROR,t);break;case"insufficient_audio_activity":this.eventEmitter.emit(x.INSUFFICIENT_AUDIO_ACTIVITY,t),this.eventEmitter.emit(x.ERROR,t);break;case"error":this.eventEmitter.emit(x.ERROR,t);break;default:console.warn("Unknown message type:",t)}}catch(t){console.error("Failed to parse WebSocket message:",t,e.data),this.eventEmitter.emit(x.ERROR,new Error(`Failed to parse message: ${t}`))}}),this.websocket.addEventListener("error",e=>{console.error("WebSocket error:",e),this.eventEmitter.emit(x.ERROR,e)}),this.websocket.addEventListener("close",e=>{if(console.log(`WebSocket closed: code=${e.code}, reason="${e.reason}", wasClean=${e.wasClean}`),!e.wasClean||1e3!==e.code&&1005!==e.code){const t=`WebSocket closed unexpectedly: ${e.code} - ${e.reason||"No reason provided"}`;console.error(t),this.eventEmitter.emit(x.ERROR,new Error(t))}this.eventEmitter.emit(x.CLOSE,e)})}on(e,t){this.eventEmitter.on(e,t)}off(e,t){this.eventEmitter.off(e,t)}send(e){var t,n;if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");const o={message_type:"input_audio_chunk",audio_base_64:e.audioBase64,commit:null!=(t=e.commit)&&t,sample_rate:null!=(n=e.sampleRate)?n:this.currentSampleRate,previous_text:e.previousText};this.websocket.send(JSON.stringify(o))}commit(){if(!this.websocket||this.websocket.readyState!==WebSocket.OPEN)throw new Error("WebSocket is not connected");this.websocket.send(JSON.stringify({message_type:"input_audio_chunk",audio_base_64:"",commit:!0,sample_rate:this.currentSampleRate}))}close(){this._audioCleanup&&this._audioCleanup(),this.websocket&&this.websocket.close(1e3,"User ended session")}}const L=w("scribeAudioProcessor",'/*\n * Scribe Audio Processor for converting microphone audio to PCM16 format\n * Supports resampling for browsers like Firefox that don\'t support\n * AudioContext sample rate constraints.\n * USED BY @elevenlabs/client\n */\n\nclass ScribeAudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffer = [];\n this.bufferSize = 4096; // Buffer size for optimal chunk transmission\n\n // Resampling state\n this.inputSampleRate = null;\n this.outputSampleRate = null;\n this.resampleRatio = 1;\n this.lastSample = 0;\n this.resampleAccumulator = 0;\n\n this.port.onmessage = ({ data }) => {\n if (data.type === "configure") {\n this.inputSampleRate = data.inputSampleRate;\n this.outputSampleRate = data.outputSampleRate;\n if (this.inputSampleRate && this.outputSampleRate) {\n this.resampleRatio = this.inputSampleRate / this.outputSampleRate;\n }\n }\n };\n }\n\n // Linear interpolation resampling\n resample(inputData) {\n if (this.resampleRatio === 1 || !this.inputSampleRate) {\n return inputData;\n }\n\n const outputSamples = [];\n\n for (let i = 0; i < inputData.length; i++) {\n const currentSample = inputData[i];\n\n // Generate output samples using linear interpolation\n while (this.resampleAccumulator < 1) {\n const interpolated =\n this.lastSample +\n (currentSample - this.lastSample) * this.resampleAccumulator;\n outputSamples.push(interpolated);\n this.resampleAccumulator += this.resampleRatio;\n }\n\n this.resampleAccumulator -= 1;\n this.lastSample = currentSample;\n }\n\n return new Float32Array(outputSamples);\n }\n\n process(inputs) {\n const input = inputs[0];\n if (input.length > 0) {\n let channelData = input[0]; // Get first channel (mono)\n\n // Resample if needed (for Firefox and other browsers that don\'t\n // support AudioContext sample rate constraints)\n if (this.resampleRatio !== 1) {\n channelData = this.resample(channelData);\n }\n\n // Add incoming audio to buffer\n for (let i = 0; i < channelData.length; i++) {\n this.buffer.push(channelData[i]);\n }\n\n // When buffer reaches threshold, convert and send\n if (this.buffer.length >= this.bufferSize) {\n const float32Array = new Float32Array(this.buffer);\n const int16Array = new Int16Array(float32Array.length);\n\n // Convert Float32 [-1, 1] to Int16 [-32768, 32767]\n for (let i = 0; i < float32Array.length; i++) {\n // Clamp the value to prevent overflow\n const sample = Math.max(-1, Math.min(1, float32Array[i]));\n // Scale to PCM16 range\n int16Array[i] = sample < 0 ? sample * 32768 : sample * 32767;\n }\n\n // Send to main thread as transferable ArrayBuffer\n this.port.postMessage(\n {\n audioData: int16Array.buffer\n },\n [int16Array.buffer]\n );\n\n // Clear buffer\n this.buffer = [];\n }\n }\n\n return true; // Continue processing\n }\n}\n\nregisterProcessor("scribeAudioProcessor", ScribeAudioProcessor);\n\n');var U,N;!function(e){e.PCM_8000="pcm_8000",e.PCM_16000="pcm_16000",e.PCM_22050="pcm_22050",e.PCM_24000="pcm_24000",e.PCM_44100="pcm_44100",e.PCM_48000="pcm_48000",e.ULAW_8000="ulaw_8000"}(U||(U={})),function(e){e.MANUAL="manual",e.VAD="vad"}(N||(N={}));class W{static getWebSocketUri(e=W.DEFAULT_BASE_URI){return`${e}/v1/speech-to-text/realtime`}static buildWebSocketUri(e){const t=W.getWebSocketUri(e.baseUri),n=new URLSearchParams;if(n.append("model_id",e.modelId),n.append("token",e.token),void 0!==e.commitStrategy&&n.append("commit_strategy",e.commitStrategy),void 0!==e.audioFormat&&n.append("audio_format",e.audioFormat),void 0!==e.vadSilenceThresholdSecs){if(e.vadSilenceThresholdSecs<=.3||e.vadSilenceThresholdSecs>3)throw new Error("vadSilenceThresholdSecs must be between 0.3 and 3.0");n.append("vad_silence_threshold_secs",e.vadSilenceThresholdSecs.toString())}if(void 0!==e.vadThreshold){if(e.vadThreshold<.1||e.vadThreshold>.9)throw new Error("vadThreshold must be between 0.1 and 0.9");n.append("vad_threshold",e.vadThreshold.toString())}if(void 0!==e.minSpeechDurationMs){if(e.minSpeechDurationMs<=50||e.minSpeechDurationMs>2e3)throw new Error("minSpeechDurationMs must be between 50 and 2000");n.append("min_speech_duration_ms",e.minSpeechDurationMs.toString())}if(void 0!==e.minSilenceDurationMs){if(e.minSilenceDurationMs<=50||e.minSilenceDurationMs>2e3)throw new Error("minSilenceDurationMs must be between 50 and 2000");n.append("min_silence_duration_ms",e.minSilenceDurationMs.toString())}void 0!==e.languageCode&&n.append("language_code",e.languageCode),void 0!==e.includeTimestamps&&n.append("include_timestamps",e.includeTimestamps?"true":"false");const o=n.toString();return o?`${t}?${o}`:t}static connect(e){if(!e.modelId)throw new Error("modelId is required");const t=new O("microphone"in e&&e.microphone?16e3:e.sampleRate),n=W.buildWebSocketUri(e),o=new WebSocket(n);return"microphone"in e&&e.microphone&&o.addEventListener("open",()=>{W.streamFromMicrophone(e,t)}),t.setWebSocket(o),t}static async streamFromMicrophone(e,t){const n=16e3;try{var o,i,s,a,r,c,l,u,d,h;const p=await navigator.mediaDevices.getUserMedia({audio:{deviceId:null==(o=e.microphone)?void 0:o.deviceId,echoCancellation:null==(i=null==(s=e.microphone)?void 0:s.echoCancellation)||i,noiseSuppression:null==(a=null==(r=e.microphone)?void 0:r.noiseSuppression)||a,autoGainControl:null==(c=null==(l=e.microphone)?void 0:l.autoGainControl)||c,channelCount:null!=(u=null==(d=e.microphone)?void 0:d.channelCount)?u:1,sampleRate:{ideal:n}}}),m=null==(h=p.getAudioTracks()[0])?void 0:h.getSettings(),v=null==m?void 0:m.sampleRate,g=new AudioContext(v?{sampleRate:v}:{});await L(g.audioWorklet);const f=g.createMediaStreamSource(p),_=new AudioWorkletNode(g,"scribeAudioProcessor");g.sampleRate!==n&&_.port.postMessage({type:"configure",inputSampleRate:g.sampleRate,outputSampleRate:n}),_.port.onmessage=e=>{const{audioData:n}=e.data,o=new Uint8Array(n);let i="";for(let e=0;e<o.length;e++)i+=String.fromCharCode(o[e]);const s=btoa(i);t.send({audioBase64:s})},f.connect(_),"suspended"===g.state&&await g.resume(),t._audioCleanup=()=>{p.getTracks().forEach(e=>{e.stop()}),f.disconnect(),_.disconnect(),g.close()}}catch(e){throw console.error("Failed to start microphone streaming:",e),e}}}W.DEFAULT_BASE_URI="wss://api.elevenlabs.io";class q extends r{static startSession(e){const t=q.getFullOptions(e);return t.textOnly?k.startSession(t):T.startSession(t)}}export{U as AudioFormat,N as CommitStrategy,q as Conversation,M as Input,D as Output,O as RealtimeConnection,x as RealtimeEvents,W as Scribe,m as SessionConnectionError,k as TextConversation,T as VoiceConversation,y as WebRTCConnection,v as WebSocketConnection,S as createConnection,F as postOverallFeedback};
2
2
  //# sourceMappingURL=lib.modern.js.map