@staylift-tech/conv-widget 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,17 +21,17 @@
21
21
  primary-color="#8b5cf6"
22
22
  ></staylift-widget>
23
23
 
24
- <script src="https://unpkg.com/@staylift/conv-widget"></script>
24
+ <script src="https://unpkg.com/@staylift-tech/conv-widget"></script>
25
25
  ```
26
26
 
27
27
  ### NPM (For developers)
28
28
 
29
29
  ```bash
30
- npm install @staylift/conv-widget
30
+ npm install @staylift-tech/conv-widget
31
31
  ```
32
32
 
33
33
  ```javascript
34
- import '@staylift/conv-widget';
34
+ import '@staylift-tech/conv-widget';
35
35
  ```
36
36
 
37
37
  ## How It Works
@@ -207,7 +207,7 @@ export default function VoiceWidget() {
207
207
  </template>
208
208
 
209
209
  <script setup>
210
- import '@staylift/conv-widget';
210
+ import '@staylift-tech/conv-widget';
211
211
  </script>
212
212
  ```
213
213
 
@@ -6,7 +6,7 @@ var appGlobals = require('./app-globals-V2Kpy_OQ.js');
6
6
  const defineCustomElements = async (win, options) => {
7
7
  if (typeof window === 'undefined') return undefined;
8
8
  await appGlobals.globalScripts();
9
- return index.bootstrapLazy([["staylift-orb.cjs",[[1,"staylift-orb",{"inputVolume":[2,"input-volume"],"outputVolume":[2,"output-volume"],"isActive":[4,"is-active"],"primaryColor":[1,"primary-color"],"size":[8],"animationFrame":[32]}]]],["staylift-widget.cjs",[[1,"staylift-widget",{"agentId":[1,"agent-id"],"positionX":[1,"position-x"],"positionY":[1,"position-y"],"variant":[1],"mode":[1],"primaryColor":[1,"primary-color"],"brandName":[1,"brand-name"],"language":[1],"autoExpand":[4,"auto-expand"],"showBranding":[4,"show-branding"],"avatarUrl":[1,"avatar-url"],"fabPrompt":[1,"fab-prompt"],"fabButtonText":[1,"fab-button-text"],"status":[32],"isExpanded":[32],"termsAccepted":[32],"errorMessage":[32],"inputVolume":[32],"outputVolume":[32],"messages":[32],"inputText":[32],"copiedIndex":[32],"startConversation":[64],"endConversation":[64],"getStatus":[64],"sendMessage":[64]}]]]], options);
9
+ return index.bootstrapLazy([["staylift-orb.cjs",[[1,"staylift-orb",{"inputVolume":[2,"input-volume"],"outputVolume":[2,"output-volume"],"isActive":[4,"is-active"],"primaryColor":[1,"primary-color"],"size":[8],"animationFrame":[32]}]]],["staylift-widget.cjs",[[1,"staylift-widget",{"agentId":[1,"agent-id"],"positionX":[1,"position-x"],"positionY":[1,"position-y"],"variant":[1],"mode":[1],"primaryColor":[1,"primary-color"],"brandName":[1,"brand-name"],"language":[1],"autoExpand":[4,"auto-expand"],"showBranding":[4,"show-branding"],"avatarUrl":[1,"avatar-url"],"fabPrompt":[1,"fab-prompt"],"fabButtonText":[1,"fab-button-text"],"status":[32],"isExpanded":[32],"termsAccepted":[32],"errorMessage":[32],"inputVolume":[32],"outputVolume":[32],"messages":[32],"inputText":[32],"copiedIndex":[32],"selectedMode":[32],"startConversation":[64],"endConversation":[64],"getStatus":[64],"sendMessage":[64]}]]]], options);
10
10
  };
11
11
 
12
12
  exports.setNonce = index.setNonce;
@@ -26053,9 +26053,9 @@ class JWSSignatureVerificationFailed extends JOSEError {
26053
26053
  }
26054
26054
  _defineProperty(JWSSignatureVerificationFailed, "code", 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED');
26055
26055
 
26056
- 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 i=new Uint8Array(0);class r{static getFullOptions(e){return s({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{},onInterruption:()=>{}},e)}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=false,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});}handleUserTranscript(e){this.options.onMessage&&this.options.onMessage({source:"user",role:"user",message:e.user_transcription_event.user_transcript});}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:false});}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:true});}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:true});}}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 i}getOutputByteFrequencyData(){return i}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.13.0";function d(e){return !!e.type}const h="conversation_initiation_client_data";function p(e){var t;const n={type:h};var o,a,s,i,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==(a=e.overrides.agent)?void 0:a.firstMessage,language:null==(s=e.overrides.agent)?void 0:s.language},tts:{voice_id:null==(i=e.overrides.tts)?void 0:i.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,a;const s=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let i;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||u,c=(null==(a=e.overrides)||null==(a=a.client)?void 0:a.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";i=`${e.signedUrl}${t}source=${c}&version=${r}`;}else i=`${s}/v1/convai/conversation?agent_id=${e.agentId}&source=${c}&version=${r}`;const h=["convai"];e.authorization&&h.push(`bearer.${e.authorization}`),t=new WebSocket(i,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:true}),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:true});}),{conversation_id:f,agent_output_audio_format:_,user_input_audio_format:w}=g,b=l(null!=w?w:"pcm_16000"),S=l(_);return new v(t,f,b,S)}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 a=_.get(e);if(a)return n.addModule(a);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"}),i=URL.createObjectURL(s);try{return await n.addModule(i),void _.set(e,i)}catch(e){URL.revokeObjectURL(i);}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 / 4;\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 S extends c{constructor(e,t,n,o,a={}){super(a),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=false,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 a,s,i;const e=(null==(a=n.overrides)||null==(a=a.client)?void 0:a.version)||u,t=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk",c=`${r=null!=(i=n.origin)?i:"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 Room;try{const e=`room_${Date.now()}`,a=l("pcm_48000"),s=l("pcm_48000"),i=new S(c,e,a,s,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var d;await c.connect(r,o),await new Promise(e=>{if(i.isConnected)e();else {const n=()=>{c.off(RoomEvent.Connected,n),e();};c.on(RoomEvent.Connected,n);}}),c.name&&(i.conversationId=(null==(d=c.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:d[0])||c.name),n.textOnly||await c.localParticipant.setMicrophoneEnabled(true);const u=p(n);return i.debug({type:h,message:u}),await i.sendMessage(u),i}catch(e){throw await c.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(RoomEvent.Connected,async function(){e.isConnected=true,console.info("WebRTC room connected");}),this.room.on(RoomEvent.Disconnected,e=>{this.isConnected=false,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})});}),this.room.on(RoomEvent.ConnectionStateChanged,e=>{e===ConnectionState.Disconnected&&(this.isConnected=false,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}));}),this.room.on(RoomEvent.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(RoomEvent.TrackSubscribed,async function(t,n,a){if(t.kind===Track.Kind.Audio&&a.identity.includes("agent")){const n=t,o=n.attach();if(o.autoplay=true,o.controls=false,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(RoomEvent.ActiveSpeakersChanged,async function(t){e.updateMode(t.length>0&&t[0].identity.startsWith("agent")?"speaking":"listening");}),this.room.on(RoomEvent.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:true});}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(Track.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 a=new AudioWorkletNode(t,"rawAudioProcessor");this.outputAnalyser.connect(a),a.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),a.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(a);}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(Track.Source.Microphone);null!=t&&t.track&&(await t.track.stop(),await this.room.localParticipant.unpublishTrack(t.track));const n={deviceId:{exact:e},echoCancellation:true,noiseSuppression:true,autoGainControl:true,channelCount:{ideal:1}},s=await createLocalAudioTrack(n);await this.room.localParticipant.publishTrack(s,{name:"microphone",source:Track.Source.Microphone});}catch(e){console.error("Failed to change input device:",e);try{await this.room.localParticipant.setMicrophoneEnabled(true);}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 y(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 S.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function k(){return ["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function E(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(k()){var o;t=null!=(o=e.ios)?o:t;}t>0&&await new Promise(e=>setTimeout(e,t));}class C extends r{static async startSession(e){const t=r.getFullOptions(e);t.onStatusChange&&t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:false}),t.onModeChange&&t.onModeChange({mode:"listening"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:false});let n=null;try{return await E(t.connectionDelay),n=await y(e),new C(t,n)}catch(e){var o;throw t.onStatusChange&&t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}const R={echoCancellation:true,noiseSuppression:true,autoGainControl:true,channelCount:{ideal:1}};class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:a,libsampleratePath:i}){let r=null,c=null;try{const l=s({sampleRate:{ideal:e}},R);if(k()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(l.deviceId={ideal:e.deviceId});}o&&(l.deviceId=M.getDeviceIdConstraint(o));const u=navigator.mediaDevices.getSupportedConstraints().sampleRate;r=new window.AudioContext(u?{sampleRate:e}:{});const d=r.createAnalyser();if(!u){const e=i||"https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js";await r.audioWorklet.addModule(e);}await b(r.audioWorklet,null==a?void 0:a.rawAudioProcessor);const h=s({voiceIsolation:true},l);c=await navigator.mediaDevices.getUserMedia({audio:h});const p=r.createMediaStreamSource(c),m=new AudioWorkletNode(r,"rawAudioProcessor");return m.port.postMessage({type:"setFormat",format:t,sampleRate:e}),p.connect(d),d.connect(m),await r.resume(),new M(r,d,m,c,p)}catch(e){var l,u;throw null==(l=c)||l.getTracks().forEach(e=>{e.stop();}),null==(u=r)||u.close(),e}}static getDeviceIdConstraint(e){if(e)return k()?{ideal:e}:{exact:e}}constructor(e,t,n,o,a){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.mediaStreamSource=void 0,this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o,this.mediaStreamSource=a;}async close(){this.inputStream.getTracks().forEach(e=>{e.stop();}),this.mediaStreamSource.disconnect(),await this.context.close();}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e});}async setInputDevice(e){try{const t=s({},R);e&&(t.deviceId=M.getDeviceIdConstraint(e));const n=s({voiceIsolation:true},t),o=await navigator.mediaDevices.getUserMedia({audio:n});this.inputStream.getTracks().forEach(e=>{e.stop();}),this.mediaStreamSource.disconnect(),this.inputStream=o,this.mediaStreamSource=this.context.createMediaStreamSource(o),this.mediaStreamSource.connect(this.analyser);}catch(e){throw console.error("Failed to switch input device:",e),e}}}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 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 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 I{static async create({sampleRate:e,format:t,outputDeviceId:n,workletPaths:o}){let a=null,s=null;try{a=new AudioContext({sampleRate:e});const i=a.createAnalyser(),r=a.createGain();s=new Audio,s.src="",s.load(),s.autoplay=true,s.style.display="none",document.body.appendChild(s);const c=a.createMediaStreamDestination();s.srcObject=c.stream,r.connect(i),i.connect(c),await A(a.audioWorklet,null==o?void 0:o.audioConcatProcessor);const l=new AudioWorkletNode(a,"audioConcatProcessor");return l.port.postMessage({type:"setFormat",format:t}),l.connect(r),await a.resume(),n&&s.setSinkId&&await s.setSinkId(n),new I(a,i,r,l,s)}catch(e){var i,r;throw null!=(i=s)&&i.parentNode&&s.parentNode.removeChild(s),null==(r=s)||r.pause(),a&&"closed"!==a.state&&await a.close(),e}}constructor(e,t,n,o,a){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=a;}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 D 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:false});let o=null,a=null,i=null,c=null,l=null;(null==(t=e.useWakeLock)||t)&&(l=await D.requestWakeLock());try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:true}),await E(n.connectionDelay),a=await y(e),[o,i]=await Promise.all([M.create(s({},a.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices,inputDeviceId:e.inputDeviceId,workletPaths:e.workletPaths,libsampleratePath:e.libsampleratePath})),I.create(s({},a.outputFormat,{outputDeviceId:e.outputDeviceId,workletPaths:e.workletPaths}))]),null==(u=c)||u.getTracks().forEach(e=>{e.stop();}),c=null,new D(n,a,o,i,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=a)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=i)?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,a){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 S?this.connection.setAudioVolume(t):this.output.gain.gain.value=t;},this.input=n,this.output=o,this.wakeLock=a,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage,a&&(this.visibilityChangeHandler=()=>{var e;"visible"===document.visibilityState&&null!=(e=this.wakeLock)&&e.released&&D.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 S||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 S?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 S?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 S&&await this.connection.setAudioInputDevice(o||""),await this.input.close();const a=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});return this.input=a,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 S&&await this.connection.setAudioOutputDevice(n||""),await this.output.close();const o=await I.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}}}var P;!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";}(P||(P={}));var U,L;!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";}(L||(L={}));class W extends r{static startSession(e){return e.textOnly?C.startSession(e):D.startSession(e)}}
26056
+ 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 i=new Uint8Array(0);class r{static getFullOptions(e){return s({clientTools:{},onConnect:()=>{},onDebug:()=>{},onDisconnect:()=>{},onError:()=>{},onMessage:()=>{},onAudio:()=>{},onModeChange:()=>{},onStatusChange:()=>{},onCanSendFeedbackChange:()=>{},onInterruption:()=>{}},e)}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=false,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});}handleUserTranscript(e){this.options.onMessage&&this.options.onMessage({source:"user",role:"user",message:e.user_transcription_event.user_transcript});}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:false});}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:true});}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:true});}}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 i}getOutputByteFrequencyData(){return i}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.13.0";function d(e){return !!e.type}const h="conversation_initiation_client_data";function p(e){var t;const n={type:h};var o,a,s,i,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==(a=e.overrides.agent)?void 0:a.firstMessage,language:null==(s=e.overrides.agent)?void 0:s.language},tts:{voice_id:null==(i=e.overrides.tts)?void 0:i.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,a;const s=null!=(n=e.origin)?n:"wss://api.elevenlabs.io";let i;const r=(null==(o=e.overrides)||null==(o=o.client)?void 0:o.version)||u,c=(null==(a=e.overrides)||null==(a=a.client)?void 0:a.source)||"js_sdk";if(e.signedUrl){const t=e.signedUrl.includes("?")?"&":"?";i=`${e.signedUrl}${t}source=${c}&version=${r}`;}else i=`${s}/v1/convai/conversation?agent_id=${e.agentId}&source=${c}&version=${r}`;const h=["convai"];e.authorization&&h.push(`bearer.${e.authorization}`),t=new WebSocket(i,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:true}),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:true});}),{conversation_id:f,agent_output_audio_format:_,user_input_audio_format:w}=g,b=l(null!=w?w:"pcm_16000"),S=l(_);return new v(t,f,b,S)}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 a=_.get(e);if(a)return n.addModule(a);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"}),i=URL.createObjectURL(s);try{return await n.addModule(i),void _.set(e,i)}catch(e){URL.revokeObjectURL(i);}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 / 4;\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 S extends c{constructor(e,t,n,o,a={}){super(a),this.conversationId=void 0,this.inputFormat=void 0,this.outputFormat=void 0,this.room=void 0,this.isConnected=false,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 a,s,i;const e=(null==(a=n.overrides)||null==(a=a.client)?void 0:a.version)||u,t=(null==(s=n.overrides)||null==(s=s.client)?void 0:s.source)||"js_sdk",c=`${r=null!=(i=n.origin)?i:"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 Room;try{const e=`room_${Date.now()}`,a=l("pcm_48000"),s=l("pcm_48000"),i=new S(c,e,a,s,n),r=n.livekitUrl||"wss://livekit.rtc.elevenlabs.io";var d;await c.connect(r,o),await new Promise(e=>{if(i.isConnected)e();else {const n=()=>{c.off(RoomEvent.Connected,n),e();};c.on(RoomEvent.Connected,n);}}),c.name&&(i.conversationId=(null==(d=c.name.match(/(conv_[a-zA-Z0-9]+)/))?void 0:d[0])||c.name),n.textOnly||await c.localParticipant.setMicrophoneEnabled(true);const u=p(n);return i.debug({type:h,message:u}),await i.sendMessage(u),i}catch(e){throw await c.disconnect(),e}}setupRoomEventListeners(){var e=this;this.room.on(RoomEvent.Connected,async function(){e.isConnected=true,console.info("WebRTC room connected");}),this.room.on(RoomEvent.Disconnected,e=>{this.isConnected=false,this.disconnect({reason:"agent",context:new CloseEvent("close",{reason:null==e?void 0:e.toString()})});}),this.room.on(RoomEvent.ConnectionStateChanged,e=>{e===ConnectionState.Disconnected&&(this.isConnected=false,this.disconnect({reason:"error",message:`LiveKit connection state changed to ${e}`,context:new Event("connection_state_changed")}));}),this.room.on(RoomEvent.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(RoomEvent.TrackSubscribed,async function(t,n,a){if(t.kind===Track.Kind.Audio&&a.identity.includes("agent")){const n=t,o=n.attach();if(o.autoplay=true,o.controls=false,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(RoomEvent.ActiveSpeakersChanged,async function(t){e.updateMode(t.length>0&&t[0].identity.startsWith("agent")?"speaking":"listening");}),this.room.on(RoomEvent.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:true});}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(Track.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 a=new AudioWorkletNode(t,"rawAudioProcessor");this.outputAnalyser.connect(a),a.port.postMessage({type:"setFormat",format:this.outputFormat.format,sampleRate:this.outputFormat.sampleRate}),a.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(a);}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(Track.Source.Microphone);null!=t&&t.track&&(await t.track.stop(),await this.room.localParticipant.unpublishTrack(t.track));const n={deviceId:{exact:e},echoCancellation:true,noiseSuppression:true,autoGainControl:true,channelCount:{ideal:1}},s=await createLocalAudioTrack(n);await this.room.localParticipant.publishTrack(s,{name:"microphone",source:Track.Source.Microphone});}catch(e){console.error("Failed to change input device:",e);try{await this.room.localParticipant.setMicrophoneEnabled(true);}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 y(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 S.create(e);default:throw new Error(`Unknown connection type: ${t}`)}}function k(){return ["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"ontouchend"in document}async function E(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(k()){var o;t=null!=(o=e.ios)?o:t;}t>0&&await new Promise(e=>setTimeout(e,t));}class C extends r{static async startSession(e){const t=r.getFullOptions(e);t.onStatusChange&&t.onStatusChange({status:"connecting"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:false}),t.onModeChange&&t.onModeChange({mode:"listening"}),t.onCanSendFeedbackChange&&t.onCanSendFeedbackChange({canSendFeedback:false});let n=null;try{return await E(t.connectionDelay),n=await y(e),new C(t,n)}catch(e){var o;throw t.onStatusChange&&t.onStatusChange({status:"disconnected"}),null==(o=n)||o.close(),e}}}const R={echoCancellation:true,noiseSuppression:true,autoGainControl:true,channelCount:{ideal:1}};class M{static async create({sampleRate:e,format:t,preferHeadphonesForIosDevices:n,inputDeviceId:o,workletPaths:a,libsampleratePath:i}){let r=null,c=null;try{const l=s({sampleRate:{ideal:e}},R);if(k()&&n){const e=(await window.navigator.mediaDevices.enumerateDevices()).find(e=>"audioinput"===e.kind&&["airpod","headphone","earphone"].find(t=>e.label.toLowerCase().includes(t)));e&&(l.deviceId={ideal:e.deviceId});}o&&(l.deviceId=M.getDeviceIdConstraint(o));const u=navigator.mediaDevices.getSupportedConstraints().sampleRate;r=new window.AudioContext(u?{sampleRate:e}:{});const d=r.createAnalyser();if(!u){const e=i||"https://cdn.jsdelivr.net/npm/@alexanderolsen/libsamplerate-js@2.1.2/dist/libsamplerate.worklet.js";await r.audioWorklet.addModule(e);}await b(r.audioWorklet,null==a?void 0:a.rawAudioProcessor);const h=s({voiceIsolation:true},l);c=await navigator.mediaDevices.getUserMedia({audio:h});const p=r.createMediaStreamSource(c),m=new AudioWorkletNode(r,"rawAudioProcessor");return m.port.postMessage({type:"setFormat",format:t,sampleRate:e}),p.connect(d),d.connect(m),await r.resume(),new M(r,d,m,c,p)}catch(e){var l,u;throw null==(l=c)||l.getTracks().forEach(e=>{e.stop();}),null==(u=r)||u.close(),e}}static getDeviceIdConstraint(e){if(e)return k()?{ideal:e}:{exact:e}}constructor(e,t,n,o,a){this.context=void 0,this.analyser=void 0,this.worklet=void 0,this.inputStream=void 0,this.mediaStreamSource=void 0,this.context=e,this.analyser=t,this.worklet=n,this.inputStream=o,this.mediaStreamSource=a;}async close(){this.inputStream.getTracks().forEach(e=>{e.stop();}),this.mediaStreamSource.disconnect(),await this.context.close();}setMuted(e){this.worklet.port.postMessage({type:"setMuted",isMuted:e});}async setInputDevice(e){try{const t=s({},R);e&&(t.deviceId=M.getDeviceIdConstraint(e));const n=s({voiceIsolation:true},t),o=await navigator.mediaDevices.getUserMedia({audio:n});this.inputStream.getTracks().forEach(e=>{e.stop();}),this.mediaStreamSource.disconnect(),this.inputStream=o,this.mediaStreamSource=this.context.createMediaStreamSource(o),this.mediaStreamSource.connect(this.analyser);}catch(e){throw console.error("Failed to switch input device:",e),e}}}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 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 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 I{static async create({sampleRate:e,format:t,outputDeviceId:n,workletPaths:o}){let a=null,s=null;try{a=new AudioContext({sampleRate:e});const i=a.createAnalyser(),r=a.createGain();s=new Audio,s.src="",s.load(),s.autoplay=true,s.style.display="none",document.body.appendChild(s);const c=a.createMediaStreamDestination();s.srcObject=c.stream,r.connect(i),i.connect(c),await A(a.audioWorklet,null==o?void 0:o.audioConcatProcessor);const l=new AudioWorkletNode(a,"audioConcatProcessor");return l.port.postMessage({type:"setFormat",format:t}),l.connect(r),await a.resume(),n&&s.setSinkId&&await s.setSinkId(n),new I(a,i,r,l,s)}catch(e){var i,r;throw null!=(i=s)&&i.parentNode&&s.parentNode.removeChild(s),null==(r=s)||r.pause(),a&&"closed"!==a.state&&await a.close(),e}}constructor(e,t,n,o,a){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=a;}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 D 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:false});let o=null,a=null,i=null,c=null,l=null;(null==(t=e.useWakeLock)||t)&&(l=await D.requestWakeLock());try{var u;return c=await navigator.mediaDevices.getUserMedia({audio:true}),await E(n.connectionDelay),a=await y(e),[o,i]=await Promise.all([M.create(s({},a.inputFormat,{preferHeadphonesForIosDevices:e.preferHeadphonesForIosDevices,inputDeviceId:e.inputDeviceId,workletPaths:e.workletPaths,libsampleratePath:e.libsampleratePath})),I.create(s({},a.outputFormat,{outputDeviceId:e.outputDeviceId,workletPaths:e.workletPaths}))]),null==(u=c)||u.getTracks().forEach(e=>{e.stop();}),c=null,new D(n,a,o,i,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=a)||h.close(),await(null==(p=o)?void 0:p.close()),await(null==(m=i)?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,a){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 S?this.connection.setAudioVolume(t):this.output.gain.gain.value=t;},this.input=n,this.output=o,this.wakeLock=a,this.input.worklet.port.onmessage=this.onInputWorkletMessage,this.output.worklet.port.onmessage=this.onOutputWorkletMessage,a&&(this.visibilityChangeHandler=()=>{var e;"visible"===document.visibilityState&&null!=(e=this.wakeLock)&&e.released&&D.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 S||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 S?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 S?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 S&&await this.connection.setAudioInputDevice(o||""),await this.input.close();const a=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});return this.input=a,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 S&&await this.connection.setAudioOutputDevice(n||""),await this.output.close();const o=await I.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}}}var P;!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";}(P||(P={}));var U,L;!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";}(L||(L={}));
26057
26057
 
26058
- const stayliftWidgetCss = () => `:host{--sl-primary:#6366f1;--sl-bg:#18181b;--sl-text:#ffffff;--sl-muted:#a1a1aa;--sl-border:#27272a;--sl-surface:#27272a;--sl-success:#22c55e;--sl-danger:#ef4444;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:14px;line-height:1.5;color:var(--sl-text)}.sl-widget *{box-sizing:border-box}.sl-floating{position:fixed;z-index:999999}.sl-x-left{left:20px;right:auto}.sl-x-center{left:50%;right:auto;transform:translateX(-50%)}.sl-x-right{right:20px;left:auto}.sl-y-top{top:20px;bottom:auto}.sl-y-bottom{bottom:20px;top:auto}.sl-fab{width:64px;height:64px;border-radius:50%;border:1px solid var(--sl-border);background:var(--sl-bg);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);transition:transform 0.2s, box-shadow 0.2s;padding:8px}.sl-fab:hover{transform:scale(1.05);box-shadow:0 12px 40px rgba(0, 0, 0, 0.5)}.sl-fab-pill{display:flex;align-items:center;gap:16px;padding:12px 16px 12px 12px;background:var(--sl-bg);border:1px solid var(--sl-border);border-radius:32px;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);animation:sl-fade-in 0.2s ease}.sl-fab-avatar{width:48px;height:48px;border-radius:50%;overflow:hidden;flex-shrink:0;border:1px solid var(--sl-border)}.sl-fab-avatar-img{width:100%;height:100%;object-fit:cover}.sl-fab-content{display:flex;flex-direction:column;gap:8px}.sl-fab-prompt{font-size:14px;font-weight:500;color:var(--sl-text);white-space:nowrap}.sl-fab-btn{padding:8px 20px;border:none;border-radius:20px;background:var(--sl-primary);color:white;font-size:13px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-fab-btn:hover{filter:brightness(1.1);transform:translateY(-1px)}.sl-card{width:400px;height:380px;max-height:calc(100vh - 100px);background:var(--sl-bg);border-radius:16px;border:1px solid var(--sl-border);box-shadow:0 20px 60px rgba(0, 0, 0, 0.5);display:flex;flex-direction:column;overflow:hidden;animation:sl-fade-in 0.2s ease}.sl-inline{width:100%;max-width:500px;height:380px;background:var(--sl-bg);border-radius:16px;border:1px solid var(--sl-border);display:flex;flex-direction:column;overflow:hidden}@keyframes sl-fade-in{from{opacity:0;transform:translateY(10px) scale(0.98)}to{opacity:1;transform:translateY(0) scale(1)}}.sl-header{display:flex;align-items:center;justify-content:space-between;padding:16px;flex-shrink:0;border-bottom:1px solid var(--sl-border)}.sl-header-left{display:flex;align-items:center;gap:16px}.sl-orb-ring{width:40px;height:40px;border-radius:50%;border:1px solid var(--sl-border);overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center}.sl-header-avatar-img{width:100%;height:100%;object-fit:cover;object-position:center center;display:block}.sl-header-text{display:flex;flex-direction:column;gap:2px}.sl-title{font-size:14px;font-weight:500;color:var(--sl-text);line-height:1.2}.sl-subtitle{font-size:12px;color:var(--sl-muted);line-height:1.2}.sl-error{color:var(--sl-danger)}.sl-connected{color:var(--sl-success)}.sl-shimmer{background:linear-gradient(90deg, var(--sl-muted) 0%, var(--sl-text) 50%, var(--sl-muted) 100%);background-size:200% 100%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:sl-shimmer 1.5s infinite;text-transform:capitalize}@keyframes sl-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}.sl-header-right{display:flex;align-items:center;gap:12px}.sl-dot{width:8px;height:8px;border-radius:50%;background:var(--sl-muted);opacity:0.4;transition:all 0.3s}.sl-dot--active{background:var(--sl-success);opacity:1;box-shadow:0 0 8px rgba(34, 197, 94, 0.5)}.sl-dot--pulse{background:var(--sl-text);opacity:0.4;animation:sl-pulse 1s infinite}@keyframes sl-pulse{0%,100%{opacity:0.4}50%{opacity:0.8}}.sl-close{width:32px;height:32px;border-radius:50%;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s}.sl-close:hover{background:var(--sl-surface);color:var(--sl-text)}.sl-content{flex:1;overflow-y:auto;padding:24px;display:flex;flex-direction:column;gap:8px}.sl-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px}.sl-empty-title{margin:0;font-size:15px;font-weight:500;color:var(--sl-text)}.sl-empty-desc{margin:0;font-size:13px;color:var(--sl-muted)}.sl-msg{display:flex;flex-direction:column;gap:4px}.sl-msg-row{display:flex;align-items:flex-end;gap:8px}.sl-msg--user .sl-msg-row{justify-content:flex-end}.sl-msg--assistant .sl-msg-row{justify-content:flex-start}.sl-msg-bubble{max-width:85%;padding:10px 14px;border-radius:12px;font-size:14px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}.sl-msg--user .sl-msg-bubble{background:var(--sl-primary);color:white;border-bottom-right-radius:4px}.sl-msg--assistant .sl-msg-bubble{background:var(--sl-surface);color:var(--sl-text);border-bottom-left-radius:4px}.sl-msg-orb{width:24px;height:24px;border-radius:50%;border:1px solid var(--sl-border);overflow:hidden;flex-shrink:0}.sl-msg-actions{display:flex;gap:4px;padding-left:32px}.sl-action{width:28px;height:28px;border-radius:6px;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s}.sl-action:hover{background:var(--sl-surface);color:var(--sl-text)}.sl-footer{padding:16px;border-top:1px solid var(--sl-border);flex-shrink:0}.sl-branding{text-align:center;margin-bottom:12px}.sl-branding a{font-size:11px;color:var(--sl-muted);text-decoration:none;opacity:0.6;transition:opacity 0.2s}.sl-branding a:hover{opacity:1}.sl-input-row{display:flex;align-items:center;gap:8px}.sl-input{flex:1;height:36px;padding:0 12px;border:none;border-radius:8px;background:transparent;color:var(--sl-text);font-size:14px;outline:none}.sl-input::placeholder{color:var(--sl-muted)}.sl-input:disabled{opacity:0.5;cursor:not-allowed}.sl-btn{width:36px;height:36px;border-radius:50%;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s;flex-shrink:0}.sl-btn:hover:not(:disabled){background:var(--sl-surface);color:var(--sl-text)}.sl-btn:disabled{opacity:0.3;cursor:not-allowed}.sl-btn--end{background:var(--sl-surface);color:var(--sl-text)}.sl-btn--end:hover:not(:disabled){background:var(--sl-danger);color:white}.sl-terms{display:flex;flex-direction:column;height:100%;padding:24px}.sl-terms-content{flex:1;display:flex;flex-direction:column;justify-content:center;gap:16px}.sl-terms-title{margin:0;font-size:18px;font-weight:600;color:var(--sl-text)}.sl-terms-text{margin:0;font-size:14px;line-height:1.6;color:var(--sl-text)}.sl-terms-warning{margin:0;font-size:13px;line-height:1.5;color:var(--sl-muted);font-style:italic}.sl-terms-actions{display:flex;gap:12px;padding-top:16px;border-top:1px solid var(--sl-border)}.sl-terms-btn{flex:1;padding:12px 20px;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-terms-btn--decline{background:var(--sl-surface);color:var(--sl-muted)}.sl-terms-btn--decline:hover{background:var(--sl-border);color:var(--sl-text)}.sl-terms-btn--agree{background:var(--sl-primary);color:white}.sl-terms-btn--agree:hover{filter:brightness(1.1)}@media (max-width: 480px){.sl-card{width:calc(100vw - 32px);max-width:400px}.sl-floating.sl-x-left,.sl-floating.sl-x-right{left:16px;right:16px}.sl-floating.sl-x-center{left:16px;right:16px;transform:none}.sl-floating.sl-y-bottom{bottom:16px}.sl-floating.sl-y-top{top:16px}}`;
26058
+ const stayliftWidgetCss = () => `:host{--sl-primary:#6366f1;--sl-bg:#18181b;--sl-text:#ffffff;--sl-muted:#a1a1aa;--sl-border:#27272a;--sl-surface:#27272a;--sl-success:#22c55e;--sl-danger:#ef4444;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:14px;line-height:1.5;color:var(--sl-text)}.sl-widget *{box-sizing:border-box}.sl-floating{position:fixed;z-index:999999}.sl-x-left{left:20px;right:auto}.sl-x-center{left:50%;right:auto;transform:translateX(-50%)}.sl-x-right{right:20px;left:auto}.sl-y-top{top:20px;bottom:auto}.sl-y-bottom{bottom:20px;top:auto}.sl-fab{width:64px;height:64px;border-radius:50%;border:1px solid var(--sl-border);background:var(--sl-bg);cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);transition:transform 0.2s, box-shadow 0.2s;padding:8px}.sl-fab:hover{transform:scale(1.05);box-shadow:0 12px 40px rgba(0, 0, 0, 0.5)}.sl-fab-pill{display:flex;align-items:center;gap:16px;padding:12px 16px 12px 12px;background:var(--sl-bg);border:1px solid var(--sl-border);border-radius:32px;box-shadow:0 8px 32px rgba(0, 0, 0, 0.4);animation:sl-fade-in 0.2s ease}.sl-fab-avatar{width:48px;height:48px;border-radius:50%;overflow:hidden;flex-shrink:0;border:1px solid var(--sl-border)}.sl-fab-avatar-img{width:100%;height:100%;object-fit:cover}.sl-fab-content{display:flex;flex-direction:column;gap:8px}.sl-fab-prompt{font-size:14px;font-weight:500;color:var(--sl-text);white-space:nowrap}.sl-fab-btn{padding:8px 20px;border:none;border-radius:20px;background:var(--sl-primary);color:white;font-size:13px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-fab-btn:hover{filter:brightness(1.1);transform:translateY(-1px)}.sl-card{width:400px;height:380px;max-height:calc(100vh - 100px);background:var(--sl-bg);border-radius:16px;border:1px solid var(--sl-border);box-shadow:0 20px 60px rgba(0, 0, 0, 0.5);display:flex;flex-direction:column;overflow:hidden;animation:sl-fade-in 0.2s ease}.sl-inline{width:100%;max-width:500px;height:380px;background:var(--sl-bg);border-radius:16px;border:1px solid var(--sl-border);display:flex;flex-direction:column;overflow:hidden}@keyframes sl-fade-in{from{opacity:0;transform:translateY(10px) scale(0.98)}to{opacity:1;transform:translateY(0) scale(1)}}.sl-header{display:flex;align-items:center;justify-content:space-between;padding:16px;flex-shrink:0;border-bottom:1px solid var(--sl-border)}.sl-header-left{display:flex;align-items:center;gap:16px}.sl-orb-ring{width:40px;height:40px;border-radius:50%;border:1px solid var(--sl-border);overflow:hidden;flex-shrink:0;display:flex;align-items:center;justify-content:center}.sl-header-avatar-img{width:100%;height:100%;object-fit:cover;object-position:center center;display:block}.sl-header-text{display:flex;flex-direction:column;gap:2px}.sl-title{font-size:14px;font-weight:500;color:var(--sl-text);line-height:1.2}.sl-subtitle{font-size:12px;color:var(--sl-muted);line-height:1.2}.sl-error{color:var(--sl-danger)}.sl-connected{color:var(--sl-success)}.sl-shimmer{background:linear-gradient(90deg, var(--sl-muted) 0%, var(--sl-text) 50%, var(--sl-muted) 100%);background-size:200% 100%;-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:sl-shimmer 1.5s infinite;text-transform:capitalize}@keyframes sl-shimmer{0%{background-position:200% 0}100%{background-position:-200% 0}}.sl-header-right{display:flex;align-items:center;gap:12px}.sl-dot{width:8px;height:8px;border-radius:50%;background:var(--sl-muted);opacity:0.4;transition:all 0.3s}.sl-dot--active{background:var(--sl-success);opacity:1;box-shadow:0 0 8px rgba(34, 197, 94, 0.5)}.sl-dot--pulse{background:var(--sl-text);opacity:0.4;animation:sl-pulse 1s infinite}@keyframes sl-pulse{0%,100%{opacity:0.4}50%{opacity:0.8}}.sl-close{width:32px;height:32px;border-radius:50%;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s}.sl-close:hover{background:var(--sl-surface);color:var(--sl-text)}.sl-content{flex:1;overflow-y:auto;padding:12px 24px 24px;display:flex;flex-direction:column;gap:8px}.sl-empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:12px}.sl-empty-title{margin:0;font-size:15px;font-weight:500;color:var(--sl-text)}.sl-empty-desc{margin:0;font-size:13px;color:var(--sl-muted)}.sl-mode-toggle{display:flex;gap:8px;margin-bottom:12px;padding:4px;background:var(--sl-surface);border-radius:10px}.sl-mode-btn{display:flex;align-items:center;gap:6px;padding:8px 16px;border:none;border-radius:8px;background:transparent;color:var(--sl-muted);font-size:13px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-mode-btn:hover{color:var(--sl-text)}.sl-mode-btn--active{background:var(--sl-bg);color:var(--sl-text);box-shadow:0 1px 3px rgba(0, 0, 0, 0.2)}.sl-voice-controls{display:flex;justify-content:center}.sl-voice-btn{display:flex;align-items:center;justify-content:center;gap:10px;width:100%;padding:14px 24px;border:none;border-radius:12px;background:var(--sl-primary);color:white;font-size:14px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-voice-btn:hover:not(:disabled){filter:brightness(1.1);transform:translateY(-1px)}.sl-voice-btn:disabled{opacity:0.6;cursor:not-allowed}.sl-voice-btn--end{background:var(--sl-danger)}.sl-voice-btn--end:hover:not(:disabled){filter:brightness(1.1)}.sl-msg{display:flex;flex-direction:column;gap:4px}.sl-msg-row{display:flex;align-items:flex-end;gap:8px}.sl-msg--user .sl-msg-row{justify-content:flex-end}.sl-msg--assistant .sl-msg-row{justify-content:flex-start}.sl-msg-bubble{max-width:85%;padding:10px 14px;border-radius:12px;font-size:14px;line-height:1.5;white-space:pre-wrap;word-wrap:break-word}.sl-msg--user .sl-msg-bubble{background:var(--sl-primary);color:white;border-bottom-right-radius:4px}.sl-msg--assistant .sl-msg-bubble{background:var(--sl-surface);color:var(--sl-text);border-bottom-left-radius:4px}.sl-msg-actions{display:flex;gap:4px;padding-left:32px}.sl-action{width:28px;height:28px;border-radius:6px;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s}.sl-action:hover{background:var(--sl-surface);color:var(--sl-text)}.sl-footer{padding:16px;border-top:1px solid var(--sl-border);flex-shrink:0}.sl-branding{text-align:center;margin-top:8px;padding-top:8px}.sl-branding a{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--sl-muted);text-decoration:none;opacity:0.6;transition:opacity 0.2s}.sl-branding a:hover{opacity:1}.sl-branding-logo{width:16px;height:16px;border-radius:3px;object-fit:cover}.sl-input-row{display:flex;align-items:center;gap:8px}.sl-input{flex:1;height:36px;padding:0 12px;border:none;border-radius:8px;background:transparent;color:var(--sl-text);font-size:14px;outline:none}.sl-input::placeholder{color:var(--sl-muted)}.sl-input:disabled{opacity:0.5;cursor:not-allowed}.sl-btn{width:36px;height:36px;border-radius:50%;border:none;background:transparent;color:var(--sl-muted);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.2s;flex-shrink:0}.sl-btn:hover:not(:disabled){background:var(--sl-surface);color:var(--sl-text)}.sl-btn:disabled{opacity:0.3;cursor:not-allowed}.sl-btn--end{background:var(--sl-surface);color:var(--sl-text)}.sl-btn--end:hover:not(:disabled){background:var(--sl-danger);color:white}.sl-terms{display:flex;flex-direction:column;height:100%;padding:24px}.sl-terms-content{flex:1;display:flex;flex-direction:column;justify-content:center;gap:16px}.sl-terms-title{margin:0;font-size:18px;font-weight:600;color:var(--sl-text)}.sl-terms-text{margin:0;font-size:14px;line-height:1.6;color:var(--sl-text)}.sl-terms-warning{margin:0;font-size:13px;line-height:1.5;color:var(--sl-muted);font-style:italic}.sl-terms-actions{display:flex;gap:12px;padding-top:16px;border-top:1px solid var(--sl-border)}.sl-terms-btn{flex:1;padding:12px 20px;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:all 0.2s}.sl-terms-btn--decline{background:var(--sl-surface);color:var(--sl-muted)}.sl-terms-btn--decline:hover{background:var(--sl-border);color:var(--sl-text)}.sl-terms-btn--agree{background:var(--sl-primary);color:white}.sl-terms-btn--agree:hover{filter:brightness(1.1)}@media (max-width: 480px){.sl-card{width:calc(100vw - 32px);max-width:400px}.sl-floating.sl-x-left,.sl-floating.sl-x-right{left:16px;right:16px}.sl-floating.sl-x-center{left:16px;right:16px;transform:none}.sl-floating.sl-y-bottom{bottom:16px}.sl-floating.sl-y-top{top:16px}}`;
26059
26059
 
26060
26060
  const StayliftWidget = class {
26061
26061
  constructor(hostRef) {
@@ -26086,6 +26086,7 @@ const StayliftWidget = class {
26086
26086
  this.messages = [];
26087
26087
  this.inputText = '';
26088
26088
  this.copiedIndex = null;
26089
+ this.selectedMode = 'text';
26089
26090
  // ============ PRIVATE ============
26090
26091
  this.conversation = null;
26091
26092
  this.volumeInterval = null;
@@ -26189,12 +26190,17 @@ const StayliftWidget = class {
26189
26190
  const timeoutId = setTimeout(() => {
26190
26191
  rejectConnected(new Error('Connection timeout'));
26191
26192
  }, CONNECTION_TIMEOUT);
26192
- this.conversation = await W.startSession({
26193
+ // Use TextConversation for text-only mode (no mic access), VoiceConversation for voice
26194
+ const ConversationClass = textOnly ? C : D;
26195
+ this.conversation = await ConversationClass.startSession({
26193
26196
  agentId: this.agentId,
26194
26197
  connectionType: textOnly ? 'websocket' : 'webrtc',
26195
26198
  overrides: {
26196
- conversation: { textOnly },
26197
- agent: { firstMessage: textOnly ? '' : undefined },
26199
+ conversation: {
26200
+ textOnly,
26201
+ // Enable streaming text responses for text mode
26202
+ client_events: textOnly ? ['agent_response', 'agent_chat_response_part', 'user_transcript'] : undefined,
26203
+ },
26198
26204
  },
26199
26205
  onStatusChange: (statusEvent) => {
26200
26206
  const newStatus = statusEvent.status;
@@ -26282,15 +26288,16 @@ const StayliftWidget = class {
26282
26288
  const msg = text || this.inputText.trim();
26283
26289
  if (!msg)
26284
26290
  return;
26285
- // If disconnected, start text-only session
26291
+ // If disconnected, start session based on selected mode
26286
26292
  if (this.status === 'disconnected') {
26287
26293
  const userMessage = { role: 'user', content: msg };
26288
26294
  this.inputText = '';
26289
26295
  this.pendingMessage = msg; // Store message to send after connection
26290
26296
  this.messages = [userMessage];
26291
26297
  this.scrollToBottom();
26298
+ const textOnly = this.selectedMode === 'text';
26292
26299
  try {
26293
- await this.handleStartConversation(true, true);
26300
+ await this.handleStartConversation(textOnly, true);
26294
26301
  // Message will be sent in onConnect callback
26295
26302
  }
26296
26303
  catch {
@@ -26318,6 +26325,14 @@ const StayliftWidget = class {
26318
26325
  await this.handleEndConversation();
26319
26326
  }
26320
26327
  }
26328
+ async handleTextButton() {
26329
+ if (this.status === 'disconnected' || this.status === null) {
26330
+ await this.handleStartConversation(true);
26331
+ }
26332
+ else if (this.status === 'connected') {
26333
+ await this.handleEndConversation();
26334
+ }
26335
+ }
26321
26336
  startVolumeMonitoring() {
26322
26337
  this.volumeInterval = setInterval(() => {
26323
26338
  if (this.conversation && this.status === 'connected') {
@@ -26367,6 +26382,12 @@ const StayliftWidget = class {
26367
26382
  termsWarning: 'If you do not wish to have your conversations recorded, please refrain from using this service.',
26368
26383
  termsAgree: 'Agree',
26369
26384
  termsDecline: 'Decline',
26385
+ modeText: 'Text',
26386
+ modeVoice: 'Voice',
26387
+ startVoice: 'Start Voice Call',
26388
+ endVoice: 'End Call',
26389
+ startText: 'Start Text Chat',
26390
+ endText: 'End Chat',
26370
26391
  },
26371
26392
  pl: {
26372
26393
  microphoneError: 'Proszę włączyć uprawnienia mikrofonu.',
@@ -26386,6 +26407,12 @@ const StayliftWidget = class {
26386
26407
  termsWarning: 'Jeśli nie chcesz, aby Twoje rozmowy były nagrywane, prosimy o nieużywanie tej usługi.',
26387
26408
  termsAgree: 'Zgadzam się',
26388
26409
  termsDecline: 'Odrzuć',
26410
+ modeText: 'Tekst',
26411
+ modeVoice: 'Głos',
26412
+ startVoice: 'Rozpocznij rozmowę głosową',
26413
+ endVoice: 'Zakończ',
26414
+ startText: 'Rozpocznij czat',
26415
+ endText: 'Zakończ czat',
26389
26416
  },
26390
26417
  de: {
26391
26418
  microphoneError: 'Bitte aktivieren Sie die Mikrofonberechtigung in Ihrem Browser.',
@@ -26405,6 +26432,12 @@ const StayliftWidget = class {
26405
26432
  termsWarning: 'Wenn Sie nicht möchten, dass Ihre Gespräche aufgezeichnet werden, verwenden Sie diesen Dienst bitte nicht.',
26406
26433
  termsAgree: 'Zustimmen',
26407
26434
  termsDecline: 'Ablehnen',
26435
+ modeText: 'Text',
26436
+ modeVoice: 'Stimme',
26437
+ startVoice: 'Sprachanruf starten',
26438
+ endVoice: 'Beenden',
26439
+ startText: 'Text-Chat starten',
26440
+ endText: 'Chat beenden',
26408
26441
  },
26409
26442
  };
26410
26443
  return translations[this.language]?.[key] || translations['en'][key] || key;
@@ -26444,18 +26477,18 @@ const StayliftWidget = class {
26444
26477
  '--sl-surface': theme.surface,
26445
26478
  };
26446
26479
  if (this.variant === 'inline') {
26447
- return (index.h("div", { class: "sl-widget sl-inline", style: cssVars }, this.renderCard(isCallActive, isTransitioning)));
26480
+ return (index.h("div", { class: "sl-widget sl-inline", style: cssVars }, this.renderCard(isTransitioning)));
26448
26481
  }
26449
- return (index.h("div", { class: `sl-widget sl-floating ${this.getPositionClasses()}`, style: cssVars }, this.isExpanded ? (index.h("div", { class: "sl-card" }, this.renderCard(isCallActive, isTransitioning))) : (index.h("div", { class: "sl-fab-pill" }, index.h("div", { class: "sl-fab-avatar" }, this.avatarUrl ? (index.h("img", { src: this.avatarUrl, alt: "", class: "sl-fab-avatar-img" })) : (index.h("staylift-orb", { size: 48, primaryColor: this.primaryColor, inputVolume: this.inputVolume, outputVolume: this.outputVolume, isActive: isCallActive }))), index.h("div", { class: "sl-fab-content" }, index.h("span", { class: "sl-fab-prompt" }, this.fabPrompt), index.h("button", { class: "sl-fab-btn", onClick: this.handleToggleExpand }, this.fabButtonText))))));
26482
+ return (index.h("div", { class: `sl-widget sl-floating ${this.getPositionClasses()}`, style: cssVars }, this.isExpanded ? (index.h("div", { class: "sl-card" }, this.renderCard(isTransitioning))) : (index.h("div", { class: "sl-fab-pill" }, index.h("div", { class: "sl-fab-avatar" }, this.avatarUrl ? (index.h("img", { src: this.avatarUrl, alt: "", class: "sl-fab-avatar-img" })) : (index.h("staylift-orb", { size: 48, primaryColor: this.primaryColor, inputVolume: this.inputVolume, outputVolume: this.outputVolume, isActive: isCallActive }))), index.h("div", { class: "sl-fab-content" }, index.h("span", { class: "sl-fab-prompt" }, this.fabPrompt), index.h("button", { class: "sl-fab-btn", onClick: this.handleToggleExpand }, this.fabButtonText))))));
26450
26483
  }
26451
- renderCard(isCallActive, isTransitioning) {
26484
+ renderCard(isTransitioning) {
26452
26485
  if (!this.termsAccepted) {
26453
26486
  return this.renderTerms();
26454
26487
  }
26455
26488
  return [
26456
26489
  this.renderHeader(isTransitioning),
26457
26490
  this.renderContent(),
26458
- this.renderFooter(isCallActive, isTransitioning),
26491
+ this.renderFooter(isTransitioning),
26459
26492
  ];
26460
26493
  }
26461
26494
  renderTerms() {
@@ -26467,10 +26500,15 @@ const StayliftWidget = class {
26467
26500
  renderContent() {
26468
26501
  const isConnecting = this.status === 'connecting';
26469
26502
  const isConnected = this.status === 'connected';
26470
- return (index.h("div", { class: "sl-content", ref: (el) => this.messagesContainer = el ?? null }, this.messages.length === 0 ? (index.h("div", { class: "sl-empty" }, index.h("staylift-orb", { size: 48, primaryColor: this.primaryColor, isActive: false }), index.h("h3", { class: "sl-empty-title" }, isConnecting ? this.t('starting') : isConnected ? this.t('talkOrType') : this.t('emptyTitle')), index.h("p", { class: "sl-empty-desc" }, isConnecting ? this.t('connecting') : isConnected ? this.t('ready') : this.t('emptyDesc')))) : (this.messages.map((message, index$1) => (index.h("div", { class: `sl-msg sl-msg--${message.role}`, key: index$1 }, index.h("div", { class: "sl-msg-row" }, index.h("div", { class: "sl-msg-bubble" }, message.content), message.role === 'assistant' && (index.h("div", { class: "sl-msg-orb" }, index.h("staylift-orb", { size: 24, primaryColor: this.primaryColor, isActive: false })))), message.role === 'assistant' && (index.h("div", { class: "sl-msg-actions" }, index.h("button", { class: "sl-action", onClick: () => this.copyToClipboard(message.content, index$1) }, this.copiedIndex === index$1 ? (index.h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("polyline", { points: "20 6 9 17 4 12" }))) : (index.h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), index.h("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }))))))))))));
26471
- }
26472
- renderFooter(isCallActive, isTransitioning) {
26473
- return (index.h("div", { class: "sl-footer" }, this.showBranding && (index.h("div", { class: "sl-branding" }, index.h("a", { href: "https://staylift.com", target: "_blank", rel: "noopener noreferrer" }, this.t('poweredBy')))), index.h("div", { class: "sl-input-row" }, index.h("input", { type: "text", class: "sl-input", placeholder: this.t('placeholder'), value: this.inputText, onInput: this.handleInputChange, onKeyDown: this.handleInputKeyDown, disabled: isTransitioning }), index.h("button", { class: "sl-btn", onClick: () => this.handleSendText(), disabled: !this.inputText.trim() || isTransitioning }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), index.h("polygon", { points: "22 2 15 22 11 13 2 9 22 2" }))), !isCallActive ? (index.h("button", { class: "sl-btn", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M2 10v3" }), index.h("path", { d: "M6 6v11" }), index.h("path", { d: "M10 3v18" }), index.h("path", { d: "M14 8v7" }), index.h("path", { d: "M18 5v13" }), index.h("path", { d: "M22 10v3" })))) : (index.h("button", { class: "sl-btn sl-btn--end", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91" }), index.h("line", { x1: "22", y1: "2", x2: "2", y2: "22" })))))));
26503
+ return (index.h("div", { class: "sl-content", ref: (el) => this.messagesContainer = el ?? null }, this.messages.length === 0 ? (index.h("div", { class: "sl-empty" }, index.h("staylift-orb", { size: 48, primaryColor: this.primaryColor, isActive: false }), index.h("h3", { class: "sl-empty-title" }, isConnecting ? this.t('starting') : isConnected ? this.t('talkOrType') : this.t('emptyTitle')), index.h("p", { class: "sl-empty-desc" }, isConnecting ? this.t('connecting') : isConnected ? this.t('ready') : this.t('emptyDesc')))) : (this.messages.map((message, index$1) => (index.h("div", { class: `sl-msg sl-msg--${message.role}`, key: index$1 }, index.h("div", { class: "sl-msg-row" }, index.h("div", { class: "sl-msg-bubble" }, message.content)), message.role === 'assistant' && (index.h("div", { class: "sl-msg-actions" }, index.h("button", { class: "sl-action", onClick: () => this.copyToClipboard(message.content, index$1) }, this.copiedIndex === index$1 ? (index.h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("polyline", { points: "20 6 9 17 4 12" }))) : (index.h("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), index.h("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }))))))))))));
26504
+ }
26505
+ renderFooter(isTransitioning) {
26506
+ const isDisconnected = this.status === 'disconnected';
26507
+ const isConnectedText = this.status === 'connected' && this.isTextOnlyMode;
26508
+ const isConnectedVoice = this.status === 'connected' && !this.isTextOnlyMode;
26509
+ // Only show text input when connected in text mode
26510
+ const showTextInput = isConnectedText;
26511
+ return (index.h("div", { class: "sl-footer" }, isDisconnected && (index.h("div", { class: "sl-mode-toggle" }, index.h("button", { class: `sl-mode-btn ${this.selectedMode === 'text' ? 'sl-mode-btn--active' : ''}`, onClick: () => this.selectedMode = 'text' }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })), this.t('modeText')), index.h("button", { class: `sl-mode-btn ${this.selectedMode === 'voice' ? 'sl-mode-btn--active' : ''}`, onClick: () => this.selectedMode = 'voice' }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }), index.h("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), index.h("line", { x1: "12", y1: "19", x2: "12", y2: "23" }), index.h("line", { x1: "8", y1: "23", x2: "16", y2: "23" })), this.t('modeVoice')))), showTextInput && (index.h("div", { class: "sl-input-row" }, index.h("button", { class: "sl-btn sl-btn--end", onClick: () => this.handleTextButton(), disabled: isTransitioning, title: this.t('endText') }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), index.h("line", { x1: "6", y1: "6", x2: "18", y2: "18" }))), index.h("input", { type: "text", class: "sl-input", placeholder: this.t('placeholder'), value: this.inputText, onInput: this.handleInputChange, onKeyDown: this.handleInputKeyDown, disabled: isTransitioning }), index.h("button", { class: "sl-btn", onClick: () => this.handleSendText(), disabled: !this.inputText.trim() || isTransitioning }, index.h("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), index.h("polygon", { points: "22 2 15 22 11 13 2 9 22 2" }))))), this.selectedMode === 'text' && isDisconnected && (index.h("div", { class: "sl-voice-controls" }, index.h("button", { class: "sl-voice-btn", onClick: () => this.handleTextButton(), disabled: isTransitioning }, index.h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" })), isTransitioning ? this.t('connecting') : this.t('startText')))), this.selectedMode === 'voice' && isDisconnected && (index.h("div", { class: "sl-voice-controls" }, index.h("button", { class: "sl-voice-btn", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, index.h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z" }), index.h("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }), index.h("line", { x1: "12", y1: "19", x2: "12", y2: "23" }), index.h("line", { x1: "8", y1: "23", x2: "16", y2: "23" })), isTransitioning ? this.t('connecting') : this.t('startVoice')))), isConnectedVoice && (index.h("div", { class: "sl-voice-controls" }, index.h("button", { class: "sl-voice-btn sl-voice-btn--end", onClick: () => this.handleVoiceButton(), disabled: isTransitioning }, index.h("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2" }, index.h("path", { d: "M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91" }), index.h("line", { x1: "22", y1: "2", x2: "2", y2: "22" })), this.t('endVoice')))), this.showBranding && (index.h("div", { class: "sl-branding" }, index.h("a", { href: "https://staylift.com", target: "_blank", rel: "noopener noreferrer" }, index.h("img", { src: "data:image/png;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBMRXhpZgAATU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAIKADAAQAAAABAAAAIAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8IAEQgAIAAgAwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAMCBAEFAAYHCAkKC//EAMMQAAEDAwIEAwQGBAcGBAgGcwECAAMRBBIhBTETIhAGQVEyFGFxIweBIJFCFaFSM7EkYjAWwXLRQ5I0ggjhU0AlYxc18JNzolBEsoPxJlQ2ZJR0wmDShKMYcOInRTdls1V1pJXDhfLTRnaA40dWZrQJChkaKCkqODk6SElKV1hZWmdoaWp3eHl6hoeIiYqQlpeYmZqgpaanqKmqsLW2t7i5usDExcbHyMnK0NTV1tfY2drg5OXm5+jp6vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAQIAAwQFBgcICQoL/8QAwxEAAgIBAwMDAgMFAgUCBASHAQACEQMQEiEEIDFBEwUwIjJRFEAGMyNhQhVxUjSBUCSRoUOxFgdiNVPw0SVgwUThcvEXgmM2cCZFVJInotIICQoYGRooKSo3ODk6RkdISUpVVldYWVpkZWZnaGlqc3R1dnd4eXqAg4SFhoeIiYqQk5SVlpeYmZqgo6SlpqeoqaqwsrO0tba3uLm6wMLDxMXGx8jJytDT1NXW19jZ2uDi4+Tl5ufo6ery8/T19vf4+fr/2wBDAAICAgICAgMCAgMFAwMDBQYFBQUFBggGBgYGBggKCAgICAgICgoKCgoKCgoMDAwMDAwODg4ODg8PDw8PDw8PDw//2wBDAQICAgQEBAcEBAcQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/2gAMAwEAAhEDEQAAAfiPo6T9gTw/lbxH1R8xAWn0XwXsuXwv2J+cvsHnGXj/AP/aAAgBAQABBQJ2O0btujuvDXiGygZS/B6bCDwr9Yvii93+QRKL5RL8C7HvO8yb3t22f0cO3xxpg2+r22+3Pbre83K/u0XUoD//2gAIAQMRAT8BwnczyRDk/enDhl9/j+j8l+8kutyx9kUA/wD/2gAIAQIRAT8B0OVnO3//2gAIAQEABj8CajttnLc48eWgqo1XV5t08MMftLWggDvtxssUw8hKlH+VTrr9rTFZoWnZ41URJQhMyx+avw8nw7SWKbmWDaf+BASqiVfyR8S7mwljSi1jhISnyTQdNPtfB8GLe1uVxRjXFLwurhcqR5E6dv/EADMQAQADAAICAgICAwEBAAACCwERACExQVFhcYGRobHB8NEQ4fEgMEBQYHCAkKCwwNDg/9oACAEBAAE/IZsYHlgL2hFFykgNxq/8i4rZQAQHMl53M1Hu+FPRCE0H7+OCoZU37nAvVx5U656pS9EIT8wQj3YdyrMaSOzIAl5fmjVxMtl8cUFL/9oADAMBAAIRAxEAABD2kov/xAAzEQEBAQADAAECBQUBAQABAQkBABEhMRBBUWEgcfCRgaGx0cHh8TBAUGBwgJCgsMDQ4P/aAAgBAxEBPxDT3M5sJdI+Ma/bs4t0zZy8v7fT4v/aAAgBAhEBPxBPA2RcX//aAAgBAQABPxCXMUdNxPzQSDHSzRnYYmQIwJUDyoU5qBhvVIgVuSPBo61iI8V5sIdq5DxIPbJQrnR+qCA5rryHQleAAiIk2UhJuwnSp6JKa/KjgYm/NRmPignYSnymSrtfVNewxg4eEjzFnxEX/9k=", alt: "Staylift", class: "sl-branding-logo" }), this.t('poweredBy'))))));
26474
26512
  }
26475
26513
  get el() { return index.getElement(this); }
26476
26514
  };
@@ -19,7 +19,7 @@ var patchBrowser = () => {
19
19
 
20
20
  patchBrowser().then(async (options) => {
21
21
  await appGlobals.globalScripts();
22
- return index.bootstrapLazy([["staylift-orb.cjs",[[1,"staylift-orb",{"inputVolume":[2,"input-volume"],"outputVolume":[2,"output-volume"],"isActive":[4,"is-active"],"primaryColor":[1,"primary-color"],"size":[8],"animationFrame":[32]}]]],["staylift-widget.cjs",[[1,"staylift-widget",{"agentId":[1,"agent-id"],"positionX":[1,"position-x"],"positionY":[1,"position-y"],"variant":[1],"mode":[1],"primaryColor":[1,"primary-color"],"brandName":[1,"brand-name"],"language":[1],"autoExpand":[4,"auto-expand"],"showBranding":[4,"show-branding"],"avatarUrl":[1,"avatar-url"],"fabPrompt":[1,"fab-prompt"],"fabButtonText":[1,"fab-button-text"],"status":[32],"isExpanded":[32],"termsAccepted":[32],"errorMessage":[32],"inputVolume":[32],"outputVolume":[32],"messages":[32],"inputText":[32],"copiedIndex":[32],"startConversation":[64],"endConversation":[64],"getStatus":[64],"sendMessage":[64]}]]]], options);
22
+ return index.bootstrapLazy([["staylift-orb.cjs",[[1,"staylift-orb",{"inputVolume":[2,"input-volume"],"outputVolume":[2,"output-volume"],"isActive":[4,"is-active"],"primaryColor":[1,"primary-color"],"size":[8],"animationFrame":[32]}]]],["staylift-widget.cjs",[[1,"staylift-widget",{"agentId":[1,"agent-id"],"positionX":[1,"position-x"],"positionY":[1,"position-y"],"variant":[1],"mode":[1],"primaryColor":[1,"primary-color"],"brandName":[1,"brand-name"],"language":[1],"autoExpand":[4,"auto-expand"],"showBranding":[4,"show-branding"],"avatarUrl":[1,"avatar-url"],"fabPrompt":[1,"fab-prompt"],"fabButtonText":[1,"fab-button-text"],"status":[32],"isExpanded":[32],"termsAccepted":[32],"errorMessage":[32],"inputVolume":[32],"outputVolume":[32],"messages":[32],"inputText":[32],"copiedIndex":[32],"selectedMode":[32],"startConversation":[64],"endConversation":[64],"getStatus":[64],"sendMessage":[64]}]]]], options);
23
23
  });
24
24
 
25
25
  exports.setNonce = index.setNonce;
@@ -278,18 +278,16 @@
278
278
  .sl-content {
279
279
  flex: 1;
280
280
  overflow-y: auto;
281
- padding: 24px;
281
+ padding: 12px 24px 24px;
282
282
  display: flex;
283
283
  flex-direction: column;
284
284
  gap: 8px;
285
285
  }
286
286
 
287
287
  .sl-empty {
288
- flex: 1;
289
288
  display: flex;
290
289
  flex-direction: column;
291
290
  align-items: center;
292
- justify-content: center;
293
291
  text-align: center;
294
292
  gap: 12px;
295
293
  }
@@ -307,6 +305,84 @@
307
305
  color: var(--sl-muted);
308
306
  }
309
307
 
308
+ /* ============ MODE TOGGLE ============ */
309
+
310
+ .sl-mode-toggle {
311
+ display: flex;
312
+ gap: 8px;
313
+ margin-bottom: 12px;
314
+ padding: 4px;
315
+ background: var(--sl-surface);
316
+ border-radius: 10px;
317
+ }
318
+
319
+ .sl-mode-btn {
320
+ display: flex;
321
+ align-items: center;
322
+ gap: 6px;
323
+ padding: 8px 16px;
324
+ border: none;
325
+ border-radius: 8px;
326
+ background: transparent;
327
+ color: var(--sl-muted);
328
+ font-size: 13px;
329
+ font-weight: 500;
330
+ cursor: pointer;
331
+ transition: all 0.2s;
332
+ }
333
+
334
+ .sl-mode-btn:hover {
335
+ color: var(--sl-text);
336
+ }
337
+
338
+ .sl-mode-btn--active {
339
+ background: var(--sl-bg);
340
+ color: var(--sl-text);
341
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
342
+ }
343
+
344
+ /* ============ VOICE CONTROLS ============ */
345
+
346
+ .sl-voice-controls {
347
+ display: flex;
348
+ justify-content: center;
349
+ }
350
+
351
+ .sl-voice-btn {
352
+ display: flex;
353
+ align-items: center;
354
+ justify-content: center;
355
+ gap: 10px;
356
+ width: 100%;
357
+ padding: 14px 24px;
358
+ border: none;
359
+ border-radius: 12px;
360
+ background: var(--sl-primary);
361
+ color: white;
362
+ font-size: 14px;
363
+ font-weight: 500;
364
+ cursor: pointer;
365
+ transition: all 0.2s;
366
+ }
367
+
368
+ .sl-voice-btn:hover:not(:disabled) {
369
+ filter: brightness(1.1);
370
+ transform: translateY(-1px);
371
+ }
372
+
373
+ .sl-voice-btn:disabled {
374
+ opacity: 0.6;
375
+ cursor: not-allowed;
376
+ }
377
+
378
+ .sl-voice-btn--end {
379
+ background: var(--sl-danger);
380
+ }
381
+
382
+ .sl-voice-btn--end:hover:not(:disabled) {
383
+ filter: brightness(1.1);
384
+ }
385
+
310
386
  /* ============ MESSAGES ============ */
311
387
 
312
388
  .sl-msg {
@@ -351,15 +427,6 @@
351
427
  border-bottom-left-radius: 4px;
352
428
  }
353
429
 
354
- .sl-msg-orb {
355
- width: 24px;
356
- height: 24px;
357
- border-radius: 50%;
358
- border: 1px solid var(--sl-border);
359
- overflow: hidden;
360
- flex-shrink: 0;
361
- }
362
-
363
430
  .sl-msg-actions {
364
431
  display: flex;
365
432
  gap: 4px;
@@ -395,10 +462,14 @@
395
462
 
396
463
  .sl-branding {
397
464
  text-align: center;
398
- margin-bottom: 12px;
465
+ margin-top: 8px;
466
+ padding-top: 8px;
399
467
  }
400
468
 
401
469
  .sl-branding a {
470
+ display: inline-flex;
471
+ align-items: center;
472
+ gap: 6px;
402
473
  font-size: 11px;
403
474
  color: var(--sl-muted);
404
475
  text-decoration: none;
@@ -410,6 +481,13 @@
410
481
  opacity: 1;
411
482
  }
412
483
 
484
+ .sl-branding-logo {
485
+ width: 16px;
486
+ height: 16px;
487
+ border-radius: 3px;
488
+ object-fit: cover;
489
+ }
490
+
413
491
  .sl-input-row {
414
492
  display: flex;
415
493
  align-items: center;